vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.0.2 vs vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.0.3

- old
+ new

@@ -9402,11 +9402,11 @@ })( window ); /** - * @license AngularJS v1.0.2 + * @license AngularJS v1.0.3 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document){ var _jQuery = window.jQuery.noConflict(true); @@ -10197,11 +10197,11 @@ return parts.length ? parts.join('&') : ''; } /** - * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow + * We need our custom method because encodeURIComponent is too agressive and doesn't follow * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path * segments: * segment = *pchar * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * pct-encoded = "%" HEXDIG HEXDIG @@ -10241,11 +10241,11 @@ /** * @ngdoc directive * @name ng.directive:ngApp * * @element ANY - * @param {angular.Module} ngApp on optional application + * @param {angular.Module} ngApp an optional application * {@link angular.module module} name to load. * * @description * * Use this directive to auto-bootstrap on application. Only @@ -10418,11 +10418,11 @@ * registered using this mechanism. * * * # Module * - * A module is a collocation of services, directives, filters, and configure information. Module + * A module is a collocation of services, directives, filters, and configuration information. Module * is used to configure the {@link AUTO.$injector $injector}. * * <pre> * // Create a new module * var myModule = angular.module('myModule', []); @@ -10448,11 +10448,11 @@ * {@link angular.bootstrap} to simplify this process for you. * * @param {!string} name The name of the module to create or retrieve. * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the * the module is being retrieved for further configuration. - * @param {Function} configFn Option configuration function for the module. Same as + * @param {Function} configFn Optional configuration function for the module. Same as * {@link angular.Module#config Module#config()}. * @returns {module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { if (requires && modules.hasOwnProperty(name)) { @@ -10603,12 +10603,12 @@ * @name angular.Module#run * @methodOf angular.Module * @param {Function} initializationFn Execute this function after injector creation. * Useful for application initialization. * @description - * Use this method to register work which needs to be performed when the injector with - * with the current module is finished loading. + * Use this method to register work which should be performed when the injector is done + * loading all modules. */ run: function(block) { runBlocks.push(block); return this; } @@ -10650,15 +10650,15 @@ * - `minor` – `{number}` – Minor version number, such as "9". * - `dot` – `{number}` – Dot version number, such as "18". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.0.2', // all of these placeholder strings will be replaced by rake's + full: '1.0.3', // all of these placeholder strings will be replaced by rake's major: 1, // compile task minor: 0, - dot: 2, - codeName: 'debilitating-awesomeness' + dot: 3, + codeName: 'bouncy-thunder' }; function publishExternalAPI(angular){ extend(angular, { @@ -10821,10 +10821,11 @@ * - [removeClass()](http://api.jquery.com/removeClass/) * - [removeData()](http://api.jquery.com/removeData/) * - [replaceWith()](http://api.jquery.com/replaceWith/) * - [text()](http://api.jquery.com/text/) * - [toggleClass()](http://api.jquery.com/toggleClass/) + * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers. * - [unbind()](http://api.jquery.com/unbind/) * - [val()](http://api.jquery.com/val/) * - [wrap()](http://api.jquery.com/wrap/) * * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite: @@ -10896,16 +10897,11 @@ while(list.length) { set = list.shift(); for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { element = jqLite(set[setIndex]); if (fireEvent) { - events = element.data('events'); - if ( (fns = events && events.$destroy) ) { - forEach(fns, function(fn){ - fn.handler(); - }); - } + element.triggerHandler('$destroy'); } else { fireEvent = !fireEvent; } for(childIndex = 0, childLength = (children = element.children()).length; childIndex < childLength; @@ -11033,25 +11029,25 @@ function JQLiteHasClass(element, selector) { return ((" " + element.className + " ").replace(/[\n\t]/g, " "). indexOf( " " + selector + " " ) > -1); } -function JQLiteRemoveClass(element, selector) { - if (selector) { - forEach(selector.split(' '), function(cssClass) { +function JQLiteRemoveClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { element.className = trim( (" " + element.className + " ") .replace(/[\n\t]/g, " ") .replace(" " + trim(cssClass) + " ", " ") ); }); } } -function JQLiteAddClass(element, selector) { - if (selector) { - forEach(selector.split(' '), function(cssClass) { +function JQLiteAddClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { if (!JQLiteHasClass(element, cssClass)) { element.className = trim(element.className + ' ' + trim(cssClass)); } }); } @@ -11495,11 +11491,19 @@ find: function(element, selector) { return element.getElementsByTagName(selector); }, - clone: JQLiteClone + clone: JQLiteClone, + + triggerHandler: function(element, eventName) { + var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; + + forEach(eventFns, function(fn) { + fn.call(element, null); + }); + } }, function(fn, name){ /** * chaining functions */ JQLite.prototype[name] = function(arg1, arg2) { @@ -11613,10 +11617,20 @@ return array[0]; } else { return array.shift(); } } + }, + + /** + * return the first item without deleting it + */ + peek: function(key) { + var array = this[hashKey(key)]; + if (array) { + return array[0]; + } } }; /** * @ngdoc function @@ -11636,11 +11650,11 @@ * Typical usage * <pre> * // create an injector * var $injector = angular.injector(['ng']); * - * // use the injector to kick of your application + * // use the injector to kick off your application * // use the type inference to auto inject arguments, or use implicit injection * $injector.invoke(function($rootScope, $compile, $document){ * $compile($document)($rootScope); * $rootScope.$digest(); * }); @@ -11656,11 +11670,11 @@ * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. */ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(_?)(.+?)\1\s*$/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; function annotate(fn) { var $inject, fnText, argDecl, @@ -12275,13 +12289,14 @@ } // does not scroll when user clicks on anchor link that is currently on // (no url change, no $locaiton.hash() change), browser native does scroll if (autoScrollingEnabled) { - $rootScope.$watch(function() {return $location.hash();}, function() { - $rootScope.$evalAsync(scroll); - }); + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction() { + $rootScope.$evalAsync(scroll); + }); } return scroll; }]; } @@ -12661,14 +12676,14 @@ * * @returns {object} Newly created cache object with the following set of methods: * * - `{object}` `info()` — Returns id, size, and options of cache. * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache. - * - `{{*}} `get({string} key) — Returns cached value for `key` or undefined for cache miss. - * - `{void}` `remove({string} key) — Removes a key-value pair from the cache. - * - `{void}` `removeAll() — Removes all cached values. - * - `{void}` `destroy() — Removes references to this cache from $cacheFactory. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * */ function $CacheFactoryProvider() { this.$get = function() { @@ -12716,10 +12731,12 @@ remove: function(key) { var lruEntry = lruHash[key]; + if (!lruEntry) return; + if (lruEntry == freshEnd) freshEnd = lruEntry.p; if (lruEntry == staleEnd) staleEnd = lruEntry.n; link(lruEntry.n,lruEntry.p); delete lruHash[key]; @@ -13123,30 +13140,30 @@ return compile; //================================ - function compile($compileNode, transcludeFn, maxPriority) { - if (!($compileNode instanceof jqLite)) { + function compile($compileNodes, transcludeFn, maxPriority) { + if (!($compileNodes instanceof jqLite)) { // jquery always rewraps, where as we need to preserve the original selector so that we can modify it. - $compileNode = jqLite($compileNode); + $compileNodes = jqLite($compileNodes); } // We can not compile top level text elements since text nodes can be merged and we will // not be able to attach scope data to them, so we will wrap them in <span> - forEach($compileNode, function(node, index){ + forEach($compileNodes, function(node, index){ if (node.nodeType == 3 /* text node */) { - $compileNode[index] = jqLite(node).wrap('<span></span>').parent()[0]; + $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; } }); - var compositeLinkFn = compileNodes($compileNode, transcludeFn, $compileNode, maxPriority); - return function(scope, cloneConnectFn){ + var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority); + return function publicLinkFn(scope, cloneConnectFn){ assertArg(scope, 'scope'); // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. var $linkNode = cloneConnectFn - ? JQLitePrototype.clone.call($compileNode) // IMPORTANT!!! - : $compileNode; + ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! + : $compileNodes; $linkNode.data('$scope', scope); safeAddClass($linkNode, 'ng-scope'); if (cloneConnectFn) cloneConnectFn($linkNode, scope); if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode); return $linkNode; @@ -13193,11 +13210,11 @@ nodeLinkFn = (directives.length) ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement) : null; - childLinkFn = (nodeLinkFn && nodeLinkFn.terminal) + childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length) ? null : compileNodes(nodeList[i].childNodes, nodeLinkFn ? nodeLinkFn.transclude : transcludeFn); linkFns.push(nodeLinkFn); @@ -13245,17 +13262,18 @@ } } /** - * Looks for directives on the given node ands them to the directive collection which is sorted. + * Looks for directives on the given node and adds them to the directive collection which is + * sorted. * - * @param node node to search - * @param directives an array to which the directives are added to. This array is sorted before + * @param node Node to search. + * @param directives An array to which the directives are added to. This array is sorted before * the function returns. - * @param attrs the shared attrs object which is used to populate the normalized attributes. - * @param {number=} max directive priority + * @param attrs The shared attrs object which is used to populate the normalized attributes. + * @param {number=} maxPriority Max directive priority. */ function collectDirectives(node, directives, attrs, maxPriority) { var nodeType = node.nodeType, attrsMap = attrs.$attr, match, @@ -13286,11 +13304,11 @@ } } // use class as directive className = node.className; - if (isString(className)) { + if (isString(className) && className !== '') { while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { nName = directiveNormalize(match[2]); if (addDirective(directives, nName, 'C', maxPriority)) { attrs[nName] = trim(match[3]); } @@ -13340,11 +13358,11 @@ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) { var terminalPriority = -Number.MAX_VALUE, preLinkFns = [], postLinkFns = [], newScopeDirective = null, - newIsolatedScopeDirective = null, + newIsolateScopeDirective = null, templateDirective = null, $compileNode = templateAttrs.$$element = jqLite(compileNode), directive, directiveName, $template, @@ -13362,14 +13380,14 @@ if (terminalPriority > directive.priority) { break; // prevent further processing of directives } if (directiveValue = directive.scope) { - assertNoDuplicate('isolated scope', newIsolatedScopeDirective, directive, $compileNode); + assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode); if (isObject(directiveValue)) { safeAddClass($compileNode, 'ng-isolate-scope'); - newIsolatedScopeDirective = directive; + newIsolateScopeDirective = directive; } safeAddClass($compileNode, 'ng-scope'); newScopeDirective = newScopeDirective || directive; } @@ -13519,16 +13537,16 @@ } else { attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); } $element = attrs.$$element; - if (newScopeDirective && isObject(newScopeDirective.scope)) { + if (newIsolateScopeDirective) { var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/; var parentScope = scope.$parent || scope; - forEach(newScopeDirective.scope, function(definiton, scopeName) { + forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) { var match = definiton.match(LOCAL_REGEXP) || [], attrName = match[2]|| scopeName, mode = match[1], // @, =, or & lastValue, parentGet, parentSet; @@ -13547,24 +13565,24 @@ parentGet = $parse(attrs[attrName]); parentSet = parentGet.assign || function() { // reset the change, or we will throw this exception on every $digest lastValue = scope[scopeName] = parentGet(parentScope); throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + - ' (directive: ' + newScopeDirective.name + ')'); + ' (directive: ' + newIsolateScopeDirective.name + ')'); }; lastValue = scope[scopeName] = parentGet(parentScope); - scope.$watch(function() { + scope.$watch(function parentValueWatch() { var parentValue = parentGet(parentScope); if (parentValue !== scope[scopeName]) { // we are out of sync and need to copy if (parentValue !== lastValue) { // parent changed and it has precedence lastValue = scope[scopeName] = parentValue; } else { // if the parent can be assigned then do so - parentSet(parentScope, lastValue = scope[scopeName]); + parentSet(parentScope, parentValue = lastValue = scope[scopeName]); } } return parentValue; }); break; @@ -13578,11 +13596,11 @@ break; } default: { throw Error('Invalid isolate scope definition for directive ' + - newScopeDirective.name + ': ' + definiton); + newIsolateScopeDirective.name + ': ' + definiton); } } }); } @@ -13712,11 +13730,11 @@ afterTemplateChildLinkFn, beforeTemplateCompileNode = $compileNode[0], origAsyncDirective = directives.shift(), // The fact that we have to copy and patch the directive seems wrong! derivedSyncDirective = extend({}, origAsyncDirective, { - controller: null, templateUrl: null, transclude: null + controller: null, templateUrl: null, transclude: null, scope: null }); $compileNode.html(''); $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}). @@ -13804,16 +13822,16 @@ function addTextInterpolateDirective(directives, text) { var interpolateFn = $interpolate(text, true); if (interpolateFn) { directives.push({ priority: 0, - compile: valueFn(function(scope, node) { + compile: valueFn(function textInterpolateLinkFn(scope, node) { var parent = node.parent(), bindings = parent.data('$binding') || []; bindings.push(interpolateFn); safeAddClass(parent.data('$binding', bindings), 'ng-binding'); - scope.$watch(interpolateFn, function(value) { + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { node[0].nodeValue = value; }); }) }); } @@ -13827,11 +13845,11 @@ // no interpolation found -> ignore if (!interpolateFn) return; directives.push({ priority: 100, - compile: valueFn(function(scope, element, attr) { + compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { var $$observers = (attr.$$observers || (attr.$$observers = {})); if (name === 'class') { // we need to interpolate classes again, in the case the element was replaced // and therefore the two class attrs got merged - we want to interpolate the result @@ -13839,11 +13857,11 @@ } attr[name] = undefined; ($$observers[name] || ($$observers[name] = [])).$$inter = true; (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function(value) { + $watch(interpolateFn, function interpolateFnWatchAction(value) { attr.$set(name, value); }); }) }); } @@ -14837,24 +14855,25 @@ // update browser var changeCounter = 0; $rootScope.$watch(function $locationWatch() { var oldUrl = $browser.url(); + var currentReplace = $location.$$replace; if (!changeCounter || oldUrl != $location.absUrl()) { changeCounter++; $rootScope.$evalAsync(function() { if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). defaultPrevented) { $location.$$parse(oldUrl); } else { - $browser.url($location.absUrl(), $location.$$replace); - $location.$$replace = false; + $browser.url($location.absUrl(), currentReplace); afterLocationChange(oldUrl); } }); } + $location.$$replace = false; return changeCounter; }); return $location; @@ -14981,11 +15000,19 @@ var OPERATORS = { 'null':function(){return null;}, 'true':function(){return true;}, 'false':function(){return false;}, undefined:noop, - '+':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)+(isDefined(b)?b:0);}, + '+':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + if (isDefined(a)) { + if (isDefined(b)) { + return a + b; + } + return a; + } + return isDefined(b)?b:undefined;}, '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, @@ -15876,11 +15903,11 @@ * var promise = asyncGreet('Robin Hood'); * promise.then(function(greeting) { * alert('Success: ' + greeting); * }, function(reason) { * alert('Failed: ' + reason); - * ); + * }); * </pre> * * At first it might not be obvious why this extra complexity is worth the trouble. The payoff * comes in the way of * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). @@ -16251,22 +16278,22 @@ * match. * * Object properties: * * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly - * created scope or the name of a {@link angular.Module#controller registered controller} + * created scope or the name of a {@link angular.Module#controller registered controller} * if passed as a string. * - `template` – `{string=}` – html template as a string that should be used by * {@link ng.directive:ngView ngView} or * {@link ng.directive:ngInclude ngInclude} directives. * this property takes precedence over `templateUrl`. * - `templateUrl` – `{string=}` – path to an html template that should be used by * {@link ng.directive:ngView ngView}. * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should * be injected into the controller. If any of these dependencies are promises, they will be * resolved and converted to a value before the controller is instantiated and the - * `$afterRouteChange` event is fired. The map object is: + * `$routeChangeSuccess` event is fired. The map object is: * * - `key` – `{string}`: a name of a dependency to be injected into the controller. * - `factory` - `{string|function}`: If `string` then it is an alias for a service. * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} * and the return value is treated as the dependency. If the result is a promise, it is resolved @@ -16596,11 +16623,11 @@ values = [], template; forEach(next.resolve || {}, function(value, key) { keys.push(key); - values.push(isFunction(value) ? $injector.invoke(value) : $injector.get(value)); + values.push(isString(value) ? $injector.get(value) : $injector.invoke(value)); }); if (isDefined(template = next.template)) { } else if (isDefined(template = next.templateUrl)) { template = $http.get(template, {cache: $templateCache}). then(function(response) { return response.data; }); @@ -16867,13 +16894,13 @@ * * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for * the scope and its child scopes to be permanently detached from the parent and thus stop * participating in model change detection and listener notification by invoking. * - * @param {boolean} isolate if true then the scoped does not prototypically inherit from the - * parent scope. The scope is isolated, as it can not se parent scope properties. - * When creating widgets it is useful for the widget to not accidently read parent + * @param {boolean} isolate if true then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets it is useful for the widget to not accidentally read parent * state. * * @returns {Object} The newly created child scope. * */ @@ -16923,22 +16950,22 @@ * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and * should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} * reruns when it detects changes the `watchExpression` can execute multiple times per * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) * - The `listener` is called only when the value from the current `watchExpression` and the - * previous call to `watchExpression' are not equal (with the exception of the initial run + * previous call to `watchExpression` are not equal (with the exception of the initial run, * see below). The inequality is determined according to - * {@link angular.equals} function. To save the value of the object for later comparison + * {@link angular.equals} function. To save the value of the object for later comparison, the * {@link angular.copy} function is used. It also means that watching complex options will * have adverse memory and performance implications. * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This * is achieved by rerunning the watchers until no changes are detected. The rerun iteration - * limit is 100 to prevent infinity loop deadlock. + * limit is 10 to prevent an infinite loop deadlock. * * * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, - * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`, + * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is * detected, be prepared for multiple calls to your listener.) * * After a watcher is registered with the scope, the `listener` fn is called asynchronously * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the @@ -16980,11 +17007,11 @@ * the `watchExpression` changes. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. * - * @param {boolean=} objectEquality Compare object for equality rather then for refference. + * @param {boolean=} objectEquality Compare object for equality rather than for reference. * @returns {function()} Returns a deregistration function for this listener. */ $watch: function(watchExp, listener, objectEquality) { var scope = this, get = compileToFn(watchExp, 'watch'), @@ -17155,11 +17182,11 @@ * @name ng.$rootScope.Scope#$destroy * @methodOf ng.$rootScope.Scope * @function * * @description - * Remove the current scope (and all of its children) from the parent scope. Removal implies + * Removes the current scope (and all of its children) from the parent scope. Removal implies * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer * propagate to the current scope and its children. Removal also implies that the current * scope is eligible for garbage collection. * * The `$destroy()` is usually used by directives such as @@ -17178,21 +17205,26 @@ if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // This is bogus code that works around Chrome's GC leak + // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = + this.$$childTail = null; }, /** * @ngdoc function * @name ng.$rootScope.Scope#$eval * @methodOf ng.$rootScope.Scope * @function * * @description * Executes the `expression` on the current scope returning the result. Any exceptions in the - * expression are propagated (uncaught). This is useful when evaluating engular expressions. + * expression are propagated (uncaught). This is useful when evaluating Angular expressions. * * # Example * <pre> var scope = ng.$rootScope.Scope(); scope.a = 1; @@ -17309,37 +17341,37 @@ * @name ng.$rootScope.Scope#$on * @methodOf ng.$rootScope.Scope * @function * * @description - * Listen on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of * event life cycle. * * @param {string} name Event name to listen on. * @param {function(event)} listener Function to call when the event is emitted. * @returns {function()} Returns a deregistration function for this listener. * * The event listener function format is: `function(event, args...)`. The `event` object * passed into the listener has the following attributes: * - * - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed. - * - `currentScope` - {Scope}: the current scope which is handling the event. - * - `name` - {string}: Name of the event. - * - `stopPropagation` - {function=}: calling `stopPropagation` function will cancel further event propagation - * (available only for events that were `$emit`-ed). - * - `preventDefault` - {function}: calling `preventDefault` sets `defaultPrevented` flag to true. - * - `defaultPrevented` - {boolean}: true if `preventDefault` was called. + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the current scope which is handling the event. + * - `name` - `{string}`: Name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event + * propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. */ $on: function(name, listener) { var namedListeners = this.$$listeners[name]; if (!namedListeners) { this.$$listeners[name] = namedListeners = []; } namedListeners.push(listener); return function() { - arrayRemove(namedListeners, listener); + namedListeners[indexOf(namedListeners, listener)] = null; }; }, /** @@ -17383,10 +17415,18 @@ do { namedListeners = scope.$$listeners[name] || empty; event.currentScope = scope; for (i=0, length=namedListeners.length; i<length; i++) { + + // if listeners were deregistered, defragment the array + if (!namedListeners[i]) { + namedListeners.splice(i, 1); + i--; + length--; + continue; + } try { namedListeners[i].apply(null, listenerArgs); if (stopPropagation) return event; } catch (e) { $exceptionHandler(e); @@ -17432,23 +17472,33 @@ preventDefault: function() { event.defaultPrevented = true; }, defaultPrevented: false }, - listenerArgs = concat([event], arguments, 1); + listenerArgs = concat([event], arguments, 1), + listeners, i, length; //down while you can, then up and next sibling or up and next sibling until back at root do { current = next; event.currentScope = current; - forEach(current.$$listeners[name], function(listener) { + listeners = current.$$listeners[name] || []; + for (i=0, length = listeners.length; i<length; i++) { + // if listeners were deregistered, defragment the array + if (!listeners[i]) { + listeners.splice(i, 1); + i--; + length--; + continue; + } + try { - listener.apply(null, listenerArgs); + listeners[i].apply(null, listenerArgs); } catch(e) { $exceptionHandler(e); } - }); + } // Insanity Warning: scope depth-first traversal // yes, this code is a bit crazy, but it works and we have tests to prove it! // this piece should be kept in sync with the traversal in $digest if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { @@ -18552,11 +18602,11 @@ * * @param {function()} fn A function, who's execution should be delayed. * @param {number=} [delay=0] Delay in milliseconds. * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @returns {*} Promise that will be resolved when the timeout is reached. The value this + * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this * promise will be resolved with is the return value of the `fn` function. */ function timeout(fn, delay, invokeApply) { var deferred = $q.defer(), promise = deferred.promise, @@ -18991,13 +19041,22 @@ number = Math.abs(number); var numStr = number + '', formatedText = '', parts = []; + var hasExponent = false; if (numStr.indexOf('e') !== -1) { - formatedText = numStr; - } else { + var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); + if (match && match[2] == '-' && match[3] > fractionSize + 1) { + numStr = '0'; + } else { + formatedText = numStr; + hasExponent = true; + } + } + + if (!hasExponent) { var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; // determine fractionSize if it is not specified if (isUndefined(fractionSize)) { fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); @@ -19194,11 +19253,11 @@ */ dateFilter.$inject = ['$locale']; function dateFilter($locale) { - var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; function jsonStringToDate(string){ var match; if (match = string.match(R_ISO8601_STR)) { var date = new Date(0), tzHour = 0, @@ -19573,10 +19632,11 @@ return function(scope, element) { element.bind('click', function(event){ // if we have no href url, then don't navigate anywhere. if (!element.attr('href')) { event.preventDefault(); + return false; // Needed for opera } }); } } }); @@ -19863,11 +19923,11 @@ ngAttributeAliasDirectives[normalized] = function() { return { priority: 100, compile: function() { return function(scope, element, attr) { - scope.$watch(attr[normalized], function(value) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { attr.$set(attrName, !!value); }); }; } }; @@ -19881,10 +19941,13 @@ ngAttributeAliasDirectives[normalized] = function() { return { priority: 99, // it needs to run after the attributes are interpolated link: function(scope, element, attr) { attr.$observe(normalized, function(value) { + if (!value) + return; + attr.$set(attrName, value); // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need // to set the property as well to achieve the desired effect @@ -20009,10 +20072,11 @@ form.$setDirty = function() { element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); form.$dirty = true; form.$pristine = false; + parentForm.$setDirty(); }; } @@ -20194,11 +20258,14 @@ * @description * Standard HTML text input with angular data binding. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -20264,10 +20331,13 @@ * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`. * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`. * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -20330,10 +20400,13 @@ * valid URL. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -20395,10 +20468,13 @@ * address. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -20817,10 +20893,13 @@ * {@link ng.directive:input input element}. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -20841,10 +20920,11 @@ * and polyfills the HTML5 validation behavior for older browsers. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the @@ -21174,26 +21254,29 @@ } }; // model -> value var ctrl = this; - $scope.$watch(ngModelGet, function(value) { - // ignore change from view - if (ctrl.$modelValue === value) return; + $scope.$watch(function ngModelWatch() { + var value = ngModelGet($scope); - var formatters = ctrl.$formatters, - idx = formatters.length; + // if scope model value and ngModel value are out of sync + if (ctrl.$modelValue !== value) { - ctrl.$modelValue = value; - while(idx--) { - value = formatters[idx](value); - } + var formatters = ctrl.$formatters, + idx = formatters.length; - if (ctrl.$viewValue !== value) { - ctrl.$viewValue = value; - ctrl.$render(); + ctrl.$modelValue = value; + while(idx--) { + value = formatters[idx](value); + } + + if (ctrl.$viewValue !== value) { + ctrl.$viewValue = value; + ctrl.$render(); + } } }); }]; @@ -21338,11 +21421,11 @@ /** * @ngdoc directive * @name ng.directive:ngList * * @description - * Text input that converts between comma-seperated string into an array of strings. + * Text input that converts between comma-separated string into an array of strings. * * @element input * @param {string=} ngList optional delimiter that should be used to split the value. If * specified in form `/something/` then the value will be converted into a regular expression. * @@ -21421,11 +21504,11 @@ return function(scope, elm, attr) { attr.$set('value', scope.$eval(attr.ngValue)); }; } else { return function(scope, elm, attr) { - scope.$watch(attr.ngValue, function(value) { + scope.$watch(attr.ngValue, function valueWatchAction(value) { attr.$set('value', value, false); }); }; } } @@ -21479,11 +21562,11 @@ </doc:scenario> </doc:example> */ var ngBindDirective = ngDirective(function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBind); - scope.$watch(attr.ngBind, function(value) { + scope.$watch(attr.ngBind, function ngBindWatchAction(value) { element.text(value == undefined ? '' : value); }); }); @@ -21562,30 +21645,68 @@ * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. */ var ngBindHtmlUnsafeDirective = [function() { return function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); - scope.$watch(attr.ngBindHtmlUnsafe, function(value) { + scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { element.html(value || ''); }); }; }]; function classDirective(name, selector) { name = 'ngClass' + name; return ngDirective(function(scope, element, attr) { - scope.$watch(attr[name], function(newVal, oldVal) { + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + var ngClass = scope.$eval(attr[name]); + ngClassWatchAction(ngClass, ngClass); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + var mod = $index % 2; + if (mod !== old$index % 2) { + if (mod == selector) { + addClass(scope.$eval(attr[name])); + } else { + removeClass(scope.$eval(attr[name])); + } + } + }); + } + + + function ngClassWatchAction(newVal, oldVal) { if (selector === true || scope.$index % 2 === selector) { if (oldVal && (newVal !== oldVal)) { - if (isObject(oldVal) && !isArray(oldVal)) - oldVal = map(oldVal, function(v, k) { if (v) return k }); - element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal); - } - if (isObject(newVal) && !isArray(newVal)) - newVal = map(newVal, function(v, k) { if (v) return k }); - if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal); } - }, true); + removeClass(oldVal); + } + addClass(newVal); + } + } + + + function removeClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + + + function addClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + if (classVal) { + element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + } }); } /** * @ngdoc directive @@ -22240,11 +22361,11 @@ } element.html(''); }; - scope.$watch(srcExp, function(src) { + scope.$watch(srcExp, function ngIncludeWatchAction(src) { var thisChangeId = ++changeCounter; if (src) { $http.get(src, {cache: $templateCache}).success(function(response) { if (thisChangeId !== changeCounter) return; @@ -22525,22 +22646,22 @@ whensExpFns[key] = $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + offset + endSymbol)); }); - scope.$watch(function() { + scope.$watch(function ngPluralizeWatch() { var value = parseFloat(scope.$eval(numberExp)); if (!isNaN(value)) { //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, //check it against pluralization rules in $locale service if (!whens[value]) value = $locale.pluralCat(value - offset); return whensExpFns[value](scope, element, true); } else { return ''; } - }, function(newVal) { + }, function ngPluralizeWatchAction(newVal) { element.text(newVal); }); } }; }]; @@ -22631,11 +22752,12 @@ // - element: previous element. // - index: position // We need an array of these objects since the same object can be returned from the iterator. // We expect this to be a rare case. var lastOrder = new HashQueueMap(); - scope.$watch(function(scope){ + + scope.$watch(function ngRepeatWatch(scope){ var index, length, collection = scope.$eval(rhs), collectionLength = size(collection, true), childScope, // Same as lastOrder but it has the current state. It will become the @@ -22660,11 +22782,13 @@ // we are not using forEach for perf reasons (trying to avoid #call) for (index = 0, length = array.length; index < length; index++) { key = (collection === array) ? index : array[index]; value = collection[key]; + last = lastOrder.shift(value); + if (last) { // if we have already seen this object, then we need to reuse the // associated scope/element childScope = last.scope; nextOrder.push(value, last); @@ -22757,26 +22881,26 @@ </doc:scenario> </doc:example> */ //TODO(misko): refactor to remove element from the DOM var ngShowDirective = ngDirective(function(scope, element, attr){ - scope.$watch(attr.ngShow, function(value){ + scope.$watch(attr.ngShow, function ngShowWatchAction(value){ element.css('display', toBoolean(value) ? '' : 'none'); }); }); /** * @ngdoc directive * @name ng.directive:ngHide * * @description - * The `ngHide` and `ngShow` directives hide or show a portion - * of the HTML conditionally. + * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML) + * conditionally. * * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} truthy then + * @param {expression} ngHide If the {@link guide/expression expression} is truthy then * the element is shown or hidden respectively. * * @example <doc:example> <doc:source> @@ -22797,11 +22921,11 @@ </doc:scenario> </doc:example> */ //TODO(misko): refactor to remove element from the DOM var ngHideDirective = ngDirective(function(scope, element, attr){ - scope.$watch(attr.ngHide, function(value){ + scope.$watch(attr.ngHide, function ngHideWatchAction(value){ element.css('display', toBoolean(value) ? 'none' : ''); }); }); /** @@ -22840,11 +22964,11 @@ }); </file> </example> */ var ngStyleDirective = ngDirective(function(scope, element, attr) { - scope.$watch(attr.ngStyle, function(newStyles, oldStyles) { + scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { if (oldStyles && (newStyles !== oldStyles)) { forEach(oldStyles, function(val, style) { element.css(style, '');}); } if (newStyles) element.css(newStyles); }, true); @@ -22920,11 +23044,11 @@ return function(scope, element){ var selectedTransclude, selectedElement, selectedScope; - scope.$watch(watchExpr, function(value) { + scope.$watch(watchExpr, function ngSwitchWatchAction(value) { if (selectedElement) { selectedScope.$destroy(); selectedElement.remove(); selectedElement = selectedScope = null; } @@ -23254,10 +23378,13 @@ * `select` model to be bound to a non-string value. This is because an option element can currently * be bound to string values only. * * @param {string} name assignable expression to data-bind to. * @param {string=} required The control is considered valid only if value is entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. * @param {comprehension_expression=} ngOptions in one of the following forms: * * * for array data sources: * * `label` **`for`** `value` **`in`** `array` * * `select` **`as`** `label` **`for`** `value` **`in`** `array` @@ -23494,11 +23621,11 @@ }); }; // we have to do it on each watch since ngModel watches reference, but // we need to work of an array, so we need to see if anything was inserted/removed - scope.$watch(function() { + scope.$watch(function selectMultipleWatch() { if (!equals(lastView, ctrl.$viewValue)) { lastView = copy(ctrl.$viewValue); ctrl.$render(); } }); @@ -23611,11 +23738,12 @@ groupIndex, index, locals = {}, selected, selectedSet = false, // nothing is selected yet lastElement, - element; + element, + label; if (multiple) { selectedSet = new HashMap(modelValue); } else if (modelValue === null || nullOption) { // if we are not multiselect, and we are null then we have to add the nullOption @@ -23635,13 +23763,15 @@ selected = selectedSet.remove(valueFn(scope, locals)) != undefined; } else { selected = modelValue === valueFn(scope, locals); selectedSet = selectedSet || selected; // see if at least one item is selected } + label = displayFn(scope, locals); // what will be seen by the user + label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values optionGroup.push({ id: keyName ? keys[index] : index, // either the index into array or key from object - label: displayFn(scope, locals) || '', // what will be seen by the user + label: label, selected: selected // determine if we should be selected }); } if (!multiple && !selectedSet) { // nothing was selected, we have to insert the undefined item @@ -23769,11 +23899,11 @@ } else { selectCtrl = nullSelectCtrl; } if (interpolateFn) { - scope.$watch(interpolateFn, function(newVal, oldVal) { + scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) { attr.$set('value', newVal); if (newVal !== oldVal) selectCtrl.removeOption(oldVal); selectCtrl.addOption(newVal); }); } else { @@ -24084,26 +24214,27 @@ var evnt = document.createEvent('MouseEvents'), originalPreventDefault = evnt.preventDefault, iframe = _jQuery('#application iframe')[0], appWindow = iframe ? iframe.contentWindow : window, fakeProcessDefault = true, - finalProcessDefault; + finalProcessDefault, + angular = appWindow.angular || {}; // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208 - appWindow.angular['ff-684208-preventDefault'] = false; + angular['ff-684208-preventDefault'] = false; evnt.preventDefault = function() { fakeProcessDefault = false; return originalPreventDefault.apply(evnt, arguments); }; evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, pressed('ctrl'), pressed('alt'), pressed('shift'), pressed('meta'), 0, element); element.dispatchEvent(evnt); - finalProcessDefault = !(appWindow.angular['ff-684208-preventDefault'] || !fakeProcessDefault); + finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault); - delete appWindow.angular['ff-684208-preventDefault']; + delete angular['ff-684208-preventDefault']; return finalProcessDefault; } } @@ -24635,10 +24766,13 @@ // forward the event self.emit('StepError', it, modelStep, error); }); + runner.on('RunnerBegin', function() { + self.emit('RunnerBegin'); + }); runner.on('RunnerEnd', function() { self.emit('RunnerEnd'); }); function complete(item) { @@ -25274,10 +25408,12 @@ angular.forEach(args, function(value, index) { selector = selector.replace('$' + (index + 1), value); }); var result = $document.find(selector); if (selector.match(NG)) { - result = result.add(selector.replace(NG, '[ng-'), $document); + angular.forEach(['[ng-','[data-ng-','[x-ng-'], function(value, index){ + result = result.add(selector.replace(NG, value), $document); + }); } if (!result.length) { throw { type: 'selector', message: 'Selector ' + selector + ' did not match any elements.' \ No newline at end of file