From 5143e7bf065a3cbdf8400cf095b653d51bc83b8f Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 6 Jan 2012 18:10:47 -0800 Subject: feat(module): new module loader --- src/Angular.js | 120 ++++++++++++++++++------------- src/AngularPublic.js | 80 ++++++++++----------- src/Injector.js | 48 ++++++++----- src/angular-bootstrap.js | 9 +-- src/angular-mocks.js | 63 +++++++++-------- src/angular.suffix | 4 +- src/directives.js | 27 ------- src/loader.js | 167 ++++++++++++++++++++++++++++++++++++++++++++ src/loader.prefix | 7 ++ src/loader.suffix | 20 ++++++ src/scenario/Application.js | 12 ++-- src/scenario/angular.suffix | 12 +++- 12 files changed, 393 insertions(+), 176 deletions(-) create mode 100644 src/loader.js create mode 100644 src/loader.prefix create mode 100644 src/loader.suffix (limited to 'src') diff --git a/src/Angular.js b/src/Angular.js index c14c6ea0..74443bd1 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -90,7 +90,7 @@ var $$scope = '$scope', /** @name angular */ angular = window.angular || (window.angular = {}), - angularModule = angular.module || (angular.module = {}), + angularModule = null, /** @name angular.markup */ angularTextMarkup = extensionMap(angular, 'markup'), /** @name angular.attrMarkup */ @@ -173,6 +173,15 @@ function forEachSorted(obj, iterator, context) { } +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) { iteratorFn(key, value) }; +} + /** * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric * characters such as '012ABC'. The reason why we are not using simply a number counter is that @@ -808,37 +817,73 @@ function encodeUriQuery(val, pctEncodeSpaces) { /** * @ngdoc directive - * @name angular.directive.ng:autobind - * @element script + * @name angular.directive.ng:app * - * @TODO ng:autobind is not a directive!! it should be documented as bootstrap parameter in a - * separate bootstrap section. - * @TODO rename to ng:autobind to ng:autoboot + * @element ANY + * @param {angular.Module} module on optional application + * {@link angular.module module} name to load. * * @description - * Technically, ng:autobind is not a directive; it is an Angular bootstrap parameter that can act - * as a directive. It must exist in the script used to boot Angular and can be used only one time. - * For details on bootstrapping Angular, see {@link guide/dev_guide.bootstrap Initializing Angular} - * in the Angular Developer Guide. * - * `ng:autobind` with no parameters tells Angular to compile and manage the whole page. + * Use this directive to auto-bootstrap on application. Only + * one directive can be used per HTML document. The directive + * designates the root of the application and is typically placed + * ot the root of the page. + * + * In the example below if the `ng:app` directive would not be placed + * on the `html` element then the document would not be compiled + * and the `{{ 1+2 }}` would not be resolved to `3`. * - * `ng:autobind="[root element ID]"` tells Angular to compile and manage part of the document, - * starting at "root element ID". + * `ng:app` is the easiest way to bootstrap an application. + * + + + I can add: 1 + 2 = {{ 1+2 }} + + * */ -function angularInit(config, document){ - var autobind = config.autobind; - - if (autobind) { - var modules = []; - forEach((config.modules || '').split(','), function(module){ - module = trim(module); - if (module) { - modules.push(module); +function angularInit(element, bootstrap) { + var elements = [element], + appElement, + module, + names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], + NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; + + function append(element) { + element && elements.push(element); + } + + forEach(names, function(name) { + names[name] = true; + append(document.getElementById(name)); + name = name.replace(':', '\\:'); + if (element.querySelectorAll) { + forEach(element.querySelectorAll('.' + name), append); + forEach(element.querySelectorAll('.' + name + '\\:'), append); + forEach(element.querySelectorAll('[' + name + ']'), append); + }; + }); + + forEach(elements, function(element) { + if (!appElement) { + var className = ' ' + element.className + ' '; + var match = NG_APP_CLASS_REGEXP.exec(className); + if (match) { + appElement = element; + module = (match[2] || '').replace(/\s+/g, ','); + } else { + forEach(element.attributes, function(attr) { + if (!appElement && names[attr.name]) { + appElement = element; + module = attr.value; + } + }); } - }); - bootstrap(jqLite(isString(autobind) ? document.getElementById(autobind) : document), modules); + } + }); + if (appElement) { + bootstrap(appElement, module ? [module] : []); } } @@ -854,9 +899,10 @@ function angularInit(config, document){ * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} */ function bootstrap(element, modules) { + element = jqLite(element); modules = modules || []; - modules.unshift(ngModule); - createInjector(modules, angularModule).invoke(null, + modules.unshift('ng'); + createInjector(modules).invoke(null, ['$rootScope', '$compile', '$injector', function(scope, compile, injector){ scope.$apply(function() { element.data('$injector', injector); @@ -866,28 +912,6 @@ function bootstrap(element, modules) { ); } -function angularJsConfig(document) { - bindJQuery(); - var scripts = document.getElementsByTagName('script'), - script = scripts[scripts.length-1], - scriptSrc = script.src, - config = {}, - hashPos; - - hashPos = scriptSrc.indexOf('#'); - if (hashPos != -1) extend(config, parseKeyValue(scriptSrc.substr(hashPos+1))); - - eachAttribute(jqLite(script), function(value, name){ - if (/^ng:/.exec(name)) { - name = name.substring(3).replace(/-/g, '_'); - value = value || true; - config[name] = value; - } - }); - - return config; -} - function bindJQuery() { // bind to jQuery if present; jQuery = window.jQuery; diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 7664ed8c..ef3d3ccb 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -30,7 +30,7 @@ function publishExternalAPI(angular){ 'equals': equals, 'element': jqLite, 'forEach': forEach, - 'injector': function(){ return createInjector(arguments, angularModule); }, + 'injector': function(){ return createInjector(arguments); }, 'noop':noop, 'bind':bind, 'toJson': toJson, @@ -51,46 +51,46 @@ function publishExternalAPI(angular){ 'callbacks': {counter: 0} }); - angularModule.ng = ngModule; -} - -ngModule.$inject = ['$provide', '$injector']; -function ngModule($provide, $injector) { -// TODO(misko): temporary services to get the compiler working; - $provide.value('$textMarkup', angularTextMarkup); - $provide.value('$attrMarkup', angularAttrMarkup); - $provide.value('$directive', angularDirective); - $provide.value('$widget', angularWidget); + angularModule = setupModuleLoader(window); + try { + angularModule('ngLocale'); + } catch (e) { + angularModule('ngLocale', []).service('$locale', $LocaleProvider); + } - // load the LOCALE if present - $injector.invoke(null, angularModule.ngLocale || function(){ - $provide.service('$locale', $LocaleProvider); - }); + angularModule('ng', ['ngLocale'], ['$provide', '$injector', + function ngModule($provide, $injector) { + // TODO(misko): temporary services to get the compiler working; + $provide.value('$textMarkup', angularTextMarkup); + $provide.value('$attrMarkup', angularAttrMarkup); + $provide.value('$directive', angularDirective); + $provide.value('$widget', angularWidget); - $provide.service('$autoScroll', $AutoScrollProvider); - $provide.service('$browser', $BrowserProvider); - $provide.service('$cacheFactory', $CacheFactoryProvider); - $provide.service('$compile', $CompileProvider); - $provide.service('$cookies', $CookiesProvider); - $provide.service('$cookieStore', $CookieStoreProvider); - $provide.service('$defer', $DeferProvider); - $provide.service('$document', $DocumentProvider); - $provide.service('$exceptionHandler', $ExceptionHandlerProvider); - $provide.service('$filter', $FilterProvider); - $provide.service('$interpolate', $InterpolateProvider); - $provide.service('$formFactory', $FormFactoryProvider); - $provide.service('$http', $HttpProvider); - $provide.service('$httpBackend', $HttpBackendProvider); - $provide.service('$location', $LocationProvider); - $provide.service('$log', $LogProvider); - $provide.service('$parse', $ParseProvider); - $provide.service('$resource', $ResourceProvider); - $provide.service('$route', $RouteProvider); - $provide.service('$routeParams', $RouteParamsProvider); - $provide.service('$rootScope', $RootScopeProvider); - $provide.service('$q', $QProvider); - $provide.service('$sniffer', $SnifferProvider); - $provide.service('$templateCache', $TemplateCacheProvider); - $provide.service('$window', $WindowProvider); + $provide.service('$autoScroll', $AutoScrollProvider); + $provide.service('$browser', $BrowserProvider); + $provide.service('$cacheFactory', $CacheFactoryProvider); + $provide.service('$compile', $CompileProvider); + $provide.service('$cookies', $CookiesProvider); + $provide.service('$cookieStore', $CookieStoreProvider); + $provide.service('$defer', $DeferProvider); + $provide.service('$document', $DocumentProvider); + $provide.service('$exceptionHandler', $ExceptionHandlerProvider); + $provide.service('$filter', $FilterProvider); + $provide.service('$interpolate', $InterpolateProvider); + $provide.service('$formFactory', $FormFactoryProvider); + $provide.service('$http', $HttpProvider); + $provide.service('$httpBackend', $HttpBackendProvider); + $provide.service('$location', $LocationProvider); + $provide.service('$log', $LogProvider); + $provide.service('$parse', $ParseProvider); + $provide.service('$resource', $ResourceProvider); + $provide.service('$route', $RouteProvider); + $provide.service('$routeParams', $RouteParamsProvider); + $provide.service('$rootScope', $RootScopeProvider); + $provide.service('$q', $QProvider); + $provide.service('$sniffer', $SnifferProvider); + $provide.service('$templateCache', $TemplateCacheProvider); + $provide.service('$window', $WindowProvider); + }]); } diff --git a/src/Injector.js b/src/Injector.js index 517e3e00..8326d645 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -201,7 +201,7 @@ function inferInjectionArgs(fn) { * * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key. * @param {(Object|function())} provider If the provider is: - * + * * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using * {@link angular.module.AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. * - `Constructor`: a new instance of the provider will be created using @@ -237,11 +237,12 @@ function inferInjectionArgs(fn) { */ -function createInjector(modulesToLoad, moduleRegistry) { +function createInjector(modulesToLoad) { var cache = {}, providerSuffix = 'Provider', path = [], - $injector; + $injector, + loadedModules = new HashMap(); value('$injector', $injector = { get: getService, @@ -249,24 +250,34 @@ function createInjector(modulesToLoad, moduleRegistry) { instantiate: instantiate }); value('$provide', { - service: service, - factory: factory, - value: value, + service: supportObject(service), + factory: supportObject(factory), + value: supportObject(value), decorator: decorator }); - loadModule(modulesToLoad); + loadModules(modulesToLoad); return $injector; //////////////////////////////////// + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + delegate(key, value); + } + } + } + function service(name, provider) { if (isFunction(provider)){ provider = instantiate(provider); } if (!provider.$get) { - throw Error('Providers must define $get factory method.'); + throw Error('Provider ' + name + ' must define $get factory method.'); } cache['#' + name + providerSuffix] = provider; } @@ -362,16 +373,21 @@ function createInjector(modulesToLoad, moduleRegistry) { return invoke(instance, Type, locals) || instance; } - function loadModule(modulesToLoad){ - forEach(isString(modulesToLoad) ? modulesToLoad.split(',') : modulesToLoad, function(module) { + function loadModules(modulesToLoad){ + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); if (isString(module)) { - if (moduleRegistry[module = trim(module)]) { - module = moduleRegistry[module]; - } else { - throw Error("Module '" + module + "' is not defined!"); + module = angularModule(module); + loadModules(module.requires); + + for(var invokeQueue = module.invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + var invokeArgs = invokeQueue[i], + service = getService(invokeArgs[0]); + + service[invokeArgs[1]].apply(service, invokeArgs[2]); } - } - if (isFunction(module) || isArray(module)) { + } else if (isFunction(module) || isArray(module)) { invoke(null, module); } else { assertArgFn(module, 'module'); diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index 169a1d5a..778eee6b 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -9,18 +9,11 @@ var filename = /^(.*\/)angular-bootstrap.js(#.*)?$/, scripts = document.getElementsByTagName("SCRIPT"), - autobind = scripts[scripts.length-1].getAttribute('ng:autobind'), config, serverPath, match, globalVars = {}; - if (autobind) { - config = {autobind: autobind}; - } else { - config = (autobind == '') ? {autobind: true} : {} - } - for(var j = 0; j < scripts.length; j++) { match = (scripts[j].src || "").match(filename); if (match) { @@ -109,7 +102,7 @@ bindJQuery(); - angularInit(config, document); + angularInit(document, angular.bootstrap); } if (window.addEventListener) { diff --git a/src/angular-mocks.js b/src/angular-mocks.js index cc7f63a8..62a37763 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -5,25 +5,14 @@ * License: MIT */ - -window.angular = window.angular || {}; -angular.module = angular.module || {}; - /** * @ngdoc overview - * @name angular.module.ngMock + * @name angular.mock * @description * - * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful - * mocks to the {@link angular.module.AUTO.$injector $injector}. + * Namespace from 'angular-mocks.js' which contains testing related code. */ -angular.module.ngMock = function($provide){ - $provide.service('$browser', angular.module.ngMock.$BrowserProvider); - $provide.service('$exceptionHandler', angular.module.ngMock.$ExceptionHandlerProvider); - $provide.service('$log', angular.module.ngMock.$LogProvider); - $provide.decorator('$httpBackend', angular.module.ngMock.$httpBackendDecorator); -}; -angular.module.ngMock.$inject = ['$provide']; +angular.mock = {}; /** * @ngdoc object @@ -42,12 +31,12 @@ angular.module.ngMock.$inject = ['$provide']; * - $browser.defer — enables testing of code that uses * {@link angular.module.ng.$defer $defer} for executing functions via the `setTimeout` api. */ -angular.module.ngMock.$BrowserProvider = function(){ +angular.mock.$BrowserProvider = function(){ this.$get = function(){ - return new angular.module.ngMock.$Browser(); + return new angular.mock.$Browser(); }; }; -angular.module.ngMock.$Browser = function() { +angular.mock.$Browser = function() { var self = this; this.isMock = true; @@ -153,7 +142,7 @@ angular.module.ngMock.$Browser = function() { return script; }; } -angular.module.ngMock.$Browser.prototype = { +angular.mock.$Browser.prototype = { /** * @name angular.module.ngMock.$browser#poll @@ -226,7 +215,7 @@ angular.module.ngMock.$Browser.prototype = { * information. */ -angular.module.ngMock.$ExceptionHandlerProvider = function(){ +angular.mock.$ExceptionHandlerProvider = function(){ var handler; /** @@ -284,7 +273,7 @@ angular.module.ngMock.$ExceptionHandlerProvider = function(){ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. * */ -angular.module.ngMock.$LogProvider = function(){ +angular.mock.$logProvider = function(){ function concat(array1, array2, index) { return array1.concat(Array.prototype.slice.call(array2, index)); @@ -379,7 +368,7 @@ angular.module.ngMock.$LogProvider = function(){ /** * @ngdoc object - * @name angular.module.ngMock.TzDate + * @name angular.mock.TzDate * @description * * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. @@ -413,7 +402,7 @@ angular.module.ngMock.$LogProvider = function(){ * * */ -angular.module.ngMock.TzDate = function (offset, timestamp) { +angular.mock.TzDate = function (offset, timestamp) { var self = new Date(0); if (angular.isString(timestamp)) { var tsStr = timestamp; @@ -516,12 +505,12 @@ angular.module.ngMock.TzDate = function (offset, timestamp) { } //make "tzDateInstance instanceof Date" return true -angular.module.ngMock.TzDate.prototype = Date.prototype; +angular.mock.TzDate.prototype = Date.prototype; /** * @ngdoc function - * @name angular.module.ngMock.debug + * @name angular.mock.debug * @description * * *NOTE*: this is not an injectable instance, just a globally available function. @@ -533,7 +522,7 @@ angular.module.ngMock.TzDate.prototype = Date.prototype; * @param {*} object - any object to turn into string. * @return a serialized string of the argument */ -angular.module.ngMock.dump = function(object){ +angular.mock.dump = function(object){ return serialize(object); function serialize(object) { @@ -593,7 +582,7 @@ angular.module.ngMock.dump = function(object){ * respond with static or dynamic responses via the `expect` and `when` apis and their shortcuts * (`expectGET`, `whenPOST`, etc). */ -angular.module.ngMock.$httpBackendDecorator = function($delegate, $defer) { +angular.mock.$httpBackendDecorator = function($delegate, $defer) { var definitions = [], expectations = [], responses = [], @@ -836,6 +825,24 @@ function MockXhr() { this.abort = angular.noop; } +/** + * @ngdoc overview + * @name angular.module.ngMock + * @description + * + * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful + * mocks to the {@link angular.module.AUTO.$injector $injector}. + */ +angular.module('ngMock', ['ng']).service({ + '$browser': angular.mock.$BrowserProvider, + '$exceptionHandler': angular.mock.$ExceptionHandlerProvider, + '$log': angular.mock.$logProvider +}).init(function($provide) { + $provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator); +}); + + + window.jstestdriver && (function(window){ /** * Global method to output any number of objects into JSTD console. Useful for debugging. @@ -843,7 +850,7 @@ window.jstestdriver && (function(window){ window.dump = function() { var args = []; angular.forEach(arguments, function(arg){ - args.push(angular.module.ngMock.dump(arg)); + args.push(angular.mock.dump(arg)); }); jstestdriver.console.log.apply(jstestdriver.console, args); }; @@ -852,7 +859,7 @@ window.jstestdriver && (function(window){ /** * @ngdoc function - * @name angular.module.ngMock.inject + * @name angular.mock.inject * @description * * *NOTE*: this is not an injectable instance, just a globally available function on window. diff --git a/src/angular.suffix b/src/angular.suffix index 99726fbd..44cddef3 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -4,10 +4,8 @@ publishExternalAPI(angular); - var config = angularJsConfig(document); - jqLiteWrap(document).ready(function() { - angularInit(config, document); + angularInit(document, bootstrap); }); })(window, document); diff --git a/src/directives.js b/src/directives.js index 17dd1531..6e565b83 100644 --- a/src/directives.js +++ b/src/directives.js @@ -9,33 +9,6 @@ * behavior of the element in which it is specified. Do not use directives to add elements to the * DOM; instead, use {@link angular.widget widgets} to add DOM elements. * - * Following is the list of built-in Angular directives: - * - * * {@link angular.directive.ng:autobind ng:autobind} - An Angular bootstrap parameter that can - * act as a directive. - * * {@link angular.directive.ng:bind ng:bind} - Creates a data-binding between an HTML text value - * and a data model. - * * {@link angular.directive.ng:bind-attr ng:bind-attr} - Creates a data-binding in a way similar - * to `ng:bind`, but uses JSON key / value pairs to do so. - * * {@link angular.directive.ng:bind-template ng:bind-template} - Replaces the text value of an - * element with a specified template. - * * {@link angular.directive.ng:class ng:class} - Conditionally set a CSS class on an element. - * * {@link angular.directive.ng:class-even ng:class-even} - Like `ng:class`, but works in - * conjunction with {@link angular.widget.@ng:repeat} to affect even rows in a collection. - * * {@link angular.directive.ng:class-odd ng:class-odd} - Like `ng:class`, but works with {@link - * angular.widget.@ng:repeat} to affect odd rows. - * * {@link angular.directive.ng:click ng:click} - Executes custom behavior when an element is - * clicked. - * * {@link angular.directive.ng:controller ng:controller} - Creates a scope object linked to the - * DOM element and assigns behavior to the scope. - * * {@link angular.directive.ng:hide ng:hide} - Conditionally hides a portion of HTML. - * * {@link angular.directive.ng:href ng:href} - Places an href in the Angular namespace. - * * {@link angular.directive.ng:init} - Initialization tasks run before a template is executed. - * * {@link angular.directive.ng:show ng:show} - Conditionally displays a portion of HTML. - * * {@link angular.directive.ng:src ng:src} - Places a `src` attribute into the Angular namespace. - * * {@link angular.directive.ng:style ng:style} - Conditionally set CSS styles on an element. - * * {@link angular.directive.ng:submit} - Binds Angular expressions to `onSubmit` events. - * * For more information about how Angular directives work, and to learn how to create your own * directives, see {@link guide/dev_guide.compiler.directives Understanding Angular Directives} in * the Angular Developer Guide. diff --git a/src/loader.js b/src/loader.js new file mode 100644 index 00000000..ec30ad9a --- /dev/null +++ b/src/loader.js @@ -0,0 +1,167 @@ +'use strict'; + +/** + * @ngdoc interface + * @name angular.Module + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + return ensure(ensure(window, 'angular', Object), 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @description + * + * The `angular.module` is a global place for registering angular modules. All modules + * (angular core or 3rd party) that should be available to an application must be registered using this mechanism. + * + * # Module + * + * A module is a collocation of services, directives, filters, and configure information. Module is used to configure the, + * {@link angular.module.AUTO.$injector $injector}. + * + *
+     * // Create a new module
+     * var myModule = angular.module('myModule', []);
+     *
+     * // configure a new service
+     * myModule.value('appName', 'MyCoolApp');
+     *
+     * // configure existing services inside initialization blocks.
+     * myModule.init(function($locationProvider) {
+     *   // Configure existing providers
+     *   $locationProvider.hashPrefix = '!';
+     * });
+     * 
+ * + * Then you can load your module like this: + * + *
+     * var injector = angular.injector('ng', 'MyModule')
+     * 
+ * + * @param {!string} name The name of the module to create or retrieve. + * @param {Array.=} requires If specified then new module is being created. If unspecified then the + * the module is being retrieved for further configuration. + * @param {Function} initFn Option configuration function for the module. Same as + * {@link angular.Module#init Module.init()}. + * @return {angular.Module} + */ + return function module(name, requires, initFn) { + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw Error('No module: ' + name); + } + + function init(fn) { + invokeQueue.push(['$injector', 'invoke', [null, fn]]); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {angular.Module} */ + var moduleInstance = { + /** + * @ngdoc property + * @name angular.Module#requires + * @propertyOf angular.Module + * @returns {Array.} List of module names which must be loaded before this module. + * @description + * Holds the list of modules which the injector will load before the current module is loaded. + */ + requires: requires, + invokeQueue: invokeQueue, + + /** + * @ngdoc method + * @name angular.Module#service + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the service. + * @description + * See {@link angular.module.AUTO.$provide#service $provide.service()}. + */ + service: invokeLater('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link angular.module.AUTO.$provide#service $provide.factory()}. + */ + factory: invokeLater('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#value + * @methodOf angular.Module + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link angular.module.AUTO.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @methodOf angular.Module + * @param {string} name filterr name + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link angular.module.ng.$filterProvider#register $filterProvider.register()}. + */ + filter: invokeLater('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#init + * @methodOf angular.Module + * @param {Function} initializationFn Execute this function on module load, allowing it to do any + * service configuration.. + * @description + * Use this method to register work which needs to be performed on module loading. + */ + init: init + }; + + if (initFn) { + init(initFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @returns {angular.Module} + */ + function invokeLater(provider, method) { + return function() { + invokeQueue.push([provider, method, arguments]); + return moduleInstance; + } + } + }); + }; + }); + +} diff --git a/src/loader.prefix b/src/loader.prefix new file mode 100644 index 00000000..b3969eb0 --- /dev/null +++ b/src/loader.prefix @@ -0,0 +1,7 @@ +/** + * @license AngularJS v"NG_VERSION_FULL" + * (c) 2010-2012 AngularJS http://angularjs.org + * License: MIT + */ +'use strict'; +( diff --git a/src/loader.suffix b/src/loader.suffix new file mode 100644 index 00000000..b8a5d43d --- /dev/null +++ b/src/loader.suffix @@ -0,0 +1,20 @@ +)(window); + +/** + * Closure compiler type information + * + * @typedef { { + * requires: !Array., + * invokeQueue: !Array.>, + * + * service: function(string, Function):angular.Module, + * factory: function(string, Function):angular.Module, + * value: function(string, *):angular.Module, + * + * filter: function(string, Function):angular.Module, + * + * init: function(Function):angular.Module + * } } + */ +angular.Module; + diff --git a/src/scenario/Application.js b/src/scenario/Application.js index 5e7b8370..ba6bbea7 100644 --- a/src/scenario/Application.js +++ b/src/scenario/Application.js @@ -90,11 +90,13 @@ angular.scenario.Application.prototype.executeAction = function(action) { if (!$window.angular) { return action.call(this, $window, _jQuery($window.document)); } - var element = $window.angular.element($window.document); - var $injector = element.inheritedData('$injector'); - $injector.invoke(null, function($browser){ - $browser.notifyWhenNoOutstandingRequests(function() { - action.call(self, $window, _jQuery($window.document)); + angularInit($window.document, function(element) { + element = $window.angular.element(element); + var $injector = element.inheritedData('$injector'); + $injector.invoke(null, function($browser){ + $browser.notifyWhenNoOutstandingRequests(function() { + action.call(self, $window, _jQuery($window.document)); + }); }); }); }; diff --git a/src/scenario/angular.suffix b/src/scenario/angular.suffix index a79fd270..c75c5cca 100644 --- a/src/scenario/angular.suffix +++ b/src/scenario/angular.suffix @@ -1,7 +1,17 @@ +bindJQuery(); publishExternalAPI(angular); var $runner = new angular.scenario.Runner(window), - config = angularJsConfig(document); + scripts = document.getElementsByTagName('script'), + script = scripts[scripts.length - 1], + config = {}; + +angular.forEach(script.attributes, function(attr) { + var match = attr.name.match(/ng[:\-](.*)/); + if (match) { + config[match[1]] = attr.value || true; + } +}); if (config.autotest) { jqLiteWrap(document).ready(function() { -- cgit v1.2.3