vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.6.1 vs vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.6.2

- old
+ new

@@ -10069,12 +10069,12 @@ return jQuery; } ); /** - * @license AngularJS v1.6.1 - * (c) 2010-2016 Google, Inc. http://angularjs.org + * @license AngularJS v1.6.2 + * (c) 2010-2017 Google, Inc. http://angularjs.org * License: MIT */ (function(window){ var _jQuery = window.jQuery.noConflict(true); @@ -10128,11 +10128,11 @@ } return match; }); - message += '\nhttp://errors.angularjs.org/1.6.1/' + + message += '\nhttp://errors.angularjs.org/1.6.2/' + (module ? module + '/' : '') + code; for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + encodeURIComponent(toDebugString(templateArgs[i])); @@ -11619,16 +11619,20 @@ } return null; } function allowAutoBootstrap(document) { - if (!document.currentScript) { + var script = document.currentScript; + var src = script && script.getAttribute('src'); + + if (!src) { return true; } - var src = document.currentScript.getAttribute('src'); + var link = document.createElement('a'); link.href = src; + if (document.location.origin === link.origin) { // Same-origin resources are always allowed, even for non-whitelisted schemes. return true; } // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web. @@ -12006,11 +12010,11 @@ if (jQuery && jQuery.fn.on) { jqLite = jQuery; extend(jQuery.fn, { scope: JQLitePrototype.scope, isolateScope: JQLitePrototype.isolateScope, - controller: JQLitePrototype.controller, + controller: /** @type {?} */ (JQLitePrototype).controller, injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); // All nodes removed from the DOM via various jQuery APIs like .remove() @@ -12648,19 +12652,19 @@ $ExceptionHandlerProvider, $FilterProvider, $$ForceReflowProvider, $InterpolateProvider, $IntervalProvider, - $$HashMapProvider, $HttpProvider, $HttpParamSerializerProvider, $HttpParamSerializerJQLikeProvider, $HttpBackendProvider, $xhrFactoryProvider, $jsonpCallbacksProvider, $LocationProvider, $LogProvider, + $$MapProvider, $ParseProvider, $RootScopeProvider, $QProvider, $$QProvider, $$SanitizeUriProvider, @@ -12694,15 +12698,15 @@ * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { // These placeholder strings will be replaced by grunt's `build` task. // They need to be double- or single-quoted. - full: '1.6.1', + full: '1.6.2', major: 1, minor: 6, - dot: 1, - codeName: 'promise-rectification' + dot: 2, + codeName: 'llamacorn-lovehug' }; function publishExternalAPI(angular) { extend(angular, { @@ -12838,11 +12842,11 @@ $$testability: $$TestabilityProvider, $timeout: $TimeoutProvider, $window: $WindowProvider, $$rAF: $$RAFProvider, $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, + $$Map: $$MapProvider, $$cookieReader: $$CookieReaderProvider }); } ]); } @@ -13986,54 +13990,74 @@ } return key; } -/** - * HashMap which can use objects as keys - */ -function HashMap(array, isolatedUid) { - if (isolatedUid) { - var uid = 0; - this.nextUid = function() { - return ++uid; - }; - } - forEach(array, this.put, this); +// A minimal ES2015 Map implementation. +// Should be bug/feature equivalent to the native implementations of supported browsers +// (for the features required in Angular). +// See https://kangax.github.io/compat-table/es6/#test-Map +var nanKey = Object.create(null); +function NgMapShim() { + this._keys = []; + this._values = []; + this._lastKey = NaN; + this._lastIndex = -1; } -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key, this.nextUid)] = value; +NgMapShim.prototype = { + _idx: function(key) { + if (key === this._lastKey) { + return this._lastIndex; + } + this._lastKey = key; + this._lastIndex = this._keys.indexOf(key); + return this._lastIndex; }, - - /** - * @param key - * @returns {Object} the value for the key - */ + _transformKey: function(key) { + return isNumberNaN(key) ? nanKey : key; + }, get: function(key) { - return this[hashKey(key, this.nextUid)]; + key = this._transformKey(key); + var idx = this._idx(key); + if (idx !== -1) { + return this._values[idx]; + } }, + set: function(key, value) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + idx = this._lastIndex = this._keys.length; + } + this._keys[idx] = key; + this._values[idx] = value; - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key, this.nextUid)]; - delete this[key]; - return value; + // Support: IE11 + // Do not `return this` to simulate the partial IE11 implementation + }, + delete: function(key) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + return false; + } + this._keys.splice(idx, 1); + this._values.splice(idx, 1); + this._lastKey = NaN; + this._lastIndex = -1; + return true; } }; -var $$HashMapProvider = [/** @this */function() { +// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations +// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map` +// implementations get more stable, we can reconsider switching to `window.Map` (when available). +var NgMap = NgMapShim; + +var $$MapProvider = [/** @this */function() { this.$get = [function() { - return HashMap; + return NgMap; }]; }]; /** * @ngdoc function @@ -14104,15 +14128,11 @@ var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var $injectorMinErr = minErr('$injector'); function stringifyFn(fn) { - // Support: Chrome 50-51 only - // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51 - // (See https://github.com/angular/angular.js/issues/14487.) - // TODO (gkalpak): Remove workaround when Chrome v52 is released - return Function.prototype.toString.call(fn) + ' '; + return Function.prototype.toString.call(fn); } function extractArgs(fn) { var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''), args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); @@ -14682,11 +14702,11 @@ function createInjector(modulesToLoad, strictDi) { strictDi = (strictDi === true); var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], - loadedModules = new HashMap([], true), + loadedModules = new NgMap(), providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), @@ -14790,11 +14810,11 @@ function loadModules(modulesToLoad) { assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); var runBlocks = [], moduleFn; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; - loadedModules.put(module, true); + loadedModules.set(module, true); function runInvokeQueue(queue) { var i, ii; for (i = 0, ii = queue.length; i < ii; i++) { var invokeArgs = queue[i], @@ -15276,11 +15296,11 @@ }; // this is prefixed with Core since it conflicts with // the animateQueueProvider defined in ngAnimate/animateQueue.js var $$CoreAnimateQueueProvider = /** @this */ function() { - var postDigestQueue = new HashMap(); + var postDigestQueue = new NgMap(); var postDigestElements = []; this.$get = ['$$AnimateRunner', '$rootScope', function($$AnimateRunner, $rootScope) { return { @@ -15355,11 +15375,11 @@ } if (toRemove) { jqLiteRemoveClass(elm, toRemove); } }); - postDigestQueue.remove(element); + postDigestQueue.delete(element); } }); postDigestElements.length = 0; } @@ -15370,11 +15390,11 @@ var classesAdded = updateData(data, add, true); var classesRemoved = updateData(data, remove, false); if (classesAdded || classesRemoved) { - postDigestQueue.put(element, data); + postDigestQueue.set(element, data); postDigestElements.push(element); if (postDigestElements.length === 1) { $rootScope.$$postDigest(handleCSSClassChanges); } @@ -16230,11 +16250,10 @@ // MSIE can reportedly throw when there is no state (UNCONFIRMED). } }; cacheState(); - lastHistoryState = cachedState; /** * @name $browser#url * * @description @@ -16284,12 +16303,10 @@ // to not firing a `hashchange` nor `popstate` event // in some cases (see #9143). if ($sniffer.history && (!sameBase || !sameState)) { history[replace ? 'replaceState' : 'pushState'](state, '', url); cacheState(); - // Do the assignment again so that those two variables are referentially identical. - lastHistoryState = cachedState; } else { if (!sameBase) { pendingLocation = url; } if (replace) { @@ -16334,12 +16351,11 @@ var urlChangeListeners = [], urlChangeInit = false; function cacheStateAndFireUrlChange() { pendingLocation = null; - cacheState(); - fireUrlChange(); + fireStateOrUrlChange(); } // This variable should be used *only* inside the cacheState function. var lastCachedState = null; function cacheState() { @@ -16349,15 +16365,20 @@ // Prevent callbacks fo fire twice if both hashchange & popstate were fired. if (equals(cachedState, lastCachedState)) { cachedState = lastCachedState; } + lastCachedState = cachedState; + lastHistoryState = cachedState; } - function fireUrlChange() { - if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { + function fireStateOrUrlChange() { + var prevLastHistoryState = lastHistoryState; + cacheState(); + + if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) { return; } lastBrowserUrl = self.url(); lastHistoryState = cachedState; @@ -16419,11 +16440,11 @@ /** * Checks whether the url has changed outside of Angular. * Needs to be exported to be able to check for changes that have been done in sync, * as hashchange/popstate events fire in async. */ - self.$$checkUrlChange = fireUrlChange; + self.$$checkUrlChange = fireStateOrUrlChange; ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// @@ -17029,11 +17050,12 @@ * had their bindings initialized (and before the pre &amp; post linking functions for the directives on * this element). This is a good place to put initialization code for your controller. * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a - * component such as cloning the bound value to prevent accidental mutation of the outer value. + * component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will + * also be called when your bindings are initialized. * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on * changes. Any actions that you wish to take in response to the changes that you detect must be * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; @@ -17884,11 +17906,11 @@ // 'on' and be composed of only English letters. var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; var bindingCache = createMap(); function parseIsolateBindings(scope, directiveName, isController) { - var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/; + var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/; var bindings = createMap(); forEach(scope, function(definition, scopeName) { if (definition in bindingCache) { @@ -20056,11 +20078,11 @@ linkQueue = null; }).catch(function(error) { if (error instanceof Error) { $exceptionHandler(error); } - }).catch(noop); + }); return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { var childBoundTranscludeFn = boundTranscludeFn; if (scope.$$destroyed) return; if (linkQueue) { @@ -22188,11 +22210,12 @@ promise.then(removePendingReq, removePendingReq); if ((config.cache || defaults.cache) && config.cache !== false && (config.method === 'GET' || config.method === 'JSONP')) { cache = isObject(config.cache) ? config.cache - : isObject(defaults.cache) ? defaults.cache + : isObject(/** @type {?} */ (defaults).cache) + ? /** @type {?} */ (defaults).cache : defaultCache; } if (cache) { cachedResp = cache.get(url); @@ -23366,10 +23389,12 @@ var search = toKeyValue(this.$$search), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + + this.$$urlUpdatedByLocation = true; }; this.$$parseLinkUrl = function(url, relHref) { if (relHref && relHref[0] === '#') { // special case for links to hash fragments: @@ -23443,11 +23468,11 @@ withoutHashUrl = withoutBaseUrl; } else { withoutHashUrl = ''; if (isUndefined(withoutBaseUrl)) { appBase = url; - this.replace(); + /** @type {?} */ (this).replace(); } } } parseAppUrl(withoutHashUrl, this); @@ -23499,10 +23524,12 @@ var search = toKeyValue(this.$$search), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + + this.$$urlUpdatedByLocation = true; }; this.$$parseLinkUrl = function(url, relHref) { if (stripHash(appBase) === stripHash(url)) { this.$$parse(url); @@ -23556,10 +23583,12 @@ hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' this.$$absUrl = appBase + hashPrefix + this.$$url; + + this.$$urlUpdatedByLocation = true; }; } @@ -23885,10 +23914,11 @@ } // The user might modify `stateObject` after invoking `$location.state(stateObject)` // but we're changing the $$state reference to $browser.state() during the $digest // so the modification window is narrow. this.$$state = isUndefined(state) ? null : state; + this.$$urlUpdatedByLocation = true; return this; }; }); @@ -24197,40 +24227,44 @@ if (!$rootScope.$$phase) $rootScope.$digest(); }); // update browser $rootScope.$watch(function $locationWatch() { - var oldUrl = trimEmptyHash($browser.url()); - var newUrl = trimEmptyHash($location.absUrl()); - var oldState = $browser.state(); - var currentReplace = $location.$$replace; - var urlOrStateChanged = oldUrl !== newUrl || - ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); + if (initializing || $location.$$urlUpdatedByLocation) { + $location.$$urlUpdatedByLocation = false; - if (initializing || urlOrStateChanged) { - initializing = false; + var oldUrl = trimEmptyHash($browser.url()); + var newUrl = trimEmptyHash($location.absUrl()); + var oldState = $browser.state(); + var currentReplace = $location.$$replace; + var urlOrStateChanged = oldUrl !== newUrl || + ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); - $rootScope.$evalAsync(function() { - var newUrl = $location.absUrl(); - var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - $location.$$state, oldState).defaultPrevented; + if (initializing || urlOrStateChanged) { + initializing = false; - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; + $rootScope.$evalAsync(function() { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - } else { - if (urlOrStateChanged) { - setBrowserUrlWithFallback(newUrl, currentReplace, - oldState === $location.$$state ? null : $location.$$state); + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + } else { + if (urlOrStateChanged) { + setBrowserUrlWithFallback(newUrl, currentReplace, + oldState === $location.$$state ? null : $location.$$state); + } + afterLocationChange(oldUrl, oldState); } - afterLocationChange(oldUrl, oldState); - } - }); + }); + } } $location.$$replace = false; // we don't need to return anything because $evalAsync will make the digest loop dirty when @@ -24304,11 +24338,11 @@ * @returns {*} current value if used as getter or itself (chaining) if used as setter */ this.debugEnabled = function(flag) { if (isDefined(flag)) { debug = flag; - return this; + return this; } else { return debug; } }; @@ -25126,10 +25160,17 @@ findConstantAndWatchExpressions(property.value, $filter); allConstants = allConstants && property.value.constant && !property.computed; if (!property.value.constant) { argsToWatch.push.apply(argsToWatch, property.value.toWatch); } + if (property.computed) { + findConstantAndWatchExpressions(property.key, $filter); + if (!property.key.constant) { + argsToWatch.push.apply(argsToWatch, property.key.toWatch); + } + } + }); ast.constant = allConstants; ast.toWatch = argsToWatch; break; case AST.ThisExpression: @@ -26191,17 +26232,17 @@ default: return addInterceptor(noop, interceptorFn); } } - function expressionInputDirtyCheck(newValue, oldValueOfValue) { + function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) { if (newValue == null || oldValueOfValue == null) { // null/undefined return newValue === oldValueOfValue; } - if (typeof newValue === 'object') { + if (typeof newValue === 'object' && !compareObjectIdentity) { // attempt to convert the value to a primitive type // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can // be cheaply dirty-checked newValue = getValueOf(newValue); @@ -26226,11 +26267,11 @@ if (inputExpressions.length === 1) { var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails inputExpressions = inputExpressions[0]; return scope.$watch(function expressionInputWatch(scope) { var newInputValue = inputExpressions(scope); - if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) { + if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) { lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); oldInputValueOf = newInputValue && getValueOf(newInputValue); } return lastResult; }, listener, objectEquality, prettyPrintExpression); @@ -26246,11 +26287,11 @@ return scope.$watch(function expressionInputsWatch(scope) { var changed = false; for (var i = 0, ii = inputExpressions.length; i < ii; i++) { var newInputValue = inputExpressions[i](scope); - if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { + if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) { oldInputValues[i] = newInputValue; oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); } } @@ -27939,10 +27980,14 @@ } catch (e) { $exceptionHandler(e); } } postDigestQueue.length = postDigestQueuePosition = 0; + + // Check for changes to browser url that happened during the $digest + // (for which no event is fired; e.g. via `history.pushState()`) + $browser.$$checkUrlChange(); }, /** * @ngdoc event @@ -29644,11 +29689,14 @@ // Chrome Packaged Apps are not allowed to access `history.pushState`. // If not sandboxed, they can be detected by the presence of `chrome.app.runtime` // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by // the presence of an extension runtime ID and the absence of other Chrome runtime APIs // (see https://developer.chrome.com/apps/manifest/sandbox). + // (NW.js apps have access to Chrome APIs, but do support `history`.) + isNw = $window.nw && $window.nw.process, isChromePackagedApp = + !isNw && $window.chrome && ($window.chrome.app && $window.chrome.app.runtime || !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id), hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState, android = @@ -32405,11 +32453,12 @@ * @restrict A * @priority 100 * * @description * - * This directive sets the `disabled` attribute on the element if the + * This directive sets the `disabled` attribute on the element (typically a form control, + * e.g. `input`, `button`, `select` etc.) if the * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. * * A special directive is necessary because we cannot use interpolation inside the `disabled` * attribute. See the {@link guide/interpolation interpolation guide} for more info. * @@ -34911,19 +34960,31 @@ function isValidForStep(viewValue, stepBase, step) { // At this point `stepBase` and `step` are expected to be non-NaN values // and `viewValue` is expected to be a valid stringified number. var value = Number(viewValue); + var isNonIntegerValue = !isNumberInteger(value); + var isNonIntegerStepBase = !isNumberInteger(stepBase); + var isNonIntegerStep = !isNumberInteger(step); + // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers. - if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) { - var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step)); + if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) { + var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0; + var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0; + var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0; + + var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals); var multiplier = Math.pow(10, decimalCount); value = value * multiplier; stepBase = stepBase * multiplier; step = step * multiplier; + + if (isNonIntegerValue) value = Math.round(value); + if (isNonIntegerStepBase) stepBase = Math.round(stepBase); + if (isNonIntegerStep) step = Math.round(step); } return (value - stepBase) % step === 0; } @@ -35476,11 +35537,14 @@ * Once the value property has been set (by adding input), it will not react to changes to * the value attribute anymore. Setting both attribute and property fixes this behavior, and * makes it possible to use ngValue as a sort of one-way bind. */ function updateElementValue(element, attr, value) { - element.prop('value', value); + // Support: IE9 only + // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`). + var propValue = isDefined(value) ? value : (msie === 9) ? '' : null; + element.prop('value', propValue); attr.$set('value', value); } return { restrict: 'A', @@ -36804,19 +36868,19 @@ var directiveName = directiveNormalize('ng-' + eventName); ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { return { restrict: 'A', compile: function($element, attr) { - // We expose the powerful $event object on the scope that provides access to the Window, - // etc. that isn't protected by the fast paths in $parse. We explicitly request better - // checks at the cost of speed since event handler expressions are not executed as - // frequently as regular change detection. - var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true); + // NOTE: + // We expose the powerful `$event` object on the scope that provides access to the Window, + // etc. This is OK, because expressions are not sandboxed any more (and the expression + // sandbox was never meant to be a security feature anyway). + var fn = $parse(attr[directiveName]); return function ngEventHandler(scope, element) { element.on(eventName, function(event) { var callback = function() { - fn(scope, {$event:event}); + fn(scope, {$event: event}); }; if (forceAsyncEvents[eventName] && $rootScope.$$phase) { scope.$evalAsync(callback); } else { scope.$apply(callback); @@ -38691,10 +38755,33 @@ } else { this.$$scope.$apply(function() { that.$commitViewValue(); }); } + }, + + /** + * @ngdoc method + * + * @name ngModel.NgModelController#$overrideModelOptions + * + * @description + * + * Override the current model options settings programmatically. + * + * The previous `ModelOptions` value will not be modified. Instead, a + * new `ModelOptions` object will inherit from the previous one overriding + * or inheriting settings that are defined in the given parameter. + * + * See {@link ngModelOptions} for information about what options can be specified + * and how model option inheritance works. + * + * @param {Object} options a hash of settings to override the previous options + * + */ + $overrideModelOptions: function(options) { + this.$options = this.$options.createChild(options); } }; function setupModelWatcher(ctrl) { // model -> value @@ -40954,157 +41041,183 @@ * @ngdoc directive * @name ngShow * @multiElement * * @description - * The `ngShow` directive shows or hides the given HTML element based on the expression - * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding - * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined - * in AngularJS and sets the display style to none (using an !important flag). - * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * The `ngShow` directive shows or hides the given HTML element based on the expression provided to + * the `ngShow` attribute. * + * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. + * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an + * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see + * {@link ng.directive:ngCsp ngCsp}). + * * ```html * <!-- when $scope.myValue is truthy (element is visible) --> * <div ng-show="myValue"></div> * * <!-- when $scope.myValue is falsy (element is hidden) --> * <div ng-show="myValue" class="ng-hide"></div> * ``` * - * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class - * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed - * from the element causing the element not to appear hidden. + * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added + * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide` + * CSS class is removed from the element causing the element not to appear hidden. * - * ## Why is !important used? + * ## Why is `!important` used? * - * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector - * can be easily overridden by heavier selectors. For example, something as simple - * as changing the display style on a HTML list item would make hidden elements appear visible. - * This also becomes a bigger issue when dealing with CSS frameworks. + * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the + * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as + * simple as changing the display style on a HTML list item would make hidden elements appear + * visible. This also becomes a bigger issue when dealing with CSS frameworks. * - * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector - * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the - * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * By using `!important`, the show and hide behavior will work as expected despite any clash between + * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a + * developer chooses to override the styling to change how to hide an element then it is just a + * matter of using `!important` in their own CSS code. * * ### Overriding `.ng-hide` * - * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change - * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` - * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope - * with extra animation classes that can be added. + * By default, the `.ng-hide` class will style the element with `display: none !important`. If you + * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for + * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually + * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added. * * ```css * .ng-hide:not(.ng-hide-animate) { - * /&#42; this is just another form of hiding an element &#42;/ + * /&#42; These are just alternative ways of hiding an element &#42;/ * display: block!important; * position: absolute; * top: -9999px; * left: -9999px; * } * ``` * - * By default you don't need to override in CSS anything and the animations will work around the display style. + * By default you don't need to override anything in CSS and the animations will work around the + * display style. * * ## A note about animations with `ngShow` * - * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression - * is true and false. This system works like the animation system present with ngClass except that - * you must also include the !important flag to override the display property - * so that you can perform an animation when the element is hidden during the time of the animation. + * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the + * directive expression is true and false. This system works like the animation system present with + * `ngClass` except that you must also include the `!important` flag to override the display + * property so that the elements are not actually hidden during the animation. * * ```css - * // - * //a working example can be found at the bottom of this page - * // + * /&#42; A working example can be found at the bottom of this page. &#42;/ * .my-element.ng-hide-add, .my-element.ng-hide-remove { - * /&#42; this is required as of 1.3x to properly - * apply all styling in a show/hide animation &#42;/ - * transition: 0s linear all; + * transition: all 0.5s linear; * } * - * .my-element.ng-hide-add-active, - * .my-element.ng-hide-remove-active { - * /&#42; the transition is defined in the active class &#42;/ - * transition: 1s linear all; - * } - * * .my-element.ng-hide-add { ... } * .my-element.ng-hide-add.ng-hide-add-active { ... } * .my-element.ng-hide-remove { ... } * .my-element.ng-hide-remove.ng-hide-remove-active { ... } * ``` * - * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display - * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property + * to block during animation states - ngAnimate will automatically handle the style toggling for you. * * @animations - * | Animation | Occurs | - * |----------------------------------|-------------------------------------| - * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden | - * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible | + * | Animation | Occurs | + * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------| + * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. | + * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. | * * @element ANY - * @param {expression} ngShow If the {@link guide/expression expression} is truthy - * then the element is shown or hidden respectively. + * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the + * element is shown/hidden respectively. * * @example - <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show"> + * A simple example, animating the element's opacity: + * + <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple"> <file name="index.html"> - Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/> - <div> - Show: - <div class="check-element animate-show" ng-show="checked"> - <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. - </div> + Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br /> + <div class="check-element animate-show-hide" ng-show="checked"> + I show up when your checkbox is checked. </div> - <div> - Hide: - <div class="check-element animate-show" ng-hide="checked"> - <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. - </div> - </div> </file> - <file name="glyphicons.css"> - @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); - </file> <file name="animations.css"> - .animate-show { - line-height: 20px; + .animate-show-hide.ng-hide { + opacity: 0; + } + + .animate-show-hide.ng-hide-add, + .animate-show-hide.ng-hide-remove { + transition: all linear 0.5s; + } + + .check-element { + border: 1px solid black; opacity: 1; padding: 10px; - border: 1px solid black; - background: white; } + </file> + <file name="protractor.js" type="protractor"> + it('should check ngShow', function() { + var checkbox = element(by.model('checked')); + var checkElem = element(by.css('.check-element')); - .animate-show.ng-hide-add, .animate-show.ng-hide-remove { - transition: all linear 0.5s; + expect(checkElem.isDisplayed()).toBe(false); + checkbox.click(); + expect(checkElem.isDisplayed()).toBe(true); + }); + </file> + </example> + * + * <hr /> + * @example + * A more complex example, featuring different show/hide animations: + * + <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex"> + <file name="index.html"> + Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br /> + <div class="check-element funky-show-hide" ng-show="checked"> + I show up when your checkbox is checked. + </div> + </file> + <file name="animations.css"> + body { + overflow: hidden; + perspective: 1000px; } - .animate-show.ng-hide { - line-height: 0; - opacity: 0; - padding: 0 10px; + .funky-show-hide.ng-hide-add { + transform: rotateZ(0); + transform-origin: right; + transition: all 0.5s ease-in-out; } + .funky-show-hide.ng-hide-add.ng-hide-add-active { + transform: rotateZ(-135deg); + } + + .funky-show-hide.ng-hide-remove { + transform: rotateY(90deg); + transform-origin: left; + transition: all 0.5s ease; + } + + .funky-show-hide.ng-hide-remove.ng-hide-remove-active { + transform: rotateY(0); + } + .check-element { - padding: 10px; border: 1px solid black; - background: white; + opacity: 1; + padding: 10px; } </file> <file name="protractor.js" type="protractor"> - var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); - var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + it('should check ngShow', function() { + var checkbox = element(by.model('checked')); + var checkElem = element(by.css('.check-element')); - it('should check ng-show / ng-hide', function() { - expect(thumbsUp.isDisplayed()).toBeFalsy(); - expect(thumbsDown.isDisplayed()).toBeTruthy(); - - element(by.model('checked')).click(); - - expect(thumbsUp.isDisplayed()).toBeTruthy(); - expect(thumbsDown.isDisplayed()).toBeFalsy(); + expect(checkElem.isDisplayed()).toBe(false); + checkbox.click(); + expect(checkElem.isDisplayed()).toBe(true); }); </file> </example> */ var ngShowDirective = ['$animate', function($animate) { @@ -41130,145 +41243,184 @@ * @ngdoc directive * @name ngHide * @multiElement * * @description - * The `ngHide` directive shows or hides the given HTML element based on the expression - * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding - * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined - * in AngularJS and sets the display style to none (using an !important flag). - * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * The `ngHide` directive shows or hides the given HTML element based on the expression provided to + * the `ngHide` attribute. * + * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. + * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an + * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see + * {@link ng.directive:ngCsp ngCsp}). + * * ```html * <!-- when $scope.myValue is truthy (element is hidden) --> * <div ng-hide="myValue" class="ng-hide"></div> * * <!-- when $scope.myValue is falsy (element is visible) --> * <div ng-hide="myValue"></div> * ``` * - * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class - * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed - * from the element causing the element not to appear hidden. + * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added + * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide` + * CSS class is removed from the element causing the element not to appear hidden. * - * ## Why is !important used? + * ## Why is `!important` used? * - * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector - * can be easily overridden by heavier selectors. For example, something as simple - * as changing the display style on a HTML list item would make hidden elements appear visible. - * This also becomes a bigger issue when dealing with CSS frameworks. + * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the + * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as + * simple as changing the display style on a HTML list item would make hidden elements appear + * visible. This also becomes a bigger issue when dealing with CSS frameworks. * - * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector - * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the - * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * By using `!important`, the show and hide behavior will work as expected despite any clash between + * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a + * developer chooses to override the styling to change how to hide an element then it is just a + * matter of using `!important` in their own CSS code. * * ### Overriding `.ng-hide` * - * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change - * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` - * class in CSS: + * By default, the `.ng-hide` class will style the element with `display: none !important`. If you + * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for + * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually + * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added. * * ```css - * .ng-hide { - * /&#42; this is just another form of hiding an element &#42;/ + * .ng-hide:not(.ng-hide-animate) { + * /&#42; These are just alternative ways of hiding an element &#42;/ * display: block!important; * position: absolute; * top: -9999px; * left: -9999px; * } * ``` * - * By default you don't need to override in CSS anything and the animations will work around the display style. + * By default you don't need to override in CSS anything and the animations will work around the + * display style. * * ## A note about animations with `ngHide` * - * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression - * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` - * CSS class is added and removed for you instead of your own CSS class. + * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the + * directive expression is true and false. This system works like the animation system present with + * `ngClass` except that you must also include the `!important` flag to override the display + * property so that the elements are not actually hidden during the animation. * * ```css - * // - * //a working example can be found at the bottom of this page - * // + * /&#42; A working example can be found at the bottom of this page. &#42;/ * .my-element.ng-hide-add, .my-element.ng-hide-remove { - * transition: 0.5s linear all; + * transition: all 0.5s linear; * } * * .my-element.ng-hide-add { ... } * .my-element.ng-hide-add.ng-hide-add-active { ... } * .my-element.ng-hide-remove { ... } * .my-element.ng-hide-remove.ng-hide-remove-active { ... } * ``` * - * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display - * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property + * to block during animation states - ngAnimate will automatically handle the style toggling for you. * * @animations - * | Animation | Occurs | - * |----------------------------------|-------------------------------------| - * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden | - * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible | + * | Animation | Occurs | + * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------| + * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. | + * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. | * * * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} is truthy then - * the element is shown or hidden respectively. + * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the + * element is hidden/shown respectively. * * @example - <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide"> + * A simple example, animating the element's opacity: + * + <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple"> <file name="index.html"> - Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/> - <div> - Show: - <div class="check-element animate-hide" ng-show="checked"> - <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. - </div> + Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br /> + <div class="check-element animate-show-hide" ng-hide="checked"> + I hide when your checkbox is checked. </div> - <div> - Hide: - <div class="check-element animate-hide" ng-hide="checked"> - <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. - </div> - </div> </file> - <file name="glyphicons.css"> - @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); - </file> <file name="animations.css"> - .animate-hide { + .animate-show-hide.ng-hide { + opacity: 0; + } + + .animate-show-hide.ng-hide-add, + .animate-show-hide.ng-hide-remove { transition: all linear 0.5s; - line-height: 20px; + } + + .check-element { + border: 1px solid black; opacity: 1; padding: 10px; - border: 1px solid black; - background: white; } + </file> + <file name="protractor.js" type="protractor"> + it('should check ngHide', function() { + var checkbox = element(by.model('checked')); + var checkElem = element(by.css('.check-element')); - .animate-hide.ng-hide { - line-height: 0; - opacity: 0; - padding: 0 10px; + expect(checkElem.isDisplayed()).toBe(true); + checkbox.click(); + expect(checkElem.isDisplayed()).toBe(false); + }); + </file> + </example> + * + * <hr /> + * @example + * A more complex example, featuring different show/hide animations: + * + <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex"> + <file name="index.html"> + Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br /> + <div class="check-element funky-show-hide" ng-hide="checked"> + I hide when your checkbox is checked. + </div> + </file> + <file name="animations.css"> + body { + overflow: hidden; + perspective: 1000px; } + .funky-show-hide.ng-hide-add { + transform: rotateZ(0); + transform-origin: right; + transition: all 0.5s ease-in-out; + } + + .funky-show-hide.ng-hide-add.ng-hide-add-active { + transform: rotateZ(-135deg); + } + + .funky-show-hide.ng-hide-remove { + transform: rotateY(90deg); + transform-origin: left; + transition: all 0.5s ease; + } + + .funky-show-hide.ng-hide-remove.ng-hide-remove-active { + transform: rotateY(0); + } + .check-element { - padding: 10px; border: 1px solid black; - background: white; + opacity: 1; + padding: 10px; } </file> <file name="protractor.js" type="protractor"> - var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); - var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + it('should check ngHide', function() { + var checkbox = element(by.model('checked')); + var checkElem = element(by.css('.check-element')); - it('should check ng-show / ng-hide', function() { - expect(thumbsUp.isDisplayed()).toBeFalsy(); - expect(thumbsDown.isDisplayed()).toBeTruthy(); - - element(by.model('checked')).click(); - - expect(thumbsUp.isDisplayed()).toBeTruthy(); - expect(thumbsDown.isDisplayed()).toBeFalsy(); + expect(checkElem.isDisplayed()).toBe(true); + checkbox.click(); + expect(checkElem.isDisplayed()).toBe(false); }); </file> </example> */ var ngHideDirective = ['$animate', function($animate) { @@ -41859,11 +42011,11 @@ */ var SelectController = ['$element', '$scope', /** @this */ function($element, $scope) { var self = this, - optionsMap = new HashMap(); + optionsMap = new NgMap(); self.selectValueMap = {}; // Keys are the hashed values, values the original values // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors self.ngModelCtrl = noopNgModelController; @@ -41980,28 +42132,28 @@ if (value === '') { self.hasEmptyOption = true; self.emptyOption = element; } var count = optionsMap.get(value) || 0; - optionsMap.put(value, count + 1); + optionsMap.set(value, count + 1); // Only render at the end of a digest. This improves render performance when many options // are added during a digest and ensures all relevant options are correctly marked as selected scheduleRender(); }; // Tell the select control that an option, with the given value, has been removed self.removeOption = function(value) { var count = optionsMap.get(value); if (count) { if (count === 1) { - optionsMap.remove(value); + optionsMap.delete(value); if (value === '') { self.hasEmptyOption = false; self.emptyOption = undefined; } } else { - optionsMap.put(value, count - 1); + optionsMap.set(value, count - 1); } } }; // Check whether the select control has an option matching the given value @@ -42124,11 +42276,11 @@ optionElement.on('$destroy', function() { var currentValue = self.readValue(); var removeValue = optionAttrs.value; self.removeOption(removeValue); - self.ngModelCtrl.$render(); + scheduleRender(); if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 || currentValue === removeValue ) { // When multiple (selected) options are destroyed at the same time, we don't want @@ -42449,12 +42601,12 @@ return array; }; // Write value now needs to set the selected property of each matching option selectCtrl.writeValue = function writeMultipleValue(value) { - var items = new HashMap(value); forEach(element.find('option'), function(option) { - option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value])); + option.selected = !!value && (includes(value, option.value) || + includes(value, selectCtrl.selectValueMap[option.value])); }); }; // 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 \ No newline at end of file