2010-04-06 04:26:52 +00:00
|
|
|
function getter(instance, path, unboundFn) {
|
2010-03-26 05:03:11 +00:00
|
|
|
if (!path) return instance;
|
|
|
|
|
var element = path.split('.');
|
|
|
|
|
var key;
|
|
|
|
|
var lastInstance = instance;
|
|
|
|
|
var len = element.length;
|
|
|
|
|
for ( var i = 0; i < len; i++) {
|
|
|
|
|
key = element[i];
|
|
|
|
|
if (!key.match(/^[\$\w][\$\w\d]*$/))
|
|
|
|
|
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
|
|
|
|
|
if (instance) {
|
|
|
|
|
lastInstance = instance;
|
|
|
|
|
instance = instance[key];
|
|
|
|
|
}
|
2010-03-26 23:27:18 +00:00
|
|
|
if (isUndefined(instance) && key.charAt(0) == '$') {
|
2010-03-26 05:03:11 +00:00
|
|
|
var type = angular['Global']['typeOf'](lastInstance);
|
|
|
|
|
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
|
2010-08-18 23:23:12 +00:00
|
|
|
var fn = type ? type[[key.substring(1)]] : _undefined;
|
2010-03-26 05:03:11 +00:00
|
|
|
if (fn) {
|
2010-04-07 17:17:15 +00:00
|
|
|
instance = bind(lastInstance, fn, lastInstance);
|
2010-03-26 05:03:11 +00:00
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-21 17:20:34 +00:00
|
|
|
if (!unboundFn && isFunction(instance)) {
|
2010-03-26 05:03:11 +00:00
|
|
|
return bind(lastInstance, instance);
|
|
|
|
|
}
|
|
|
|
|
return instance;
|
2010-04-04 00:04:36 +00:00
|
|
|
}
|
2010-03-26 05:03:11 +00:00
|
|
|
|
|
|
|
|
function setter(instance, path, value){
|
|
|
|
|
var element = path.split('.');
|
|
|
|
|
for ( var i = 0; element.length > 1; i++) {
|
|
|
|
|
var key = element.shift();
|
|
|
|
|
var newInstance = instance[key];
|
|
|
|
|
if (!newInstance) {
|
|
|
|
|
newInstance = {};
|
|
|
|
|
instance[key] = newInstance;
|
|
|
|
|
}
|
|
|
|
|
instance = newInstance;
|
|
|
|
|
}
|
|
|
|
|
instance[element.shift()] = value;
|
|
|
|
|
return value;
|
2010-04-04 00:04:36 +00:00
|
|
|
}
|
2010-03-26 05:03:11 +00:00
|
|
|
|
2010-05-31 02:42:21 +00:00
|
|
|
///////////////////////////////////
|
2010-09-14 21:22:15 +00:00
|
|
|
var scopeId = 0,
|
2010-08-11 19:04:02 +00:00
|
|
|
getterFnCache = {},
|
|
|
|
|
compileCache = {},
|
|
|
|
|
JS_KEYWORDS = {};
|
2010-07-08 17:40:54 +00:00
|
|
|
foreach(
|
2010-07-15 16:41:25 +00:00
|
|
|
["abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default",
|
2010-08-18 23:23:12 +00:00
|
|
|
"delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", $function, "goto",
|
|
|
|
|
"if", "implements", "import", "ininstanceof", "intinterface", "long", "native", "new", $null, "package", "private",
|
2010-07-15 16:41:25 +00:00
|
|
|
"protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws",
|
2010-08-18 23:23:12 +00:00
|
|
|
"transient", "true", "try", "typeof", "var", "volatile", "void", $undefined, "while", "with"],
|
2010-07-08 17:40:54 +00:00
|
|
|
function(key){ JS_KEYWORDS[key] = true;}
|
|
|
|
|
);
|
2010-05-31 02:42:21 +00:00
|
|
|
function getterFn(path){
|
|
|
|
|
var fn = getterFnCache[path];
|
|
|
|
|
if (fn) return fn;
|
|
|
|
|
|
2010-10-15 04:01:25 +00:00
|
|
|
var code = 'var l, fn, t;\n';
|
2010-05-31 02:42:21 +00:00
|
|
|
foreach(path.split('.'), function(key) {
|
2010-07-08 17:40:54 +00:00
|
|
|
key = (JS_KEYWORDS[key]) ? '["' + key + '"]' : '.' + key;
|
2010-10-15 04:01:25 +00:00
|
|
|
code += 'if(!s) return s;\n' +
|
|
|
|
|
'l=s;\n' +
|
|
|
|
|
's=s' + key + ';\n' +
|
|
|
|
|
'if(typeof s=="function") s = function(){ return l'+key+'.apply(l, arguments); };\n';
|
2010-06-02 22:05:34 +00:00
|
|
|
if (key.charAt(1) == '$') {
|
2010-05-31 02:42:21 +00:00
|
|
|
// special code for super-imposed functions
|
2010-06-02 22:05:34 +00:00
|
|
|
var name = key.substr(2);
|
2010-10-15 04:01:25 +00:00
|
|
|
code += 'if(!s) {\n' +
|
|
|
|
|
' t = angular.Global.typeOf(l);\n' +
|
|
|
|
|
' fn = (angular[t.charAt(0).toUpperCase() + t.substring(1)]||{})["' + name + '"];\n' +
|
|
|
|
|
' if (fn) s = function(){ return fn.apply(l, [l].concat(Array.prototype.slice.call(arguments, 0, arguments.length))); };\n' +
|
|
|
|
|
'}\n';
|
2010-05-31 02:42:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
2010-10-15 04:01:25 +00:00
|
|
|
code += 'return s;';
|
|
|
|
|
fn = Function('s', code);
|
2010-07-03 00:26:26 +00:00
|
|
|
fn["toString"] = function(){ return code; };
|
2010-05-31 02:42:21 +00:00
|
|
|
|
|
|
|
|
return getterFnCache[path] = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////
|
|
|
|
|
|
2010-03-26 05:03:11 +00:00
|
|
|
function expressionCompile(exp){
|
2010-08-18 23:23:12 +00:00
|
|
|
if (typeof exp === $function) return exp;
|
2010-05-30 23:45:35 +00:00
|
|
|
var fn = compileCache[exp];
|
|
|
|
|
if (!fn) {
|
2010-03-26 05:03:11 +00:00
|
|
|
var parser = new Parser(exp);
|
2010-05-30 23:45:35 +00:00
|
|
|
var fnSelf = parser.statements();
|
2010-03-26 05:03:11 +00:00
|
|
|
parser.assertAllConsumed();
|
2010-05-30 23:45:35 +00:00
|
|
|
fn = compileCache[exp] = extend(
|
|
|
|
|
function(){ return fnSelf(this);},
|
|
|
|
|
{fnSelf: fnSelf});
|
2010-03-26 05:03:11 +00:00
|
|
|
}
|
2010-05-30 23:45:35 +00:00
|
|
|
return fn;
|
2010-03-26 23:27:18 +00:00
|
|
|
}
|
2010-03-26 05:03:11 +00:00
|
|
|
|
2010-03-30 22:39:51 +00:00
|
|
|
function errorHandlerFor(element, error) {
|
2010-03-31 20:57:25 +00:00
|
|
|
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
|
2010-03-26 05:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
function createScope(parent, providers, instanceCache) {
|
2010-03-26 05:03:11 +00:00
|
|
|
function Parent(){}
|
2010-04-04 00:04:36 +00:00
|
|
|
parent = Parent.prototype = (parent || {});
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
var instance = new Parent();
|
2010-09-21 17:20:34 +00:00
|
|
|
var evalLists = {sorted:[]};
|
|
|
|
|
var postList = [], postHash = {}, postId = 0;
|
2010-03-26 05:03:11 +00:00
|
|
|
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
extend(instance, {
|
2010-04-02 18:10:36 +00:00
|
|
|
'this': instance,
|
2010-04-02 18:49:48 +00:00
|
|
|
$id: (scopeId++),
|
2010-03-26 05:03:11 +00:00
|
|
|
$parent: parent,
|
|
|
|
|
$bind: bind(instance, bind, instance),
|
|
|
|
|
$get: bind(instance, getter, instance),
|
|
|
|
|
$set: bind(instance, setter, instance),
|
|
|
|
|
|
2010-03-30 04:49:12 +00:00
|
|
|
$eval: function $eval(exp) {
|
2010-08-10 18:23:23 +00:00
|
|
|
var type = typeof exp;
|
2010-09-21 17:20:34 +00:00
|
|
|
var i, iSize;
|
|
|
|
|
var j, jSize;
|
|
|
|
|
var queue;
|
|
|
|
|
var fn;
|
2010-08-18 23:23:12 +00:00
|
|
|
if (type == $undefined) {
|
2010-09-21 17:20:34 +00:00
|
|
|
for ( i = 0, iSize = evalLists.sorted.length; i < iSize; i++) {
|
|
|
|
|
for ( queue = evalLists.sorted[i],
|
2010-07-26 22:54:50 +00:00
|
|
|
jSize = queue.length,
|
|
|
|
|
j= 0; j < jSize; j++) {
|
2010-06-02 22:05:34 +00:00
|
|
|
instance.$tryEval(queue[j].fn, queue[j].handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-21 17:20:34 +00:00
|
|
|
while(postList.length) {
|
|
|
|
|
fn = postList.shift();
|
|
|
|
|
delete postHash[fn.$postEvalId];
|
|
|
|
|
instance.$tryEval(fn);
|
|
|
|
|
}
|
2010-08-18 23:23:12 +00:00
|
|
|
} else if (type === $function) {
|
2010-07-26 22:54:50 +00:00
|
|
|
return exp.call(instance);
|
2010-08-10 18:23:23 +00:00
|
|
|
} else if (type === 'string') {
|
2010-07-26 22:54:50 +00:00
|
|
|
return expressionCompile(exp).call(instance);
|
2010-03-26 05:03:11 +00:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
$tryEval: function (expression, exceptionHandler) {
|
2010-08-10 18:23:23 +00:00
|
|
|
var type = typeof expression;
|
2010-03-26 05:03:11 +00:00
|
|
|
try {
|
2010-08-18 23:23:12 +00:00
|
|
|
if (type == $function) {
|
2010-07-26 22:54:50 +00:00
|
|
|
return expression.call(instance);
|
2010-08-10 18:23:23 +00:00
|
|
|
} else if (type == 'string'){
|
2010-07-26 22:54:50 +00:00
|
|
|
return expressionCompile(expression).call(instance);
|
|
|
|
|
}
|
2010-03-26 05:03:11 +00:00
|
|
|
} catch (e) {
|
2010-07-21 00:13:31 +00:00
|
|
|
(instance.$log || {error:error}).error(e);
|
2010-03-26 05:03:11 +00:00
|
|
|
if (isFunction(exceptionHandler)) {
|
|
|
|
|
exceptionHandler(e);
|
|
|
|
|
} else if (exceptionHandler) {
|
2010-03-30 22:39:51 +00:00
|
|
|
errorHandlerFor(exceptionHandler, e);
|
2010-04-15 21:17:33 +00:00
|
|
|
} else if (isFunction(instance.$exceptionHandler)) {
|
|
|
|
|
instance.$exceptionHandler(e);
|
2010-03-26 05:03:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
$watch: function(watchExp, listener, exceptionHandler) {
|
2010-04-01 00:56:16 +00:00
|
|
|
var watch = expressionCompile(watchExp),
|
2010-04-05 18:46:53 +00:00
|
|
|
last;
|
2010-07-26 22:32:08 +00:00
|
|
|
listener = expressionCompile(listener);
|
2010-04-05 18:46:53 +00:00
|
|
|
function watcher(){
|
2010-05-07 20:43:54 +00:00
|
|
|
var value = watch.call(instance),
|
|
|
|
|
lastValue = last;
|
2010-04-01 00:56:16 +00:00
|
|
|
if (last !== value) {
|
|
|
|
|
last = value;
|
2010-07-26 22:32:08 +00:00
|
|
|
instance.$tryEval(function(){
|
|
|
|
|
return listener.call(instance, value, lastValue);
|
|
|
|
|
}, exceptionHandler);
|
2010-04-01 00:56:16 +00:00
|
|
|
}
|
2010-04-05 18:46:53 +00:00
|
|
|
}
|
|
|
|
|
instance.$onEval(PRIORITY_WATCH, watcher);
|
|
|
|
|
watcher();
|
2010-03-26 05:03:11 +00:00
|
|
|
},
|
|
|
|
|
|
2010-04-01 00:56:16 +00:00
|
|
|
$onEval: function(priority, expr, exceptionHandler){
|
|
|
|
|
if (!isNumber(priority)) {
|
|
|
|
|
exceptionHandler = expr;
|
|
|
|
|
expr = priority;
|
|
|
|
|
priority = 0;
|
|
|
|
|
}
|
2010-04-16 21:01:29 +00:00
|
|
|
var evalList = evalLists[priority];
|
|
|
|
|
if (!evalList) {
|
|
|
|
|
evalList = evalLists[priority] = [];
|
|
|
|
|
evalList.priority = priority;
|
|
|
|
|
evalLists.sorted.push(evalList);
|
|
|
|
|
evalLists.sorted.sort(function(a,b){return a.priority-b.priority;});
|
|
|
|
|
}
|
2010-03-26 05:03:11 +00:00
|
|
|
evalList.push({
|
|
|
|
|
fn: expressionCompile(expr),
|
|
|
|
|
handler: exceptionHandler
|
|
|
|
|
});
|
2010-04-06 04:26:52 +00:00
|
|
|
},
|
|
|
|
|
|
2010-09-21 17:20:34 +00:00
|
|
|
$postEval: function(expr) {
|
|
|
|
|
if (expr) {
|
|
|
|
|
var fn = expressionCompile(expr);
|
|
|
|
|
var id = fn.$postEvalId;
|
|
|
|
|
if (!id) {
|
|
|
|
|
id = '$' + instance.$id + "_" + (postId++);
|
|
|
|
|
fn.$postEvalId = id;
|
|
|
|
|
}
|
|
|
|
|
if (!postHash[id]) {
|
|
|
|
|
postList.push(postHash[id] = fn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2010-04-06 04:26:52 +00:00
|
|
|
$become: function(Class) {
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
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));
|
2010-10-14 19:36:29 +00:00
|
|
|
|
|
|
|
|
//TODO: backwards compatibility hack, remove when we don't depend on init methods
|
|
|
|
|
if (isFunction(Class.prototype.init)) {
|
|
|
|
|
instance.init();
|
|
|
|
|
}
|
2010-10-11 21:31:32 +00:00
|
|
|
}
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
$new: function(Class) {
|
|
|
|
|
var child = createScope(instance);
|
|
|
|
|
child.$become.apply(instance, concat([Class], arguments, 1));
|
|
|
|
|
instance.$onEval(child.$eval);
|
|
|
|
|
return child;
|
2010-03-26 05:03:11 +00:00
|
|
|
}
|
2010-04-06 04:26:52 +00:00
|
|
|
|
2010-03-26 05:03:11 +00:00
|
|
|
});
|
|
|
|
|
|
2010-04-04 00:04:36 +00:00
|
|
|
if (!parent.$root) {
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
instance.$root = instance;
|
|
|
|
|
instance.$parent = instance;
|
|
|
|
|
(instance.$inject = createInjector(instance, providers, instanceCache))();
|
2010-04-04 00:04:36 +00:00
|
|
|
}
|
|
|
|
|
|
2010-03-26 05:03:11 +00:00
|
|
|
return instance;
|
|
|
|
|
}
|