diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Angular.js | 71 | ||||
| -rw-r--r-- | src/Compiler.js | 1 | ||||
| -rw-r--r-- | src/Parser.js | 4 | ||||
| -rw-r--r-- | src/Scope.js | 55 | ||||
| -rw-r--r-- | src/UrlWatcher.js | 40 | ||||
| -rw-r--r-- | src/services.js | 34 |
6 files changed, 139 insertions, 66 deletions
diff --git a/src/Angular.js b/src/Angular.js index db177082..8eef6ac0 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -4,6 +4,9 @@ if (typeof document.getAttribute == 'undefined') if (!window['console']) window['console']={'log':noop, 'error':noop}; var consoleNode, + PRIORITY_FIRST = -99999; + PRIORITY_WATCH = -1000; + PRIORITY_LAST = 99999; NOOP = 'noop', NG_ERROR = 'ng-error', NG_EXCEPTION = 'ng-exception', @@ -21,11 +24,30 @@ var consoleNode, angularValidator = extensionMap(angular, 'validator'), angularFilter = extensionMap(angular, 'filter'), angularFormatter = extensionMap(angular, 'formatter'), + angularService = extensionMap(angular, 'service'), angularCallbacks = extensionMap(angular, 'callbacks'), - angularAlert = angular['alert'] || (angular['alert'] = function(){ - log(arguments); window.alert.apply(window, arguments); - }); -angular['copy'] = copy; + urlWatcher = new UrlWatcher(window.location); + +function angularAlert(){ + log(arguments); window.alert.apply(window, arguments); +}; + +extend(angular, { + 'compile': compile, + 'startUrlWatch': bind(urlWatcher, urlWatcher.start), + 'copy': copy, + 'extend': extend, + 'foreach': foreach, + 'noop':noop, + 'identity':identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isNumber': isNumber, + 'isArray': isArray, + 'alert': angularAlert +}); function foreach(obj, iterator, context) { var key; @@ -43,6 +65,17 @@ function foreach(obj, iterator, context) { return obj; } +function foreachSorted(obj, iterator, context) { + var keys = []; + for (var key in obj) keys.push(key); + keys.sort(); + for ( var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + function extend(dst) { foreach(arguments, function(obj){ if (obj !== dst) { @@ -285,19 +318,21 @@ function merge(src, dst) { } } -///////////////////////////////////////////////// - -angular['compile'] = function(element, config) { - config = extend({ - 'onUpdateView': noop, - 'server': "", - 'location': {'get':noop, 'set':noop, 'listen':noop} - }, config||{}); - +function compile(element, config) { var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); $element = jqLite(element), - rootScope = { - '$window': window - }; - return rootScope['$root'] = compiler.compile($element)($element, rootScope); -}; + rootScope = createScope({ + $element: $element, + $config: extend({ + 'onUpdateView': noop, + 'server': "", + 'location': { + 'get':bind(urlWatcher, urlWatcher.get), + 'set':bind(urlWatcher, urlWatcher.set), + 'watch':bind(urlWatcher, urlWatcher.watch) + } + }, config || {}) + }, serviceAdapter(angularService)); + return compiler.compile($element)($element, rootScope); +} +///////////////////////////////////////////////// diff --git a/src/Compiler.js b/src/Compiler.js index 361d6946..c9039928 100644 --- a/src/Compiler.js +++ b/src/Compiler.js @@ -65,7 +65,6 @@ Compiler.prototype = { element = jqLite(element); parentScope = parentScope || {}; var scope = createScope(parentScope); - parentScope.$root = parentScope.$root || scope; return extend(scope, { $element:element, $init: function() { diff --git a/src/Parser.js b/src/Parser.js index ef1465a0..ec58295a 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -11,8 +11,8 @@ Lexer.OPERATORS = { 'true':function(self){return true;}, 'false':function(self){return false;}, 'undefined':noop, - '+':function(self, a,b){return (a||0)+(b||0);}, - '-':function(self, a,b){return (a||0)-(b||0);}, + '+':function(self, a,b){return (isDefined(a)?a:0)+(isDefined(b)?b:0);}, + '-':function(self, a,b){return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, '*':function(self, a,b){return a*b;}, '/':function(self, a,b){return a/b;}, '%':function(self, a,b){return a%b;}, diff --git a/src/Scope.js b/src/Scope.js index 4144d456..ba86e24f 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -89,7 +89,11 @@ function createScope(parent, Class) { function API(){} function Behavior(){} - var instance, behavior, api, watchList = [], evalList = []; + var instance, behavior, api, evalLists = {}; + if (isFunction(parent)) { + Class = parent; + parent = {}; + } Class = Class || noop; parent = Parent.prototype = parent || {}; @@ -107,15 +111,10 @@ function createScope(parent, Class) { if (isDefined(exp)) { return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length)); } else { - foreach(watchList, function(watch) { - var value = instance.$tryEval(watch.watch, watch.handler); - if (watch.last !== value) { - instance.$tryEval(watch.listener, watch.handler, value, watch.last); - watch.last = value; - } - }); - foreach(evalList, function(eval) { - instance.$tryEval(eval.fn, eval.handler); + foreachSorted(evalLists, function(list) { + foreach(list, function(eval) { + instance.$tryEval(eval.fn, eval.handler); + }); }); } }, @@ -134,16 +133,24 @@ function createScope(parent, Class) { }, $watch: function(watchExp, listener, exceptionHandler) { - var watch = expressionCompile(watchExp); - watchList.push({ - watch: watch, - last: watch.call(instance), - handler: exceptionHandler, - listener:expressionCompile(listener) + var watch = expressionCompile(watchExp), + last = watch.call(instance); + instance.$onEval(PRIORITY_WATCH, function(){ + var value = watch.call(instance); + if (last !== value) { + instance.$tryEval(listener, exceptionHandler, value, last); + last = value; + } }); }, - $onEval: function(expr, exceptionHandler){ + $onEval: function(priority, expr, exceptionHandler){ + if (!isNumber(priority)) { + exceptionHandler = expr; + expr = priority; + priority = 0; + } + var evalList = evalLists[priority] || (evalLists[priority] = []); evalList.push({ fn: expressionCompile(expr), handler: exceptionHandler @@ -151,7 +158,21 @@ function createScope(parent, Class) { } }); + if (isUndefined(instance.$root)) { + behavior.$root = instance; + behavior.$parent = instance; + } + Class.apply(instance, slice.call(arguments, 2, arguments.length)); return instance; } + +function serviceAdapter(services) { + return function(){ + var self = this; + foreach(services, function(service, name){ + self[name] = service.call(self); + }); + }; +}; diff --git a/src/UrlWatcher.js b/src/UrlWatcher.js index 0892eb1a..1b2a9cf0 100644 --- a/src/UrlWatcher.js +++ b/src/UrlWatcher.js @@ -9,42 +9,26 @@ function UrlWatcher(location) { this.setTimeout = function(fn, delay) { window.setTimeout(fn, delay); }; - this.listener = function(url) { - return url; - }; this.expectedUrl = location.href; + this.listeners = []; } UrlWatcher.prototype = { - listen: function(fn){ - this.listener = fn; + watch: function(fn){ + this.listeners.push(fn); }, - watch: function() { + + start: function() { var self = this; - var pull = function() { + (function pull () { if (self.expectedUrl !== self.location.href) { - var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/); - if (notify) { - if (!self.expectedUrl.match(/#/)) { - self.expectedUrl += "#"; - } - self.location.href = self.expectedUrl; - var id = '_iframe_notify_' + notify[1]; - var notifyFn = angularCallbacks[id]; - delete angularCallbacks[id]; - try { - (notifyFn||noop)(); - } catch (e) { - alert(e); - } - } else { - self.listener(self.location.href); - self.expectedUrl = self.location.href; - } + foreach(self.listeners, function(listener){ + listener(self.location.href); + }); + self.expectedUrl = self.location.href; } self.setTimeout(pull, self.delay); - }; - pull(); + })(); }, set: function(url) { @@ -57,6 +41,6 @@ UrlWatcher.prototype = { }, get: function() { - return window.location.href; + return this.location.href; } }; diff --git a/src/services.js b/src/services.js new file mode 100644 index 00000000..14c71363 --- /dev/null +++ b/src/services.js @@ -0,0 +1,34 @@ +angularService("$window", bind(window, identity, window)); + +angularService("$anchor", function(){ + var scope = this; + function anchor(url){ + if (isDefined(url)) { + if (url.charAt(0) == '#') url = url.substr(1); + var pathQuery = url.split('?'); + anchor.path = decodeURIComponent(pathQuery[0]); + anchor.param = {}; + foreach((pathQuery[1] || "").split('&'), function(keyValue){ + if (keyValue) { + var parts = keyValue.split('='); + var key = decodeURIComponent(parts[0]); + var value = parts[1]; + if (!value) value = true; + anchor.param[key] = decodeURIComponent(value); + } + }); + } + var params = []; + foreach(anchor.param, function(value, key){ + params.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + return (anchor.path ? anchor.path : '') + (params.length ? '?' + params.join('&') : ''); + }; + this.$config.location.watch(function(url){ + anchor(url); + }); + this.$onEval(PRIORITY_LAST, function(){ + scope.$config.location.set(anchor()); + }); + return anchor; +}); |
