diff options
Diffstat (limited to 'src/Scope.js')
| -rw-r--r-- | src/Scope.js | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/Scope.js b/src/Scope.js new file mode 100644 index 00000000..637fc25e --- /dev/null +++ b/src/Scope.js @@ -0,0 +1,224 @@ +function getter(instance, path, unboundFn) { + 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]; + } + if (isUndefined(instance) && key.charAt(0) == '$') { + var type = angular['Global']['typeOf'](lastInstance); + type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; + var fn = type ? type[[key.substring(1)]] : undefined; + if (fn) { + instance = bind(lastInstance, fn, lastInstance); + return instance; + } + } + } + if (!unboundFn && isFunction(instance) && !instance['$$factory']) { + return bind(lastInstance, instance); + } + return instance; +} + +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; +} + +/////////////////////////////////// + +var getterFnCache = {}; +function getterFn(path){ + var fn = getterFnCache[path]; + if (fn) return fn; + + var code = 'function (self){\n'; + code += ' var last, fn, type;\n'; + foreach(path.split('.'), function(key) { + key = (key == 'this') ? '["this"]' : '.' + key; + code += ' if(!self) return self;\n'; + code += ' last = self;\n'; + code += ' self = self' + key + ';\n'; + code += ' if(typeof self == "function") \n'; + code += ' self = function(){ return last'+key+'.apply(last, arguments); };\n'; + if (key.charAt(1) == '$') { + // special code for super-imposed functions + var name = key.substr(2); + code += ' if(!self) {\n'; + code += ' type = angular.Global.typeOf(last);\n'; + code += ' fn = (angular[type.charAt(0).toUpperCase() + type.substring(1)]||{})["' + name + '"];\n'; + code += ' if (fn)\n'; + code += ' self = function(){ return fn.apply(last, [last].concat(slice.call(arguments, 0, arguments.length))); };\n'; + code += ' }\n'; + } + }); + code += ' return self;\n}'; + fn = eval('(' + code + ')'); + fn.toString = function(){ return code; }; + + return getterFnCache[path] = fn; +} + +/////////////////////////////////// + +var compileCache = {}; +function expressionCompile(exp){ + if (isFunction(exp)) return exp; + var fn = compileCache[exp]; + if (!fn) { + var parser = new Parser(exp); + var fnSelf = parser.statements(); + parser.assertAllConsumed(); + fn = compileCache[exp] = extend( + function(){ return fnSelf(this);}, + {fnSelf: fnSelf}); + } + return fn; +} + +function rethrow(e) { throw e; } +function errorHandlerFor(element, error) { + elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error); +} + +var scopeId = 0; +function createScope(parent, services, existing) { + function Parent(){} + function API(){} + function Behavior(){} + + var instance, behavior, api, evalLists = {sorted:[]}, servicesCache = extend({}, existing); + + parent = Parent.prototype = (parent || {}); + api = API.prototype = new Parent(); + behavior = Behavior.prototype = new API(); + instance = new Behavior(); + + extend(api, { + 'this': instance, + $id: (scopeId++), + $parent: parent, + $bind: bind(instance, bind, instance), + $get: bind(instance, getter, instance), + $set: bind(instance, setter, instance), + + $eval: function $eval(exp) { + if (exp !== undefined) { + return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length)); + } else { + for ( var i = 0, iSize = evalLists.sorted.length; i < iSize; i++) { + for ( var queue = evalLists.sorted[i], + jSize = queue.length, + j= 0; j < jSize; j++) { + instance.$tryEval(queue[j].fn, queue[j].handler); + } + } + } + }, + + $tryEval: function (expression, exceptionHandler) { + try { + return expressionCompile(expression).apply(instance, slice.call(arguments, 2, arguments.length)); + } catch (e) { + error(e); + if (isFunction(exceptionHandler)) { + exceptionHandler(e); + } else if (exceptionHandler) { + errorHandlerFor(exceptionHandler, e); + } else if (isFunction(instance.$exceptionHandler)) { + instance.$exceptionHandler(e); + } + } + }, + + $watch: function(watchExp, listener, exceptionHandler) { + var watch = expressionCompile(watchExp), + last; + function watcher(){ + var value = watch.call(instance), + lastValue = last; + if (last !== value) { + last = value; + instance.$tryEval(listener, exceptionHandler, value, lastValue); + } + } + instance.$onEval(PRIORITY_WATCH, watcher); + watcher(); + }, + + $onEval: function(priority, expr, exceptionHandler){ + if (!isNumber(priority)) { + exceptionHandler = expr; + expr = priority; + priority = 0; + } + 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;}); + } + evalList.push({ + fn: expressionCompile(expr), + handler: exceptionHandler + }); + }, + + $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); + } + + }); + + 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; + } + + foreach(services, function(_, name){ + var service = inject(name); + if (service) { + setter(instance, name, service); + } + }); + + return instance; +} |
