aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js6
-rw-r--r--src/AngularPublic.js1
-rw-r--r--src/Compiler.js7
-rw-r--r--src/Injector.js59
-rw-r--r--src/Scope.js61
-rw-r--r--src/angular-bootstrap.js1
-rw-r--r--src/services.js67
-rw-r--r--src/widgets.js6
8 files changed, 127 insertions, 81 deletions
diff --git a/src/Angular.js b/src/Angular.js
index f7351bdc..5bffac53 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -330,6 +330,10 @@ function escapeAttr(html) {
'"');
}
+function concat(array1, array2, index) {
+ return array1.concat(slice.call(array2, index, array2.length));
+}
+
function bind(self, fn) {
var curryArgs = arguments.length > 2 ? slice.call(arguments, 2, arguments.length) : [];
if (typeof fn == $function) {
@@ -403,7 +407,7 @@ function angularInit(config){
// TODO default to the source of angular.js
var scope = compile(window.document, _null, {'$config':config});
if (config.css)
- scope.$browser.addCss(config.base_url + config.css);
+ scope.$inject('$browser').addCss(config.base_url + config.css);
scope.$init();
}
}
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index 617a7e2e..2b5d4fbc 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -21,6 +21,7 @@ extend(angular, {
'extend': extend,
'equals': equals,
'foreach': foreach,
+ 'injector': createInjector,
'noop':noop,
'bind':bind,
'toJson': toJson,
diff --git a/src/Compiler.js b/src/Compiler.js
index 0c80d3fc..a78a04c9 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -34,7 +34,7 @@ Template.prototype = {
foreach(this.inits, function(fn) {
queue.push(function() {
childScope.$tryEval(function(){
- return fn.call(childScope, element);
+ return childScope.$inject(fn, childScope, element);
}, element);
});
});
@@ -96,8 +96,7 @@ Compiler.prototype = {
return function(element, parentScope){
element = jqLite(element);
var scope = parentScope && parentScope.$eval ?
- parentScope :
- createScope(parentScope || {}, angularService);
+ parentScope : createScope(parentScope);
return extend(scope, {
$element:element,
$init: function() {
@@ -124,7 +123,7 @@ Compiler.prototype = {
text:function(text) {return jqLite(document.createTextNode(text));},
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
directives: function(value){ if(isDefined(value)) directives = value; return directives;},
- scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value ; return template.newScope;}
+ scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;}
};
try {
priority = element.attr('ng:eval-order') || priority || 0;
diff --git a/src/Injector.js b/src/Injector.js
new file mode 100644
index 00000000..554e9089
--- /dev/null
+++ b/src/Injector.js
@@ -0,0 +1,59 @@
+/**
+ * Create an inject method
+ * @param providerScope provider's "this"
+ * @param providers a function(name) which returns provider function
+ * @param cache place where instances are saved for reuse
+ * @returns {Function}
+ */
+function createInjector(providerScope, providers, cache) {
+ providers = providers || angularService;
+ cache = cache || {};
+ providerScope = providerScope || {};
+ /**
+ * injection function
+ * @param value: string, array, object or function.
+ * @param scope: optional function "this"
+ * @param args: optional arguments to pass to function after injection
+ * parameters
+ * @returns depends on value:
+ * string: return an instance for the injection key.
+ * array of keys: returns an array of instances.
+ * function: look at $inject property of function to determine instances
+ * and then call the function with instances and scope. Any
+ * additional arguments are passed on to function.
+ * object: initialize eager providers and publish them the ones with publish here.
+ * none: same as object but use providerScope as place to publish.
+ */
+ return function inject(value, scope, args){
+ var returnValue, provider, creation;
+ if (isString(value)) {
+ if (!cache.hasOwnProperty(value)) {
+ provider = providers[value];
+ if (!provider) throw "Unknown provider for '"+value+"'.";
+ cache[value] = inject(provider, providerScope);
+ }
+ returnValue = cache[value];
+ } else if (isArray(value)) {
+ returnValue = [];
+ foreach(value, function(name) {
+ returnValue.push(inject(name));
+ });
+ } else if (isFunction(value)) {
+ returnValue = inject(value.$inject || []);
+ returnValue = value.apply(scope, concat(returnValue, arguments, 2));
+ } else if (isObject(value)) {
+ foreach(providers, function(provider, name){
+ creation = provider.$creation;
+ if (creation == 'eager') {
+ inject(name);
+ }
+ if (creation == 'eager-published') {
+ setter(value, name, inject(name));
+ }
+ });
+ } else {
+ returnValue = inject(providerScope);
+ }
+ return returnValue;
+ };
+} \ No newline at end of file
diff --git a/src/Scope.js b/src/Scope.js
index 3aaff361..ed608e95 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -108,20 +108,14 @@ function errorHandlerFor(element, error) {
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
}
-function createScope(parent, services, existing) {
+function createScope(parent, providers, instanceCache) {
function Parent(){}
- function API(){}
- function Behavior(){}
-
parent = Parent.prototype = (parent || {});
+ var instance = new Parent();
var evalLists = {sorted:[]};
var postList = [], postHash = {}, postId = 0;
- var servicesCache = extend({}, existing);
- var api = API.prototype = new Parent();
- var behavior = Behavior.prototype = new API();
- var instance = new Behavior();
- extend(api, {
+ extend(instance, {
'this': instance,
$id: (scopeId++),
$parent: parent,
@@ -227,46 +221,29 @@ function createScope(parent, services, existing) {
},
$become: function(Class) {
- // remove existing
- foreach(behavior, function(value, key){ delete behavior[key]; });
- foreach((Class || noop).prototype, function(fn, name){
- behavior[name] = bind(instance, fn);
- });
- (Class || noop).call(instance);
-
- //TODO: backwards compatibility hack, remove when Feedback's init methods are removed
- if (behavior.hasOwnProperty('init')) {
- behavior.init();
+ if (isFunction(Class)) {
+ instance.constructor = Class;
+ foreach(Class.prototype, function(fn, name){
+ instance[name] = bind(instance, fn);
+ });
+ instance.$inject.apply(instance, concat([Class, instance], arguments, 1));
}
+ },
+
+ $new: function(Class) {
+ var child = createScope(instance);
+ child.$become.apply(instance, concat([Class], arguments, 1));
+ instance.$onEval(child.$eval);
+ return child;
}
});
if (!parent.$root) {
- api.$root = instance;
- api.$parent = instance;
- }
-
- function inject(name){
- var service = servicesCache[name], factory, args = [];
- if (isUndefined(service)) {
- factory = services[name];
- if (!isFunction(factory))
- throw "Don't know how to inject '" + name + "'.";
- foreach(factory.inject, function(dependency){
- args.push(inject(dependency));
- });
- servicesCache[name] = service = factory.apply(instance, args);
- }
- return service;
+ instance.$root = instance;
+ instance.$parent = instance;
+ (instance.$inject = createInjector(instance, providers, instanceCache))();
}
- foreach(services, function(_, name){
- var service = inject(name);
- if (service) {
- setter(instance, name, service);
- }
- });
-
return instance;
}
diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js
index 1f03b8a3..581f4ff9 100644
--- a/src/angular-bootstrap.js
+++ b/src/angular-bootstrap.js
@@ -41,6 +41,7 @@
addScript("/JSON.js");
addScript("/Compiler.js");
addScript("/Scope.js");
+ addScript("/Injector.js");
addScript("/jqLite.js");
addScript("/Parser.js");
addScript("/Resource.js");
diff --git a/src/services.js b/src/services.js
index 9dc3b456..5b65f8fd 100644
--- a/src/services.js
+++ b/src/services.js
@@ -1,13 +1,19 @@
var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?([^\?#]+)(\?([^#]*))?(#(.*))?$/,
HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/,
- DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
+ DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21},
+ EAGER = 'eager',
+ EAGER_PUBLISHED = EAGER + '-published';
-angularService("$window", bind(window, identity, window));
-angularService("$document", function(window){
+function angularServiceInject(name, fn, inject, eager) {
+ angularService(name, fn, {$inject:inject, $creation:eager});
+}
+
+angularServiceInject("$window", bind(window, identity, window), [], EAGER_PUBLISHED);
+angularServiceInject("$document", function(window){
return jqLite(window.document);
-}, {inject:['$window']});
+}, ['$window'], EAGER_PUBLISHED);
-angularService("$location", function(browser){
+angularServiceInject("$location", function(browser){
var scope = this,
location = {parse:parseUrl, toString:toString, update:update},
lastLocation = {};
@@ -92,9 +98,9 @@ angularService("$location", function(browser){
update();
return location.href;
}
-}, {inject: ['$browser']});
+}, ['$browser'], EAGER_PUBLISHED);
-angularService("$log", function($window){
+angularServiceInject("$log", function($window){
var console = $window.console || {log: noop, warn: noop, info: noop, error: noop},
log = console.log || noop;
return {
@@ -103,15 +109,15 @@ angularService("$log", function($window){
info: bind(console, console.info || log),
error: bind(console, console.error || log)
};
-}, {inject:['$window']});
+}, ['$window'], EAGER_PUBLISHED);
-angularService('$exceptionHandler', function($log){
+angularServiceInject('$exceptionHandler', function($log){
return function(e) {
$log.error(e);
};
-}, {inject:['$log']});
+}, ['$log'], EAGER_PUBLISHED);
-angularService("$hover", function(browser, document) {
+angularServiceInject("$hover", function(browser, document) {
var tooltip, self = this, error, width = 300, arrowWidth = 10, body = jqLite(document[0].body);
browser.hover(function(element, show){
if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) {
@@ -155,14 +161,13 @@ angularService("$hover", function(browser, document) {
tooltip = _null;
}
});
-}, {inject:['$browser', '$document']});
-
+}, ['$browser', '$document'], EAGER);
/* Keeps references to all invalid widgets found during validation. Can be queried to find if there
* are invalid widgets currently displayed
*/
-angularService("$invalidWidgets", function(){
+angularServiceInject("$invalidWidgets", function(){
var invalidWidgets = [];
@@ -217,7 +222,7 @@ angularService("$invalidWidgets", function(){
}
return invalidWidgets;
-});
+}, [], EAGER_PUBLISHED);
@@ -244,7 +249,7 @@ function switchRouteMatcher(on, when, dstName) {
return match ? dst : _null;
}
-angularService('$route', function(location){
+angularServiceInject('$route', function(location){
var routes = {},
onChange = [],
matcher = switchRouteMatcher,
@@ -284,9 +289,9 @@ angularService('$route', function(location){
}
this.$watch(function(){return dirty + location.hash;}, updateRoute);
return $route;
-}, {inject: ['$location']});
+}, ['$location']);
-angularService('$xhr', function($browser, $error, $log){
+angularServiceInject('$xhr', function($browser, $error, $log){
var self = this;
return function(method, url, post, callback){
if (isFunction(post)) {
@@ -315,15 +320,15 @@ angularService('$xhr', function($browser, $error, $log){
}
});
};
-}, {inject:['$browser', '$xhr.error', '$log']});
+}, ['$browser', '$xhr.error', '$log']);
-angularService('$xhr.error', function($log){
+angularServiceInject('$xhr.error', function($log){
return function(request, response){
$log.error('ERROR: XHR: ' + request.url, request, response);
};
-}, {inject:['$log']});
+}, ['$log']);
-angularService('$xhr.bulk', function($xhr, $error, $log){
+angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
var requests = [],
scope = this;
function bulkXHR(method, url, post, callback) {
@@ -371,9 +376,9 @@ angularService('$xhr.bulk', function($xhr, $error, $log){
};
this.$onEval(PRIORITY_LAST, bulkXHR.flush);
return bulkXHR;
-}, {inject:['$xhr', '$xhr.error', '$log']});
+}, ['$xhr', '$xhr.error', '$log']);
-angularService('$xhr.cache', function($xhr){
+angularServiceInject('$xhr.cache', function($xhr){
var inflight = {}, self = this;
function cache(method, url, post, callback, verifyCache){
if (isFunction(post)) {
@@ -415,12 +420,12 @@ angularService('$xhr.cache', function($xhr){
cache.data = {};
cache.delegate = $xhr;
return cache;
-}, {inject:['$xhr.bulk']});
+}, ['$xhr.bulk']);
-angularService('$resource', function($xhr){
+angularServiceInject('$resource', function($xhr){
var resource = new ResourceFactory($xhr);
return bind(resource, resource.route);
-}, {inject: ['$xhr.cache']});
+}, ['$xhr.cache']);
/**
@@ -430,7 +435,7 @@ angularService('$resource', function($xhr){
* Only a simple Object is exposed and by adding or removing properties to/from this object, new
* cookies are created or deleted from the browser at the end of the current eval.
*/
-angularService('$cookies', function($browser) {
+angularServiceInject('$cookies', function($browser) {
var rootScope = this,
cookies = {},
lastCookies = {},
@@ -499,14 +504,14 @@ angularService('$cookies', function($browser) {
}
}
}
-}, {inject: ['$browser']});
+}, ['$browser'], EAGER_PUBLISHED);
/**
* $cookieStore provides a key-value (string-object) storage that is backed by session cookies.
* Objects put or retrieved from this storage are automatically serialized or deserialized.
*/
-angularService('$cookieStore', function($store) {
+angularServiceInject('$cookieStore', function($store) {
return {
get: function(/**string*/key) {
@@ -522,4 +527,4 @@ angularService('$cookieStore', function($store) {
}
};
-}, {inject: ['$cookies']});
+}, ['$cookies'], EAGER_PUBLISHED);
diff --git a/src/widgets.js b/src/widgets.js
index 90f6837d..c9dfab99 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -253,7 +253,7 @@ angularWidget('ng:include', function(element){
this.directives(true);
} else {
element[0]['ng:compiled'] = true;
- return function(element){
+ return extend(function(xhr, element){
var scope = this, childScope;
var changeCounter = 0;
function incrementChange(){ changeCounter++;}
@@ -266,7 +266,7 @@ angularWidget('ng:include', function(element){
var src = this.$eval(srcExp),
useScope = this.$eval(scopeExp);
if (src) {
- scope.$xhr.cache('GET', src, function(code, response){
+ xhr('GET', src, function(code, response){
element.html(response);
childScope = useScope || createScope(scope);
compiler.compile(element)(element, childScope);
@@ -276,7 +276,7 @@ angularWidget('ng:include', function(element){
element.html('');
}
});
- };
+ }, {$inject:['$xhr.cache']});
}
});