diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Angular.js | 36 | ||||
| -rw-r--r-- | src/Injector.js | 162 | ||||
| -rw-r--r-- | src/scenario/Runner.js | 2 | ||||
| -rw-r--r-- | src/scenario/dsl.js | 8 | ||||
| -rw-r--r-- | src/service/compiler.js | 9 | ||||
| -rw-r--r-- | src/widget/form.js | 4 | ||||
| -rw-r--r-- | src/widget/input.js | 14 | ||||
| -rw-r--r-- | src/widget/select.js | 4 | ||||
| -rw-r--r-- | src/widgets.js | 12 |
9 files changed, 89 insertions, 162 deletions
diff --git a/src/Angular.js b/src/Angular.js index bbd43d3b..4344425a 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -100,6 +100,7 @@ var _undefined = undefined, /** @name angular */ angular = window[$angular] || (window[$angular] = {}), + angularModules = angular.modules || (angular.modules = {}), /** @name angular.markup */ angularTextMarkup = extensionMap(angular, 'markup'), /** @name angular.attrMarkup */ @@ -274,6 +275,7 @@ function inherit(parent, extra) { </pre> */ function noop() {} +noop.$inject = []; /** @@ -292,6 +294,7 @@ function noop() {} </pre> */ function identity($) {return $;} +identity.$inject = []; function valueFn(value) {return function() {return value;};} @@ -945,14 +948,20 @@ function encodeUriQuery(val, pctEncodeSpaces) { */ function angularInit(config, document){ var autobind = config.autobind; - + if (autobind) { - var element = isString(autobind) ? document.getElementById(autobind) : document, - injector = createInjector(angularService), - scope = injector('$rootScope'); - - injector('$compile')(element)(scope); - scope.$apply(); + var modules = [ngModule]; + forEach((config.modules || '').split(','), function(module){ + module = trim(module); + if (module) { + modules.push(module); + } + }); + createInjector(modules, angularModules)(['$rootScope', '$compile', function(scope, compile){ + scope.$apply(function(){ + compile(isString(autobind) ? document.getElementById(autobind) : document)(scope); + }); + }]); } } @@ -1017,13 +1026,11 @@ function assertArgFn(arg, name) { function publishExternalAPI(angular){ extend(angular, { - // disabled for now until we agree on public name - //'annotate': annotate, 'copy': copy, 'extend': extend, 'equals': equals, 'forEach': forEach, - 'injector': createInjector, + 'injector': function(){ return createInjector(arguments, angularModules); }, 'noop':noop, 'bind':bind, 'toJson': toJson, @@ -1041,6 +1048,15 @@ function publishExternalAPI(angular){ 'lowercase': lowercase, 'uppercase': uppercase }); + + angularModules.NG = ngModule; +} + +ngModule.$inject = ['$provide']; +function ngModule($provide) { + forEach(angularService, function(factory, name){ + $provide.factory(name, factory); + }); } diff --git a/src/Injector.js b/src/Injector.js index fe0cc5e9..12c2ffa6 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -22,108 +22,16 @@ * The injector function also has these properties: * * * An `invoke` property which can be used to invoke methods with dependency-injected arguments. - * `injector.invoke(self, fn, curryArgs)` + * `injector.invoke(self, fn, locals)` * * `self` - The "`this`" to be used when invoking the function. * * `fn` - The function to be invoked. The function may have the `$inject` property that * lists the set of arguments which should be auto-injected. * (see {@link guide/dev_guide.di dependency injection}). - * * `curryArgs(array)` - Optional array of arguments to pass to the function - * invocation after the injection arguments (also known as curry arguments or currying). + * * `locals(array)` - Optional array of arguments to pass to the function + * invocation after the injection arguments. * * An `eager` property which is used to initialize the eager services. * `injector.eager()` */ -function createInjector(factories) { - var instanceCache = { - $injector: injector - }; - factories = factories || angularService; - - injector.invoke = invoke; - - forEach(factories, function(factory, name){ - if (factory.$eager) - injector(name); - }); - return instanceCache.$injector; - - function injector(serviceId, path){ - if (typeof serviceId == 'string') { - if (!(serviceId in instanceCache)) { - var factory = factories[serviceId]; - path = path || []; - path.unshift(serviceId); - if (!factory) throw Error("Unknown provider for '" + path.join("' <- '") + "'."); - inferInjectionArgs(factory); - instanceCache[serviceId] = invoke(null, factory, [], path); - path.shift(); - } - return instanceCache[serviceId]; - } else { - return invoke(null, serviceId, path); - } - } - - function invoke(self, fn, args, path){ - args = args || []; - var injectNames; - var i; - if (typeof fn == 'function') { - injectNames = fn.$inject || []; - i = injectNames.length; - } else if (fn instanceof Array) { - injectNames = fn; - i = injectNames.length; - fn = injectNames[--i]; - } - assertArgFn(fn, 'fn'); - while(i--) { - args.unshift(injector(injectNames[i], path)); - } - return fn.apply(self, args); - } -} - -/** - * THIS IS NOT PUBLIC DOC YET! - * - * @name angular.annotate - * @function - * - * @description - * Annotate the function with injection arguments. This is equivalent to setting the `$inject` - * property as described in {@link guide.di dependency injection}. - * - * <pre> - * var MyController = angular.annotate('$location', function($location){ ... }); - * </pre> - * - * is the same as - * - * <pre> - * var MyController = function($location){ ... }; - * MyController.$inject = ['$location']; - * </pre> - * - * @param {String|Array} serviceName... zero or more service names to inject into the - * `annotatedFunction`. - * @param {function} annotatedFunction function to annotate with `$inject` - * functions. - * @returns {function} `annotatedFunction` - */ -function annotate(services, fn) { - if (services instanceof Array) { - fn.$inject = services; - return fn; - } else { - var i = 0, - length = arguments.length - 1, // last one is the destination function - $inject = arguments[length].$inject = []; - for (; i < length; i++) { - $inject.push(arguments[i]); - } - return arguments[length]; // return the last one - } -} function angularServiceInject(name, fn, inject, eager) { angularService(name, fn, {$inject:inject, $eager:eager}); @@ -156,22 +64,16 @@ function inferInjectionArgs(fn) { } /////////////////////////////////////// -function createInjector2(modulesToLoad, moduleRegistry) { +function createInjector(modulesToLoad, moduleRegistry) { var cache = {}, $injector = internalInjector(cache), providerSuffix = 'Provider', providerSuffixLength = providerSuffix.length; - function $provide(name) { - var provider = cache['#' + name + providerSuffix]; - if (provider) { - return provider; - } else { - throw Error("No provider for: " + name); - } - } + value('$injector', $injector); + value('$provide', {service: service, factory: factory, value: value}); - $provide.service = function(name, provider) { + function service(name, provider) { if (isFunction(provider)){ provider = $injector.instantiate(provider); } @@ -180,11 +82,8 @@ function createInjector2(modulesToLoad, moduleRegistry) { } cache['#' + name + providerSuffix] = provider; }; - $provide.factory = function(name, factoryFn) { $provide.service(name, { $get:factoryFn }); }; - $provide.value = function(name, value) { $provide.factory(name, valueFn(value)); }; - - $provide.value('$injector', $injector); - $provide.value('$provide', $provide); + function factory(name, factoryFn) { service(name, { $get:factoryFn }); }; + function value(name, value) { factory(name, valueFn(value)); }; function internalInjector(cache) { var path = []; @@ -194,9 +93,10 @@ function createInjector2(modulesToLoad, moduleRegistry) { case 'function': return invoke(null, value); case 'string': - var instanceKey = '#' + value; - if (cache[instanceKey]) { - return cache[instanceKey]; + var instanceKey = '#' + value, + instance = cache[instanceKey]; + if (instance !== undefined || cache.hasOwnProperty(instanceKey)) { + return instance; } try { path.unshift(value); @@ -219,28 +119,32 @@ function createInjector2(modulesToLoad, moduleRegistry) { } } - function invoke(self, fn){ + function invoke(self, fn, locals){ var args = [], $inject, - length; - switch(typeof fn){ - case 'function': - $inject = inferInjectionArgs(fn); + length, + key; + + if (fn instanceof Function) { + $inject = inferInjectionArgs(fn); + length = $inject.length; + } else { + if (fn instanceof Array) { + $inject = fn; length = $inject.length; - break; - case 'object': - if (typeof fn.length == 'number') { - $inject = fn; - length = $inject.length; - fn = $inject[--length]; - } - default: - assertArgFn(fn, 'fn'); - }; + fn = $inject[--length]; + } + assertArgFn(fn, 'fn'); + } while(length--) { - args.unshift(injector($inject[length], path)); + key = $inject[length]; + args.unshift( + locals && locals.hasOwnProperty(key) + ? locals[key] + : injector($inject[length], path) + ); } switch (self ? -1 : args.length) { diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js index d7e1a82b..fba076a8 100644 --- a/src/scenario/Runner.js +++ b/src/scenario/Runner.js @@ -163,7 +163,7 @@ angular.scenario.Runner.prototype.createSpecRunner_ = function(scope) { */ angular.scenario.Runner.prototype.run = function(application) { var self = this; - var $root = angular.injector()('$rootScope'); + var $root = angular.injector('NG')('$rootScope'); angular.extend($root, this); angular.forEach(angular.scenario.Runner.prototype, function(fn, name) { $root[name] = angular.bind(self, fn); diff --git a/src/scenario/dsl.js b/src/scenario/dsl.js index 13ae8b8f..97749787 100644 --- a/src/scenario/dsl.js +++ b/src/scenario/dsl.js @@ -103,25 +103,25 @@ angular.scenario.dsl('browser', function() { api.url = function() { return this.addFutureAction('$location.url()', function($window, $document, done) { - done(null, $window.angular.injector()('$location').url()); + done(null, $window.angular.injector('NG')('$location').url()); }); }; api.path = function() { return this.addFutureAction('$location.path()', function($window, $document, done) { - done(null, $window.angular.injector()('$location').path()); + done(null, $window.angular.injector('NG')('$location').path()); }); }; api.search = function() { return this.addFutureAction('$location.search()', function($window, $document, done) { - done(null, $window.angular.injector()('$location').search()); + done(null, $window.angular.injector('NG')('$location').search()); }); }; api.hash = function() { return this.addFutureAction('$location.hash()', function($window, $document, done) { - done(null, $window.angular.injector()('$location').hash()); + done(null, $window.angular.injector('NG')('$location').hash()); }); }; diff --git a/src/service/compiler.js b/src/service/compiler.js index c1728f7d..0bd3e54d 100644 --- a/src/service/compiler.js +++ b/src/service/compiler.js @@ -22,14 +22,15 @@ angularServiceInject('$compile', function($injector, $exceptionHandler, $textMar Template.prototype = { link: function(element, scope) { - var childScope = scope; + var childScope = scope, + locals = {$element: element}; if (this.newScope) { childScope = isFunction(this.newScope) ? scope.$new(this.newScope(scope)) : scope.$new(); element.data($$scope, childScope); } forEach(this.linkFns, function(fn) { try { - childScope.$service.invoke(childScope, fn, [element]); + $injector.invoke(childScope, fn, locals); } catch (e) { $exceptionHandler(e); } @@ -54,6 +55,10 @@ angularServiceInject('$compile', function($injector, $exceptionHandler, $textMar addLinkFn:function(linkingFn) { if (linkingFn) { + //TODO(misko): temporary hack. + if (isFunction(linkingFn) && !linkingFn.$inject) { + linkingFn.$inject = ['$element']; + } this.linkFns.push(linkingFn); } }, diff --git a/src/widget/form.js b/src/widget/form.js index 065ee74d..4608efc1 100644 --- a/src/widget/form.js +++ b/src/widget/form.js @@ -56,7 +56,7 @@ angularWidget('form', function(form){ this.descend(true); this.directives(true); - return annotate('$formFactory', function($formFactory, formElement) { + return ['$formFactory', '$element', function($formFactory, formElement) { var name = formElement.attr('name'), parentForm = $formFactory.forElement(formElement), form = $formFactory(parentForm); @@ -74,7 +74,7 @@ angularWidget('form', function(form){ formElement[value ? 'addClass' : 'removeClass']('ng-' + name); }); } - }); + }]; }); angularWidget('ng:form', angularWidget('form')); diff --git a/src/widget/input.js b/src/widget/input.js index cf29d0f1..5108a4e4 100644 --- a/src/widget/input.js +++ b/src/widget/input.js @@ -569,7 +569,7 @@ angularInputType('radio', function(inputElement) { function numericRegexpInputType(regexp, error) { - return function(inputElement) { + return ['$element', function(inputElement) { var widget = this, min = 1 * (inputElement.attr('min') || Number.MIN_VALUE), max = 1 * (inputElement.attr('max') || Number.MAX_VALUE); @@ -598,7 +598,7 @@ function numericRegexpInputType(regexp, error) { widget.$viewValue = '' + widget.$modelValue; } }; - }; + }]; } @@ -713,7 +713,7 @@ angularWidget('input', function(inputElement){ this.descend(true); var modelExp = inputElement.attr('ng:model'); return modelExp && - annotate('$defer', '$formFactory', function($defer, $formFactory, inputElement){ + ['$defer', '$formFactory', '$element', function($defer, $formFactory, inputElement){ var form = $formFactory.forElement(inputElement), // We have to use .getAttribute, since jQuery tries to be smart and use the // type property. Trouble is some browser change unknown to text. @@ -761,14 +761,16 @@ angularWidget('input', function(inputElement){ } } - !TypeController.$inject && (TypeController.$inject = []); + //TODO(misko): setting $inject is a hack + !TypeController.$inject && (TypeController.$inject = ['$element']); widget = form.$createWidget({ scope: modelScope, model: modelExp, onChange: inputElement.attr('ng:change'), alias: inputElement.attr('name'), controller: TypeController, - controllerArgs: [inputElement]}); + controllerArgs: {$element: inputElement} + }); watchElementProperty(this, widget, 'value', inputElement); watchElementProperty(this, widget, 'required', inputElement); @@ -830,7 +832,7 @@ angularWidget('input', function(inputElement){ } }); } - }); + }]; }); diff --git a/src/widget/select.js b/src/widget/select.js index 2e328b26..c70b5ded 100644 --- a/src/widget/select.js +++ b/src/widget/select.js @@ -131,7 +131,7 @@ angularWidget('select', function(element){ this.directives(true); this.descend(true); return element.attr('ng:model') && - annotate('$formFactory', '$compile', function($formFactory, $compile, selectElement){ + ['$formFactory', '$compile', '$element', function($formFactory, $compile, selectElement){ var modelScope = this, match, form = $formFactory.forElement(selectElement), @@ -433,5 +433,5 @@ angularWidget('select', function(element){ } }; } - }); + }]; }); diff --git a/src/widgets.js b/src/widgets.js index 11d9a2f0..6ecd27a1 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -90,7 +90,7 @@ angularWidget('ng:include', function(element){ this.directives(true); } else { element[0]['ng:compiled'] = true; - return extend(function(xhr, element){ + return ['$xhr.cache', '$element', function(xhr, element){ var scope = this, changeCounter = 0, releaseScopes = [], @@ -129,7 +129,7 @@ angularWidget('ng:include', function(element){ element.html(''); } }); - }, {$inject:['$xhr.cache']}); + }]; } }); @@ -555,7 +555,7 @@ angularWidget('ng:view', function(element) { if (!element[0]['ng:compiled']) { element[0]['ng:compiled'] = true; - return annotate('$xhr.cache', '$route', function($xhr, $route, element){ + return ['$xhr.cache', '$route', '$element', function($xhr, $route, element){ var template; var changeCounter = 0; @@ -578,7 +578,7 @@ angularWidget('ng:view', function(element) { element.html(''); } }); - }); + }]; } else { compiler.descend(true); compiler.directives(true); @@ -759,7 +759,7 @@ angularWidget('ng:pluralize', function(element) { whenExp = element.attr('when'), offset = element.attr('offset') || 0; - return annotate('$locale', function($locale, element) { + return ['$locale', '$element', function($locale, element) { var scope = this, whens = scope.$eval(whenExp), whensExpFns = {}; @@ -783,5 +783,5 @@ angularWidget('ng:pluralize', function(element) { }, function(scope, newVal) { element.text(newVal); }); - }); + }]; }); |
