diff --git a/README.md b/README.md index d90ef3c..5fd2070 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ ngMeteor - [Creating and inserting template views](https://github.com/loneleeandroo/ngMeteor#creating-and-inserting-template-views) - [Routing](https://github.com/loneleeandroo/ngMeteor#routing) - [Module Injection](https://github.com/loneleeandroo/ngMeteor#module-injection) +- [Flexible module bootstrapping](https://github.com/loneleeandroo/ngMeteor#flexistrap) ### New Data-Binding to avoid conflict To prevent conflicts with Handlebars, ngMeteor has changed the default AngularJS data bindings from <code>{{foo}}</code> to <code>[[foo]]</code>. For example: @@ -165,14 +166,12 @@ You can render this template using handlebars as you would for any other Meteor {{> foo}} -Templates will also be added to the $templateCache of the ngMeteor angular module. To invoke the template in AngularJS you could use ng-view and specify the template in the $templateCache when defining your routes using the $routeProvider or your could use the ng-template directive to render your template like this: +Meteor templates can also be rendered in your Angular views using the ng-template directive like this: <ANY ng-template="foo"></ANY> <!--Add the ng-controller attribute if you want to load a controller at the same time.--> <ANY ng-template="foo" ng-controller="fooCtrl"></ANY> - -Templates with names starting with an underscore, for example "_foo", will not be put into the $templateCache, so you will not be able to access those templates using ng-template, ng-include or ng-view. ### Routing The [ngRoute](http://docs.angularjs.org/api/ngRoute) module developed by the AngularJS team is included in ngMeteor, which will satisfy those with simple routing needs. For example, if you want to call a template called 'foo' and a controller called 'TodoCtrl' when someone lands on your home page you would define your route like this: @@ -212,10 +211,38 @@ Using this method, additional functionality has been provided to ngMeteor in the Feel free to make ngMeteor module smart packages, and please contact [loneleeandroo](https://github.com/loneleeandroo) if you would like your package to be listed here as well. +### Flexible module bootstrapping (flexistrap) + +By default, the ngMeteor angular module is bootstrapped into the top level document on all routes. Since modules cannot be nested in angular, should you want to have parts of your DOM controlled by different modules you can do so using a flexible bootstrapping mechanism called flexistrap which is built in to the ngMeteor package. To use this mechanism you call ngMeteor.addFlexistrap() and provide a jQuery-like selector and an angular module name (or array of module names) as parameters. Fleximodel will then instruct angular to bootstrap the modules with the name (or names, if you provided an array of names) into the DOM elements matching the provided selector. + +Optionally, you can provide a path string (or array of path strings) as the 3rd parameter to addFlexistrap. This will apply your modules to the selected DOM elements only when the path(s) identified by the provided strings are being rendered. You can provide a value of '*' to apply your flexistrap configuration to all paths (this is the default behavior if this parameter is not provided). + +Optionally, you can provide a boolean as the 4th parameter. If this value is true, the configuration you provide will replace any existing flexistrap configuration for the given path(s) and selector. If False, it will ONLY update EXISTING configurations. If not supplied or null, it will update existing configurations or add new ones if they don't already exist. + +Example: + +Define two new modules: 'myModule' and 'yourModule': + + myCoolModule = angular.module('myModule',[]); + yourCoolModule = angular.module('yourModule',[]); + + +Remove existing (default) bootstrapping of ngMeteor on the toplevel document: + + ngMeteor.removeFlexistrap(); // removes all existing flexistrap configurations for all paths and selectors. + +Bootstrap div with id 'xxx' with new module named 'myModule' + + ngMeteor.addFlexistrap('#xxx', 'myModule') + +Bootstrap div with id 'yyy' with both the new module 'yourModule' and the original 'ngMeteor' module: + + ngMeteor.addFlexistrap('#yyy', ['yourModule', 'ngMeteor']) + <!--- ### Dynamic routing -Routes will automaticlly be created based on a template's name, however, you can override the dynamic routes by manually assigning a route using $routeProvider. The route will load that template and conditionally load a controller with the same name if it exists. You can prevent a template from creating a route by adding a "_" infront of the template name. Based on the URL, this is how you should name your templates: +Routes will automaticlly be created based on a template's name, however, you can override the dynamic routes by manually assigning a route using $routeProvider. The route will load that template and conditionally load a controller with the same name if it exists. You can prevent a template from creating a route by adding a "_" in front of the template name. Based on the URL, this is how you should name your templates: | URL | Template / Controller name | $routeParams | | :-------------------------------- | :----------------------------- | :----------- | diff --git a/lib/bindonce.js b/lib/bindonce.js new file mode 100644 index 0000000..3a6f43d --- /dev/null +++ b/lib/bindonce.js @@ -0,0 +1,321 @@ +(function () { + "use strict"; + /** + * Bindonce - Zero watches binding for AngularJs + * @version v0.3.1 + * @link https://github.com/Pasvaz/bindonce + * @author Pasquale Vazzana <pasqualevazzana@gmail.com> + * @license MIT License, http://www.opensource.org/licenses/MIT + */ + + var bindonceModule = angular.module('pasvaz.bindonce', []); + + bindonceModule.directive('bindonce', function () + { + var toBoolean = function (value) + { + if (value && value.length !== 0) + { + var v = angular.lowercase("" + value); + value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]'); + } + else + { + value = false; + } + return value; + }; + + var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); + if (isNaN(msie)) + { + msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); + } + + var bindonceDirective = + { + restrict: "AM", + controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate) + { + var showHideBinder = function (elm, attr, value) + { + var show = (attr === 'show') ? '' : 'none'; + var hide = (attr === 'hide') ? '' : 'none'; + elm.css('display', toBoolean(value) ? show : hide); + }; + var classBinder = function (elm, value) + { + if (angular.isObject(value) && !angular.isArray(value)) + { + var results = []; + angular.forEach(value, function (value, index) + { + if (value) results.push(index); + }); + value = results; + } + if (value) + { + elm.addClass(angular.isArray(value) ? value.join(' ') : value); + } + }; + var transclude = function (transcluder, scope) + { + transcluder.transclude(scope, function (clone) + { + var parent = transcluder.element.parent(); + var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1]; + var parentNode = parent && parent[0] || afterNode && afterNode.parentNode; + var afterNextSibling = (afterNode && afterNode.nextSibling) || null; + angular.forEach(clone, function (node) + { + parentNode.insertBefore(node, afterNextSibling); + }); + }); + }; + + var ctrl = + { + watcherRemover: undefined, + binders: [], + group: $attrs.boName, + element: $element, + ran: false, + + addBinder: function (binder) + { + this.binders.push(binder); + + // In case of late binding (when using the directive bo-name/bo-parent) + // it happens only when you use nested bindonce, if the bo-children + // are not dom children the linking can follow another order + if (this.ran) + { + this.runBinders(); + } + }, + + setupWatcher: function (bindonceValue) + { + var that = this; + this.watcherRemover = $scope.$watch(bindonceValue, function (newValue) + { + if (newValue === undefined) return; + that.removeWatcher(); + that.checkBindonce(newValue); + }, true); + }, + + checkBindonce: function (value) + { + var that = this, promise = (value.$promise) ? value.$promise.then : value.then; + // since Angular 1.2 promises are no longer + // undefined until they don't get resolved + if (typeof promise === 'function') + { + promise(function () + { + that.runBinders(); + }); + } + else + { + that.runBinders(); + } + }, + + removeWatcher: function () + { + if (this.watcherRemover !== undefined) + { + this.watcherRemover(); + this.watcherRemover = undefined; + } + }, + + runBinders: function () + { + while (this.binders.length > 0) + { + var binder = this.binders.shift(); + if (this.group && this.group != binder.group) continue; + var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value); + switch (binder.attr) + { + case 'boIf': + if (toBoolean(value)) + { + transclude(binder, binder.scope.$new()); + } + break; + case 'boSwitch': + var selectedTranscludes, switchCtrl = binder.controller[0]; + if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?'])) + { + binder.scope.$eval(binder.attrs.change); + angular.forEach(selectedTranscludes, function (selectedTransclude) + { + transclude(selectedTransclude, binder.scope.$new()); + }); + } + break; + case 'boSwitchWhen': + var ctrl = binder.controller[0]; + ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []); + ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element }); + break; + case 'boSwitchDefault': + var ctrl = binder.controller[0]; + ctrl.cases['?'] = (ctrl.cases['?'] || []); + ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element }); + break; + case 'hide': + case 'show': + showHideBinder(binder.element, binder.attr, value); + break; + case 'class': + classBinder(binder.element, value); + break; + case 'text': + binder.element.text(value); + break; + case 'html': + binder.element.html(value); + break; + case 'style': + binder.element.css(value); + break; + case 'src': + binder.element.attr(binder.attr, value); + if (msie) binder.element.prop('src', value); + break; + case 'attr': + angular.forEach(binder.attrs, function (attrValue, attrKey) + { + var newAttr, newValue; + if (attrKey.match(/^boAttr./) && binder.attrs[attrKey]) + { + newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + newValue = binder.scope.$eval(binder.attrs[attrKey]); + binder.element.attr(newAttr, newValue); + } + }); + break; + case 'href': + case 'alt': + case 'title': + case 'id': + case 'value': + binder.element.attr(binder.attr, value); + break; + } + } + this.ran = true; + } + }; + + return ctrl; + }], + + link: function (scope, elm, attrs, bindonceController) + { + var value = attrs.bindonce && scope.$eval(attrs.bindonce); + if (value !== undefined) + { + bindonceController.checkBindonce(value); + } + else + { + bindonceController.setupWatcher(attrs.bindonce); + elm.bind("$destroy", bindonceController.removeWatcher); + } + } + }; + + return bindonceDirective; + }); + + angular.forEach( + [ + { directiveName: 'boShow', attribute: 'show' }, + { directiveName: 'boHide', attribute: 'hide' }, + { directiveName: 'boClass', attribute: 'class' }, + { directiveName: 'boText', attribute: 'text' }, + { directiveName: 'boBind', attribute: 'text' }, + { directiveName: 'boHtml', attribute: 'html' }, + { directiveName: 'boSrcI', attribute: 'src', interpolate: true }, + { directiveName: 'boSrc', attribute: 'src' }, + { directiveName: 'boHrefI', attribute: 'href', interpolate: true }, + { directiveName: 'boHref', attribute: 'href' }, + { directiveName: 'boAlt', attribute: 'alt' }, + { directiveName: 'boTitle', attribute: 'title' }, + { directiveName: 'boId', attribute: 'id' }, + { directiveName: 'boStyle', attribute: 'style' }, + { directiveName: 'boValue', attribute: 'value' }, + { directiveName: 'boAttr', attribute: 'attr' }, + + { directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 }, + { directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } }, + { directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' }, + { directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' } + ], + function (boDirective) + { + var childPriority = 200; + return bindonceModule.directive(boDirective.directiveName, function () + { + var bindonceDirective = + { + priority: boDirective.priority || childPriority, + transclude: boDirective.transclude || false, + terminal: boDirective.terminal || false, + require: ['^bindonce'].concat(boDirective.require || []), + controller: boDirective.controller, + compile: function (tElement, tAttrs, transclude) + { + return function (scope, elm, attrs, controllers) + { + var bindonceController = controllers[0]; + var name = attrs.boParent; + if (name && bindonceController.group !== name) + { + var element = bindonceController.element.parent(); + bindonceController = undefined; + var parentValue; + + while (element[0].nodeType !== 9 && element.length) + { + if ((parentValue = element.data('$bindonceController')) + && parentValue.group === name) + { + bindonceController = parentValue; + break; + } + element = element.parent(); + } + if (!bindonceController) + { + throw new Error("No bindonce controller: " + name); + } + } + + bindonceController.addBinder( + { + element: elm, + attr: boDirective.attribute || boDirective.directiveName, + attrs: attrs, + value: attrs[boDirective.directiveName], + interpolate: boDirective.interpolate, + group: name, + transclude: transclude, + controller: controllers.slice(1), + scope: scope + }); + }; + } + }; + + return bindonceDirective; + }); + }) +})(); diff --git a/modules/ngMeteor-template.js b/modules/ngMeteor-template.js index 785b6b5..beed613 100644 --- a/modules/ngMeteor-template.js +++ b/modules/ngMeteor-template.js @@ -1,26 +1,14 @@ var ngMeteorTemplate = angular.module('ngMeteor.template', []); -ngMeteorTemplate.run(['$templateCache', - function($templateCache) { - angular.forEach(Template, function(template, name){ - if(name.charAt(0) != "_"){ - var templateString = HTML.toHTML(template.render()); - $templateCache.put(name, templateString); - } - }); - } -]); - -ngMeteorTemplate.directive('ngTemplate', ['$templateCache', '$compile', - function($templateCache, $compile) { +ngMeteorTemplate.directive('ngTemplate', ['$compile', + function($compile) { return { restrict: 'AE', scope: true, link: function(scope, element, attributes) { - var name = attributes.ngTemplate || attributes.name, - template = $templateCache.get(name); - if(angular.isDefined(template)){ - element.html(template); + var name = attributes.ngTemplate || attributes.name + if(name && Template[name]){ + element.html(ngMeteor.renderTemplateInContext(name, scope)); element.replaceWith($compile(element.html())(scope)); } else{ console.error("ngMeteor: There is no template with the name '" + attributes.ngTemplate + "'"); @@ -30,22 +18,126 @@ ngMeteorTemplate.directive('ngTemplate', ['$templateCache', '$compile', } ]); -// Re-compiles template when rendering with Iron-Router +// Configure Angular to automatically re-compile the template when rendering with Iron-Router angular.element(document).ready(function() { + + // Apply the default flexistrap configuration (to match the old behaviour of the module pre-flexistrap + ngMeteor.addFlexistrap('body', 'ngMeteor', '*', false); + if(Package['iron-router']){ + Router.scopes = {}; var oldRun = Router.run; Router.run = function() { + var routePath = arguments[0].path; var runResult = oldRun.apply(this, arguments); - key = this._currentController.template - var oldRendered = Template[key].rendered; - Template[key].rendered = function(){ - angular.element(document).injector().invoke(['$compile', '$document', '$rootScope', function($compile, $document, $rootScope){ - $compile($document)($rootScope); - $rootScope.$digest(); - oldRendered.apply(this, arguments); - }]); - Template[key].rendered = oldRendered; - } + var templateKey = this._currentController.route.options.template ? this._currentController.route.options.template : this._currentController.route.name; + var oldRendered = Template[templateKey].rendered; + Template[templateKey].rendered = function(){ + Deps.nonreactive(function(){ + // Apply flexistrap if one has been configured. + var map = ngMeteor.getFlexistrap(templateKey); + if(!Router.scopes[routePath]){ + Router.scopes[routePath] = []; + }; + // console.log("Router scopes"); + // console.log(Router.scopes); + // console.log("--------------"); + if(routePath != '/signup'){ + var myScopes = Router.scopes['/signup']; + Router.scopes['/signup'] = []; + _.each(myScopes, function(theScope){ + var killScope = function(aScope){ + var currentChild = aScope.$$childHead; + if(currentChild){ + while(currentChild) { + var nextChild = currentChild.$$nextSibling; + killScope(currentChild); + currentChild = nextChild; + // nextChild = null; + } + currentChild = null; + } + + if(aScope.$$watchers) { + aScope.$$watchers.length = 0; + } + + + +// aScope.$$watchers = null; + aScope.model = null; + aScope.field = null; + aScope.modelId = null; + aScope.xid = null; + aScope.flexiModelname = null; + delete aScope.properties; + delete aScope.fieldPersonEmailsForm; + delete aScope.unwrapped; + delete aScope.model; + delete aScope.field; + delete aScope.modelId; + delete aScope.xid; + delete aScope.flexiModelname; + if(aScope.options){ + aScope.options.length = 0; + delete aScope.options; + } + if(aScope.collection){ + aScope.collection.length = 0; + delete aScope.collection; + } + + delete aScope.personForm; + delete aScope.fieldPersonHomeAddressesForm; + aScope.$destroy(); + aScope.$$listeners = {}; + aScope.$$listenerCount = {}; + + aScope.__proto__ = {}; + + console.log('destroying: ' + aScope.$id) + }; + killScope(theScope); + }); + if(myScopes){ + // Router.scopes['/signup'].length = 0; + myScopes.length = 0; + myScopes = null; + } + + } + $.each( map, function( key, value ) { + var eleArray = $(key); + _.each(eleArray, function(element){ + var moduleList = _.clone(value); + if (!element.bootstrapped && !angular.element(element).injector()){ + console.log('bootstrapping') + var inj = angular.bootstrap(element, moduleList); + inj.invoke(['$rootScope', function($rootScope){Router.scopes[routePath].push($rootScope)}]); + element.bootstrapped = true; + }else { + console.log("did not bootstrap"); + // console.log(element); + // console.log("-----------"); + angular.element(element).injector().invoke( + ['$compile', '$rootScope', function ($compile, $rootScope) { + if($rootScope.$$childHead){ + $rootScope.$$childHead.$destroy(); + } + $compile(element)($rootScope); + $rootScope.$digest(); + }] + ) + + } + }); + }); + if(oldRendered && typeof oldRendered == 'function') { + oldRendered.apply(this, arguments); + }; + Template[templateKey].rendered = oldRendered; + }); + }; return runResult; }; } diff --git a/ngMeteor.js b/ngMeteor.js index 3142480..41441ba 100644 --- a/ngMeteor.js +++ b/ngMeteor.js @@ -1,4 +1,6 @@ -// Define ngMeteor and its dependencies +/** + * Define the ngMeteor angular module and its dependencies + */ ngMeteor = angular.module('ngMeteor', [ 'ngMeteor.collections', 'ngMeteor.template', @@ -11,17 +13,95 @@ ngMeteor = angular.module('ngMeteor', [ 'ngSanitize' ]); +/** + * Define the 'holder' for flexistrap (flexible bootstrap) configurations. + * The intent is to have this keyed by the 'path' (in case you have multiple routes) with values being another hash object + * with keys being jQuery-like selectors and values being arrays of module names to be bootstrapped into elements matching + * those selectors. + */ +ngMeteor.flexistrapRegister = {}; + +/** + * Remove a flexistrap configuration. + * @param selector: JQuery-style selector to select the DOM element(s) to which the flexistrap operation will apply. If not supplied, it defaults to removing all configurations for the specified path(s). + * @param angularModuleNameOrArray: The name (or array of names) of the module(s) to be added to the flexistrop configuration for the selected element(s). If not provided, it will + * @param pathOrArray: The path string (or array of path strings) for which, when rendered, we wish to have a flexible bootstrap configuration (flexistrap) applied. + * If not provided, it applies to all paths specifications including '*'. + * If you only want it to affect the '*' flexistrap configuration, ensure you provide '*' as the value for this parameter. + * @returns: ngMeteor to allow easy method chaining. + */ +ngMeteor.removeFlexistrap = function(selector, angularModuleNameOrArray, pathOrArray){ + return ngMeteor.addFlexistrap(selector, null, pathOrArray, true); +}; + +/** + * Add or update a flexistrap configuration. + * If replaceFlag is true, replace any existing flexistrap configuration for the provided <selector>. + * If false, only insert if the configuration doesn't already exist, otherwise ignore. + * If null, add to existing or create new. + * @param selector: JQuery-style selector to select the DOM element(s) to which the flexistrap operation will apply. If null, it defaults to an empty array. + * @param angularModuleNameOrArray: The name (or array of names) of the module(s) to be added to the flexistrop configuration for the selected element(s). + * If null, it defaults to an empty array. + * @param pathOrArray: (optional) The url (or fragment) which, when rendered, we wish to have this flexible bootstrap configuration applied. + * Note that a value of '*' will add the provided configuration to all path configurations (as a merge function at run time). + * If null, it will default to '*'. + * @param replaceFlag: (optional) If true, replace existing flexistrap configuration. If false, only add it if one has + * not yet been registered for this selector and path. If null or not supplied, add the provided module name or names + * to the existing flexistrap configuration or create a new one to bootstrap with the provided module name(s). + * @returns: ngMeteor to allow easy method chaining. + */ +ngMeteor.addFlexistrap = function(selector, angularModuleNameOrArray, pathOrArray, replaceFlag){ + var moduleNames = angularModuleNameOrArray ? (_.isArray(angularModuleNameOrArray) ? angularModuleNameOrArray : [angularModuleNameOrArray]) : []; + var paths = pathOrArray ? (_.isArray(pathOrArray) ? pathOrArray : [pathOrArray]) : ['*']; + + _.each(paths, function(ele){ + var pathEntry = this[ele] ? this[ele] : null; + if(_.isUndefined(replaceFlag) || _.isNull(replaceFlag)){ + if(_.isUndefined(pathEntry) || _.isNull(pathEntry)){ + pathEntry = {}; + } + }else{ + pathEntry = {}; + if(replaceFlag == false){ + return; + } + } + var moduleArray = selector ? (pathEntry[selector] ? pathEntry[selector] : []) : []; + moduleArray = _.union(moduleArray, moduleNames); + pathEntry[selector] = moduleArray; + this[ele] = pathEntry; + return ngMeteor; + }, ngMeteor.flexistrapRegister); +}; + +/** + * Retrieve the flexible bootstrap configuration map for the provided path. + * @param path: The routing path being rendered. + * @returns A map object with keys being jQuery-like selector strings and values being arrays of module names which + * will be bootstrapped by angular into the elements obtained from the provided selector. + */ +ngMeteor.getFlexistrap = function(path){ + var flexistrap = path ? ngMeteor.flexistrapRegister[path] : null; + flexistrap = flexistrap ? flexistrap : {}; + var all = ngMeteor.flexistrapRegister['*'] ? ngMeteor.flexistrapRegister['*'] : {}; + return _.extend(flexistrap, all); +}; + +/** Utility method for use with the Blaze rendering engine. + * Render the named template as HTML using the 'context' param as the source for variable substitution. + */ +ngMeteor.renderTemplateInContext = function(templateOrName, context) { + var template = (typeof templateOrName === 'string') ? Template[templateOrName] : templateOrName; + var div = document.createElement('div'); + var component = UI.renderWithData(template, context); + UI.insert(component, div); + return div.innerHTML; +}; + // Change the data-bindings from {{foo}} to [[foo]] ngMeteor.config(['$interpolateProvider', - function ($interpolateProvider) { - $interpolateProvider.startSymbol('[['); - $interpolateProvider.endSymbol(']]'); - } + function ($interpolateProvider) { + $interpolateProvider.startSymbol('[['); + $interpolateProvider.endSymbol(']]'); + } ]); - -// Manual initilisation of ngMeteor -angular.element(document).ready(function() { - if (!angular.element(document).injector()){ - angular.bootstrap(document, ['ngMeteor']); - } -}); diff --git a/package.js b/package.js index 7835c59..fee6523 100644 --- a/package.js +++ b/package.js @@ -1,29 +1,34 @@ Package.describe({ - summary: "The simplest no-conflict way to use AngularJS with Meteor, Meteorite and Atmosphere Smart Packages." + summary: "The simplest, no-conflict way to use AngularJS with Meteor, Meteorite and Atmosphere Smart Packages." }); Package.on_use(function (api) { - // Exports the ngMeteor package scope - api.export('ngMeteor', 'client'); - + api.use('templating', 'client'); + api.use('underscore', 'client'); + // Files to load in Client only. api.add_files([ // Lib Files - 'lib/angular.js', - 'lib/angular-csp.css', - 'lib/angular-animate.js', - 'lib/angular-cookies.js', - //'lib/angular-mocks.js', - 'lib/angular-resource.js', - 'lib/angular-route.js', - //'lib/angular-scenario.js', - 'lib/angular-sanitize.js', - 'lib/angular-touch.js', - 'lib/angular-hash-key-copier.js', + 'lib/angular.js', + 'lib/bindonce.js', + 'lib/angular-csp.css', + 'lib/angular-animate.js', + 'lib/angular-cookies.js', + 'lib/angular-resource.js', + 'lib/angular-route.js', + 'lib/angular-sanitize.js', + 'lib/angular-touch.js', + 'lib/angular-hash-key-copier.js', + // Module Files - 'modules/ngMeteor-collections.js', - 'modules/ngMeteor-template.js', + 'modules/ngMeteor-collections.js', + 'modules/ngMeteor-template.js', + // Finally load ngMeteor File - 'ngMeteor.js' + 'ngMeteor.js' + ], 'client'); + + // Exports the ngMeteor package scope + api.export('ngMeteor', 'client'); });