diff --git a/build/browser/app.js b/build/browser/app.js index 46bf9bb5..a9a8d0ac 100644 --- a/build/browser/app.js +++ b/build/browser/app.js @@ -9121,7 +9121,7 @@ angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCac !angular.$$csp() && angular.element(document).find('head').prepend(''); },{}],8:[function(require,module,exports){ /** - * @license AngularJS v1.4.7 + * @license AngularJS v1.4.8 * (c) 2010-2015 Google, Inc. http://angularjs.org * License: MIT */ @@ -9179,7 +9179,7 @@ function minErr(module, ErrorConstructor) { return match; }); - message += '\nhttp://errors.angularjs.org/1.4.7/' + + message += '\nhttp://errors.angularjs.org/1.4.8/' + (module ? module + '/' : '') + code; for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { @@ -9389,20 +9389,24 @@ msie = document.documentMode; * String ...) */ function isArrayLike(obj) { - if (obj == null || isWindow(obj)) { - return false; - } + + // `null`, `undefined` and `window` are not array-like + if (obj == null || isWindow(obj)) return false; + + // arrays, strings and jQuery/jqLite objects are array like + // * jqLite is either the jQuery or jqLite constructor function + // * we have to check the existance of jqLite first as this method is called + // via the forEach method when constructing the jqLite object in the first place + if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true; // Support: iOS 8.2 (not reproducible in simulator) // "length" in obj used to prevent JIT error (gh-11508) var length = "length" in Object(obj) && obj.length; - if (obj.nodeType === NODE_TYPE_ELEMENT && length) { - return true; - } - - return isString(obj) || isArray(obj) || length === 0 || - typeof length === 'number' && length > 0 && (length - 1) in obj; + // NodeList objects (with `item` method) and + // other objects with suitable length characteristics are array-like + return isNumber(length) && + (length >= 0 && (length - 1) in obj || typeof obj.item == 'function'); } /** @@ -9547,6 +9551,10 @@ function baseExtend(dst, objs, deep) { dst[key] = new Date(src.valueOf()); } else if (isRegExp(src)) { dst[key] = new RegExp(src); + } else if (src.nodeName) { + dst[key] = src.cloneNode(true); + } else if (isElement(src)) { + dst[key] = src.clone(); } else { if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; baseExtend(dst[key], [src], true); @@ -9662,7 +9670,7 @@ identity.$inject = []; function valueFn(value) {return function() {return value;};} function hasCustomToString(obj) { - return isFunction(obj.toString) && obj.toString !== Object.prototype.toString; + return isFunction(obj.toString) && obj.toString !== toString; } @@ -9861,9 +9869,9 @@ function isPromiseLike(obj) { } -var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/; +var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/; function isTypedArray(value) { - return TYPED_ARRAY_REGEXP.test(toString.call(value)); + return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value)); } @@ -9985,100 +9993,111 @@ function arrayRemove(array, value) { */ -function copy(source, destination, stackSource, stackDest) { - if (isWindow(source) || isScope(source)) { - throw ngMinErr('cpws', - "Can't copy! Making copies of Window or Scope instances is not supported."); - } - if (isTypedArray(destination)) { - throw ngMinErr('cpta', - "Can't copy! TypedArray destination cannot be mutated."); - } +function copy(source, destination) { + var stackSource = []; + var stackDest = []; - if (!destination) { - destination = source; - if (isObject(source)) { - var index; - if (stackSource && (index = stackSource.indexOf(source)) !== -1) { - return stackDest[index]; - } - - // TypedArray, Date and RegExp have specific copy functionality and must be - // pushed onto the stack before returning. - // Array and other objects create the base object and recurse to copy child - // objects. The array/object will be pushed onto the stack when recursed. - if (isArray(source)) { - return copy(source, [], stackSource, stackDest); - } else if (isTypedArray(source)) { - destination = new source.constructor(source); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isRegExp(source)) { - destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); - destination.lastIndex = source.lastIndex; - } else if (isFunction(source.cloneNode)) { - destination = source.cloneNode(true); - } else { - var emptyObject = Object.create(getPrototypeOf(source)); - return copy(source, emptyObject, stackSource, stackDest); - } - - if (stackDest) { - stackSource.push(source); - stackDest.push(destination); - } + if (destination) { + if (isTypedArray(destination)) { + throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated."); } - } else { - if (source === destination) throw ngMinErr('cpi', - "Can't copy! Source and destination are identical."); - - stackSource = stackSource || []; - stackDest = stackDest || []; - - if (isObject(source)) { - stackSource.push(source); - stackDest.push(destination); + if (source === destination) { + throw ngMinErr('cpi', "Can't copy! Source and destination are identical."); } + // Empty the destination object + if (isArray(destination)) { + destination.length = 0; + } else { + forEach(destination, function(value, key) { + if (key !== '$$hashKey') { + delete destination[key]; + } + }); + } + + stackSource.push(source); + stackDest.push(destination); + return copyRecurse(source, destination); + } + + return copyElement(source); + + function copyRecurse(source, destination) { + var h = destination.$$hashKey; var result, key; if (isArray(source)) { - destination.length = 0; - for (var i = 0; i < source.length; i++) { - destination.push(copy(source[i], null, stackSource, stackDest)); + for (var i = 0, ii = source.length; i < ii; i++) { + destination.push(copyElement(source[i])); + } + } else if (isBlankObject(source)) { + // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty + for (key in source) { + destination[key] = copyElement(source[key]); + } + } else if (source && typeof source.hasOwnProperty === 'function') { + // Slow path, which must rely on hasOwnProperty + for (key in source) { + if (source.hasOwnProperty(key)) { + destination[key] = copyElement(source[key]); + } } } else { - var h = destination.$$hashKey; - if (isArray(destination)) { - destination.length = 0; - } else { - forEach(destination, function(value, key) { - delete destination[key]; - }); - } - if (isBlankObject(source)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in source) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } else if (source && typeof source.hasOwnProperty === 'function') { - // Slow path, which must rely on hasOwnProperty - for (key in source) { - if (source.hasOwnProperty(key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } - } else { - // Slowest path --- hasOwnProperty can't be called as a method - for (key in source) { - if (hasOwnProperty.call(source, key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } + // Slowest path --- hasOwnProperty can't be called as a method + for (key in source) { + if (hasOwnProperty.call(source, key)) { + destination[key] = copyElement(source[key]); } } - setHashKey(destination,h); } + setHashKey(destination, h); + return destination; + } + + function copyElement(source) { + // Simple values + if (!isObject(source)) { + return source; + } + + // Already copied values + var index = stackSource.indexOf(source); + if (index !== -1) { + return stackDest[index]; + } + + if (isWindow(source) || isScope(source)) { + throw ngMinErr('cpws', + "Can't copy! Making copies of Window or Scope instances is not supported."); + } + + var needsRecurse = false; + var destination; + + if (isArray(source)) { + destination = []; + needsRecurse = true; + } else if (isTypedArray(source)) { + destination = new source.constructor(source); + } else if (isDate(source)) { + destination = new Date(source.getTime()); + } else if (isRegExp(source)) { + destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); + destination.lastIndex = source.lastIndex; + } else if (isFunction(source.cloneNode)) { + destination = source.cloneNode(true); + } else { + destination = Object.create(getPrototypeOf(source)); + needsRecurse = true; + } + + stackSource.push(source); + stackDest.push(destination); + + return needsRecurse + ? copyRecurse(source, destination) + : destination; } - return destination; } /** @@ -11202,7 +11221,7 @@ function setupModuleLoader(window) { * @param {string} name constant name * @param {*} object Constant value. * @description - * Because the constant are fixed, they get applied before other provide methods. + * Because the constants are fixed, they get applied before other provide methods. * See {@link auto.$provide#constant $provide.constant()}. */ constant: invokeLater('$provide', 'constant', 'unshift'), @@ -11501,11 +11520,11 @@ function toDebugString(obj) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.4.7', // all of these placeholder strings will be replaced by grunt's + full: '1.4.8', // all of these placeholder strings will be replaced by grunt's major: 1, // package task minor: 4, - dot: 7, - codeName: 'dark-luminescence' + dot: 8, + codeName: 'ice-manipulation' }; @@ -11887,6 +11906,14 @@ function jqLiteParseHTML(html, context) { return []; } + +// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. +var jqLiteContains = Node.prototype.contains || function(arg) { + // jshint bitwise: false + return !!(this.compareDocumentPosition(arg) & 16); + // jshint bitwise: true +}; + ///////////////////////////////////////////// function JQLite(element) { if (element instanceof JQLite) { @@ -11945,17 +11972,23 @@ function jqLiteOff(element, type, fn, unsupported) { delete events[type]; } } else { - forEach(type.split(' '), function(type) { - if (isDefined(fn)) { - var listenerFns = events[type]; - arrayRemove(listenerFns || [], fn); - if (listenerFns && listenerFns.length > 0) { - return; - } - } - removeEventListenerFn(element, type, handle); - delete events[type]; + var removeHandler = function(type) { + var listenerFns = events[type]; + if (isDefined(fn)) { + arrayRemove(listenerFns || [], fn); + } + if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { + removeEventListenerFn(element, type, handle); + delete events[type]; + } + }; + + forEach(type.split(' '), function(type) { + removeHandler(type); + if (MOUSE_EVENT_MAP[type]) { + removeHandler(MOUSE_EVENT_MAP[type]); + } }); } } @@ -12410,6 +12443,9 @@ function createEventHandler(element, events) { return event.immediatePropagationStopped === true; }; + // Some events have special handlers that wrap the real handler + var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper; + // Copy event handlers in case event handlers array is modified during execution. if ((eventFnsLength > 1)) { eventFns = shallowCopy(eventFns); @@ -12417,7 +12453,7 @@ function createEventHandler(element, events) { for (var i = 0; i < eventFnsLength; i++) { if (!event.isImmediatePropagationStopped()) { - eventFns[i].call(element, event); + handlerWrapper(element, event, eventFns[i]); } } }; @@ -12428,6 +12464,22 @@ function createEventHandler(element, events) { return eventHandler; } +function defaultHandlerWrapper(element, event, handler) { + handler.call(element, event); +} + +function specialMouseHandlerWrapper(target, event, handler) { + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if (!related || (related !== target && !jqLiteContains.call(target, related))) { + handler.call(target, event); + } +} + ////////////////////////////////////////// // Functions iterating traversal. // These functions chain results into a single @@ -12456,35 +12508,28 @@ forEach({ var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; var i = types.length; - while (i--) { - type = types[i]; + var addHandler = function(type, specialHandlerWrapper, noEventListener) { var eventFns = events[type]; if (!eventFns) { - events[type] = []; - - if (type === 'mouseenter' || type === 'mouseleave') { - // Refer to jQuery's implementation of mouseenter & mouseleave - // Read about mouseenter and mouseleave: - // http://www.quirksmode.org/js/events_mouse.html#link8 - - jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { - var target = this, related = event.relatedTarget; - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if (!related || (related !== target && !target.contains(related))) { - handle(event, type); - } - }); - - } else { - if (type !== '$destroy') { - addEventListenerFn(element, type, handle); - } + eventFns = events[type] = []; + eventFns.specialHandlerWrapper = specialHandlerWrapper; + if (type !== '$destroy' && !noEventListener) { + addEventListenerFn(element, type, handle); } - eventFns = events[type]; } + eventFns.push(fn); + }; + + while (i--) { + type = types[i]; + if (MOUSE_EVENT_MAP[type]) { + addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper); + addHandler(type, undefined, true); + } else { + addHandler(type); + } } }, @@ -13665,7 +13710,7 @@ function $AnchorScrollProvider() { * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified * in the - * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). + * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document). * * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to * match any anchor whenever it changes. This can be disabled by calling @@ -14180,7 +14225,7 @@ var $AnimateProvider = ['$provide', function($provide) { * when an animation is detected (and animations are enabled), $animate will do the heavy lifting * to ensure that animation runs with the triggered DOM operation. * - * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't + * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't * included and only when it is active then the animation hooks that `$animate` triggers will be * functional. Once active then all structural `ng-` directives will trigger animations as they perform * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, @@ -15032,9 +15077,9 @@ function $CacheFactoryProvider() { var size = 0, stats = extend({}, options, {id: cacheId}), - data = {}, + data = createMap(), capacity = (options && options.capacity) || Number.MAX_VALUE, - lruHash = {}, + lruHash = createMap(), freshEnd = null, staleEnd = null; @@ -15162,6 +15207,8 @@ function $CacheFactoryProvider() { delete lruHash[key]; } + if (!(key in data)) return; + delete data[key]; size--; }, @@ -15176,9 +15223,9 @@ function $CacheFactoryProvider() { * Clears the cache object of any entries. */ removeAll: function() { - data = {}; + data = createMap(); size = 0; - lruHash = {}; + lruHash = createMap(); freshEnd = staleEnd = null; }, @@ -16579,6 +16626,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); }, NG_ATTR_BINDING = /^ngAttr[A-Z]/; + var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/; compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { var bindings = $element.data('$binding') || []; @@ -16631,6 +16679,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return function publicLinkFn(scope, cloneConnectFn, options) { assertArg(scope, 'scope'); + if (previousCompileContext && previousCompileContext.needsNewScope) { + // A parent directive did a replace and a directive on this element asked + // for transclusion, which caused us to lose a layer of element on which + // we could hold the new transclusion scope, so we will create it manually + // here. + scope = scope.$parent.$new(); + } + options = options || {}; var parentBoundTranscludeFn = options.parentBoundTranscludeFn, transcludeControllers = options.transcludeControllers, @@ -16776,11 +16832,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (nodeLinkFn.scope) { childScope = scope.$new(); compile.$$addScopeInfo(jqLite(node), childScope); - var destroyBindings = nodeLinkFn.$$destroyBindings; - if (destroyBindings) { - nodeLinkFn.$$destroyBindings = null; - childScope.$on('$destroyed', destroyBindings); - } } else { childScope = scope; } @@ -16799,8 +16850,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { childBoundTranscludeFn = null; } - nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn, - nodeLinkFn); + nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); } else if (childLinkFn) { childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); @@ -16869,13 +16919,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { }); } - var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); - if (directiveIsMultiElement(directiveNName)) { - if (ngAttrName === directiveNName + 'Start') { - attrStartName = name; - attrEndName = name.substr(0, name.length - 5) + 'end'; - name = name.substr(0, name.length - 6); - } + var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE); + if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) { + attrStartName = name; + attrEndName = name.substr(0, name.length - 5) + 'end'; + name = name.substr(0, name.length - 6); } nName = directiveNormalize(name.toLowerCase()); @@ -17114,7 +17162,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } else { $template = jqLite(jqLiteClone(compileNode)).contents(); $compileNode.empty(); // clear contents - childTranscludeFn = compile($template, transcludeFn); + childTranscludeFn = compile($template, transcludeFn, undefined, + undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope}); } } @@ -17156,8 +17205,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); - if (newIsolateScopeDirective) { - markDirectivesAsIsolate(templateDirectives); + if (newIsolateScopeDirective || newScopeDirective) { + // The original directive caused the current element to be replaced but this element + // also needs to have a new scope, so we need to tell the template directives + // that they would need to get their scope from further up, if they require transclusion + markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective); } directives = directives.concat(templateDirectives).concat(unprocessedDirectives); mergeTemplateAttributes(templateAttrs, newTemplateAttrs); @@ -17310,10 +17362,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return elementControllers; } - function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn, - thisLinkFn) { - var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element, - attrs; + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element, + attrs, removeScopeBindingWatches, removeControllerBindingWatches; if (compileNode === linkNode) { attrs = templateAttrs; @@ -17323,8 +17374,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { attrs = new Attributes($element, templateAttrs); } + controllerScope = scope; if (newIsolateScopeDirective) { isolateScope = scope.$new(true); + } else if (newScopeDirective) { + controllerScope = scope.$parent; } if (boundTranscludeFn) { @@ -17345,42 +17399,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { compile.$$addScopeClass($element, true); isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings; - initializeDirectiveBindings(scope, attrs, isolateScope, - isolateScope.$$isolateBindings, - newIsolateScopeDirective, isolateScope); - } - if (elementControllers) { - // Initialize bindToController bindings for new/isolate scopes - var scopeDirective = newIsolateScopeDirective || newScopeDirective; - var bindings; - var controllerForBindings; - if (scopeDirective && elementControllers[scopeDirective.name]) { - bindings = scopeDirective.$$bindings.bindToController; - controller = elementControllers[scopeDirective.name]; - - if (controller && controller.identifier && bindings) { - controllerForBindings = controller; - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controller.instance, - bindings, scopeDirective); - } + removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope, + isolateScope.$$isolateBindings, + newIsolateScopeDirective); + if (removeScopeBindingWatches) { + isolateScope.$on('$destroy', removeScopeBindingWatches); } - for (i in elementControllers) { - controller = elementControllers[i]; - var controllerResult = controller(); + } - if (controllerResult !== controller.instance) { - // If the controller constructor has a return value, overwrite the instance - // from setupControllers and update the element data - controller.instance = controllerResult; - $element.data('$' + i + 'Controller', controllerResult); - if (controller === controllerForBindings) { - // Remove and re-install bindToController bindings - thisLinkFn.$$destroyBindings(); - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective); - } - } + // Initialize bindToController bindings + for (var name in elementControllers) { + var controllerDirective = controllerDirectives[name]; + var controller = elementControllers[name]; + var bindings = controllerDirective.$$bindings.bindToController; + + if (controller.identifier && bindings) { + removeControllerBindingWatches = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } + + var controllerResult = controller(); + if (controllerResult !== controller.instance) { + // If the controller constructor has a return value, overwrite the instance + // from setupControllers + controller.instance = controllerResult; + $element.data('$' + controllerDirective.name + 'Controller', controllerResult); + removeControllerBindingWatches && removeControllerBindingWatches(); + removeControllerBindingWatches = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); } } @@ -17440,10 +17486,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } } - function markDirectivesAsIsolate(directives) { - // mark all directives as needing isolate scope. + // Depending upon the context in which a directive finds itself it might need to have a new isolated + // or child scope created. For instance: + // * if the directive has been pulled into a template because another directive with a higher priority + // asked for element transclusion + // * if the directive itself asks for transclusion but it is at the root of a template and the original + // element was replaced. See https://github.com/angular/angular.js/issues/12936 + function markDirectiveScope(directives, isolateScope, newScope) { for (var j = 0, jj = directives.length; j < jj; j++) { - directives[j] = inherit(directives[j], {$$isolateScope: true}); + directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope}); } } @@ -17590,7 +17641,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); if (isObject(origAsyncDirective.scope)) { - markDirectivesAsIsolate(templateDirectives); + // the original directive that caused the template to be loaded async required + // an isolate scope + markDirectiveScope(templateDirectives, true); } directives = templateDirectives.concat(directives); mergeTemplateAttributes(tAttrs, tempTemplateAttrs); @@ -17639,7 +17692,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { childBoundTranscludeFn = boundTranscludeFn; } afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, - childBoundTranscludeFn, afterTemplateNodeLinkFn); + childBoundTranscludeFn); } linkQueue = null; }); @@ -17656,8 +17709,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (afterTemplateNodeLinkFn.transcludeOnThisElement) { childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn, - afterTemplateNodeLinkFn); + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); } }; } @@ -17869,7 +17921,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { // Copy over user data (that includes Angular's $scope etc.). Don't copy private // data here because there's no public interface in jQuery to do that and copying over // event listeners (which is the main use of private data) wouldn't work anyway. - jqLite(newNode).data(jqLite(firstElementToRemove).data()); + jqLite.data(newNode, jqLite.data(firstElementToRemove)); // Remove data of the replaced element. We cannot just call .remove() // on the element it since that would deallocate scope that is needed @@ -17917,9 +17969,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { // Set up $watches for isolate scope and controller bindings. This process // only occurs for isolate scopes and new scopes with controllerAs. - function initializeDirectiveBindings(scope, attrs, destination, bindings, - directive, newScope) { - var onNewScopeDestroyed; + function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) { + var removeWatchCollection = []; forEach(bindings, function(definition, scopeName) { var attrName = definition.attrName, optional = definition.optional, @@ -17981,14 +18032,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return lastValue = parentValue; }; parentValueWatch.$stateful = true; - var unwatch; + var removeWatch; if (definition.collection) { - unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); + removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch); } else { - unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); } - onNewScopeDestroyed = (onNewScopeDestroyed || []); - onNewScopeDestroyed.push(unwatch); + removeWatchCollection.push(removeWatch); break; case '&': @@ -18004,16 +18054,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { break; } }); - var destroyBindings = onNewScopeDestroyed ? function destroyBindings() { - for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) { - onNewScopeDestroyed[i](); + + return removeWatchCollection.length && function removeWatches() { + for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) { + removeWatchCollection[i](); } - } : noop; - if (newScope && destroyBindings !== noop) { - newScope.$on('$destroy', destroyBindings); - return noop; - } - return destroyBindings; + }; } }]; } @@ -18738,9 +18784,9 @@ function $HttpProvider() { * Configure `$http` service to return promises without the shorthand methods `success` and `error`. * This should be used to make sure that applications work without these methods. * - * Defaults to false. If no value is specified, returns the current configured value. + * Defaults to true. If no value is specified, returns the current configured value. * - * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods. + * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods. * * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. * otherwise, returns the current configured value. @@ -19392,11 +19438,8 @@ function $HttpProvider() { function transformResponse(response) { // make a copy since the response must be cacheable var resp = extend({}, response); - if (!response.data) { - resp.data = response.data; - } else { - resp.data = transformData(response.data, response.headers, response.status, config.transformResponse); - } + resp.data = transformData(response.data, response.headers, response.status, + config.transformResponse); return (isSuccess(response.status)) ? resp : $q.reject(resp); @@ -21030,9 +21073,9 @@ var locationPrototype = { * @description * This method is getter / setter. * - * Return hash fragment when called without any parameter. + * Returns the hash fragment when called without any parameters. * - * Change hash fragment when called with parameter and return `$location`. + * Changes the hash fragment when called with a parameter and returns `$location`. * * * ```js @@ -21053,8 +21096,8 @@ var locationPrototype = { * @name $location#replace * * @description - * If called, all changes to $location during current `$digest` will be replacing current history - * record, instead of adding new one. + * If called, all changes to $location during the current `$digest` will replace the current history + * record, instead of adding a new one. */ replace: function() { this.$$replace = true; @@ -21374,7 +21417,7 @@ function $LocationProvider() { var oldUrl = $location.absUrl(); var oldState = $location.$$state; var defaultPrevented; - + newUrl = trimEmptyHash(newUrl); $location.$$parse(newUrl); $location.$$state = newState; @@ -23515,13 +23558,14 @@ function $ParseProvider() { function addInterceptor(parsedExpression, interceptorFn) { if (!interceptorFn) return parsedExpression; var watchDelegate = parsedExpression.$$watchDelegate; + var useInputs = false; var regularWatch = watchDelegate !== oneTimeLiteralWatchDelegate && watchDelegate !== oneTimeWatchDelegate; var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); + var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); return interceptorFn(value, scope, locals); } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { var value = parsedExpression(scope, locals, assign, inputs); @@ -23539,6 +23583,7 @@ function $ParseProvider() { // If there is an interceptor, but no watchDelegate then treat the interceptor like // we treat filters - it is assumed to be a pure function unless flagged with $stateful fn.$$watchDelegate = inputsWatchDelegate; + useInputs = !parsedExpression.inputs; fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; } @@ -23600,6 +23645,8 @@ function $ParseProvider() { * * Note: progress/notify callbacks are not currently supported via the ES6-style interface. * + * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise. + * * However, the more traditional CommonJS-style usage is still available, and documented below. * * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an @@ -24184,15 +24231,15 @@ function $$RAFProvider() { //rAF * exposed as $$____ properties * * Loop operations are optimized by using while(count--) { ... } - * - this means that in order to keep the same order of execution as addition we have to add + * - This means that in order to keep the same order of execution as addition we have to add * items to the array at the beginning (unshift) instead of at the end (push) * * Child scopes are created and removed often - * - Using an array would be slow since inserts in middle are expensive so we use linked list + * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists * - * There are few watches then a lot of observers. This is why you don't want the observer to be - * implemented in the same way as watch. Watch requires return of initialization function which - * are expensive to construct. + * There are fewer watches than observers. This is why you don't want the observer to be implemented + * in the same way as watch. Watch requires return of the initialization function which is expensive + * to construct. */ @@ -24234,7 +24281,7 @@ function $$RAFProvider() { //rAF * Every application has a single root {@link ng.$rootScope.Scope scope}. * All other scopes are descendant scopes of the root scope. Scopes provide separation * between the model and the view, via a mechanism for watching the model for changes. - * They also provide an event emission/broadcast and subscription facility. See the + * They also provide event emission/broadcast and subscription facility. See the * {@link guide/scope developer guide on scopes}. */ function $RootScopeProvider() { @@ -24271,6 +24318,29 @@ function $RootScopeProvider() { $event.currentScope.$$destroyed = true; } + function cleanUpScope($scope) { + + if (msie === 9) { + // There is a memory leak in IE9 if all child scopes are not disconnected + // completely when a scope is destroyed. So this code will recurse up through + // all this scopes children + // + // See issue https://github.com/angular/angular.js/issues/10706 + $scope.$$childHead && cleanUpScope($scope.$$childHead); + $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling); + } + + // The code below works around IE9 and V8's memory leaks + // + // See: + // - https://code.google.com/p/v8/issues/detail?id=2073#c26 + // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 + // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + + $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead = + $scope.$$childTail = $scope.$root = $scope.$$watchers = null; + } + /** * @ngdoc type * @name $rootScope.Scope @@ -25067,16 +25137,9 @@ function $RootScopeProvider() { this.$on = this.$watch = this.$watchGroup = function() { return noop; }; this.$$listeners = {}; - // All of the code below is bogus code that works around V8's memory leak via optimized code - // and inline caches. - // - // see: - // - https://code.google.com/p/v8/issues/detail?id=2073#c26 - // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 - // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 - - this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = - this.$$childTail = this.$root = this.$$watchers = null; + // Disconnect the next sibling to prevent `cleanUpScope` destroying those too + this.$$nextSibling = null; + cleanUpScope(this); }, /** @@ -26072,7 +26135,7 @@ function $SceDelegateProvider() { * By default, Angular only loads templates from the same domain and protocol as the application * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or - * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist + * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. * * *Please note*: @@ -28323,7 +28386,7 @@ function limitToFilter() { if (!isArray(input) && !isString(input)) return input; begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); - begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin; + begin = (begin < 0) ? Math.max(0, input.length + begin) : begin; if (limit >= 0) { return input.slice(begin, begin + limit); @@ -29683,7 +29746,8 @@ var ngFormDirective = formDirectiveFactory(true); // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; +// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) +var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/; @@ -33249,7 +33313,13 @@ var ngIfDirective = ['$animate', function($animate) { * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. * @param {string=} onload Expression to evaluate when a new partial is loaded. - * + *
+ * **Note:** When using onload on SVG elements in IE11, the browser will try to call + * a function with the name on the window element, which will usually throw a + * "function is undefined" error. To fix this, you can instead use `data-onload` or a + * different form that {@link guide/directive#normalization matches} `onload`. + *
+ * * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll * $anchorScroll} to scroll the viewport after the content is loaded. * @@ -34842,12 +34912,13 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
user.name = 
+
user.data = 
angular.module('optionsExample', []) .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'say', data: '' }; + $scope.user = { name: 'John', data: '' }; $scope.cancel = function(e) { if (e.keyCode == 27) { @@ -34862,20 +34933,20 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; var other = element(by.model('user.data')); it('should allow custom events', function() { - input.sendKeys(' hello'); + input.sendKeys(' Doe'); input.click(); - expect(model.getText()).toEqual('say'); + expect(model.getText()).toEqual('John'); other.click(); - expect(model.getText()).toEqual('say hello'); + expect(model.getText()).toEqual('John Doe'); }); it('should $rollbackViewValue when model changes', function() { - input.sendKeys(' hello'); - expect(input.getAttribute('value')).toEqual('say hello'); + input.sendKeys(' Doe'); + expect(input.getAttribute('value')).toEqual('John Doe'); input.sendKeys(protractor.Key.ESCAPE); - expect(input.getAttribute('value')).toEqual('say'); + expect(input.getAttribute('value')).toEqual('John'); other.click(); - expect(model.getText()).toEqual('say'); + expect(model.getText()).toEqual('John'); }); @@ -34901,7 +34972,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; angular.module('optionsExample', []) .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'say' }; + $scope.user = { name: 'Igor' }; }]); @@ -35134,19 +35205,27 @@ var ngOptionsMinErr = minErr('ngOptions'); * * ## Complex Models (objects or collections) * - * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when - * binding any input directive to a model that is an object or a collection. + * By default, `ngModel` watches the model by reference, not value. This is important to know when + * binding the select to a model that is an object or a collection. * - * Since this is a common situation for `ngOptions` the directive additionally watches the model using - * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in - * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual - * object/collection has not changed identity but only a property on the object or an item in the collection - * changes. + * One issue occurs if you want to preselect an option. For example, if you set + * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection, + * because the objects are not identical. So by default, you should always reference the item in your collection + * for preselections, e.g.: `$scope.selected = $scope.collection[3]`. + * + * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity + * of the item not by reference, but by the result of the `track by` expression. For example, if your + * collection items have an id property, you would `track by item.id`. + * + * A different issue with objects or collections is that ngModel won't detect if an object property or + * a collection item changes. For that reason, `ngOptions` additionally watches the model using + * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute. + * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection + * has not changed identity, but only a property on the object or an item in the collection changes. * * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection - * if the model is an array). This means that changing a property deeper inside the object/collection that the - * first level will not trigger a re-rendering. - * + * if the model is an array). This means that changing a property deeper than the first level inside the + * object/collection will not trigger a re-rendering. * * ## `select` **`as`** * @@ -35159,17 +35238,13 @@ var ngOptionsMinErr = minErr('ngOptions'); * ### `select` **`as`** and **`track by`** * *
- * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together. + * Be careful when using `select` **`as`** and **`track by`** in the same expression. *
* - * Consider the following example: - * - * ```html - * - * ``` + * Given this array of items on the $scope: * * ```js - * $scope.values = [{ + * $scope.items = [{ * id: 1, * label: 'aLabel', * subItem: { name: 'aSubItem' } @@ -35178,20 +35253,33 @@ var ngOptionsMinErr = minErr('ngOptions'); * label: 'bLabel', * subItem: { name: 'bSubItem' } * }]; - * - * $scope.selected = { name: 'aSubItem' }; * ``` * - * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element - * of the data source (to `item` in this example). To calculate whether an element is selected, we do the - * following: + * This will work: * - * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]` - * 2. Apply **`track by`** to the already selected value in `ngModel`. - * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected - * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to - * a wrong object, the selected element can't be found, ` + * ``` + * ```js + * $scope.selected = $scope.items[0]; + * ``` + * + * but this will not work: + * + * ```html + * + * ``` + * ```js + * $scope.selected = $scope.items[0].subItem; + * ``` + * + * In both examples, the **`track by`** expression is applied successfully to each `item` in the + * `items` array. Because the selected option has been set programmatically in the controller, the + * **`track by`** expression is also applied to the `ngModel` value. In the first example, the + * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with + * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`** + * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value + * is not matched against any `