vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.2.0.rc3 vs vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.2.0

- old
+ new

@@ -9788,11 +9788,11 @@ } })( window ); /** - * @license AngularJS v1.2.0-rc.3 + * @license AngularJS v1.2.0 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document){ var _jQuery = window.jQuery.noConflict(true); @@ -9869,10 +9869,94 @@ return new Error(message); }; } +/* We need to tell jshint what variables are being exported */ +/* global + -angular, + -msie, + -jqLite, + -jQuery, + -slice, + -push, + -toString, + -ngMinErr, + -_angular, + -angularModule, + -nodeName_, + -uid, + + -lowercase, + -uppercase, + -manualLowercase, + -manualUppercase, + -nodeName_, + -isArrayLike, + -forEach, + -sortedKeys, + -forEachSorted, + -reverseParams, + -nextUid, + -setHashKey, + -extend, + -int, + -inherit, + -noop, + -identity, + -valueFn, + -isUndefined, + -isDefined, + -isObject, + -isString, + -isNumber, + -isDate, + -isArray, + -isFunction, + -isRegExp, + -isWindow, + -isScope, + -isFile, + -isBoolean, + -trim, + -isElement, + -makeMap, + -map, + -size, + -includes, + -indexOf, + -arrayRemove, + -isLeafNode, + -copy, + -shallowCopy, + -equals, + -csp, + -concat, + -sliceArgs, + -bind, + -toJsonReplacer, + -toJson, + -fromJson, + -toBoolean, + -startingTag, + -tryDecodeURIComponent, + -parseKeyValue, + -toKeyValue, + -encodeUriSegment, + -encodeUriQuery, + -angularInit, + -bootstrap, + -snake_case, + -bindJQuery, + -assertArg, + -assertArgFn, + -assertNotHasOwnProperty, + -getter, + -getBlockElements + +*/ + //////////////////////////////////// /** * @ngdoc function * @name angular.lowercase @@ -9896,15 +9980,17 @@ */ var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; var manualLowercase = function(s) { + /* jshint bitwise: false */ return isString(s) ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) : s; }; var manualUppercase = function(s) { + /* jshint bitwise: false */ return isString(s) ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) : s; }; @@ -9946,11 +10032,12 @@ /** * @private * @param {*} obj - * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, String ...) + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, + * String ...) */ function isArrayLike(obj) { if (obj == null || isWindow(obj)) { return false; } @@ -10040,11 +10127,11 @@ * when using forEach the params are value, key, but it is often useful to have key, value. * @param {function(string, *)} iteratorFn * @returns {function(*, string)} */ function reverseParams(iteratorFn) { - return function(value, key) { iteratorFn(key, value) }; + return function(value, key) { iteratorFn(key, value); }; } /** * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric * characters such as '012ABC'. The reason why we are not using simply a number counter is that @@ -10399,21 +10486,21 @@ * @param {Object|Array|string} obj Object, array, or string to inspect. * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. */ function size(obj, ownPropsOnly) { - var size = 0, key; + var count = 0, key; if (isArray(obj) || isString(obj)) { return obj.length; } else if (isObject(obj)){ for (key in obj) if (!ownPropsOnly || obj.hasOwnProperty(key)) - size++; + count++; } - return size; + return count; } function includes(array, obj) { return indexOf(array, obj) != -1; @@ -10459,13 +10546,10 @@ * * If a destination is provided, all of its elements (for array) or properties (for objects) * are deleted and then all elements/properties from the source are copied to it. * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. * * If `source` is identical to 'destination' an exception will be thrown. * - * Note: this function is used to augment the Object type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * * @param {*} source The source that will be used to make a copy. * Can be any type, including primitives, `null`, and `undefined`. * @param {(Object|Array)=} destination Destination into which the source is copied. If * provided, must be of the same type as `source`. * @returns {*} The copy or updated `destination`, if `destination` was specified. @@ -10506,11 +10590,12 @@ </doc:source> </doc:example> */ function copy(source, destination){ if (isWindow(source) || isScope(source)) { - throw ngMinErr('cpws', "Can't copy! Making copies of Window or Scope instances is not supported."); + throw ngMinErr('cpws', + "Can't copy! Making copies of Window or Scope instances is not supported."); } if (!destination) { destination = source; if (source) { @@ -10523,11 +10608,12 @@ } else if (isObject(source)) { destination = copy(source, {}); } } } else { - if (source === destination) throw ngMinErr('cpi', "Can't copy! Source and destination are identical."); + if (source === destination) throw ngMinErr('cpi', + "Can't copy! Source and destination are identical."); if (isArray(source)) { destination.length = 0; for ( var i = 0; i < source.length; i++) { destination.push(copy(source[i])); } @@ -10567,17 +10653,18 @@ * @ngdoc function * @name angular.equals * @function * * @description - * Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and - * objects. + * Determines if two objects or two values are equivalent. Supports value types, regular + * expressions, arrays and objects. * * Two objects or values are considered equivalent if at least one of the following is true: * * * Both objects or values pass `===` comparison. - * * Both objects or values are of the same type and all of their properties pass `===` comparison. + * * Both objects or values are of the same type and all of their properties are equal by + * comparing them with `angular.equals`. * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) * * Both values represent the same regular expression (In JavasScript, * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual * representation matches). * @@ -10629,35 +10716,44 @@ } return false; } +function csp() { + return (document.securityPolicy && document.securityPolicy.isActive) || + (document.querySelector && + !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]'))); +} + + function concat(array1, array2, index) { return array1.concat(slice.call(array2, index)); } function sliceArgs(args, startIndex) { return slice.call(args, startIndex || 0); } +/* jshint -W101 */ /** * @ngdoc function * @name angular.bind * @function * * @description * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for * `fn`). You can supply optional `args` that are prebound to the function. This feature is also - * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as distinguished - * from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). + * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as + * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). * * @param {Object} self Context which `fn` should be evaluated in. * @param {function()} fn Function to be bound. * @param {...*} args Optional arguments to be prebound to the `fn` function call. * @returns {function()} Function that wraps the `fn` with all the specified bindings. */ +/* jshint +W101 */ function bind(self, fn) { var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; if (isFunction(fn) && !(fn instanceof RegExp)) { return curryArgs.length ? function() { @@ -10813,14 +10909,16 @@ function toKeyValue(obj) { var parts = []; forEach(obj, function(value, key) { if (isArray(value)) { forEach(value, function(arrayValue) { - parts.push(encodeUriQuery(key, true) + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); + parts.push(encodeUriQuery(key, true) + + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); }); } else { - parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); + parts.push(encodeUriQuery(key, true) + + (value === true ? '' : '=' + encodeUriQuery(value, true))); } }); return parts.length ? parts.join('&') : ''; } @@ -10878,12 +10976,12 @@ * Use this directive to auto-bootstrap an application. Only * one ngApp directive can be used per HTML document. The directive * designates the root of the application and is typically placed * at the root of the page. * - * The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an - * HTML document you must manually bootstrap them using {@link angular.bootstrap}. + * The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in + * an HTML document you must manually bootstrap them using {@link angular.bootstrap}. * Applications cannot be nested. * * In the example below if the `ngApp` directive were not placed * on the `html` element then the document would not be compiled * and the `{{ 1+2 }}` would not be resolved to `3`. @@ -10953,11 +11051,12 @@ * They must use {@link api/ng.directive:ngApp ngApp}. * * @param {Element} element DOM element which is the root of angular application. * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. * Each item in the array should be the name of a predefined module or a (DI annotated) - * function that will be invoked by the injector as a run block. See: {@link angular.module modules} + * function that will be invoked by the injector as a run block. + * See: {@link angular.module modules} * @returns {AUTO.$injector} Returns the newly created injector for this app. */ function bootstrap(element, modules) { var doBootstrap = function() { element = jqLite(element); @@ -10977,11 +11076,10 @@ function(scope, element, compile, injector, animate) { scope.$apply(function() { element.data('$injector', injector); compile(element)(scope); }); - animate.enabled(true); }] ); return injector; }; @@ -11014,18 +11112,20 @@ // reset to jQuery or default to us. if (jQuery) { jqLite = jQuery; extend(jQuery.fn, { scope: JQLitePrototype.scope, + isolateScope: JQLitePrototype.isolateScope, controller: JQLitePrototype.controller, injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); - // Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) - JQLitePatchJQueryRemove('remove', true, true, false); - JQLitePatchJQueryRemove('empty', false, false, false); - JQLitePatchJQueryRemove('html', false, false, true); + // Method signature: + // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) + jqLitePatchJQueryRemove('remove', true, true, false); + jqLitePatchJQueryRemove('empty', false, false, false); + jqLitePatchJQueryRemove('html', false, false, true); } else { jqLite = JQLite; } angular.element = jqLite; } @@ -11087,10 +11187,32 @@ } return obj; } /** + * Return the siblings between `startNode` and `endNode`, inclusive + * @param {Object} object with `startNode` and `endNode` properties + * @returns jQlite object containing the elements + */ +function getBlockElements(block) { + if (block.startNode === block.endNode) { + return jqLite(block.startNode); + } + + var element = block.startNode; + var elements = [element]; + + do { + element = element.nextSibling; + if (!element) break; + elements.push(element); + } while (element !== block.endNode); + + return jqLite(elements); +} + +/** * @ngdoc interface * @name angular.Module * @description * * Interface for configuring angular {@link angular.module modules}. @@ -11111,11 +11233,12 @@ /** * @ngdoc function * @name angular.module * @description * - * The `angular.module` is a global place for creating, registering and retrieving Angular modules. + * The `angular.module` is a global place for creating, registering and retrieving Angular + * modules. * All modules (angular core or 3rd party) that should be available to an application must be * registered using this mechanism. * * When passed two or more arguments, a new module is created. If passed only one argument, an * existing module (the name passed as the first argument to `module`) is retrieved. @@ -11149,26 +11272,26 @@ * However it's more likely that you'll just use * {@link ng.directive:ngApp ngApp} or * {@link angular.bootstrap} to simplify this process for you. * * @param {!string} name The name of the module to create or retrieve. - * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the - * the module is being retrieved for further configuration. + * @param {Array.<string>=} requires If specified then new module is being created. If + * unspecified then the the module is being retrieved for further configuration. * @param {Function} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. + * {@link angular.Module#methods_config Module#config()}. * @returns {module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { assertNotHasOwnProperty(name, 'module'); if (requires && modules.hasOwnProperty(name)) { modules[name] = null; } return ensure(modules, name, function() { if (!requires) { - throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled the module name " + - "or forgot to load it. If registering a module ensure that you specify the dependencies as the second " + - "argument.", name); + throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + + "the module name or forgot to load it. If registering a module ensure that you " + + "specify the dependencies as the second argument.", name); } /** @type {!Array.<Array.<*>>} */ var invokeQueue = []; @@ -11187,11 +11310,12 @@ * @ngdoc property * @name angular.Module#requires * @propertyOf angular.Module * @returns {Array.<string>} List of module names which must be loaded before this module. * @description - * Holds the list of modules which the injector will load before the current module is loaded. + * Holds the list of modules which the injector will load before the current module is + * loaded. */ requires: requires, /** * @ngdoc property @@ -11206,11 +11330,12 @@ /** * @ngdoc method * @name angular.Module#provider * @methodOf angular.Module * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the service. + * @param {Function} providerType Construction function for creating new instance of the + * service. * @description * See {@link AUTO.$provide#provider $provide.provider()}. */ provider: invokeLater('$provide', 'provider'), @@ -11262,18 +11387,19 @@ /** * @ngdoc method * @name angular.Module#animation * @methodOf angular.Module * @param {string} name animation name - * @param {Function} animationFactory Factory function for creating new instance of an animation. + * @param {Function} animationFactory Factory function for creating new instance of an + * animation. * @description * * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. * * - * Defines an animation hook that can be later used with {@link ngAnimate.$animate $animate} service and - * directives that use this service. + * Defines an animation hook that can be later used with + * {@link ngAnimate.$animate $animate} service and directives that use this service. * * <pre> * module.animation('.animation-name', function($inject1, $inject2) { * return { * eventName : function(element, done) { @@ -11322,11 +11448,11 @@ * @param {string|Object} name Directive name, or an object map of directives where the * keys are the names and the values are the factories. * @param {Function} directiveFactory Factory function for creating new instance of * directives. * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + * See {@link ng.$compileProvider#methods_directive $compileProvider.directive()}. */ directive: invokeLater('$compileProvider', 'directive'), /** * @ngdoc method @@ -11369,18 +11495,92 @@ */ function invokeLater(provider, method, insertMethod) { return function() { invokeQueue[insertMethod || 'push']([provider, method, arguments]); return moduleInstance; - } + }; } }); }; }); } +/* global + angularModule: true, + version: true, + + $LocaleProvider, + $CompileProvider, + + htmlAnchorDirective, + inputDirective, + inputDirective, + formDirective, + scriptDirective, + selectDirective, + styleDirective, + optionDirective, + ngBindDirective, + ngBindHtmlDirective, + ngBindTemplateDirective, + ngClassDirective, + ngClassEvenDirective, + ngClassOddDirective, + ngCspDirective, + ngCloakDirective, + ngControllerDirective, + ngFormDirective, + ngHideDirective, + ngIfDirective, + ngIncludeDirective, + ngInitDirective, + ngNonBindableDirective, + ngPluralizeDirective, + ngRepeatDirective, + ngShowDirective, + ngStyleDirective, + ngSwitchDirective, + ngSwitchWhenDirective, + ngSwitchDefaultDirective, + ngOptionsDirective, + ngTranscludeDirective, + ngModelDirective, + ngListDirective, + ngChangeDirective, + requiredDirective, + requiredDirective, + ngValueDirective, + ngAttributeAliasDirectives, + ngEventDirectives, + + $AnchorScrollProvider, + $AnimateProvider, + $BrowserProvider, + $CacheFactoryProvider, + $ControllerProvider, + $DocumentProvider, + $ExceptionHandlerProvider, + $FilterProvider, + $InterpolateProvider, + $IntervalProvider, + $HttpProvider, + $HttpBackendProvider, + $LocationProvider, + $LogProvider, + $ParseProvider, + $RootScopeProvider, + $QProvider, + $SceProvider, + $SceDelegateProvider, + $SnifferProvider, + $TemplateCacheProvider, + $TimeoutProvider, + $WindowProvider +*/ + + /** * @ngdoc property * @name angular.version * @description * An object that contains information about the current AngularJS version. This object has the @@ -11391,15 +11591,15 @@ * - `minor` – `{number}` – Minor version number, such as "9". * - `dot` – `{number}` – Dot version number, such as "18". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.2.0-rc.3', // all of these placeholder strings will be replaced by grunt's + full: '1.2.0', // all of these placeholder strings will be replaced by grunt's major: 1, // package task - minor: 2, + minor: "NG_VERSION_MINOR", dot: 0, - codeName: 'ferocious-twitch' + codeName: 'timely-delivery' }; function publishExternalAPI(angular){ extend(angular, { @@ -11421,16 +11621,17 @@ 'isFunction': isFunction, 'isObject': isObject, 'isNumber': isNumber, 'isElement': isElement, 'isArray': isArray, - '$$minErr': minErr, 'version': version, 'isDate': isDate, 'lowercase': lowercase, 'uppercase': uppercase, - 'callbacks': {counter: 0} + 'callbacks': {counter: 0}, + '$$minErr': minErr, + '$$csp': csp }); angularModule = setupModuleLoader(window); try { angularModule('ngLocale'); @@ -11454,11 +11655,10 @@ ngBindHtml: ngBindHtmlDirective, ngBindTemplate: ngBindTemplateDirective, ngClass: ngClassDirective, ngClassEven: ngClassEvenDirective, ngClassOdd: ngClassOddDirective, - ngCsp: ngCspDirective, ngCloak: ngCloakDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, @@ -11510,10 +11710,18 @@ }); } ]); } +/* global + + -JQLitePrototype, + -addEventListenerFn, + -removeEventListenerFn, + -BOOLEAN_ATTR +*/ + ////////////////////////////////// //JQLite ////////////////////////////////// /** @@ -11587,10 +11795,13 @@ * camelCase directive name, then the controller for this directive will be retrieved (e.g. * `'ngModel'`). * - `injector()` - retrieves the injector of the current element or its parent. * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current * element or its parent. + * - `isolateScope()` - retrieves an isolate {@link api/ng.$rootScope.Scope scope} if one is attached directly to the + * current element. This getter should be used only on elements that contain a directive which starts a new isolate + * scope. Calling `scope()` on this element always returns the original non-isolate scope. * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * parent element is reached. * * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. * @returns {Object} jQuery object. @@ -11632,17 +11843,18 @@ // In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a // $destroy event on all DOM nodes being removed. // ///////////////////////////////////////////// -function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) { +function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) { var originalJqFn = jQuery.fn[name]; originalJqFn = originalJqFn.$original || originalJqFn; removePatch.$original = originalJqFn; jQuery.fn[name] = removePatch; function removePatch(param) { + // jshint -W040 var list = filterElems && param ? [this.filter(param)] : [this], fireEvent = dispatchThis, set, setIndex, setLength, element, childIndex, childLength, children; @@ -11684,34 +11896,34 @@ var div = document.createElement('div'); // Read about the NoScope elements here: // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx div.innerHTML = '<div>&#160;</div>' + element; // IE insanity to make NoScope elements work! div.removeChild(div.firstChild); // remove the superfluous div - JQLiteAddNodes(this, div.childNodes); + jqLiteAddNodes(this, div.childNodes); var fragment = jqLite(document.createDocumentFragment()); fragment.append(this); // detach the elements from the temporary DOM div. } else { - JQLiteAddNodes(this, element); + jqLiteAddNodes(this, element); } } -function JQLiteClone(element) { +function jqLiteClone(element) { return element.cloneNode(true); } -function JQLiteDealoc(element){ - JQLiteRemoveData(element); +function jqLiteDealoc(element){ + jqLiteRemoveData(element); for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { - JQLiteDealoc(children[i]); + jqLiteDealoc(children[i]); } } -function JQLiteOff(element, type, fn, unsupported) { +function jqLiteOff(element, type, fn, unsupported) { if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); + var events = jqLiteExpandoStore(element, 'events'), + handle = jqLiteExpandoStore(element, 'handle'); if (!handle) return; //no listeners registered if (isUndefined(type)) { forEach(events, function(eventHandler, type) { @@ -11728,11 +11940,11 @@ } }); } } -function JQLiteRemoveData(element, name) { +function jqLiteRemoveData(element, name) { var expandoId = element[jqName], expandoStore = jqCache[expandoId]; if (expandoStore) { if (name) { @@ -11740,18 +11952,18 @@ return; } if (expandoStore.handle) { expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); - JQLiteOff(element); + jqLiteOff(element); } delete jqCache[expandoId]; element[jqName] = undefined; // ie does not allow deletion of attributes on elements. } } -function JQLiteExpandoStore(element, key, value) { +function jqLiteExpandoStore(element, key, value) { var expandoId = element[jqName], expandoStore = jqCache[expandoId || -1]; if (isDefined(value)) { if (!expandoStore) { @@ -11762,18 +11974,18 @@ } else { return expandoStore && expandoStore[key]; } } -function JQLiteData(element, key, value) { - var data = JQLiteExpandoStore(element, 'data'), +function jqLiteData(element, key, value) { + var data = jqLiteExpandoStore(element, 'data'), isSetter = isDefined(value), keyDefined = !isSetter && isDefined(key), isSimpleGetter = keyDefined && !isObject(key); if (!data && !isSimpleGetter) { - JQLiteExpandoStore(element, 'data', data = {}); + jqLiteExpandoStore(element, 'data', data = {}); } if (isSetter) { data[key] = value; } else { @@ -11788,17 +12000,17 @@ return data; } } } -function JQLiteHasClass(element, selector) { +function jqLiteHasClass(element, selector) { if (!element.getAttribute) return false; return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). indexOf( " " + selector + " " ) > -1); } -function JQLiteRemoveClass(element, cssClasses) { +function jqLiteRemoveClass(element, cssClasses) { if (cssClasses && element.setAttribute) { forEach(cssClasses.split(' '), function(cssClass) { element.setAttribute('class', trim( (" " + (element.getAttribute('class') || '') + " ") .replace(/[\n\t]/g, " ") @@ -11806,11 +12018,11 @@ ); }); } } -function JQLiteAddClass(element, cssClasses) { +function jqLiteAddClass(element, cssClasses) { if (cssClasses && element.setAttribute) { var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') .replace(/[\n\t]/g, " "); forEach(cssClasses.split(' '), function(cssClass) { @@ -11822,36 +12034,40 @@ element.setAttribute('class', trim(existingClasses)); } } -function JQLiteAddNodes(root, elements) { +function jqLiteAddNodes(root, elements) { if (elements) { elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) ? elements : [ elements ]; for(var i=0; i < elements.length; i++) { root.push(elements[i]); } } } -function JQLiteController(element, name) { - return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); +function jqLiteController(element, name) { + return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); } -function JQLiteInheritedData(element, name, value) { +function jqLiteInheritedData(element, name, value) { element = jqLite(element); // if element is the document object work with the html element instead // this makes $(document).scope() possible if(element[0].nodeType == 9) { element = element.find('html'); } + var names = isArray(name) ? name : [name]; while (element.length) { - if ((value = element.data(name)) !== undefined) return value; + + for (var i = 0, ii = names.length; i < ii; i++) { + if ((value = element.data(names[i])) !== undefined) return value; + } element = element.parent(); } } ////////////////////////////////////////// @@ -11871,11 +12087,13 @@ if (document.readyState === 'complete'){ setTimeout(trigger); } else { this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 // we can not use jqLite since we are not done loading and jQuery could be loaded later. + // jshint -W064 JQLite(window).on('load', trigger); // fallback to window.onload for others + // jshint +W064 } }, toString: function() { var value = []; forEach(this, function(e){ value.push('' + e);}); @@ -11913,28 +12131,34 @@ // booleanAttr is here twice to minimize DOM access return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } forEach({ - data: JQLiteData, - inheritedData: JQLiteInheritedData, + data: jqLiteData, + inheritedData: jqLiteInheritedData, scope: function(element) { - return JQLiteInheritedData(element, '$scope'); + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); }, - controller: JQLiteController , + isolateScope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate'); + }, + controller: jqLiteController , + injector: function(element) { - return JQLiteInheritedData(element, '$injector'); + return jqLiteInheritedData(element, '$injector'); }, removeAttr: function(element,name) { element.removeAttribute(name); }, - hasClass: JQLiteHasClass, + hasClass: jqLiteHasClass, css: function(element, name, value) { name = camelCase(name); if (isDefined(value)) { @@ -12006,11 +12230,11 @@ } getText.$dv = ''; return getText; function getText(element, value) { - var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType] + var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType]; if (isUndefined(value)) { return textProp ? element[textProp] : ''; } element[textProp] = value; } @@ -12035,29 +12259,29 @@ html: function(element, value) { if (isUndefined(value)) { return element.innerHTML; } for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { - JQLiteDealoc(childNodes[i]); + jqLiteDealoc(childNodes[i]); } element.innerHTML = value; } }, function(fn, name){ /** * Properties: writes return selection, reads return first value */ JQLite.prototype[name] = function(arg1, arg2) { var i, key; - // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it // in a way that survives minification. - if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) { + if (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined) { if (isObject(arg1)) { // we are a write, but the object properties are the key/values for(i=0; i < this.length; i++) { - if (fn === JQLiteData) { + if (fn === jqLiteData) { // data() takes the whole object in jQuery fn(this[i], arg1); } else { for (key in arg1) { fn(this[i], key, arg1[key]); @@ -12068,11 +12292,11 @@ return this; } else { // we are a read, so read the first child. var value = fn.$dv; // Only if we have $dv do we iterate over all, otherwise it is just the first element. - var jj = value == undefined ? Math.min(this.length, 1) : this.length; + var jj = (value === undefined) ? Math.min(this.length, 1) : this.length; for (var j = 0; j < jj; j++) { var nodeValue = fn(this[j], arg1, arg2); value = value ? value + nodeValue : nodeValue; } return value; @@ -12114,11 +12338,11 @@ }; event.defaultPrevented = false; } event.isDefaultPrevented = function() { - return event.defaultPrevented || event.returnValue == false; + return event.defaultPrevented || event.returnValue === false; }; forEach(events[type || event.type], function(fn) { fn.call(element, event); }); @@ -12145,30 +12369,31 @@ // Functions iterating traversal. // These functions chain results into a single // selector. ////////////////////////////////////////// forEach({ - removeData: JQLiteRemoveData, + removeData: jqLiteRemoveData, - dealoc: JQLiteDealoc, + dealoc: jqLiteDealoc, on: function onFn(element, type, fn, unsupported){ if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); + var events = jqLiteExpandoStore(element, 'events'), + handle = jqLiteExpandoStore(element, 'handle'); - if (!events) JQLiteExpandoStore(element, 'events', events = {}); - if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); + if (!events) jqLiteExpandoStore(element, 'events', events = {}); + if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); forEach(type.split(' '), function(type){ var eventFns = events[type]; if (!eventFns) { if (type == 'mouseenter' || type == 'mouseleave') { var contains = document.body.contains || document.body.compareDocumentPosition ? function( a, b ) { + // jshint bitwise: false var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : @@ -12204,21 +12429,21 @@ } else { addEventListenerFn(element, type, handle); events[type] = []; } - eventFns = events[type] + eventFns = events[type]; } eventFns.push(fn); }); }, - off: JQLiteOff, + off: jqLiteOff, replaceWith: function(element, replaceNode) { var index, parent = element.parentNode; - JQLiteDealoc(element); + jqLiteDealoc(element); forEach(new JQLite(replaceNode), function(node){ if (index) { parent.insertBefore(node, index.nextSibling); } else { parent.replaceChild(node, element); @@ -12265,11 +12490,11 @@ } wrapNode.appendChild(element); }, remove: function(element) { - JQLiteDealoc(element); + jqLiteDealoc(element); var parent = element.parentNode; if (parent) parent.removeChild(element); }, after: function(element, newElement) { @@ -12278,18 +12503,18 @@ parent.insertBefore(node, index.nextSibling); index = node; }); }, - addClass: JQLiteAddClass, - removeClass: JQLiteRemoveClass, + addClass: jqLiteAddClass, + removeClass: jqLiteRemoveClass, toggleClass: function(element, selector, condition) { if (isUndefined(condition)) { - condition = !JQLiteHasClass(element, selector); + condition = !jqLiteHasClass(element, selector); } - (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); + (condition ? jqLiteAddClass : jqLiteRemoveClass)(element, selector); }, parent: function(element) { var parent = element.parentNode; return parent && parent.nodeType !== 11 ? parent : null; @@ -12310,15 +12535,15 @@ find: function(element, selector) { return element.getElementsByTagName(selector); }, - clone: JQLiteClone, + clone: jqLiteClone, triggerHandler: function(element, eventName, eventData) { - var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; - + var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName]; + eventData = eventData || []; var event = [{ preventDefault: noop, stopPropagation: noop @@ -12333,21 +12558,21 @@ * chaining functions */ JQLite.prototype[name] = function(arg1, arg2, arg3) { var value; for(var i=0; i < this.length; i++) { - if (value == undefined) { + if (isUndefined(value)) { value = fn(this[i], arg1, arg2, arg3); - if (value !== undefined) { + if (isDefined(value)) { // any function which returns a value needs to be wrapped value = jqLite(value); } } else { - JQLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); + jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); } } - return value == undefined ? this : value; + return isDefined(value) ? value : this; }; // bind legacy bind/unbind to on/off JQLite.prototype.bind = JQLite.prototype.on; JQLite.prototype.unbind = JQLite.prototype.off; @@ -12532,13 +12757,13 @@ * $injector.invoke(['serviceA', function(serviceA){}]); * </pre> * * ## Inference * - * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be - * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation - * tools since these tools change the argument names. + * In JavaScript calling `toString()` on a function returns the function definition. The definition + * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with + * minification, and obfuscation tools since these tools change the argument names. * * ## `$inject` Annotation * By adding a `$inject` property onto a function the injection parameters can be specified. * * ## Inline @@ -12566,12 +12791,12 @@ * Invoke the method and supply the method arguments from the `$injector`. * * @param {!function} fn The function to invoke. Function parameters are injected according to the * {@link guide/di $inject Annotation} rules. * @param {Object=} self The `this` for the invoked method. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. * @returns {*} the value returned by the invoked `fn` function. */ /** * @ngdoc method @@ -12588,50 +12813,53 @@ /** * @ngdoc method * @name AUTO.$injector#instantiate * @methodOf AUTO.$injector * @description - * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies - * all of the arguments to the constructor function as specified by the constructor annotation. + * Create a new instance of JS type. The method takes a constructor function invokes the new + * operator and supplies all of the arguments to the constructor function as specified by the + * constructor annotation. * * @param {function} Type Annotated constructor function. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. * @returns {Object} new instance of `Type`. */ /** * @ngdoc method * @name AUTO.$injector#annotate * @methodOf AUTO.$injector * * @description - * Returns an array of service names which the function is requesting for injection. This API is used by the injector - * to determine which services need to be injected into the function when the function is invoked. There are three - * ways in which the function can be annotated with the needed dependencies. + * Returns an array of service names which the function is requesting for injection. This API is + * used by the injector to determine which services need to be injected into the function when the + * function is invoked. There are three ways in which the function can be annotated with the needed + * dependencies. * * # Argument names * - * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting - * the function into a string using `toString()` method and extracting the argument names. + * The simplest form is to extract the dependencies from the arguments of the function. This is done + * by converting the function into a string using `toString()` method and extracting the argument + * names. * <pre> * // Given * function MyController($scope, $route) { * // ... * } * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * </pre> * - * This method does not work with code minification / obfuscation. For this reason the following annotation strategies - * are supported. + * This method does not work with code minification / obfuscation. For this reason the following + * annotation strategies are supported. * * # The `$inject` property * - * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of - * services to be injected into the function. + * If a function has an `$inject` property and its value is an array of strings, then the strings + * represent names of services to be injected into the function. * <pre> * // Given * var MyController = function(obfuscatedScope, obfuscatedRoute) { * // ... * } @@ -12642,13 +12870,13 @@ * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * </pre> * * # The array notation * - * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very - * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives - * minification is a better choice: + * It is often desirable to inline Injected functions and that's when setting the `$inject` property + * is very inconvenient. In these situations using the array notation to specify the dependencies in + * a way that survives minification is a better choice: * * <pre> * // We wish to write this (not minification / obfuscation safe) * injector.invoke(function($compile, $rootScope) { * // ... @@ -12670,12 +12898,12 @@ * expect(injector.annotate( * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) * ).toEqual(['$compile', '$rootScope']); * </pre> * - * @param {function|Array.<string|Function>} fn Function for which dependent service names need to be retrieved as described - * above. + * @param {function|Array.<string|Function>} fn Function for which dependent service names need to + * be retrieved as described above. * * @returns {Array.<string>} The names of the services which the function requires. */ @@ -12685,76 +12913,82 @@ * @ngdoc object * @name AUTO.$provide * * @description * - * The {@link AUTO.$provide $provide} service has a number of methods for registering components with - * the {@link AUTO.$injector $injector}. Many of these functions are also exposed on {@link angular.Module}. + * The {@link AUTO.$provide $provide} service has a number of methods for registering components + * with the {@link AUTO.$injector $injector}. Many of these functions are also exposed on + * {@link angular.Module}. * * An Angular **service** is a singleton object created by a **service factory**. These **service * factories** are functions which, in turn, are created by a **service provider**. - * The **service providers** are constructor functions. When instantiated they must contain a property - * called `$get`, which holds the **service factory** function. - * + * The **service providers** are constructor functions. When instantiated they must contain a + * property called `$get`, which holds the **service factory** function. + * * When you request a service, the {@link AUTO.$injector $injector} is responsible for finding the * correct **service provider**, instantiating it and then calling its `$get` **service factory** * function to get the instance of the **service**. - * + * * Often services have no configuration options and there is no need to add methods to the service * provider. The provider will be no more than a constructor function with a `$get` property. For * these cases the {@link AUTO.$provide $provide} service has additional helper methods to register * services without specifying a provider. * - * * {@link AUTO.$provide#provider provider(provider)} - registers a **service provider** with the + * * {@link AUTO.$provide#methods_provider provider(provider)} - registers a **service provider** with the * {@link AUTO.$injector $injector} - * * {@link AUTO.$provide#constant constant(obj)} - registers a value/object that can be accessed by + * * {@link AUTO.$provide#methods_constant constant(obj)} - registers a value/object that can be accessed by * providers and services. - * * {@link AUTO.$provide#value value(obj)} - registers a value/object that can only be accessed by + * * {@link AUTO.$provide#methods_value value(obj)} - registers a value/object that can only be accessed by * services, not providers. - * * {@link AUTO.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, that - * will be wrapped in a **service provider** object, whose `$get` property will contain the given - * factory function. - * * {@link AUTO.$provide#service service(class)} - registers a **constructor function**, `class` that - * will be wrapped in a **service provider** object, whose `$get` property will instantiate a new - * object using the given constructor function. + * * {@link AUTO.$provide#methods_factory factory(fn)} - registers a service **factory function**, `fn`, + * that will be wrapped in a **service provider** object, whose `$get` property will contain the + * given factory function. + * * {@link AUTO.$provide#methods_service service(class)} - registers a **constructor function**, `class` that + * that will be wrapped in a **service provider** object, whose `$get` property will instantiate + * a new object using the given constructor function. * * See the individual methods for more information and examples. */ /** * @ngdoc method * @name AUTO.$provide#provider * @methodOf AUTO.$provide * @description * - * Register a **provider function** with the {@link AUTO.$injector $injector}. Provider functions are - * constructor functions, whose instances are responsible for "providing" a factory for a service. - * + * Register a **provider function** with the {@link AUTO.$injector $injector}. Provider functions + * are constructor functions, whose instances are responsible for "providing" a factory for a + * service. + * * Service provider names start with the name of the service they provide followed by `Provider`. - * For example, the {@link ng.$log $log} service has a provider called {@link ng.$logProvider $logProvider}. + * For example, the {@link ng.$log $log} service has a provider called + * {@link ng.$logProvider $logProvider}. * - * Service provider objects can have additional methods which allow configuration of the provider and - * its service. Importantly, you can configure what kind of service is created by the `$get` method, - * or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a method - * {@link ng.$logProvider#debugEnabled debugEnabled} + * Service provider objects can have additional methods which allow configuration of the provider + * and its service. Importantly, you can configure what kind of service is created by the `$get` + * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a + * method {@link ng.$logProvider#debugEnabled debugEnabled} * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the * console or not. * - * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + + 'Provider'` key. * @param {(Object|function())} provider If the provider is: * * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using - * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. + * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be + * created. * - `Constructor`: a new instance of the provider will be created using - * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as + * `object`. * * @returns {Object} registered provider instance * @example * * The following example shows how to create a simple event tracking service and register it using - * {@link AUTO.$provide#provider $provide.provider()}. + * {@link AUTO.$provide#methods_provider $provide.provider()}. * * <pre> * // Define the eventTracker provider * function EventTrackerProvider() { * var trackingUrl = '/track'; @@ -12821,20 +13055,20 @@ * @description * * Register a **service factory**, which will be called to return the service instance. * This is short for registering a service where its provider consists of only a `$get` property, * which is the given service factory function. - * You should use {@link AUTO.$provide#factory $provide.factor(getFn)} if you do not need to configure - * your service in a provider. + * You should use {@link AUTO.$provide#factory $provide.factory(getFn)} if you do not need to + * configure your service in a provider. * * @param {string} name The name of the instance. - * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for - * `$provide.provider(name, {$get: $getFn})`. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand + * for `$provide.provider(name, {$get: $getFn})`. * @returns {Object} registered provider instance * * @example - * Here is an example of registering a service + * Here is an example of registering a service * <pre> * $provide.factory('ping', ['$http', function($http) { * return function ping() { * return $http.send('/ping'); * }; @@ -12853,30 +13087,31 @@ * @ngdoc method * @name AUTO.$provide#service * @methodOf AUTO.$provide * @description * - * Register a **service constructor**, which will be invoked with `new` to create the service instance. + * Register a **service constructor**, which will be invoked with `new` to create the service + * instance. * This is short for registering a service where its provider's `$get` property is the service * constructor function that will be used to instantiate the service instance. * - * You should use {@link AUTO.$provide#service $provide.service(class)} if you define your service + * You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service * as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}. * * @param {string} name The name of the instance. * @param {Function} constructor A class (constructor function) that will be instantiated. * @returns {Object} registered provider instance * * @example - * Here is an example of registering a service using {@link AUTO.$provide#service $provide.service(class)} - * that is defined as a CoffeeScript class. + * Here is an example of registering a service using + * {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class. * <pre> * class Ping * constructor: (@$http)-> * send: ()=> * @$http.get('/ping') - * + * * $provide.service('ping', ['$http', Ping]) * </pre> * You would then inject and use this service like this: * <pre> * someModule.controller 'Ctrl', ['ping', (ping)-> @@ -12890,29 +13125,31 @@ * @ngdoc method * @name AUTO.$provide#value * @methodOf AUTO.$provide * @description * - * Register a **value service** with the {@link AUTO.$injector $injector}, such as a string, a number, - * an array, an object or a function. This is short for registering a service where its provider's - * `$get` property is a factory function that takes no arguments and returns the **value service**. + * Register a **value service** with the {@link AUTO.$injector $injector}, such as a string, a + * number, an array, an object or a function. This is short for registering a service where its + * provider's `$get` property is a factory function that takes no arguments and returns the **value + * service**. * - * Value services are similar to constant services, except that they cannot be injected into a module - * configuration function (see {@link angular.Module#config}) but they can be overridden by an Angular + * Value services are similar to constant services, except that they cannot be injected into a + * module configuration function (see {@link angular.Module#config}) but they can be overridden by + * an Angular * {@link AUTO.$provide#decorator decorator}. - * + * * @param {string} name The name of the instance. * @param {*} value The value. * @returns {Object} registered provider instance * * @example * Here are some examples of creating value services. * <pre> * $provide.constant('ADMIN_USER', 'admin'); - * + * * $provide.constant('RoleLookup', { admin: 0, writer: 1, reader: 2 }); - * + * * $provide.constant('halfOf', function(value) { * return value / 2; * }); * </pre> */ @@ -12922,26 +13159,26 @@ * @ngdoc method * @name AUTO.$provide#constant * @methodOf AUTO.$provide * @description * - * Register a **constant service**, such as a string, a number, an array, an object or a function, with - * the {@link AUTO.$injector $injector}. Unlike {@link AUTO.$provide#value value} it can be injected - * into a module configuration function (see {@link angular.Module#config}) and it cannot be - * overridden by an Angular {@link AUTO.$provide#decorator decorator}. + * Register a **constant service**, such as a string, a number, an array, an object or a function, + * with the {@link AUTO.$injector $injector}. Unlike {@link AUTO.$provide#value value} it can be + * injected into a module configuration function (see {@link angular.Module#config}) and it cannot + * be overridden by an Angular {@link AUTO.$provide#decorator decorator}. * * @param {string} name The name of the constant. * @param {*} value The constant value. * @returns {Object} registered instance * * @example * Here a some examples of creating constants: * <pre> * $provide.constant('SHARD_HEIGHT', 306); - * + * * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); - * + * * $provide.constant('double', function(value) { * return value * 2; * }); * </pre> */ @@ -12953,12 +13190,12 @@ * @methodOf AUTO.$provide * @description * * Register a **service decorator** with the {@link AUTO.$injector $injector}. A service decorator * intercepts the creation of a service, allowing it to override or modify the behaviour of the - * service. The object returned by the decorator may be the original service, or a new service object - * which replaces or wraps and delegates to the original service. + * service. The object returned by the decorator may be the original service, or a new service + * object which replaces or wraps and delegates to the original service. * * @param {string} name The name of the service to decorate. * @param {function()} decorator This function will be invoked when the service needs to be * instantiated and should return the decorated service instance. The function is called using * the {@link AUTO.$injector#invoke injector.invoke} method and is therefore fully injectable. @@ -13019,11 +13256,11 @@ if (isObject(key)) { forEach(key, reverseParams(delegate)); } else { return delegate(key, value); } - } + }; } function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { @@ -13041,11 +13278,11 @@ return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } - function value(name, value) { return factory(name, valueFn(value)); } + function value(name, val) { return factory(name, valueFn(val)); } function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; @@ -13063,21 +13300,21 @@ //////////////////////////////////// // Module Loading //////////////////////////////////// function loadModules(modulesToLoad){ - var runBlocks = []; + var runBlocks = [], moduleFn, invokeQueue, i, ii; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; loadedModules.put(module, true); try { if (isString(module)) { - var moduleFn = angularModule(module); + moduleFn = angularModule(module); runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); - for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { var invokeArgs = invokeQueue[i], provider = providerInjector.get(invokeArgs[0]); provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } @@ -13091,16 +13328,19 @@ } catch (e) { if (isArray(module)) { module = module[module.length - 1]; } if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { - // Safari & FF's stack traces don't contain error.message content unlike those of Chrome and IE + // Safari & FF's stack traces don't contain error.message content + // unlike those of Chrome and IE // So if stack doesn't contain message, we create a new string that contains both. // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. + /* jshint -W022 */ e = e.message + '\n' + e.stack; } - throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e); + throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", + module, e.stack || e.message || e); } }); return runBlocks; } @@ -13134,11 +13374,12 @@ key; for(i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; if (typeof key !== 'string') { - throw $injectorMinErr('itkn', 'Incorrect injection token! Expected service name as string, got {0}', key); + throw $injectorMinErr('itkn', + 'Incorrect injection token! Expected service name as string, got {0}', key); } args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key) @@ -13159,12 +13400,14 @@ case 4: return fn(args[0], args[1], args[2], args[3]); case 5: return fn(args[0], args[1], args[2], args[3], args[4]); case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], + args[8]); + case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], + args[8], args[9]); default: return fn.apply(self, args); } } function instantiate(Type, locals) { @@ -13175,11 +13418,11 @@ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; instance = new Constructor(); returnedValue = invoke(Type, instance, locals); - return isObject(returnedValue) ? returnedValue : instance; + return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; } return { invoke: invoke, instantiate: instantiate, @@ -13190,11 +13433,10 @@ } }; } } - /** * @ngdoc function * @name ng.$anchorScroll * @requires $window * @requires $location @@ -13298,34 +13540,38 @@ /** * @ngdoc object * @name ng.$animateProvider * * @description - * Default implementation of $animate that doesn't perform any animations, instead just synchronously performs DOM + * Default implementation of $animate that doesn't perform any animations, instead just + * synchronously performs DOM * updates and calls done() callbacks. * * In order to enable animations the ngAnimate module has to be loaded. * * To see the functional implementation check out src/ngAnimate/animate.js */ var $AnimateProvider = ['$provide', function($provide) { + this.$$selectors = {}; /** * @ngdoc function * @name ng.$animateProvider#register * @methodOf ng.$animateProvider * * @description - * Registers a new injectable animation factory function. The factory function produces the animation object which - * contains callback functions for each event that is expected to be animated. + * Registers a new injectable animation factory function. The factory function produces the + * animation object which contains callback functions for each event that is expected to be + * animated. * - * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` must be called once the - * element animation is complete. If a function is returned then the animation service will use this function to - * cancel the animation whenever a cancel event is triggered. + * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` + * must be called once the element animation is complete. If a function is returned then the + * animation service will use this function to cancel the animation whenever a cancel event is + * triggered. * * *<pre> * return { * eventFn : function(element, done) { @@ -13337,11 +13583,12 @@ * } * } *</pre> * * @param {string} name The name of the animation. - * @param {function} factory The factory function that will be executed to return the animation object. + * @param {function} factory The factory function that will be executed to return the animation + * object. */ this.register = function(name, factory) { var key = name + '-animation'; if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); @@ -13350,40 +13597,43 @@ }; this.$get = ['$timeout', function($timeout) { /** + * * @ngdoc object * @name ng.$animate + * @description The $animate service provides rudimentary DOM manipulation functions to + * insert, remove and move elements within the DOM, as well as adding and removing classes. + * This service is the core service used by the ngAnimate $animator service which provides + * high-level animation hooks for CSS and JavaScript. * - * @description - * The $animate service provides rudimentary DOM manipulation functions to insert, remove and move elements within - * the DOM, as well as adding and removing classes. This service is the core service used by the ngAnimate $animator - * service which provides high-level animation hooks for CSS and JavaScript. + * $animate is available in the AngularJS core, however, the ngAnimate module must be included + * to enable full out animation support. Otherwise, $animate will only perform simple DOM + * manipulation operations. * - * $animate is available in the AngularJS core, however, the ngAnimate module must be included to enable full out - * animation support. Otherwise, $animate will only perform simple DOM manipulation operations. - * - * To learn more about enabling animation support, click here to visit the {@link ngAnimate ngAnimate module page} - * as well as the {@link ngAnimate.$animate ngAnimate $animate service page}. + * To learn more about enabling animation support, click here to visit the {@link ngAnimate + * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service + * page}. */ return { /** + * * @ngdoc function * @name ng.$animate#enter * @methodOf ng.$animate * @function - * - * @description - * Inserts the element into the DOM either after the `after` element or within the `parent` element. Once complete, - * the done() callback will be fired (if provided). - * + * @description Inserts the element into the DOM either after the `after` element or within + * the `parent` element. Once complete, the done() callback will be fired (if provided). * @param {jQuery/jqLite element} element the element which will be inserted into the DOM - * @param {jQuery/jqLite element} parent the parent element which will append the element as a child (if the after element is not present) - * @param {jQuery/jqLite element} after the sibling element which will append the element after itself - * @param {function=} done callback function that will be called after the element has been inserted into the DOM + * @param {jQuery/jqLite element} parent the parent element which will append the element as + * a child (if the after element is not present) + * @param {jQuery/jqLite element} after the sibling element which will append the element + * after itself + * @param {function=} done callback function that will be called after the element has been + * inserted into the DOM */ enter : function(element, parent, after, done) { var afterNode = after && after[after.length - 1]; var parentNode = parent && parent[0] || afterNode && afterNode.parentNode; // IE does not like undefined so we have to pass null. @@ -13393,89 +13643,95 @@ }); done && $timeout(done, 0, false); }, /** + * * @ngdoc function * @name ng.$animate#leave * @methodOf ng.$animate * @function - * - * @description - * Removes the element from the DOM. Once complete, the done() callback will be fired (if provided). - * + * @description Removes the element from the DOM. Once complete, the done() callback will be + * fired (if provided). * @param {jQuery/jqLite element} element the element which will be removed from the DOM - * @param {function=} done callback function that will be called after the element has been removed from the DOM + * @param {function=} done callback function that will be called after the element has been + * removed from the DOM */ leave : function(element, done) { element.remove(); done && $timeout(done, 0, false); }, /** + * * @ngdoc function * @name ng.$animate#move * @methodOf ng.$animate * @function - * - * @description - * Moves the position of the provided element within the DOM to be placed either after the `after` element or inside of the `parent` element. - * Once complete, the done() callback will be fired (if provided). - * - * @param {jQuery/jqLite element} element the element which will be moved around within the DOM - * @param {jQuery/jqLite element} parent the parent element where the element will be inserted into (if the after element is not present) - * @param {jQuery/jqLite element} after the sibling element where the element will be positioned next to - * @param {function=} done the callback function (if provided) that will be fired after the element has been moved to its new position + * @description Moves the position of the provided element within the DOM to be placed + * either after the `after` element or inside of the `parent` element. Once complete, the + * done() callback will be fired (if provided). + * + * @param {jQuery/jqLite element} element the element which will be moved around within the + * DOM + * @param {jQuery/jqLite element} parent the parent element where the element will be + * inserted into (if the after element is not present) + * @param {jQuery/jqLite element} after the sibling element where the element will be + * positioned next to + * @param {function=} done the callback function (if provided) that will be fired after the + * element has been moved to its new position */ move : function(element, parent, after, done) { // Do not remove element before insert. Removing will cause data associated with the // element to be dropped. Insert will implicitly do the remove. this.enter(element, parent, after, done); }, /** + * * @ngdoc function * @name ng.$animate#addClass * @methodOf ng.$animate * @function - * - * @description - * Adds the provided className CSS class value to the provided element. Once complete, the done() callback will be fired (if provided). - * - * @param {jQuery/jqLite element} element the element which will have the className value added to it + * @description Adds the provided className CSS class value to the provided element. Once + * complete, the done() callback will be fired (if provided). + * @param {jQuery/jqLite element} element the element which will have the className value + * added to it * @param {string} className the CSS class which will be added to the element - * @param {function=} done the callback function (if provided) that will be fired after the className value has been added to the element + * @param {function=} done the callback function (if provided) that will be fired after the + * className value has been added to the element */ addClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { - JQLiteAddClass(element, className); + jqLiteAddClass(element, className); }); done && $timeout(done, 0, false); }, /** + * * @ngdoc function * @name ng.$animate#removeClass * @methodOf ng.$animate * @function - * - * @description - * Removes the provided className CSS class value from the provided element. Once complete, the done() callback will be fired (if provided). - * - * @param {jQuery/jqLite element} element the element which will have the className value removed from it + * @description Removes the provided className CSS class value from the provided element. + * Once complete, the done() callback will be fired (if provided). + * @param {jQuery/jqLite element} element the element which will have the className value + * removed from it * @param {string} className the CSS class which will be removed from the element - * @param {function=} done the callback function (if provided) that will be fired after the className value has been removed from the element + * @param {function=} done the callback function (if provided) that will be fired after the + * className value has been removed from the element */ removeClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { - JQLiteRemoveClass(element, className); + jqLiteRemoveClass(element, className); }); done && $timeout(done, 0, false); }, enabled : noop @@ -13755,34 +14011,39 @@ * @description * The cookies method provides a 'private' low level access to browser cookies. * It is not meant to be used directly, use the $cookie service instead. * * The return values vary depending on the arguments that the method was called with as follows: - * <ul> - * <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li> - * <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li> - * <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li> - * </ul> - * + * + * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify + * it + * - cookies(name, value) -> set name to value, if value is undefined delete the cookie + * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that + * way) + * * @returns {Object} Hash of all cookies (if called without any parameter) */ self.cookies = function(name, value) { + /* global escape: false, unescape: false */ var cookieLength, cookieArray, cookie, i, index; if (name) { if (value === undefined) { - rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; + rawDocument.cookie = escape(name) + "=;path=" + cookiePath + + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; } else { if (isString(value)) { - cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; + cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + + ';path=' + cookiePath).length + 1; // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: // - 300 cookies // - 20 cookies per unique domain // - 4096 bytes per cookie if (cookieLength > 4096) { - $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ + $log.warn("Cookie '"+ name + + "' possibly not set or overflowed because it was too large ("+ cookieLength + " > 4096 bytes)!"); } } } } else { @@ -13793,11 +14054,11 @@ for (i = 0; i < cookieArray.length; i++) { cookie = cookieArray[i]; index = cookie.indexOf('='); if (index > 0) { //ignore nameless cookies - var name = unescape(cookie.substring(0, index)); + name = unescape(cookie.substring(0, index)); // the first value that is seen for a cookie is the most // specific one. values for the same cookie name that // follow are for less specific paths. if (lastCookies[name] === undefined) { lastCookies[name] = unescape(cookie.substring(index + 1)); @@ -13843,11 +14104,12 @@ * * @description * Cancels a deferred task identified with `deferId`. * * @param {*} deferId Token returned by the `$browser.defer` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. */ self.defer.cancel = function(deferId) { if (pendingDeferIds[deferId]) { delete pendingDeferIds[deferId]; clearTimeout(deferId); @@ -13879,13 +14141,14 @@ * expect($cacheFactory.get('cacheId')).toBe(cache); * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); * * cache.put("key", "value"); * cache.put("another key", "another value"); + * + * // We've specified no options on creation + * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); * - * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation - * * </pre> * * * @param {string} cacheId Name or id of the newly created cache. * @param {object=} options Options object that specifies the cache behavior. Properties: @@ -13893,11 +14156,12 @@ * - `{number=}` `capacity` — turns the cache into LRU cache. * * @returns {object} Newly created cache object with the following set of methods: * * - `{object}` `info()` — Returns id, size, and options of cache. - * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns + * it. * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. * - `{void}` `removeAll()` — Removes all cached values. * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * @@ -14060,13 +14324,13 @@ /** * @ngdoc object * @name ng.$templateCache * * @description - * The first time a template is used, it is loaded in the template cache for quick retrieval. You can - * load templates directly into the cache in a `script` tag, or by consuming the `$templateCache` - * service directly. + * The first time a template is used, it is loaded in the template cache for quick retrieval. You + * can load templates directly into the cache in a `script` tag, or by consuming the + * `$templateCache` service directly. * * Adding via the `script` tag: * <pre> * <html ng-app> * <head> @@ -14076,12 +14340,12 @@ * </head> * ... * </html> * </pre> * - * **Note:** the `script` tag containing the template does not need to be included in the `head` of the document, but - * it must be below the `ng-app` definition. + * **Note:** the `script` tag containing the template does not need to be included in the `head` of + * the document, but it must be below the `ng-app` definition. * * Adding via the $templateCache service: * * <pre> * var myApp = angular.module('myApp', []); @@ -14132,25 +14396,359 @@ * @name ng.$compile * @function * * @description * Compiles a piece of HTML string or DOM into a template and produces a template function, which - * can then be used to link {@link ng.$rootScope.Scope scope} and the template together. + * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. * - * The compilation is a process of walking the DOM tree and trying to match DOM elements to - * {@link ng.$compileProvider#directive directives}. For each match it - * executes corresponding template function and collects the - * instance functions into a single template function which is then returned. + * The compilation is a process of walking the DOM tree and matching DOM elements to + * {@link ng.$compileProvider#methods_directive directives}. * - * The template function can then be used once to produce the view or as it is the case with - * {@link ng.directive:ngRepeat repeater} many-times, in which - * case each call results in a view that is a DOM clone of the original template. + * <div class="alert alert-warning"> + * **Note:** This document is an in-depth reference of all directive options. + * For a gentle introduction to directives with examples of common use cases, + * see the {@link guide/directive directive guide}. + * </div> * + * ## Comprehensive Directive API + * + * There are many different options for a directive. + * + * The difference resides in the return value of the factory function. + * You can either return a "Directive Definition Object" (see below) that defines the directive properties, + * or just the `postLink` function (all other properties will have the default values). + * + * <div class="alert alert-success"> + * **Best Practice:** It's recommended to use the "directive definition object" form. + * </div> + * + * Here's an example directive declared with a Directive Definition Object: + * + * <pre> + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * priority: 0, + * template: '<div></div>', // or // function(tElement, tAttrs) { ... }, + * // or + * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, + * replace: false, + * transclude: false, + * restrict: 'A', + * scope: false, + * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, + * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], + * compile: function compile(tElement, tAttrs, transclude) { + * return { + * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * post: function postLink(scope, iElement, iAttrs, controller) { ... } + * } + * // or + * // return function postLink( ... ) { ... } + * }, + * // or + * // link: { + * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * // post: function postLink(scope, iElement, iAttrs, controller) { ... } + * // } + * // or + * // link: function postLink( ... ) { ... } + * }; + * return directiveDefinitionObject; + * }); + * </pre> + * + * <div class="alert alert-warning"> + * **Note:** Any unspecified options will use the default value. You can see the default values below. + * </div> + * + * Therefore the above can be simplified as: + * + * <pre> + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * link: function postLink(scope, iElement, iAttrs) { ... } + * }; + * return directiveDefinitionObject; + * // or + * // return function postLink(scope, iElement, iAttrs) { ... } + * }); + * </pre> + * + * + * + * ### Directive Definition Object + * + * The directive definition object provides instructions to the {@link api/ng.$compile + * compiler}. The attributes are: + * + * #### `priority` + * When there are multiple directives defined on a single DOM element, sometimes it + * is necessary to specify the order in which the directives are applied. The `priority` is used + * to sort the directives before their `compile` functions get called. Priority is defined as a + * number. Directives with greater numerical `priority` are compiled first. The order of directives with + * the same priority is undefined. The default priority is `0`. + * + * #### `terminal` + * If set to true then the current `priority` will be the last set of directives + * which will execute (any directives at the current priority will still execute + * as the order of execution on same `priority` is undefined). + * + * #### `scope` + * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the + * same element request a new scope, only one new scope is created. The new scope rule does not + * apply for the root of the template since the root of the template always gets a new scope. + * + * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from + * normal scope in that it does not prototypically inherit from the parent scope. This is useful + * when creating reusable components, which should not accidentally read or modify data in the + * parent scope. + * + * The 'isolate' scope takes an object hash which defines a set of local scope properties + * derived from the parent scope. These local properties are useful for aliasing values for + * templates. Locals definition is a hash of local scope property to its source: + * + * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is + * always a string since DOM attributes are strings. If no `attr` name is specified then the + * attribute name is assumed to be the same as the local name. + * Given `<widget my-attr="hello {{name}}">` and widget definition + * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect + * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the + * `localName` property on the widget scope. The `name` is read from the parent scope (not + * component scope). + * + * * `=` or `=attr` - set up bi-directional binding between a local scope property and the + * parent scope property of name defined via the value of the `attr` attribute. If no `attr` + * name is specified then the attribute name is assumed to be the same as the local name. + * Given `<widget my-attr="parentModel">` and widget definition of + * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the + * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected + * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent + * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You + * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. + * + * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. + * If no `attr` name is specified then the attribute name is assumed to be the same as the + * local name. Given `<widget my-attr="count = count + value">` and widget definition of + * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to + * a function wrapper for the `count = count + value` expression. Often it's desirable to + * pass data from the isolated scope via an expression and to the parent scope, this can be + * done by passing a map of local variable names and values into the expression wrapper fn. + * For example, if the expression is `increment(amount)` then we can specify the amount value + * by calling the `localFn` as `localFn({amount: 22})`. + * + * + * + * #### `controller` + * Controller constructor function. The controller is instantiated before the + * pre-linking phase and it is shared with other directives (see + * `require` attribute). This allows the directives to communicate with each other and augment + * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: + * + * * `$scope` - Current scope associated with the element + * * `$element` - Current element + * * `$attrs` - Current attributes object for the element + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: + * `function(cloneLinkingFn)`. + * + * + * #### `require` + * Require another directive and inject its controller as the fourth argument to the linking function. The + * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the + * injected argument will be an array in corresponding order. If no such directive can be + * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with: + * + * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. + * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. + * * `^` - Locate the required controller by searching the element's parents. Throw an error if not found. + * * `?^` - Attempt to locate the required controller by searching the element's parentsor pass `null` to the + * `link` fn if not found. + * + * + * #### `controllerAs` + * Controller alias at the directive scope. An alias for the controller so it + * can be referenced at the directive template. The directive needs to define a scope for this + * configuration to be used. Useful in the case when directive is used as component. + * + * + * #### `restrict` + * String of subset of `EACM` which restricts the directive to a specific directive + * declaration style. If omitted, the default (attributes only) is used. + * + * * `E` - Element name: `<my-directive></my-directive>` + * * `A` - Attribute (default): `<div my-directive="exp"></div>` + * * `C` - Class: `<div class="my-directive: exp;"></div>` + * * `M` - Comment: `<!-- directive: my-directive exp -->` + * + * + * #### `template` + * replace the current element with the contents of the HTML. The replacement process + * migrates all of the attributes / classes from the old element to the new one. See the + * {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive + * Directives Guide} for an example. + * + * You can specify `template` as a string representing the template or as a function which takes + * two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and + * returns a string value representing the template. + * + * + * #### `templateUrl` + * Same as `template` but the template is loaded from the specified URL. Because + * the template loading is asynchronous the compilation/linking is suspended until the template + * is loaded. + * + * You can specify `templateUrl` as a string representing the URL or as a function which takes two + * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns + * a string value representing the url. In either case, the template URL is passed through {@link + * api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl}. + * + * + * #### `replace` + * specify where the template should be inserted. Defaults to `false`. + * + * * `true` - the template will replace the current element. + * * `false` - the template will replace the contents of the current element. + * + * + * #### `transclude` + * compile the content of the element and make it available to the directive. + * Typically used with {@link api/ng.directive:ngTransclude + * ngTransclude}. The advantage of transclusion is that the linking function receives a + * transclusion function which is pre-bound to the correct scope. In a typical setup the widget + * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate` + * scope. This makes it possible for the widget to have private state, and the transclusion to + * be bound to the parent (pre-`isolate`) scope. + * + * * `true` - transclude the content of the directive. + * * `'element'` - transclude the whole element including any directives defined at lower priority. + * + * + * #### `compile` + * + * <pre> + * function compile(tElement, tAttrs, transclude) { ... } + * </pre> + * + * The compile function deals with transforming the template DOM. Since most directives do not do + * template transformation, it is not used often. Examples that require compile functions are + * directives that transform template DOM, such as {@link + * api/ng.directive:ngRepeat ngRepeat}, or load the contents + * asynchronously, such as {@link api/ngRoute.directive:ngView ngView}. The + * compile function takes the following arguments. + * + * * `tElement` - template element - The element where the directive has been declared. It is + * safe to do template transformation on the element and child elements only. + * + * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared + * between all directive compile functions. + * + * * `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`. + * + * <div class="alert alert-warning"> + * **Note:** The template instance and the link instance may be different objects if the template has + * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that + * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration + * should be done in a linking function rather than in a compile function. + * </div> + * + * A compile function can have a return value which can be either a function or an object. + * + * * returning a (post-link) function - is equivalent to registering the linking function via the + * `link` property of the config object when the compile function is empty. + * + * * returning an object with function(s) registered via `pre` and `post` properties - allows you to + * control when a linking function should be called during the linking phase. See info about + * pre-linking and post-linking functions below. + * + * + * #### `link` + * This property is used only if the `compile` property is not defined. + * + * <pre> + * function link(scope, iElement, iAttrs, controller) { ... } + * </pre> + * + * The link function is responsible for registering DOM listeners as well as updating the DOM. It is + * executed after the template has been cloned. This is where most of the directive logic will be + * put. + * + * * `scope` - {@link api/ng.$rootScope.Scope Scope} - The scope to be used by the + * directive for registering {@link api/ng.$rootScope.Scope#methods_$watch watches}. + * + * * `iElement` - instance element - The element where the directive is to be used. It is safe to + * manipulate the children of the element only in `postLink` function since the children have + * already been linked. + * + * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared + * between all directive linking functions. + * + * * `controller` - a controller instance - A controller instance if at least one directive on the + * element defines a controller. The controller is shared among all the directives, which allows + * the directives to use the controllers as a communication channel. + * + * + * + * #### Pre-linking function + * + * Executed before the child elements are linked. Not safe to do DOM transformation since the + * compiler linking function will fail to locate the correct elements for linking. + * + * #### Post-linking function + * + * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function. + * + * <a name="Attributes"></a> + * ### Attributes + * + * The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the + * `link()` or `compile()` functions. It has a variety of uses. + * + * accessing *Normalized attribute names:* + * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. + * the attributes object allows for normalized access to + * the attributes. + * + * * *Directive inter-communication:* All directives share the same instance of the attributes + * object which allows the directives to use the attributes object as inter directive + * communication. + * + * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object + * allowing other directives to read the interpolated value. + * + * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes + * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also + * the only way to easily get the actual value because during the linking phase the interpolation + * hasn't been evaluated yet and so the value is at this time set to `undefined`. + * + * <pre> + * function linkingFn(scope, elm, attrs, ctrl) { + * // get the attribute value + * console.log(attrs.ngModel); + * + * // change the attribute + * attrs.$set('ngModel', 'new value'); + * + * // observe changes to interpolated attribute + * attrs.$observe('ngModel', function(value) { + * console.log('ngModel has changed value to ' + value); + * }); + * } + * </pre> + * + * Below is an example using `$compileProvider`. + * + * <div class="alert alert-warning"> + * **Note**: Typically directives are registered with `module.directive`. The example below is + * to illustrate how `$compile` works. + * </div> + * <doc:example module="compile"> <doc:source> <script> - // declare a new module, and inject the $compileProvider angular.module('compile', [], function($compileProvider) { // configure new 'compile' directive by passing a directive // factory function. The factory function injects the '$compile' $compileProvider.directive('compile', function($compile) { // directive factory creates a link function @@ -14204,20 +14802,20 @@ * root element(s), not their children) * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template * (a DOM element/tree) to a scope. Where: * * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. - * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the `template` - * and call the `cloneAttachFn` function allowing the caller to attach the + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is * called as: <br> `cloneAttachFn(clonedElement, scope)` where: * * * `clonedElement` - is a clone of the original `element` passed into the compiler. * * `scope` - is the current scope with which the linking function is working with. * - * Calling the linking function returns the element of the template. It is either the original element - * passed in, or the clone of the element if the `cloneAttachFn` is provided. + * Calling the linking function returns the element of the template. It is either the original + * element passed in, or the clone of the element if the `cloneAttachFn` is provided. * * After linking the view is not updated until after a call to $digest which typically is done by * Angular automatically. * * If you need access to the bound view, there are two ways to do it: @@ -14364,14 +14962,14 @@ * Retrieves or overrides the default regular expression that is used for whitelisting of safe * urls during img[src] sanitization. * * The sanitization is a security measure aimed at prevent XSS attacks via html links. * - * Any url about to be assigned to img[src] via data-binding is first normalized and turned into an - * absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` regular - * expression. If a match is found, the original url is written into the dom. Otherwise, the - * absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. * * @param {RegExp=} regexp New regexp to whitelist urls with. * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for * chaining otherwise. */ @@ -14422,12 +15020,12 @@ * @name ng.$compile.directive.Attributes#$removeClass * @methodOf ng.$compile.directive.Attributes * @function * * @description - * Removes the CSS class value specified by the classVal parameter from the element. If animations - * are enabled then an animation will be triggered for the class removal. + * Removes the CSS class value specified by the classVal parameter from the element. If + * animations are enabled then an animation will be triggered for the class removal. * * @param {string} classVal The className value that will be removed from the element */ $removeClass : function(classVal) { if(classVal && classVal.length > 0) { @@ -14523,11 +15121,11 @@ if(token == tokens2[j]) continue outer; } values.push(token); } return values; - }; + } }, /** * @ngdoc function @@ -14576,23 +15174,27 @@ return compile; //================================ - function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { + function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, + previousCompileContext) { if (!($compileNodes instanceof jqLite)) { - // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it. + // jquery always rewraps, whereas we need to preserve the original selector so that we can + // modify it. $compileNodes = jqLite($compileNodes); } // We can not compile top level text elements since text nodes can be merged and we will // not be able to attach scope data to them, so we will wrap them in <span> forEach($compileNodes, function(node, index){ if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { $compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0]; } }); - var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority, ignoreDirective, previousCompileContext); + var compositeLinkFn = + compileNodes($compileNodes, transcludeFn, $compileNodes, + maxPriority, ignoreDirective, previousCompileContext); return function publicLinkFn(scope, cloneConnectFn){ assertArg(scope, 'scope'); // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. var $linkNode = cloneConnectFn @@ -14629,46 +15231,52 @@ * function, which is the a linking function for the node. * * @param {NodeList} nodeList an array of nodes or NodeList to compile * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the - * rootElement must be set the jqLite collection of the compile root. This is + * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then + * the rootElement must be set the jqLite collection of the compile root. This is * needed so that the jqLite collection items can be replaced with widgets. * @param {number=} max directive priority * @returns {?function} A composite linking function of all of the matched directives or null. */ - function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, previousCompileContext) { + function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, + previousCompileContext) { var linkFns = [], nodeLinkFn, childLinkFn, directives, attrs, linkFnFound; for(var i = 0; i < nodeList.length; i++) { attrs = new Attributes(); // we must always refer to nodeList[i] since the nodes can be replaced underneath us. - directives = collectDirectives(nodeList[i], [], attrs, i == 0 ? maxPriority : undefined, ignoreDirective); + directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, + ignoreDirective); nodeLinkFn = (directives.length) - ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, null, [], [], previousCompileContext) + ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, + null, [], [], previousCompileContext) : null; - childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length) + childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || + !nodeList[i].childNodes || + !nodeList[i].childNodes.length) ? null : compileNodes(nodeList[i].childNodes, nodeLinkFn ? nodeLinkFn.transclude : transcludeFn); linkFns.push(nodeLinkFn); linkFns.push(childLinkFn); linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn); - previousCompileContext = null; //use the previous context only for the first element in the virtual group + //use the previous context only for the first element in the virtual group + previousCompileContext = null; } // return a linking function if we have found anything, null otherwise return linkFnFound ? compositeLinkFn : null; function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) { - var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n; + var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n; // copy nodeList so that linking doesn't break due to live list updates. var stableNodeList = []; for (i = 0, ii = nodeList.length; i < ii; i++) { stableNodeList.push(nodeList[i]); @@ -14676,15 +15284,17 @@ for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) { node = stableNodeList[n]; nodeLinkFn = linkFns[i++]; childLinkFn = linkFns[i++]; + $node = jqLite(node); if (nodeLinkFn) { if (nodeLinkFn.scope) { - childScope = scope.$new(isObject(nodeLinkFn.scope)); - jqLite(node).data('$scope', childScope); + childScope = scope.$new(); + $node.data('$scope', childScope); + safeAddClass($node, 'ng-scope'); } else { childScope = scope; } childTranscludeFn = nodeLinkFn.transclude; if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) { @@ -14761,11 +15371,12 @@ : attr.value); if (getBooleanAttrName(node, nName)) { attrs[nName] = true; // presence means true } addAttrInterpolateDirective(node, directives, value, nName); - addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, attrEndName); + addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, + attrEndName); } } // use class as directive className = node.className; @@ -14790,22 +15401,24 @@ if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { attrs[nName] = trim(match[2]); } } } catch (e) { - // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. + // turns out that under some circumstances IE9 throws errors when one attempts to read + // comment's node value. // Just ignore it and continue. (Can't seem to reproduce in test case.) } break; } directives.sort(byPriority); return directives; } /** - * Given a node with an directive-start it collects all of the siblings until it find directive-end. + * Given a node with an directive-start it collects all of the siblings until it finds + * directive-end. * @param node * @param attrStart * @param attrEnd * @returns {*} */ @@ -14814,11 +15427,13 @@ var depth = 0; if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { var startNode = node; do { if (!node) { - throw $compileMinErr('uterdir', "Unterminated attribute, found '{0}' but no matching '{1}' found.", attrStart, attrEnd); + throw $compileMinErr('uterdir', + "Unterminated attribute, found '{0}' but no matching '{1}' found.", + attrStart, attrEnd); } if (node.nodeType == 1 /** Element **/) { if (node.hasAttribute(attrStart)) depth++; if (node.hasAttribute(attrEnd)) depth--; } @@ -14842,11 +15457,11 @@ */ function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { return function(scope, element, attrs, controllers) { element = groupScan(element[0], attrStart, attrEnd); return linkFn(scope, element, attrs, controllers); - } + }; } /** * Once the directives have been collected, their compile functions are executed. This method * is responsible for inlining directive templates as well as terminating the application @@ -14855,36 +15470,40 @@ * @param {Array} directives Array of collected directives to execute their compile function. * this needs to be pre-sorted by priority order. * @param {Node} compileNode The raw DOM node to apply the compile functions to * @param {Object} templateAttrs The shared attribute function * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. + * scope argument is auto-generated to the new + * child of the transcluded parent scope. * @param {JQLite} jqCollection If we are working on the root of the compile tree then this - * argument has the root jqLite array so that we can replace nodes on it. - * @param {Object=} originalReplaceDirective An optional directive that will be ignored when compiling - * the transclusion. + * argument has the root jqLite array so that we can replace nodes + * on it. + * @param {Object=} originalReplaceDirective An optional directive that will be ignored when + * compiling the transclusion. * @param {Array.<Function>} preLinkFns * @param {Array.<Function>} postLinkFns - * @param {Object} previousCompileContext Context used for previous compilation of the current node + * @param {Object} previousCompileContext Context used for previous compilation of the current + * node * @returns linkFn */ - function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection, - originalReplaceDirective, preLinkFns, postLinkFns, previousCompileContext) { + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, + jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, + previousCompileContext) { previousCompileContext = previousCompileContext || {}; var terminalPriority = -Number.MAX_VALUE, newScopeDirective, + controllerDirectives = previousCompileContext.controllerDirectives, newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, templateDirective = previousCompileContext.templateDirective, + transcludeDirective = previousCompileContext.transcludeDirective, $compileNode = templateAttrs.$$element = jqLite(compileNode), directive, directiveName, $template, - transcludeDirective = previousCompileContext.transcludeDirective, replaceDirective = originalReplaceDirective, childTranscludeFn = transcludeFn, - controllerDirectives, linkFn, directiveValue; // executes all directives on the current element for(var i = 0, ii = directives.length; i < ii; i++) { @@ -14892,30 +15511,29 @@ var attrStart = directive.$$start; var attrEnd = directive.$$end; // collect multiblock sections if (attrStart) { - $compileNode = groupScan(compileNode, attrStart, attrEnd) + $compileNode = groupScan(compileNode, attrStart, attrEnd); } $template = undefined; if (terminalPriority > directive.priority) { break; // prevent further processing of directives } if (directiveValue = directive.scope) { newScopeDirective = newScopeDirective || directive; - // skip the check for directives with async templates, we'll check the derived sync directive when - // the template arrives + // skip the check for directives with async templates, we'll check the derived sync + // directive when the template arrives if (!directive.templateUrl) { - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, $compileNode); + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, + $compileNode); if (isObject(directiveValue)) { - safeAddClass($compileNode, 'ng-isolate-scope'); newIsolateScopeDirective = directive; } - safeAddClass($compileNode, 'ng-scope'); } } directiveName = directive.name; @@ -14926,33 +15544,40 @@ controllerDirectives[directiveName], directive, $compileNode); controllerDirectives[directiveName] = directive; } if (directiveValue = directive.transclude) { - // Special case ngRepeat so that we don't complain about duplicate transclusion, ngRepeat knows how to handle - // this on its own. - if (directiveName !== 'ngRepeat') { + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. + // This option should only be used by directives that know how to how to safely handle element transclusion, + // where the transcluded nodes are added or replaced after linking. + if (!directive.$$tlb) { assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); transcludeDirective = directive; } if (directiveValue == 'element') { terminalPriority = directive.priority; $template = groupScan(compileNode, attrStart, attrEnd); $compileNode = templateAttrs.$$element = - jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); + jqLite(document.createComment(' ' + directiveName + ': ' + + templateAttrs[directiveName] + ' ')); compileNode = $compileNode[0]; replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode); childTranscludeFn = compile($template, transcludeFn, terminalPriority, replaceDirective && replaceDirective.name, { - newIsolateScopeDirective: newIsolateScopeDirective, - transcludeDirective: transcludeDirective, - templateDirective: templateDirective + // Don't pass in: + // - controllerDirectives - otherwise we'll create duplicates controllers + // - newIsolateScopeDirective or templateDirective - combining templates with + // element transclusion doesn't make sense. + // + // We need only transcludeDirective so that we prevent putting transclusion + // on the same element more than once. + transcludeDirective: transcludeDirective }); } else { - $template = jqLite(JQLiteClone(compileNode)).contents(); + $template = jqLite(jqLiteClone(compileNode)).contents(); $compileNode.html(''); // clear contents childTranscludeFn = compile($template, transcludeFn); } } @@ -14972,29 +15597,31 @@ trim(directiveValue) + '</div>').contents(); compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { - throw $compileMinErr('tplrt', "Template for directive '{0}' must have exactly one root element. {1}", directiveName, ''); + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + directiveName, ''); } replaceWith(jqCollection, $compileNode, compileNode); var newTemplateAttrs = {$attr: {}}; // combine directives from the original node and from the template: // - take the array of directives for this element - // - split it into two parts, those that were already applied and those that weren't - // - collect directives from the template, add them to the second group and sort them - // - append the second group with new directives to the first group - directives = directives.concat( - collectDirectives( - compileNode, - directives.splice(i + 1, directives.length - (i + 1)), - newTemplateAttrs - ) - ); + // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) + // - collect directives from the template and sort them by priority + // - combine directives as: processed + template + unprocessed + var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); + var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); + + if (newIsolateScopeDirective) { + markDirectivesAsIsolate(templateDirectives); + } + directives = directives.concat(templateDirectives).concat(unprocessedDirectives); mergeTemplateAttributes(templateAttrs, newTemplateAttrs); ii = directives.length; } else { $compileNode.html(directiveValue); @@ -15009,13 +15636,14 @@ replaceDirective = directive; } nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, templateAttrs, jqCollection, childTranscludeFn, preLinkFns, postLinkFns, { + controllerDirectives: controllerDirectives, newIsolateScopeDirective: newIsolateScopeDirective, - transcludeDirective: transcludeDirective, - templateDirective: templateDirective + templateDirective: templateDirective, + transcludeDirective: transcludeDirective }); ii = directives.length; } else if (directive.compile) { try { linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); @@ -15034,11 +15662,11 @@ terminalPriority = Math.max(terminalPriority, directive.priority); } } - nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; // might be normal or delayed nodeLinkFn depending on if templateUrl is present return nodeLinkFn; @@ -15046,15 +15674,21 @@ function addLinkFns(pre, post, attrStart, attrEnd) { if (pre) { if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); pre.require = directive.require; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + pre = cloneAndAnnotateFn(pre, {isolateScope: true}); + } preLinkFns.push(pre); } if (post) { if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); post.require = directive.require; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + post = cloneAndAnnotateFn(post, {isolateScope: true}); + } postLinkFns.push(post); } } @@ -15075,11 +15709,13 @@ value = value || $element[0].$$controller; $element[0].$$controller = null; } if (!value && !optional) { - throw $compileMinErr('ctreq', "Controller '{0}', required by directive '{1}', can't be found!", require, directiveName); + throw $compileMinErr('ctreq', + "Controller '{0}', required by directive '{1}', can't be found!", + require, directiveName); } return value; } else if (isArray(require)) { value = []; forEach(require, function(require) { @@ -15089,98 +15725,109 @@ return value; } function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { - var attrs, $element, i, ii, linkFn, controller; + var attrs, $element, i, ii, linkFn, controller, isolateScope; if (compileNode === linkNode) { attrs = templateAttrs; } else { attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); } $element = attrs.$$element; if (newIsolateScopeDirective) { var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; + var $linkNode = jqLite(linkNode); - var parentScope = scope.$parent || scope; + isolateScope = scope.$new(true); + if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) { + $linkNode.data('$isolateScope', isolateScope) ; + } else { + $linkNode.data('$isolateScopeNoTemplate', isolateScope); + } + + + + safeAddClass($linkNode, 'ng-isolate-scope'); + forEach(newIsolateScopeDirective.scope, function(definition, scopeName) { var match = definition.match(LOCAL_REGEXP) || [], attrName = match[3] || scopeName, optional = (match[2] == '?'), mode = match[1], // @, =, or & lastValue, parentGet, parentSet; - scope.$$isolateBindings[scopeName] = mode + attrName; + isolateScope.$$isolateBindings[scopeName] = mode + attrName; switch (mode) { - case '@': { + case '@': attrs.$observe(attrName, function(value) { - scope[scopeName] = value; + isolateScope[scopeName] = value; }); - attrs.$$observers[attrName].$$scope = parentScope; + attrs.$$observers[attrName].$$scope = scope; if( attrs[attrName] ) { - // If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn - scope[scopeName] = $interpolate(attrs[attrName])(parentScope); + // If the attribute has been provided then we trigger an interpolation to ensure + // the value is there for use in the link fn + isolateScope[scopeName] = $interpolate(attrs[attrName])(scope); } break; - } - case '=': { + case '=': if (optional && !attrs[attrName]) { return; } parentGet = $parse(attrs[attrName]); parentSet = parentGet.assign || function() { // reset the change, or we will throw this exception on every $digest - lastValue = scope[scopeName] = parentGet(parentScope); - throw $compileMinErr('nonassign', "Expression '{0}' used with directive '{1}' is non-assignable!", + lastValue = isolateScope[scopeName] = parentGet(scope); + throw $compileMinErr('nonassign', + "Expression '{0}' used with directive '{1}' is non-assignable!", attrs[attrName], newIsolateScopeDirective.name); }; - lastValue = scope[scopeName] = parentGet(parentScope); - scope.$watch(function parentValueWatch() { - var parentValue = parentGet(parentScope); + lastValue = isolateScope[scopeName] = parentGet(scope); + isolateScope.$watch(function parentValueWatch() { + var parentValue = parentGet(scope); - if (parentValue !== scope[scopeName]) { + if (parentValue !== isolateScope[scopeName]) { // we are out of sync and need to copy if (parentValue !== lastValue) { // parent changed and it has precedence - lastValue = scope[scopeName] = parentValue; + lastValue = isolateScope[scopeName] = parentValue; } else { // if the parent can be assigned then do so - parentSet(parentScope, parentValue = lastValue = scope[scopeName]); + parentSet(scope, parentValue = lastValue = isolateScope[scopeName]); } } return parentValue; }); break; - } - case '&': { + case '&': parentGet = $parse(attrs[attrName]); - scope[scopeName] = function(locals) { - return parentGet(parentScope, locals); + isolateScope[scopeName] = function(locals) { + return parentGet(scope, locals); }; break; - } - default: { - throw $compileMinErr('iscp', "Invalid isolate scope definition for directive '{0}'. Definition: {... {1}: '{2}' ...}", + default: + throw $compileMinErr('iscp', + "Invalid isolate scope definition for directive '{0}'." + + " Definition: {... {1}: '{2}' ...}", newIsolateScopeDirective.name, scopeName, definition); - } } }); } if (controllerDirectives) { forEach(controllerDirectives, function(directive) { var locals = { - $scope: scope, + $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, $element: $element, $attrs: attrs, $transclude: boundTranscludeFn }, controllerInstance; @@ -15208,33 +15855,45 @@ // PRELINKING for(i = 0, ii = preLinkFns.length; i < ii; i++) { try { linkFn = preLinkFns[i]; - linkFn(scope, $element, attrs, + linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, linkFn.require && getControllers(linkFn.require, $element)); } catch (e) { $exceptionHandler(e, startingTag($element)); } } // RECURSION - childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn); + // We only pass the isolate scope, if the isolate directive has a template, + // otherwise the child elements do not belong to the isolate directive. + var scopeToChild = scope; + if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { + scopeToChild = isolateScope; + } + childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); // POSTLINKING for(i = postLinkFns.length - 1; i >= 0; i--) { try { linkFn = postLinkFns[i]; - linkFn(scope, $element, attrs, + linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, linkFn.require && getControllers(linkFn.require, $element)); } catch (e) { $exceptionHandler(e, startingTag($element)); } } } } + function markDirectivesAsIsolate(directives) { + // mark all directives as needing isolate scope. + for (var j = 0, jj = directives.length; j < jj; j++) { + directives[j] = inherit(directives[j], {$$isolateScope: true}); + } + } /** * looks up the directive and decorates it with exception handling and proper parameters. We * call this the boundDirective. * @@ -15246,11 +15905,12 @@ * * `A': attribute * * `C`: class * * `M`: comment * @returns true if directive was added. */ - function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, endAttrName) { + function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, + endAttrName) { if (name === ignoreDirective) return null; var match = null; if (hasDirectives.hasOwnProperty(name)) { for(var directive, directives = $injector.get(name + Suffix), i = 0, ii = directives.length; i<ii; i++) { @@ -15319,11 +15979,11 @@ afterTemplateChildLinkFn, beforeTemplateCompileNode = $compileNode[0], origAsyncDirective = directives.shift(), // The fact that we have to copy and patch the directive seems wrong! derivedSyncDirective = extend({}, origAsyncDirective, { - templateUrl: null, transclude: null, replace: null + templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective }), templateUrl = (isFunction(origAsyncDirective.templateUrl)) ? origAsyncDirective.templateUrl($compileNode, tAttrs) : origAsyncDirective.templateUrl; @@ -15338,27 +15998,34 @@ if (origAsyncDirective.replace) { $template = jqLite('<div>' + trim(content) + '</div>').contents(); compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { - throw $compileMinErr('tplrt', "Template for directive '{0}' must have exactly one root element. {1}", + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", origAsyncDirective.name, templateUrl); } tempTemplateAttrs = {$attr: {}}; replaceWith($rootElement, $compileNode, compileNode); - collectDirectives(compileNode, directives, tempTemplateAttrs); + var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); + + if (isObject(origAsyncDirective.scope)) { + markDirectivesAsIsolate(templateDirectives); + } + directives = templateDirectives.concat(directives); mergeTemplateAttributes(tAttrs, tempTemplateAttrs); } else { compileNode = beforeTemplateCompileNode; $compileNode.html(content); } directives.unshift(derivedSyncDirective); afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, - childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, previousCompileContext); + childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, + previousCompileContext); forEach($rootElement, function(node, i) { if (node == compileNode) { $rootElement[i] = $compileNode[0]; } }); @@ -15372,15 +16039,16 @@ controller = linkQueue.shift(), linkNode = $compileNode[0]; if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { // it was cloned therefore we have to clone as well. - linkNode = JQLiteClone(compileNode); + linkNode = jqLiteClone(compileNode); replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller); + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, + controller); } linkQueue = null; }). error(function(response, code, headers, config) { throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url); @@ -15453,53 +16121,59 @@ // no interpolation found -> ignore if (!interpolateFn) return; if (name === "multiple" && nodeName_(node) === "SELECT") { - throw $compileMinErr("selmulti", "Binding to the 'multiple' attribute is not supported. Element: {0}", + throw $compileMinErr("selmulti", + "Binding to the 'multiple' attribute is not supported. Element: {0}", startingTag(node)); } directives.push({ - priority: -100, - compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { - var $$observers = (attr.$$observers || (attr.$$observers = {})); + priority: 100, + compile: function() { + return { + pre: function attrInterpolatePreLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); - if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { - throw $compileMinErr('nodomevents', - "Interpolations for HTML DOM event attributes are disallowed. Please use the ng- " + - "versions (such as ng-click instead of onclick) instead."); - } + if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { + throw $compileMinErr('nodomevents', + "Interpolations for HTML DOM event attributes are disallowed. Please use the " + + "ng- versions (such as ng-click instead of onclick) instead."); + } - // we need to interpolate again, in case the attribute value has been updated - // (e.g. by another directive's compile function) - interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name)); + // we need to interpolate again, in case the attribute value has been updated + // (e.g. by another directive's compile function) + interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name)); - // if attribute was updated so that there is no interpolation going on we don't want to - // register any observers - if (!interpolateFn) return; + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; - // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the actual attr value - attr[name] = interpolateFn(scope); - ($$observers[name] || ($$observers[name] = [])).$$inter = true; - (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function interpolateFnWatchAction(value) { - attr.$set(name, value); - }); - }) + // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the + // actual attr value + attr[name] = interpolateFn(scope); + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(value) { + attr.$set(name, value); + }); + } + }; + } }); } /** * This is a special jqLite.replaceWith, which can replace items which * have no parents, provided that the containing jqLite collection is provided. * * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep the shell, - * but replace its DOM node reference. + * in the root of the tree. + * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep + * the shell, but replace its DOM node reference. * @param {Node} newNode The new DOM node. */ function replaceWith($rootElement, elementsToRemove, newNode) { var firstElementToRemove = elementsToRemove[0], removeCount = elementsToRemove.length, @@ -15537,12 +16211,17 @@ fragment.appendChild(element); delete elementsToRemove[k]; } elementsToRemove[0] = newNode; - elementsToRemove.length = 1 + elementsToRemove.length = 1; } + + + function cloneAndAnnotateFn(fn, annotation) { + return extend(function() { return fn.apply(null, arguments); }, fn, annotation); + } }]; } var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; /** @@ -15561,25 +16240,25 @@ } /** * @ngdoc object * @name ng.$compile.directive.Attributes + * * @description + * A shared object between directive compile / linking functions which contains normalized DOM + * element attributes. The values reflect current binding state `{{ }}`. The normalization is + * needed since all of these are treated as equivalent in Angular: * - * A shared object between directive compile / linking functions which contains normalized DOM element - * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed - * since all of these are treated as equivalent in Angular: - * - * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> + * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> */ /** * @ngdoc property * @name ng.$compile.directive.Attributes#$attr * @propertyOf ng.$compile.directive.Attributes * @returns {object} A map of DOM element attribute names to the normalized name. This is - * needed to do reverse lookup from normalized name back to actual name. + * needed to do reverse lookup from normalized name back to actual name. */ /** * @ngdoc function @@ -15624,11 +16303,11 @@ * @description * The {@link ng.$controller $controller service} is used by Angular to create new * controllers. * * This provider allows controller registration via the - * {@link ng.$controllerProvider#register register} method. + * {@link ng.$controllerProvider#methods_register register} method. */ function $ControllerProvider() { var controllers = {}, CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; @@ -15643,11 +16322,11 @@ * annotations in the array notation). */ this.register = function(name, constructor) { assertNotHasOwnProperty(name, 'controller'); if (isObject(name)) { - extend(controllers, name) + extend(controllers, name); } else { controllers[name] = constructor; } }; @@ -15693,11 +16372,13 @@ instance = $injector.instantiate(expression, locals); if (identifier) { if (!(locals && typeof locals.$scope == 'object')) { - throw minErr('$controller')('noscp', "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", constructor || expression.name, identifier); + throw minErr('$controller')('noscp', + "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", + constructor || expression.name, identifier); } locals.$scope[identifier] = instance; } @@ -15983,27 +16664,28 @@ * will result in the success callback being called. Note that if the response is a redirect, * XMLHttpRequest will transparently follow it, meaning that the error callback will not be * called for such responses. * * # Calling $http from outside AngularJS - * The `$http` service will not actually send the request until the next `$digest()` is executed. - * Normally this is not an issue, since almost all the time your call to `$http` will be from within - * a `$apply()` block. - * If you are calling `$http` from outside Angular, then you should wrap it in a call to `$apply` - * to cause a $digest to occur and also to handle errors in the block correctly. + * The `$http` service will not actually send the request until the next `$digest()` is + * executed. Normally this is not an issue, since almost all the time your call to `$http` will + * be from within a `$apply()` block. + * If you are calling `$http` from outside Angular, then you should wrap it in a call to + * `$apply` to cause a $digest to occur and also to handle errors in the block correctly. * * ``` * $scope.$apply(function() { * $http(...); * }); * ``` * * # Writing Unit Tests that use $http - * When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do not - * trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have been - * made and `$httpBackend.expect(...)` expectations will fail. The solution is to run the code - * that calls the `$http()` method inside a $apply block as explained in the previous section. + * When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do + * not trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have + * been made and `$httpBackend.expect(...)` expectations will fail. The solution is to run the + * code that calls the `$http()` method inside a $apply block as explained in the previous + * section. * * ``` * $httpBackend.expectGET(...); * $scope.$apply(function() { * $http.get(...); @@ -16022,16 +16704,16 @@ * $http.post('/someUrl', data).success(successCallback); * </pre> * * Complete list of shortcut methods: * - * - {@link ng.$http#get $http.get} - * - {@link ng.$http#head $http.head} - * - {@link ng.$http#post $http.post} - * - {@link ng.$http#put $http.put} - * - {@link ng.$http#delete $http.delete} - * - {@link ng.$http#jsonp $http.jsonp} + * - {@link ng.$http#methods_get $http.get} + * - {@link ng.$http#methods_head $http.head} + * - {@link ng.$http#methods_post $http.post} + * - {@link ng.$http#methods_put $http.put} + * - {@link ng.$http#methods_delete $http.delete} + * - {@link ng.$http#methods_jsonp $http.jsonp} * * * # Setting HTTP Headers * * The $http service will automatically add certain HTTP headers to all requests. These defaults @@ -16048,37 +16730,40 @@ * To add or overwrite these defaults, simply add or remove a property from these configuration * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object * with the lowercased HTTP method name as the key, e.g. * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }. * - * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same - * fashion. + * The defaults can also be set at runtime via the `$http.defaults` object in the same + * fashion. In addition, you can supply a `headers` property in the config object passed when + * calling `$http(config)`, which overrides the defaults without changing them globally. * * * # Transforming Requests and Responses * * Both requests and responses can be transformed using transform functions. By default, Angular * applies these transformations: * * Request transformations: * - * - If the `data` property of the request configuration object contains an object, serialize it into - * JSON format. + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. * * Response transformations: * * - If XSRF prefix is detected, strip it (see Security Considerations section below). * - If JSON response is detected, deserialize it using a JSON parser. * - * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and - * `$httpProvider.defaults.transformResponse` properties. These properties are by default an - * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the - * transformation chain. You can also decide to completely override any default transformations by assigning your + * To globally augment or override the default transforms, modify the + * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse` + * properties. These properties are by default an array of transform functions, which allows you + * to `push` or `unshift` a new transformation function into the transformation chain. You can + * also decide to completely override any default transformations by assigning your * transformation functions to these properties directly without the array wrapper. * - * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or - * `transformResponse` properties of the configuration object passed into `$http`. + * Similarly, to locally override the request/response transforms, augment the + * `transformRequest` and/or `transformResponse` properties of the configuration object passed + * into `$http`. * * * # Caching * * To enable caching, set the configuration property `cache` to `true`. When the cache is @@ -16112,20 +16797,20 @@ * adding them to the `$httpProvider.interceptors` array. The factory is called and * injected with dependencies (if specified) and returns the interceptor. * * There are two kinds of interceptors (and two kinds of rejection interceptors): * - * * `request`: interceptors get called with http `config` object. The function is free to modify - * the `config` or create a new one. The function needs to return the `config` directly or as a - * promise. - * * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved - * with a rejection. - * * `response`: interceptors get called with http `response` object. The function is free to modify - * the `response` or create a new one. The function needs to return the `response` directly or as a - * promise. - * * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved - * with a rejection. + * * `request`: interceptors get called with http `config` object. The function is free to + * modify the `config` or create a new one. The function needs to return the `config` + * directly or as a promise. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to + * modify the `response` or create a new one. The function needs to return the `response` + * directly or as a promise. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. * * * <pre> * // register the interceptor as a service * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { @@ -16174,10 +16859,11 @@ * // same as above * }, * 'response': function(response) { * // same as above * } + * }; * }); * </pre> * * # Response interceptors (DEPRECATED) * @@ -16270,35 +16956,39 @@ * * To take advantage of this, your server needs to set a token in a JavaScript readable session * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure * that only JavaScript running on your domain could have sent the request. The token must be - * unique for each user and must be verifiable by the server (to prevent the JavaScript from making - * up its own tokens). We recommend that the token is a digest of your site's authentication - * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security. + * unique for each user and must be verifiable by the server (to prevent the JavaScript from + * making up its own tokens). We recommend that the token is a digest of your site's + * authentication cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} + * for added security. * * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName * properties of either $httpProvider.defaults, or the per-request config object. * * * @param {object} config Object describing the request to be made and how it should be * processed. The object has following properties: * * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned to - * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified. + * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned + * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be + * JSONified. * - **data** – `{string|Object}` – Data to be sent as the request message data. * - **headers** – `{Object}` – Map of strings or functions which return strings representing - * HTTP headers to send to the server. If the return value of a function is null, the header will - * not be sent. + * HTTP headers to send to the server. If the return value of a function is null, the + * header will not be sent. * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. - * - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – + * - **transformRequest** – + * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – * transform function or an array of such functions. The transform function takes the http * request body and headers and returns its transformed (typically serialized) version. - * - **transformResponse** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – + * - **transformResponse** – + * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – * transform function or an array of such functions. The transform function takes the http * response body and headers and returns its transformed (typically deserialized) version. * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the * GET request, otherwise if a cache instance built with * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for @@ -16317,89 +17007,97 @@ * response object. The `success` and `error` methods take a single argument - a function that * will be called when the request succeeds or fails respectively. The arguments passed into * these functions are destructured representation of the response object passed into the * `then` method. The response object has these properties: * - * - **data** – `{string|Object}` – The response body transformed with the transform functions. + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. * - **status** – `{number}` – HTTP status code of the response. * - **headers** – `{function([headerName])}` – Header getter function. * - **config** – `{Object}` – The configuration object that was used to generate the request. * * @property {Array.<Object>} pendingRequests Array of config objects for currently pending * requests. This is primarily meant to be used for debugging purposes. * * * @example - <example> - <file name="index.html"> - <div ng-controller="FetchCtrl"> - <select ng-model="method"> - <option>GET</option> - <option>JSONP</option> - </select> - <input type="text" ng-model="url" size="80"/> - <button ng-click="fetch()">fetch</button><br> - <button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> - <button ng-click="updateModel('JSONP', 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">Sample JSONP</button> - <button ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">Invalid JSONP</button> - <pre>http status code: {{status}}</pre> - <pre>http response data: {{data}}</pre> - </div> - </file> - <file name="script.js"> - function FetchCtrl($scope, $http, $templateCache) { - $scope.method = 'GET'; - $scope.url = 'http-hello.html'; +<example> +<file name="index.html"> + <div ng-controller="FetchCtrl"> + <select ng-model="method"> + <option>GET</option> + <option>JSONP</option> + </select> + <input type="text" ng-model="url" size="80"/> + <button ng-click="fetch()">fetch</button><br> + <button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> + <button + ng-click="updateModel('JSONP', + 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')"> + Sample JSONP + </button> + <button + ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')"> + Invalid JSONP + </button> + <pre>http status code: {{status}}</pre> + <pre>http response data: {{data}}</pre> + </div> +</file> +<file name="script.js"> + function FetchCtrl($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; - $scope.fetch = function() { - $scope.code = null; - $scope.response = null; + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; - $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - success(function(data, status) { - $scope.status = status; - $scope.data = data; - }). - error(function(data, status) { - $scope.data = data || "Request failed"; - $scope.status = status; - }); - }; + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + success(function(data, status) { + $scope.status = status; + $scope.data = data; + }). + error(function(data, status) { + $scope.data = data || "Request failed"; + $scope.status = status; + }); + }; - $scope.updateModel = function(method, url) { - $scope.method = method; - $scope.url = url; - }; - } - </file> - <file name="http-hello.html"> - Hello, $http! - </file> - <file name="scenario.js"> - it('should make an xhr GET request', function() { - element(':button:contains("Sample GET")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Hello, \$http!/); - }); + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + } +</file> +<file name="http-hello.html"> + Hello, $http! +</file> +<file name="scenario.js"> + it('should make an xhr GET request', function() { + element(':button:contains("Sample GET")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Hello, \$http!/); + }); - it('should make a JSONP request to angularjs.org', function() { - element(':button:contains("Sample JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Super Hero!/); - }); + it('should make a JSONP request to angularjs.org', function() { + element(':button:contains("Sample JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Super Hero!/); + }); - it('should make JSONP request to invalid URL and invoke the error handler', - function() { - element(':button:contains("Invalid JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('0'); - expect(binding('data')).toBe('Request failed'); - }); - </file> - </example> + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + element(':button:contains("Invalid JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('0'); + expect(binding('data')).toBe('Request failed'); + }); +</file> +</example> */ function $http(requestConfig) { var config = { transformRequest: defaults.transformRequest, transformResponse: defaults.transformResponse @@ -16755,11 +17453,11 @@ function buildUrl(url, params) { if (!params) return url; var parts = []; forEachSorted(params, function(value, key) { - if (value == null || value == undefined) return; + if (value === null || isUndefined(value)) return; if (!isArray(value)) value = [value]; forEach(value, function(v) { if (isObject(v)) { v = toJson(v); @@ -16774,10 +17472,11 @@ }]; } var XHR = window.XMLHttpRequest || function() { + /* global ActiveXObject */ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest."); }; @@ -16844,11 +17543,11 @@ xhr.onreadystatechange = function() { if (xhr.readyState == 4) { var responseHeaders = xhr.getAllResponseHeaders(); // responseText is the old-school way of retrieving response (supported by IE8 & 9) - // response and responseType properties were introduced in XHR Level2 spec (supported by IE10) + // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) completeRequest(callback, status || xhr.status, (xhr.responseType ? xhr.response : xhr.responseText), responseHeaders); } @@ -16932,35 +17631,35 @@ * @description * * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. * * @example - <doc:example module="customInterpolationApp"> - <doc:source> - <script> - var customInterpolationApp = angular.module('customInterpolationApp', []); +<doc:example module="customInterpolationApp"> +<doc:source> +<script> + var customInterpolationApp = angular.module('customInterpolationApp', []); - customInterpolationApp.config(function($interpolateProvider) { - $interpolateProvider.startSymbol('//'); - $interpolateProvider.endSymbol('//'); - }); + customInterpolationApp.config(function($interpolateProvider) { + $interpolateProvider.startSymbol('//'); + $interpolateProvider.endSymbol('//'); + }); - customInterpolationApp.controller('DemoController', function DemoController() { - this.label = "This bindings is brought you you by // interpolation symbols."; - }); - </script> - <div ng-app="App" ng-controller="DemoController as demo"> - //demo.label// - </div> - </doc:source> - <doc:scenario> - it('should interpolate binding with custom symbols', function() { - expect(binding('demo.label')).toBe('This bindings is brought you you by // interpolation symbols.'); - }); - </doc:scenario> - </doc:example> + customInterpolationApp.controller('DemoController', function DemoController() { + this.label = "This binding is brought you by // interpolation symbols."; + }); +</script> +<div ng-app="App" ng-controller="DemoController as demo"> + //demo.label// +</div> +</doc:source> +<doc:scenario> + it('should interpolate binding with custom symbols', function() { + expect(binding('demo.label')).toBe('This binding is brought you by // interpolation symbols.'); + }); +</doc:scenario> +</doc:example> */ function $InterpolateProvider() { var startSymbol = '{{'; var endSymbol = '}}'; @@ -17033,15 +17732,15 @@ * @param {string} text The text with markup to interpolate. * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have * embedded expression in order to return an interpolation function. Strings with no * embedded expression will return null for the interpolation function. * @param {string=} trustedContext when provided, the returned function passes the interpolated - * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, + * result through {@link ng.$sce#methods_getTrusted $sce.getTrusted(interpolatedResult, * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that * provides Strict Contextual Escaping for details. - * @returns {function(context)} an interpolation function which is used to compute the interpolated - * string. The function has these parameters: + * @returns {function(context)} an interpolation function which is used to compute the + * interpolated string. The function has these parameters: * * * `context`: an object against which any expressions embedded in the strings are evaluated * against. * */ @@ -17075,16 +17774,16 @@ // we added, nothing, must have been an empty string. parts.push(''); length = 1; } - // Concatenating expressions makes it hard to reason about whether some combination of concatenated - // values are unsafe to use and could easily lead to XSS. By requiring that a single - // expression be used for iframe[src], object[src], etc., we ensure that the value that's used - // is assigned or constructed by some JS code somewhere that is more testable or make it - // obvious that you bound the value to some user controlled value. This helps reduce the load - // when auditing for XSS issues. + // Concatenating expressions makes it hard to reason about whether some combination of + // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a + // single expression be used for iframe[src], object[src], etc., we ensure that the value + // that's used is assigned or constructed by some JS code somewhere that is more testable or + // make it obvious that you bound the value to some user controlled value. This helps reduce + // the load when auditing for XSS issues. if (trustedContext && parts.length > 1) { throw $interpolateMinErr('noconcat', "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + "interpolations that concatenate multiple expressions when a trusted value is " + "required. See http://docs.angularjs.org/api/ng.$sce", text); @@ -17100,22 +17799,23 @@ if (trustedContext) { part = $sce.getTrusted(trustedContext, part); } else { part = $sce.valueOf(part); } - if (part == null || part == undefined) { + if (part === null || isUndefined(part)) { part = ''; } else if (typeof part != 'string') { part = toJson(part); } } concat[i] = part; } return concat.join(''); } catch(err) { - var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); + var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, + err.toString()); $exceptionHandler(newErr); } }; fn.exp = text; fn.parts = parts; @@ -17136,11 +17836,11 @@ * * @returns {string} start symbol. */ $interpolate.startSymbol = function() { return startSymbol; - } + }; /** * @ngdoc method * @name ng.$interpolate#endSymbol @@ -17153,11 +17853,11 @@ * * @returns {string} start symbol. */ $interpolate.endSymbol = function() { return endSymbol; - } + }; return $interpolate; }]; } @@ -17179,31 +17879,31 @@ * notified upon each tick of the interval, and will be resolved after `count` iterations, or * run indefinitely if `count` is not defined. The value of the notification will be the * number of iterations that have run. * To cancel an interval, call `$interval.cancel(promise)`. * - * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to * move forward by `millis` milliseconds and trigger any functions scheduled to run in that * time. * * @param {function()} fn A function that should be called repeatedly. * @param {number} delay Number of milliseconds between each function call. * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat * indefinitely. * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block. * @returns {promise} A promise which will be notified on each iteration. */ function interval(fn, delay, count, invokeApply) { var setInterval = $window.setInterval, - clearInterval = $window.clearInterval; - - var deferred = $q.defer(), + clearInterval = $window.clearInterval, + deferred = $q.defer(), promise = deferred.promise, - count = (isDefined(count)) ? count : 0, iteration = 0, skipApply = (isDefined(invokeApply) && !invokeApply); + + count = isDefined(count) ? count : 0, promise.then(null, null, fn); promise.$$intervalId = setInterval(function tick() { deferred.notify(iteration++); @@ -17292,12 +17992,13 @@ ], CURRENCY_SYM: '$' }, DATETIME_FORMATS: { - MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' - .split(','), + MONTH: + 'January,February,March,April,May,June,July,August,September,October,November,December' + .split(','), SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), AMPMS: ['AM','PM'], medium: 'MMM d, y h:mm:ss a', @@ -17355,27 +18056,31 @@ var prefixed = (relativeUrl.charAt(0) !== '/'); if (prefixed) { relativeUrl = '/' + relativeUrl; } var match = urlResolve(relativeUrl); - locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname); + locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? + match.pathname.substring(1) : match.pathname); locationObj.$$search = parseKeyValue(match.search); locationObj.$$hash = decodeURIComponent(match.hash); // make sure path starts with '/'; - if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') locationObj.$$path = '/' + locationObj.$$path; + if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { + locationObj.$$path = '/' + locationObj.$$path; + } } /** * * @param {string} begin * @param {string} whole - * @returns {string} returns text from whole after begin or undefined if it does not begin with expected string. + * @returns {string} returns text from whole after begin or undefined if it does not begin with + * expected string. */ function beginsWith(begin, whole) { - if (whole.indexOf(begin) == 0) { + if (whole.indexOf(begin) === 0) { return whole.substr(begin.length); } } @@ -17416,11 +18121,12 @@ * @private */ this.$$parse = function(url) { var pathUrl = beginsWith(appBaseNoFile, url); if (!isString(pathUrl)) { - throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, appBaseNoFile); + throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, + appBaseNoFile); } parseAppUrl(pathUrl, this); if (!this.$$path) { @@ -17455,11 +18161,11 @@ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { return appBaseNoFile + appUrl; } else if (appBaseNoFile == url + '/') { return appBaseNoFile; } - } + }; } /** * LocationHashbangUrl represents url @@ -17488,11 +18194,12 @@ : (this.$$html5) ? withoutBaseUrl : ''; if (!isString(withoutHashUrl)) { - throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, hashPrefix); + throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, + hashPrefix); } parseAppUrl(withoutHashUrl, this); this.$$compose(); }; @@ -17510,11 +18217,11 @@ this.$$rewrite = function(url) { if(stripHash(appBase) == stripHash(url)) { return url; } - } + }; } /** * LocationHashbangUrl represents url @@ -17539,11 +18246,11 @@ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { return appBase + hashPrefix + appUrl; } else if ( appBaseNoFile === url + '/') { return appBaseNoFile; } - } + }; } LocationHashbangInHtml5Url.prototype = LocationHashbangUrl.prototype = @@ -17678,15 +18385,18 @@ * * Return search part (as object) of current url when called without any parameter. * * Change search part when called with parameter and return `$location`. * - * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or hash object. Hash object - * may contain an array of values, which will be decoded as duplicates in the url. - * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a - * single search parameter. If the value is `null`, the parameter will be deleted. + * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or + * hash object. Hash object may contain an array of values, which will be decoded as duplicates in + * the url. * + * @param {(string|Array<string>)=} paramValue If `search` is a string, then `paramValue` will override only a + * single search parameter. If `paramValue` is an array, it will set the parameter as a + * comma-separated value. If `paramValue` is `null`, the parameter will be deleted. + * * @return {string} search */ search: function(search, paramValue) { switch (arguments.length) { case 0: @@ -17695,15 +18405,16 @@ if (isString(search)) { this.$$search = parseKeyValue(search); } else if (isObject(search)) { this.$$search = search; } else { - throw $locationMinErr('isrcharg', 'The first argument of the `$location#search()` call must be a string or an object.'); + throw $locationMinErr('isrcharg', + 'The first argument of the `$location#search()` call must be a string or an object.'); } break; default: - if (paramValue == undefined || paramValue == null) { + if (isUndefined(paramValue) || paramValue === null) { delete this.$$search[search]; } else { this.$$search[search] = paramValue; } } @@ -17835,10 +18546,39 @@ } else { return html5Mode; } }; + /** + * @ngdoc event + * @name ng.$location#$locationChangeStart + * @eventOf ng.$location + * @eventType broadcast on root scope + * @description + * Broadcasted before a URL will change. This change can be prevented by calling + * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more + * details about event object. Upon successful change + * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + */ + + /** + * @ngdoc event + * @name ng.$location#$locationChangeSuccess + * @eventOf ng.$location + * @eventType broadcast on root scope + * @description + * Broadcasted after a URL was changed. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + */ + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', function( $rootScope, $browser, $sniffer, $rootElement) { var $location, LocationMode, baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' @@ -17891,11 +18631,12 @@ } // update $location when $browser url changes $browser.onUrlChange(function(newUrl) { if ($location.absUrl() != newUrl) { - if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) { + if ($rootScope.$broadcast('$locationChangeStart', newUrl, + $location.absUrl()).defaultPrevented) { $browser.url($location.absUrl()); return; } $rootScope.$evalAsync(function() { var oldUrl = $location.absUrl(); @@ -17942,11 +18683,11 @@ * @ngdoc object * @name ng.$log * @requires $window * * @description - * Simple service for logging. Default implementation writes the message + * Simple service for logging. Default implementation safely writes the message * into the browser's console (if present). * * The main purpose of this service is to simplify debugging and troubleshooting. * * The default is not to log `debug` messages. You can use @@ -17991,16 +18732,16 @@ * @description * @param {string=} flag enable or disable debug level messages * @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; - } else { - return debug; - } + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } }; this.$get = ['$window', function($window){ return { /** @@ -18050,17 +18791,17 @@ * * @description * Write a debug message */ debug: (function () { - var fn = consoleLog('debug'); - - return function() { - if (debug) { - fn.apply(self, arguments); - } - } + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + }; }()) }; function formatError(arg) { if (arg instanceof Error) { @@ -18091,73 +18832,93 @@ // we are IE which either doesn't have window.console => this is noop and we do nothing, // or we are IE where console.log doesn't have apply so we log at least first 2 args return function(arg1, arg2) { logFn(arg1, arg2 == null ? '' : arg2); - } + }; } }]; } var $parseMinErr = minErr('$parse'); var promiseWarningCache = {}; var promiseWarning; // Sandboxing Angular Expressions // ------------------------------ -// Angular expressions are generally considered safe because these expressions only have direct access to $scope and -// locals. However, one can obtain the ability to execute arbitrary JS code by obtaining a reference to native JS -// functions such as the Function constructor. +// Angular expressions are generally considered safe because these expressions only have direct +// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by +// obtaining a reference to native JS functions such as the Function constructor, the global Window +// or Document object. In addition, many powerful functions for use by JavaScript code are +// published on scope that shouldn't be available from within an Angular expression. // // As an example, consider the following Angular expression: // // {}.toString.constructor(alert("evil JS code")) // -// We want to prevent this type of access. For the sake of performance, during the lexing phase we disallow any "dotted" -// access to any member named "constructor". +// We want to prevent this type of access. For the sake of performance, during the lexing phase we +// disallow any "dotted" access to any member named "constructor" or to any member whose name begins +// or ends with an underscore. The latter allows one to exclude the private / JavaScript only API +// available on the scope and controllers from the context of an Angular expression. // -// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor while evaluating -// the expression, which is a stronger but more expensive test. Since reflective calls are expensive anyway, this is not -// such a big deal compared to static dereferencing. +// For reflective calls (a[b]), we check that the value of the lookup is not the Function +// constructor, Window or DOM node while evaluating the expression, which is a stronger but more +// expensive test. Since reflective calls are expensive anyway, this is not such a big deal compared +// to static dereferencing. // -// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits against the -// expression language, but not to prevent exploits that were enabled by exposing sensitive JavaScript or browser apis -// on Scope. Exposing such objects on a Scope is never a good practice and therefore we are not even trying to protect -// against interaction with an object explicitly exposed in this way. +// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits +// against the expression language, but not to prevent exploits that were enabled by exposing +// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good +// practice and therefore we are not even trying to protect against interaction with an object +// explicitly exposed in this way. // -// A developer could foil the name check by aliasing the Function constructor under a different name on the scope. +// A developer could foil the name check by aliasing the Function constructor under a different +// name on the scope. // -// In general, it is not possible to access a Window object from an angular expression unless a window or some DOM -// object that has a reference to window is published onto a Scope. +// In general, it is not possible to access a Window object from an angular expression unless a +// window or some DOM object that has a reference to window is published onto a Scope. -function ensureSafeMemberName(name, fullExpression) { - if (name === "constructor") { +function ensureSafeMemberName(name, fullExpression, allowConstructor) { + if (typeof name !== 'string' && toString.apply(name) !== "[object String]") { + return name; + } + if (name === "constructor" && !allowConstructor) { throw $parseMinErr('isecfld', - 'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}', fullExpression); + 'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}', + fullExpression); } + if (name.charAt(0) === '_' || name.charAt(name.length-1) === '_') { + throw $parseMinErr('isecprv', + 'Referencing private fields in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } return name; -}; +} function ensureSafeObject(obj, fullExpression) { // nifty check if obj is Function that is fast and works across iframes and other contexts if (obj && obj.constructor === obj) { throw $parseMinErr('isecfn', - 'Referencing Function in Angular expressions is disallowed! Expression: {0}', fullExpression); + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); } else if (// isWindow(obj) obj && obj.document && obj.location && obj.alert && obj.setInterval) { throw $parseMinErr('isecwindow', - 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', fullExpression); + 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', + fullExpression); } else if (// isElement(obj) obj && (obj.nodeName || (obj.on && obj.find))) { throw $parseMinErr('isecdom', - 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', fullExpression); + 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', + fullExpression); } else { return obj; } } var OPERATORS = { + /* jshint bitwise : false */ 'null':function(){return null;}, 'true':function(){return true;}, 'false':function(){return false;}, undefined:noop, '+':function(self, locals, a,b){ @@ -18167,11 +18928,14 @@ return a + b; } return a; } return isDefined(b)?b:undefined;}, - '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, + '-':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + return (isDefined(a)?a:0)-(isDefined(b)?b:0); + }, '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, '=':noop, @@ -18188,10 +18952,11 @@ '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, // '|':function(self, locals, a,b){return a|b;}, '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, '!':function(self, locals, a){return !a(self, locals);} }; +/* jshint bitwise: true */ var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; ///////////////////////////////////////// @@ -18288,12 +19053,13 @@ isNumber: function(ch) { return ('0' <= ch && ch <= '9'); }, isWhitespace: function(ch) { + // IE treats non-breaking space as \u00A0 return (ch === ' ' || ch === '\r' || ch === '\t' || - ch === '\n' || ch === '\v' || ch === '\u00A0'); // IE treats non-breaking space as \u00A0 + ch === '\n' || ch === '\v' || ch === '\u00A0'); }, isIdent: function(ch) { return ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || @@ -18806,11 +19572,14 @@ var indexFn = this.expression(); this.consume(']'); return extend(function(self, locals) { var o = obj(self, locals), - i = indexFn(self, locals), + // In the getter, we will not block looking up "constructor" by name in order to support user defined + // constructors. However, if value looked up is the Function constructor, we will still block it in the + // ensureSafeObject call right after we look up o[i] (a few lines below.) + i = ensureSafeMemberName(indexFn(self, locals), parser.text, true /* allowConstructor */), v, p; if (!o) return undefined; v = ensureSafeObject(o[i], parser.text); if (v && v.then && parser.options.unwrapPromises) { @@ -18822,11 +19591,11 @@ v = v.$$v; } return v; }, { assign: function(self, value, locals) { - var key = indexFn(self, locals); + var key = ensureSafeMemberName(indexFn(self, locals), parser.text); // prevent overwriting of Function.constructor which would break ensureSafeObject check var safe = ensureSafeObject(obj(self, locals), parser.text); return safe[key] = value; } }); @@ -18850,10 +19619,11 @@ for (var i = 0; i < argsFn.length; i++) { args.push(argsFn[i](scope, locals)); } var fnPtr = fn(scope, locals, context) || noop; + ensureSafeObject(context, parser.text); ensureSafeObject(fnPtr, parser.text); // IE stupidity! (IE doesn't have apply for some native functions) var v = fnPtr.apply ? fnPtr.apply(context, args) @@ -19055,11 +19825,11 @@ promise.then(function(val) { promise.$$v = val; }); } pathVal = pathVal.$$v; } return pathVal; - } + }; } function getterFn(path, options, fullExp) { // Check whether the cache has this getter already. // We can use hasOwnProperty directly on the cache because we ensure, @@ -19071,24 +19841,26 @@ var pathKeys = path.split('.'), pathKeysLength = pathKeys.length, fn; if (options.csp) { - fn = (pathKeysLength < 6) - ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, options) - : function(scope, locals) { - var i = 0, val; - do { - val = cspSafeGetterFn( - pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], fullExp, options - )(scope, locals); + if (pathKeysLength < 6) { + fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, + options); + } else { + fn = function(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], + pathKeys[i++], fullExp, options)(scope, locals); - locals = undefined; // clear after first iteration - scope = val; - } while (i < pathKeysLength); - return val; - } + locals = undefined; // clear after first iteration + scope = val; + } while (i < pathKeysLength); + return val; + }; + } } else { var code = 'var l, fn, p;\n'; forEach(pathKeys, function(key, index) { ensureSafeMemberName(key, fullExp); code += 'if(s === null || s === undefined) return s;\n' + @@ -19110,11 +19882,13 @@ '}\n' : ''); }); code += 'return s;'; - var evaledFnGetter = Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning + /* jshint -W054 */ + var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning + /* jshint +W054 */ evaledFnGetter.toString = function() { return code; }; fn = function(scope, locals) { return evaledFnGetter(scope, locals, promiseWarning); }; } @@ -19174,11 +19948,12 @@ * @ngdoc object * @name ng.$parseProvider * @function * * @description - * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} service. + * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} + * service. */ function $ParseProvider() { var cache = {}; var $parseOptions = { @@ -19196,39 +19971,44 @@ * @methodOf ng.$parseProvider * @description * * **This feature is deprecated, see deprecation notes below for more info** * - * If set to true (default is false), $parse will unwrap promises automatically when a promise is found at any part of - * the expression. In other words, if set to true, the expression will always result in a non-promise value. + * If set to true (default is false), $parse will unwrap promises automatically when a promise is + * found at any part of the expression. In other words, if set to true, the expression will always + * result in a non-promise value. * - * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, the fulfillment value - * is used in place of the promise while evaluating the expression. + * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, + * the fulfillment value is used in place of the promise while evaluating the expression. * * **Deprecation notice** * - * This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data - * access in templates (accessed as raw values) and controller code (accessed as promises). + * This is a feature that didn't prove to be wildly useful or popular, primarily because of the + * dichotomy between data access in templates (accessed as raw values) and controller code + * (accessed as promises). * - * In most code we ended up resolving promises manually in controllers anyway and thus unifying the model access there. + * In most code we ended up resolving promises manually in controllers anyway and thus unifying + * the model access there. * * Other downsides of automatic promise unwrapping: * * - when building components it's often desirable to receive the raw promises * - adds complexity and slows down expression evaluation - * - makes expression code pre-generation unattractive due to the amount of code that needs to be generated + * - makes expression code pre-generation unattractive due to the amount of code that needs to be + * generated * - makes IDE auto-completion and tool support hard * * **Warning Logs** * - * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce - * the noise, each expression is logged only once). To disable this logging use + * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a + * promise (to reduce the noise, each expression is logged only once). To disable this logging use * `$parseProvider.logPromiseWarnings(false)` api. * * * @param {boolean=} value New value. - * @returns {boolean|self} Returns the current setting when used as getter and self if used as setter. + * @returns {boolean|self} Returns the current setting when used as getter and self if used as + * setter. */ this.unwrapPromises = function(value) { if (isDefined(value)) { $parseOptions.unwrapPromises = !!value; return this; @@ -19251,11 +20031,12 @@ * The default is set to `true`. * * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well. * * @param {boolean=} value New value. - * @returns {boolean|self} Returns the current setting when used as getter and self if used as setter. + * @returns {boolean|self} Returns the current setting when used as getter and self if used as + * setter. */ this.logPromiseWarnings = function(value) { if (isDefined(value)) { $parseOptions.logPromiseWarnings = value; return this; @@ -19355,12 +20136,12 @@ * alert('Got notification: ' + update); * }); * </pre> * * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of - * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). + * comes in the way of guarantees that promise and deferred APIs make, see + * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. * * Additionally the promise api allows for composition that is very hard to do with the * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the * section on serial or parallel joining of promises. @@ -19403,12 +20184,13 @@ * as soon as the result is available. The callbacks are called with a single argument: the result * or rejection reason. Additionally, the notify callback may be called zero or more times to * provide a progress indication, before the promise is resolved or rejected. * * This method *returns a new promise* which is resolved or rejected via the return value of the - * `successCallback`, `errorCallback`. It also notifies via the return value of the `notifyCallback` - * method. The promise can not be resolved or rejected from the notifyCallback method. + * `successCallback`, `errorCallback`. It also notifies via the return value of the + * `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback + * method. * * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` * * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, * but to do so without modifying the final value. This is useful to release resources or do some @@ -19420,12 +20202,12 @@ * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to * make your code IE8 compatible. * * # Chaining promises * - * Because calling the `then` method of a promise returns a new derived promise, it is easily possible - * to create a chain of promises: + * Because calling the `then` method of a promise returns a new derived promise, it is easily + * possible to create a chain of promises: * * <pre> * promiseB = promiseA.then(function(result) { * return result + 1; * }); @@ -19445,12 +20227,10 @@ * There are three main differences: * * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation * mechanism in angular, which means faster propagation of resolution or rejection into your * models and avoiding unnecessary browser repaints, which would result in flickering UI. - * - $q promises are recognized by the templating engine in angular, which means that in templates - * you can treat promises attached to a scope as if they were the resulting values. * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains * all the important functionality needed for common async tasks. * * # Testing * @@ -19784,13 +20564,13 @@ * Combines multiple promises into a single promise that is resolved when all of the input * promises are resolved. * * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, - * each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of - * the promises is resolved with a rejection, this resulting promise will be rejected with the - * same rejection value. + * each value corresponding to the promise at the same index/key in the `promises` array/hash. + * If any of the promises is resolved with a rejection, this resulting promise will be rejected + * with the same rejection value. */ function all(promises) { var deferred = defer(), counter = 0, results = isArray(promises) ? [] : {}; @@ -19860,15 +20640,23 @@ * @ngdoc function * @name ng.$rootScopeProvider#digestTtl * @methodOf ng.$rootScopeProvider * @description * - * Sets the number of digest iterations the scope should attempt to execute before giving up and + * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and * assuming that the model is unstable. * * The current default is 10 iterations. * + * In complex applications it's possible that the dependencies between `$watch`s will result in + * several digest iterations. However if an application needs more than the default 10 digest + * iterations for its model to stabilize then you should investigate what is causing the model to + * continuously change during the digest. + * + * Increasing the TTL could have performance implications, so you should not change it without + * proper justification. + * * @param {number} limit The number of digest iterations. */ /** @@ -19901,11 +20689,11 @@ * @name ng.$rootScope.Scope * * @description * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the * {@link AUTO.$injector $injector}. Child scopes are created using the - * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * {@link ng.$rootScope.Scope#methods_$new $new()} method. (Most scopes are created automatically when * compiled HTML template is executed.) * * Here is a simple scope snippet to show how you can interact with the scope. * <pre> * <file src="./test/ng/rootScopeSpec.js" tag="docs1" /> @@ -19925,15 +20713,16 @@ expect(child.salutation).toEqual('Welcome'); expect(parent.salutation).toEqual('Hello'); * </pre> * * - * @param {Object.<string, function()>=} providers Map of service factory which need to be provided - * for the current scope. Defaults to {@link ng}. + * @param {Object.<string, function()>=} providers Map of service factory which need to be + * provided for the current scope. Defaults to {@link ng}. * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy when unit-testing and having - * the need to override a default service. + * append/override services provided by `providers`. This is handy + * when unit-testing and having the need to override a default + * service. * @returns {Object} Newly created scope. * */ function Scope() { this.$id = nextUid(); @@ -19967,16 +20756,16 @@ * * @description * Creates a new child {@link ng.$rootScope.Scope scope}. * * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and - * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope - * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the + * scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. * - * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for - * the scope and its child scopes to be permanently detached from the parent and thus stop - * participating in model change detection and listener notification by invoking. + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is + * desired for the scope and its child scopes to be permanently detached from the parent and + * thus stop participating in model change detection and listener notification by invoking. * * @param {boolean} isolate If true, then the scope does not prototypically inherit from the * parent scope. The scope is isolated, as it can not see parent scope properties. * When creating widgets, it is useful for the widget to not accidentally read parent * state. @@ -20023,71 +20812,107 @@ * @function * * @description * Registers a `listener` callback to be executed whenever the `watchExpression` changes. * - * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and - * should return the value that will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} - * reruns when it detects changes the `watchExpression` can execute multiple times per + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest + * $digest()} and should return the value that will be watched. (Since + * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the + * `watchExpression` can execute multiple times per * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) * - The `listener` is called only when the value from the current `watchExpression` and the * previous call to `watchExpression` are not equal (with the exception of the initial run, * see below). The inequality is determined according to - * {@link angular.equals} function. To save the value of the object for later comparison, the - * {@link angular.copy} function is used. It also means that watching complex options will - * have adverse memory and performance implications. - * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This - * is achieved by rerunning the watchers until no changes are detected. The rerun iteration - * limit is 10 to prevent an infinite loop deadlock. + * {@link angular.equals} function. To save the value of the object for later comparison, + * the {@link angular.copy} function is used. It also means that watching complex options + * will have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. + * This is achieved by rerunning the watchers until no changes are detected. The rerun + * iteration limit is 10 to prevent an infinite loop deadlock. * * * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` - * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is - * detected, be prepared for multiple calls to your listener.) + * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a + * change is detected, be prepared for multiple calls to your listener.) * * After a watcher is registered with the scope, the `listener` fn is called asynchronously * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the * watcher. In rare cases, this is undesirable because the listener is called when the result * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the * listener was called due to initialization. * + * The example below contains an illustration of using a function as your $watch listener * + * * # Example * <pre> // let's assume that scope was dependency injected as the $rootScope var scope = $rootScope; scope.name = 'misko'; scope.counter = 0; expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; }); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); expect(scope.counter).toEqual(0); scope.$digest(); // no variable change expect(scope.counter).toEqual(0); scope.name = 'adam'; scope.$digest(); expect(scope.counter).toEqual(1); + + + + // Using a listener function + var food; + scope.foodCounter = 0; + expect(scope.foodCounter).toEqual(0); + scope.$watch( + // This is the listener function + function() { return food; }, + // This is the change handler + function(newValue, oldValue) { + if ( newValue !== oldValue ) { + // Only increment the counter if the value changed + scope.foodCounter = scope.foodCounter + 1; + } + } + ); + // No digest has been run so the counter will be zero + expect(scope.foodCounter).toEqual(0); + + // Run the digest but since food has not changed cout will still be zero + scope.$digest(); + expect(scope.foodCounter).toEqual(0); + + // Update food and run digest. Now the counter will increment + food = 'cheeseburger'; + scope.$digest(); + expect(scope.foodCounter).toEqual(1); + * </pre> * * * * @param {(function()|string)} watchExpression Expression that is evaluated on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a - * call to the `listener`. + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers + * a call to the `listener`. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(scope)`: called with current `scope` as a parameter. * @param {(function()|string)=} listener Callback called whenever the return value of * the `watchExpression` changes. * * - `string`: Evaluated as {@link guide/expression expression} - * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. + * - `function(newValue, oldValue, scope)`: called with current and previous values as + * parameters. * * @param {boolean=} objectEquality Compare object for equality rather than for reference. * @returns {function()} Returns a deregistration function for this listener. */ $watch: function(watchExp, listener, objectEquality) { @@ -20135,17 +20960,17 @@ * @methodOf ng.$rootScope.Scope * @function * * @description * Shallow watches the properties of an object and fires whenever any of the properties change - * (for arrays, this implies watching the array items; for object maps, this implies watching the properties). - * If a change is detected, the `listener` callback is fired. + * (for arrays, this implies watching the array items; for object maps, this implies watching + * the properties). If a change is detected, the `listener` callback is fired. * - * - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to - * see if any items have been added, removed, or moved. - * - The `listener` is called whenever anything within the `obj` has changed. Examples include adding, removing, - * and moving items belonging to an object or array. + * - The `obj` collection is observed via standard $watch operation and is examined on every + * call to $digest() to see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include + * adding, removing, and moving items belonging to an object or array. * * * # Example * <pre> $scope.names = ['igor', 'matias', 'misko', 'james']; @@ -20167,23 +20992,23 @@ //now there's been a change expect($scope.dataCount).toEqual(3); * </pre> * * - * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value - * should evaluate to an object or an array which is observed on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger - * a call to the `listener`. + * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The + * expression value should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the + * collection will trigger a call to the `listener`. * - * @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both - * the `newCollection` and `oldCollection` as parameters. - * The `newCollection` object is the newly modified data obtained from the `obj` expression and the - * `oldCollection` object is a copy of the former collection data. + * @param {function(newCollection, oldCollection, scope)} listener a callback function that is + * fired with both the `newCollection` and `oldCollection` as parameters. + * The `newCollection` object is the newly modified data obtained from the `obj` expression + * and the `oldCollection` object is a copy of the former collection data. * The `scope` refers to the current scope. * - * @returns {function()} Returns a de-registration function for this listener. When the de-registration function - * is executed, the internal watch operation is terminated. + * @returns {function()} Returns a de-registration function for this listener. When the + * de-registration function is executed, the internal watch operation is terminated. */ $watchCollection: function(obj, listener) { var self = this; var oldValue; var newValue; @@ -20274,25 +21099,26 @@ * @name ng.$rootScope.Scope#$digest * @methodOf ng.$rootScope.Scope * @function * * @description - * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. - * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the - * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are - * firing. This means that it is possible to get into an infinite loop. This function will throw - * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and + * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change + * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} + * until no more listeners are firing. This means that it is possible to get into an infinite + * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of + * iterations exceeds 10. * * Usually, you don't call `$digest()` directly in * {@link ng.directive:ngController controllers} or in - * {@link ng.$compileProvider#directive directives}. - * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a - * {@link ng.$compileProvider#directive directives}), which will force a `$digest()`. + * {@link ng.$compileProvider#methods_directive directives}. + * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within + * a {@link ng.$compileProvider#methods_directive directives}), which will force a `$digest()`. * * If you want to be notified whenever `$digest()` is called, - * you can register a `watchExpression` function with {@link ng.$rootScope.Scope#$watch $watch()} - * with no `listener`. + * you can register a `watchExpression` function with + * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. * * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. * * # Example * <pre> @@ -20386,11 +21212,12 @@ } while ((current = next)); if(dirty && !(ttl--)) { clearPhase(); throw $rootScopeMinErr('infdig', - '{0} $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: {1}', + '{0} $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: {1}', TTL, toJson(watchLog)); } } while (dirty || asyncQueue.length); clearPhase(); @@ -20465,12 +21292,13 @@ * @name ng.$rootScope.Scope#$eval * @methodOf ng.$rootScope.Scope * @function * * @description - * Executes the `expression` on the current scope and returns the result. Any exceptions in the - * expression are propagated (uncaught). This is useful when evaluating Angular expressions. + * Executes the `expression` on the current scope and returns the result. Any exceptions in + * the expression are propagated (uncaught). This is useful when evaluating Angular + * expressions. * * # Example * <pre> var scope = ng.$rootScope.Scope(); scope.a = 1; @@ -20482,11 +21310,12 @@ * * @param {(string|function())=} expression An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. - * + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. * @returns {*} The result of evaluating the expression. */ $eval: function(expr, locals) { return $parse(expr)(this, locals); }, @@ -20498,31 +21327,34 @@ * @function * * @description * Executes the expression on the current scope at a later point in time. * - * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only + * that: * - * - it will execute after the function that scheduled the evaluation (preferably before DOM rendering). - * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after `expression` execution. + * - it will execute after the function that scheduled the evaluation (preferably before DOM + * rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. * * Any exceptions from the execution of the expression are forwarded to the * {@link ng.$exceptionHandler $exceptionHandler} service. * - * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle will be scheduled. - * However, it is encouraged to always call code that changes the model from within an `$apply` call. - * That includes code evaluated via `$evalAsync`. + * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle + * will be scheduled. However, it is encouraged to always call code that changes the model + * from within an `$apply` call. That includes code evaluated via `$evalAsync`. * * @param {(string|function())=} expression An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * */ $evalAsync: function(expr) { - // if we are outside of an $digest loop and this is the first time we are scheduling async task also schedule - // async auto-flush + // if we are outside of an $digest loop and this is the first time we are scheduling async + // task also schedule async auto-flush if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { $browser.defer(function() { if ($rootScope.$$asyncQueue.length) { $rootScope.$digest(); } @@ -20541,14 +21373,14 @@ * @name ng.$rootScope.Scope#$apply * @methodOf ng.$rootScope.Scope * @function * * @description - * `$apply()` is used to execute an expression in angular from outside of the angular framework. - * (For example from browser DOM events, setTimeout, XHR or third party libraries). - * Because we are calling into the angular framework we need to perform proper scope life cycle - * of {@link ng.$exceptionHandler exception handling}, + * `$apply()` is used to execute an expression in angular from outside of the angular + * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life + * cycle of {@link ng.$exceptionHandler exception handling}, * {@link ng.$rootScope.Scope#$digest executing watches}. * * ## Life cycle * * # Pseudo-Code of `$apply()` @@ -20569,12 +21401,12 @@ * * 1. The {@link guide/expression expression} is executed using the * {@link ng.$rootScope.Scope#$eval $eval()} method. * 2. Any exceptions from the execution of the expression are forwarded to the * {@link ng.$exceptionHandler $exceptionHandler} service. - * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression - * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the + * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. * * * @param {(string|function())=} exp An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. @@ -20604,22 +21436,24 @@ * @name ng.$rootScope.Scope#$on * @methodOf ng.$rootScope.Scope * @function * * @description - * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of - * event life cycle. + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for + * discussion of event life cycle. * * The event listener function format is: `function(event, args...)`. The `event` object * passed into the listener has the following attributes: * - * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or + * `$broadcast`-ed. * - `currentScope` - `{Scope}`: the current scope which is handling the event. * - `name` - `{string}`: name of the event. - * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event - * propagation (available only for events that were `$emit`-ed). - * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel + * further event propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag + * to true. * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. * * @param {string} name Event name to listen on. * @param {function(event, args...)} listener Function to call when the event is emitted. * @returns {function()} Returns a deregistration function for this listener. @@ -20646,13 +21480,14 @@ * @description * Dispatches an event `name` upwards through the scope hierarchy notifying the * registered {@link ng.$rootScope.Scope#$on} listeners. * * The event life cycle starts at the scope on which `$emit` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. - * Afterwards, the event traverses upwards toward the root scope and calls all registered - * listeners along the way. The event will stop propagating if one of the listeners cancels it. + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event traverses upwards toward the root scope and calls all + * registered listeners along the way. The event will stop propagating if one of the listeners + * cancels it. * * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed * onto the {@link ng.$exceptionHandler $exceptionHandler} service. * * @param {string} name Event name to emit. @@ -20714,13 +21549,13 @@ * @description * Dispatches an event `name` downwards to all child scopes (and their children) notifying the * registered {@link ng.$rootScope.Scope#$on} listeners. * * The event life cycle starts at the scope on which `$broadcast` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. - * Afterwards, the event propagates to all direct and indirect scopes of the current scope and - * calls all registered listeners along the way. The event cannot be canceled. + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event propagates to all direct and indirect scopes of the current + * scope and calls all registered listeners along the way. The event cannot be canceled. * * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed * onto the {@link ng.$exceptionHandler $exceptionHandler} service. * * @param {string} name Event name to broadcast. @@ -20826,11 +21661,11 @@ // http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962 // Prereq: s is a string. function escapeForRegexp(s) { return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). replace(/\x08/g, '\\x08'); -}; +} function adjustMatcher(matcher) { if (matcher === 'self') { return matcher; @@ -20890,25 +21725,25 @@ * * The default instance of `$sceDelegate` should work out of the box with little pain. While you * can override it completely to change the behavior of `$sce`, the common case would * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as - * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist + * templates. Refer {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist * $sceDelegateProvider.resourceUrlWhitelist} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} */ /** * @ngdoc object * @name ng.$sceDelegateProvider * @description * * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure * that the URLs used for sourcing Angular templates are safe. Refer {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and - * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * ng.$sceDelegateProvider#methods_resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and + * {@link ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} * * For the general details about this service in Angular, read the main page for {@link ng.$sce * Strict Contextual Escaping (SCE)}. * * **Example**: Consider the following case. <a name="example"></a> @@ -20950,12 +21785,12 @@ * * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value * provided. This must be an array or null. A snapshot of this array is used so further * changes to the array are ignored. * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items allowed in - * this array. + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. * * Note: **an empty whitelist array will block all URLs**! * * @return {Array} the currently set whitelist array. * @@ -20980,23 +21815,23 @@ * * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value * provided. This must be an array or null. A snapshot of this array is used so further * changes to the array are ignored. * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items allowed in - * this array. + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. * - * The typical usage for the blacklist is to **block [open redirects](http://cwe.mitre.org/data/definitions/601.html)** - * served by your domain as these would otherwise be trusted but actually return content from the redirected - * domain. + * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. * * Finally, **the blacklist overrides the whitelist** and has the final say. * * @return {Array} the currently set blacklist array. * - * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there is - * no blacklist.) + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) * * @description * Sets/Gets the blacklist of trusted resource URLs. */ @@ -21048,25 +21883,25 @@ } } return allowed; } - function generateHolderType(base) { + function generateHolderType(Base) { var holderType = function TrustedValueHolderType(trustedValue) { this.$$unwrapTrustedValue = function() { return trustedValue; }; }; - if (base) { - holderType.prototype = new base(); + if (Base) { + holderType.prototype = new Base(); } holderType.prototype.valueOf = function sceValueOf() { return this.$$unwrapTrustedValue(); - } + }; holderType.prototype.toString = function sceToString() { return this.$$unwrapTrustedValue().toString(); - } + }; return holderType; } var trustedValueHolderBase = generateHolderType(), byType = {}; @@ -21094,13 +21929,14 @@ * @param {*} value The value that that should be considered trusted/safe. * @returns {*} A value that can be used to stand in for the provided `value` in places * where Angular expects a $sce.trustAs() return value. */ function trustAs(type, trustedValue) { - var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); - if (!constructor) { - throw $sceMinErr('icontext', 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', + var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (!Constructor) { + throw $sceMinErr('icontext', + 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', type, trustedValue); } if (trustedValue === null || trustedValue === undefined || trustedValue === '') { return trustedValue; } @@ -21109,31 +21945,31 @@ if (typeof trustedValue !== 'string') { throw $sceMinErr('itype', 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', type); } - return new constructor(trustedValue); + return new Constructor(trustedValue); } /** * @ngdoc method * @name ng.$sceDelegate#valueOf * @methodOf ng.$sceDelegate * * @description - * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs + * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#methods_trustAs * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. + * ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}. * * If the passed parameter is not a value that had been returned by {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. + * ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}, returns it as-is. * - * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} + * @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`} * call or anything else. - * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns `value` - * unchanged. + * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs + * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns + * `value` unchanged. */ function valueOf(maybeTrusted) { if (maybeTrusted instanceof trustedValueHolderBase) { return maybeTrusted.$$unwrapTrustedValue(); } else { @@ -21145,18 +21981,18 @@ * @ngdoc method * @name ng.$sceDelegate#getTrusted * @methodOf ng.$sceDelegate * * @description - * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and returns the - * originally supplied value if the queried context type is a supertype of the created type. If - * this condition isn't satisfied, throws an exception. + * Takes the result of a {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`} call and + * returns the originally supplied value if the queried context type is a supertype of the + * created type. If this condition isn't satisfied, throws an exception. * * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} call. - * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs + * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#methods_trustAs + * `$sceDelegate.trustAs`} call. + * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. */ function getTrusted(type, maybeTrusted) { if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { return maybeTrusted; @@ -21171,11 +22007,12 @@ if (type === SCE_CONTEXTS.RESOURCE_URL) { if (isResourceUrlAllowedByPolicy(maybeTrusted)) { return maybeTrusted; } else { throw $sceMinErr('insecurl', - 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', maybeTrusted.toString()); + 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', + maybeTrusted.toString()); } } else if (type === SCE_CONTEXTS.HTML) { return htmlSanitizer(maybeTrusted); } throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); @@ -21198,10 +22035,12 @@ * - override the default implementation with a custom delegate * * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. */ +/* jshint maxlen: false*/ + /** * @ngdoc service * @name ng.$sce * @function * @@ -21254,24 +22093,24 @@ * for those values that you can easily tell are safe - because they were received from your server, * sanitized by your library, etc. You can organize your codebase to help with this - perhaps * allowing only the files in a specific directory to do this. Ensuring that the internal API * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. * - * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} (and shorthand - * methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to obtain values that will be - * accepted by SCE / privileged contexts. + * In the case of AngularJS' SCE service, one uses {@link ng.$sce#methods_trustAs $sce.trustAs} + * (and shorthand methods such as {@link ng.$sce#methods_trustAsHtml $sce.trustAsHtml}, etc.) to + * obtain values that will be accepted by SCE / privileged contexts. * * * ## How does it work? * - * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted + * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#methods_getTrusted * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link - * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the - * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. + * ng.$sce#methods_parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the + * {@link ng.$sce#methods_getTrusted $sce.getTrusted} behind the scenes on non-constant literals. * * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link - * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly + * ng.$sce#methods_parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly * simplified): * * <pre class="prettyprint"> * var ngBindHtmlDirective = ['$sce', function($sce) { * return function(scope, element, attr) { @@ -21286,14 +22125,14 @@ * * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as * `templateUrl`'s specified by {@link guide/directive directives}. * * 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 + * document. This is done by calling {@link ng.$sce#methods_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 - * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. + * protocols, you may either either {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist + * them} or {@link ng.$sce#methods_trustAsResourceUrl wrap it} into a trusted value. * * *Please note*: * The browser's * {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest * Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing (CORS)} @@ -21309,34 +22148,35 @@ * If your expressions are constant literals, they're automatically trusted and you don't need to * call `$sce.trustAs` on them. (e.g. * `<div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div>`) just works. * * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them - * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. + * through {@link ng.$sce#methods_getTrusted $sce.getTrusted}. SCE doesn't play a role here. * * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load * templates in `ng-include` from your application's domain without having to even know about SCE. * It blocks loading templates from other domains or loading templates over http from an https * served document. You can change these by setting your own custom {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. + * ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelists} and {@link + * ng.$sceDelegateProvider#methods_resourceUrlBlacklist blacklists} for matching such URLs. * * This significantly reduces the overhead. It is far easier to pay the small overhead and have an * application that's secure and can be audited to verify that with much more ease than bolting * security onto an application later. * - * ## What trusted context types are supported?<a name="contexts"></a> + * <a name="contexts"></a> + * ## What trusted context types are supported? * * | Context | Notes | * |---------------------|----------------| * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. | * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. | * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | * - * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> + * ## Format of items in {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#methods_resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> * * Each element in these arrays must be one of the following: * * - **'self'** * - The special **string**, `'self'`, can be used to match against all URLs of the **same @@ -21377,69 +22217,72 @@ * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). * Javascript lacks a similar built in function for escaping. Take a look at Google * Closure library's [goog.string.regExpEscape(s)]( * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). * - * Refer {@link ng.$sceDelegateProvider#example $sceDelegateProvider} for an example. + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. * * ## Show me an example using SCE. * * @example - <example module="mySceApp"> - <file name="index.html"> - <div ng-controller="myAppController as myCtrl"> - <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> - <b>User comments</b><br> - By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when $sanitize is available. If $sanitize isn't available, this results in an error instead of an exploit. - <div class="well"> - <div ng-repeat="userComment in myCtrl.userComments"> - <b>{{userComment.name}}</b>: - <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> - <br> - </div> +<example module="mySceApp"> +<file name="index.html"> + <div ng-controller="myAppController as myCtrl"> + <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> + <b>User comments</b><br> + By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when + $sanitize is available. If $sanitize isn't available, this results in an error instead of an + exploit. + <div class="well"> + <div ng-repeat="userComment in myCtrl.userComments"> + <b>{{userComment.name}}</b>: + <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> + <br> </div> </div> - </file> + </div> +</file> - <file name="script.js"> - var mySceApp = angular.module('mySceApp', ['ngSanitize']); +<file name="script.js"> + var mySceApp = angular.module('mySceApp', ['ngSanitize']); - mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) { - var self = this; - $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { - self.userComments = userComments; - }); - self.explicitlyTrustedHtml = $sce.trustAsHtml( - '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' + - 'sanitization.&quot;">Hover over this text.</span>'); + mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) { + var self = this; + $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { + self.userComments = userComments; }); - </file> + self.explicitlyTrustedHtml = $sce.trustAsHtml( + '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' + + 'sanitization.&quot;">Hover over this text.</span>'); + }); +</file> - <file name="test_data.json"> - [ - { "name": "Alice", - "htmlComment": "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" - }, - { "name": "Bob", - "htmlComment": "<i>Yes!</i> Am I the only other one?" - } - ] - </file> +<file name="test_data.json"> +[ + { "name": "Alice", + "htmlComment": + "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" + }, + { "name": "Bob", + "htmlComment": "<i>Yes!</i> Am I the only other one?" + } +] +</file> - <file name="scenario.js"> - describe('SCE doc demo', function() { - it('should sanitize untrusted values', function() { - expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>'); - }); - it('should NOT sanitize explicitly trusted values', function() { - expect(element('#explicitlyTrustedHtml').html()).toBe( - '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' + - 'sanitization.&quot;">Hover over this text.</span>'); - }); +<file name="scenario.js"> + describe('SCE doc demo', function() { + it('should sanitize untrusted values', function() { + expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>'); }); - </file> - </example> + it('should NOT sanitize explicitly trusted values', function() { + expect(element('#explicitlyTrustedHtml').html()).toBe( + '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' + + 'sanitization.&quot;">Hover over this text.</span>'); + }); + }); +</file> +</example> * * * * ## Can I disable SCE completely? * @@ -21458,10 +22301,11 @@ * $sceProvider.enabled(false); * }); * </pre> * */ +/* jshint maxlen: 100 */ function $SceProvider() { var enabled = true; /** @@ -21503,17 +22347,17 @@ * * - getTrusted(contextEnum, value) * This function should return the a value that is safe to use in the context specified by * contextEnum or throw and exception otherwise. * - * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be opaque - * or wrapped in some holder object. That happens to be an implementation detail. For instance, - * an implementation could maintain a registry of all trusted objects by context. In such a case, - * trustAs() would return the same object that was passed in. getTrusted() would return the same - * object passed in if it was found in the registry under a compatible context or throw an - * exception otherwise. An implementation might only wrap values some of the time based on - * some criteria. getTrusted() might return a value and not throw an exception for special + * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be + * opaque or wrapped in some holder object. That happens to be an implementation detail. For + * instance, an implementation could maintain a registry of all trusted objects by context. In + * such a case, trustAs() would return the same object that was passed in. getTrusted() would + * return the same object passed in if it was found in the registry under a compatible context or + * throw an exception otherwise. An implementation might only wrap values some of the time based + * on some criteria. getTrusted() might return a value and not throw an exception for special * constants or objects even if not wrapped. All such implementations fulfill this contract. * * * A note on the inheritance model for SCE contexts * ------------------------------------------------ @@ -21564,23 +22408,23 @@ sce.trustAs = $sceDelegate.trustAs; sce.getTrusted = $sceDelegate.getTrusted; sce.valueOf = $sceDelegate.valueOf; if (!enabled) { - sce.trustAs = sce.getTrusted = function(type, value) { return value; }, - sce.valueOf = identity + sce.trustAs = sce.getTrusted = function(type, value) { return value; }; + sce.valueOf = identity; } /** * @ngdoc method * @name ng.$sce#parse * @methodOf ng.$sce * * @description * Converts Angular {@link guide/expression expression} into a function. This is like {@link * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it - * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, + * wraps the expression in a call to {@link ng.$sce#methods_getTrusted $sce.getTrusted(*type*, * *result*)} * * @param {string} type The kind of SCE context in which this result will be used. * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: @@ -21595,25 +22439,26 @@ if (parsed.literal && parsed.constant) { return parsed; } else { return function sceParseAsTrusted(self, locals) { return sce.getTrusted(type, parsed(self, locals)); - } + }; } }; /** * @ngdoc method * @name ng.$sce#trustAs * @methodOf ng.$sce * * @description - * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns an object - * that is trusted by angular for use in specified strict contextual escaping contexts (such as - * ng-html-bind-unsafe, ng-include, any src attribute interpolation, any dom event binding - * attribute interpolation such as for onclick, etc.) that uses the provided value. See * - * {@link ng.$sce $sce} for enabling strict contextual escaping. + * Delegates to {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}. As such, + * returns an objectthat is trusted by angular for use in specified strict contextual + * escaping contexts (such as ng-html-bind-unsafe, ng-include, any src attribute + * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) + * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual + * escaping. * * @param {string} type The kind of context in which this value is safe for use. e.g. url, * resource_url, html, js and css. * @param {*} value The value that that should be considered trusted/safe. * @returns {*} A value that can be used to stand in for the provided `value` in places @@ -21624,88 +22469,95 @@ * @ngdoc method * @name ng.$sce#trustAsHtml * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.trustAsHtml(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} + * Shorthand method. `$sce.trustAsHtml(value)` → + * {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.HTML, value)`} * * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml + * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedHtml * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name ng.$sce#trustAsUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.trustAsUrl(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} + * Shorthand method. `$sce.trustAsUrl(value)` → + * {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.URL, value)`} * * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl + * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedUrl * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name ng.$sce#trustAsResourceUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.trustAsResourceUrl(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} + * Shorthand method. `$sce.trustAsResourceUrl(value)` → + * {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} * * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl + * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedResourceUrl * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the return - * value of {@link ng.$sce#trustAs $sce.trustAs}.) + * value of {@link ng.$sce#methods_trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name ng.$sce#trustAsJs * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.trustAsJs(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} + * Shorthand method. `$sce.trustAsJs(value)` → + * {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs($sce.JS, value)`} * * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs + * @returns {*} An object that can be passed to {@link ng.$sce#methods_getTrustedJs * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * return value of {@link ng.$sce#methods_trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name ng.$sce#getTrusted * @methodOf ng.$sce * * @description - * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, takes - * the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the originally supplied - * value if the queried context type is a supertype of the created type. If this condition - * isn't satisfied, throws an exception. + * Delegates to {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted`}. As such, + * takes the result of a {@link ng.$sce#methods_trustAs `$sce.trustAs`}() call and returns the + * originally supplied value if the queried context type is a supertype of the created type. + * If this condition isn't satisfied, throws an exception. * * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} call. - * @returns {*} The value the was originally provided to {@link ng.$sce#trustAs `$sce.trustAs`} if - * valid in this context. Otherwise, throws an exception. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#methods_trustAs `$sce.trustAs`} + * call. + * @returns {*} The value the was originally provided to + * {@link ng.$sce#methods_trustAs `$sce.trustAs`} if valid in this context. + * Otherwise, throws an exception. */ /** * @ngdoc method * @name ng.$sce#getTrustedHtml * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.getTrustedHtml(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} + * Shorthand method. `$sce.getTrustedHtml(value)` → + * {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` */ @@ -21713,11 +22565,12 @@ * @ngdoc method * @name ng.$sce#getTrustedCss * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.getTrustedCss(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} + * Shorthand method. `$sce.getTrustedCss(value)` → + * {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` */ @@ -21725,11 +22578,12 @@ * @ngdoc method * @name ng.$sce#getTrustedUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.getTrustedUrl(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} + * Shorthand method. `$sce.getTrustedUrl(value)` → + * {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` */ @@ -21737,11 +22591,12 @@ * @ngdoc method * @name ng.$sce#getTrustedResourceUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.getTrustedResourceUrl(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} + * Shorthand method. `$sce.getTrustedResourceUrl(value)` → + * {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} * * @param {*} value The value to pass to `$sceDelegate.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` */ @@ -21749,11 +22604,12 @@ * @ngdoc method * @name ng.$sce#getTrustedJs * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.getTrustedJs(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} + * Shorthand method. `$sce.getTrustedJs(value)` → + * {@link ng.$sceDelegate#methods_getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` */ @@ -21761,11 +22617,12 @@ * @ngdoc method * @name ng.$sce#parseAsHtml * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.parseAsHtml(expression string)` → {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`} + * Shorthand method. `$sce.parseAsHtml(expression string)` → + * {@link ng.$sce#methods_parse `$sce.parseAs($sce.HTML, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings @@ -21778,11 +22635,12 @@ * @ngdoc method * @name ng.$sce#parseAsCss * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.parseAsCss(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`} + * Shorthand method. `$sce.parseAsCss(value)` → + * {@link ng.$sce#methods_parse `$sce.parseAs($sce.CSS, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings @@ -21795,11 +22653,12 @@ * @ngdoc method * @name ng.$sce#parseAsUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.parseAsUrl(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`} + * Shorthand method. `$sce.parseAsUrl(value)` → + * {@link ng.$sce#methods_parse `$sce.parseAs($sce.URL, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings @@ -21812,11 +22671,12 @@ * @ngdoc method * @name ng.$sce#parseAsResourceUrl * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.parseAsResourceUrl(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`} + * Shorthand method. `$sce.parseAsResourceUrl(value)` → + * {@link ng.$sce#methods_parse `$sce.parseAs($sce.RESOURCE_URL, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings @@ -21829,11 +22689,12 @@ * @ngdoc method * @name ng.$sce#parseAsJs * @methodOf ng.$sce * * @description - * Shorthand method. `$sce.parseAsJs(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`} + * Shorthand method. `$sce.parseAsJs(value)` → + * {@link ng.$sce#methods_parse `$sce.parseAs($sce.JS, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings @@ -21849,17 +22710,17 @@ forEach(SCE_CONTEXTS, function (enumValue, name) { var lName = lowercase(name); sce[camelCase("parse_as_" + lName)] = function (expr) { return parse(enumValue, expr); - } + }; sce[camelCase("get_trusted_" + lName)] = function (value) { return getTrusted(enumValue, value); - } + }; sce[camelCase("trust_as_" + lName)] = function (value) { return trustAs(enumValue, value); - } + }; }); return sce; }]; } @@ -21880,11 +22741,12 @@ * This is very simple implementation of testing browser's features. */ function $SnifferProvider() { this.$get = ['$window', '$document', function($window, $document) { var eventSupport = {}, - android = int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), + android = + int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), boxee = /Boxee/i.test(($window.navigator || {}).userAgent), document = $document[0] || {}, vendorPrefix, vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, bodyStyle = document.body && document.body.style, @@ -21921,11 +22783,14 @@ // http://code.google.com/p/android/issues/detail?id=17471 // https://github.com/angular/angular.js/issues/904 // older webit browser (533.9) on Boxee box has exactly the same problem as Android has // so let's not use the history API also + // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined + // jshint -W018 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), + // jshint +W018 hashchange: 'onhashchange' in $window && // IE8 compatible mode lies (!document.documentMode || document.documentMode > 7), hasEvent: function(event) { // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have @@ -21938,14 +22803,15 @@ eventSupport[event] = 'on' + event in divElm; } return eventSupport[event]; }, - csp: document.securityPolicy ? document.securityPolicy.isActive : false, + csp: csp(), vendorPrefix: vendorPrefix, transitions : transitions, - animations : animations + animations : animations, + msie : msie }; }]; } function $TimeoutProvider() { @@ -21973,11 +22839,11 @@ * synchronously flush the queue of deferred functions. * * @param {function()} fn A function, whose execution should be delayed. * @param {number=} [delay=0] Delay in milliseconds. * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block. * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this * promise will be resolved with is the return value of the `fn` function. * * @example <doc:example module="time"> @@ -22200,11 +23066,12 @@ host: urlParsingNode.host, search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', hostname: urlParsingNode.hostname, port: urlParsingNode.port, - pathname: urlParsingNode.pathname && urlParsingNode.pathname.charAt(0) === '/' ? urlParsingNode.pathname : '/' + urlParsingNode.pathname + pathname: urlParsingNode.pathname && urlParsingNode.pathname.charAt(0) === '/' ? + urlParsingNode.pathname : '/' + urlParsingNode.pathname }; } /** @@ -22265,13 +23132,13 @@ /** * @ngdoc object * @name ng.$filterProvider * @description * - * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To - * achieve this a filter definition consists of a factory function which is annotated with dependencies and is - * responsible for creating a filter function. + * Filters are just functions which transform input to an output. However filters need to be + * Dependency Injected. To achieve this a filter definition consists of a factory function which is + * annotated with dependencies and is responsible for creating a filter function. * * <pre> * // Filter registration * function MyModule($provide, $filterProvider) { * // create a service to demonstrate injection (not always needed) @@ -22290,11 +23157,13 @@ * }; * }); * } * </pre> * - * The filter function is registered with the `$injector` under the filter name suffix with `Filter`. + * The filter function is registered with the `$injector` under the filter name suffix with + * `Filter`. + * * <pre> * it('should be the same instance', inject( * function($filterProvider) { * $filterProvider.register('reverse', function(){ * return ...; @@ -22305,12 +23174,11 @@ * }); * </pre> * * * For more information about how angular filters work, and how to create your own filters, see - * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer - * Guide. + * {@link guide/filter Filters} in the Angular Developer Guide. */ /** * @ngdoc method * @name ng.$filterProvider#register * @methodOf ng.$filterProvider @@ -22367,10 +23235,22 @@ return $injector.get(name + suffix); }; }]; //////////////////////////////////////// + + /* global + currencyFilter: false, + dateFilter: false, + filterFilter: false, + jsonFilter: false, + limitToFilter: false, + lowercaseFilter: false, + numberFilter: false, + orderByFilter: false, + uppercaseFilter: false, + */ register('currency', currencyFilter); register('date', dateFilter); register('filter', filterFilter); register('json', jsonFilter); @@ -22387,13 +23267,10 @@ * @function * * @description * Selects a subset of items from `array` and returns it as a new array. * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * * @param {Array} array The source array. * @param {string|Object|function()} expression The predicate to be used for selecting items from * `array`. * * Can be one of: @@ -22484,51 +23361,51 @@ }); </doc:scenario> </doc:example> */ function filterFilter() { - return function(array, expression, comperator) { + return function(array, expression, comparator) { if (!isArray(array)) return array; - var predicates = []; + + var comparatorType = typeof(comparator), + predicates = []; + predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { if(!predicates[j](value)) { return false; } } return true; }; - switch(typeof comperator) { - case "function": - break; - case "boolean": - if(comperator == true) { - comperator = function(obj, text) { - return angular.equals(obj, text); - } - break; - } - default: - comperator = function(obj, text) { + + if (comparatorType !== 'function') { + if (comparatorType === 'boolean' && comparator) { + comparator = function(obj, text) { + return angular.equals(obj, text); + }; + } else { + comparator = function(obj, text) { text = (''+text).toLowerCase(); return (''+obj).toLowerCase().indexOf(text) > -1; }; + } } + var search = function(obj, text){ if (typeof text == 'string' && text.charAt(0) === '!') { return !search(obj, text.substr(1)); } switch (typeof obj) { case "boolean": case "number": case "string": - return comperator(obj, text); + return comparator(obj, text); case "object": switch (typeof text) { case "object": - return comperator(obj, text); - break; + return comparator(obj, text); default: for ( var objKey in obj) { if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { return true; } @@ -22549,17 +23426,20 @@ }; switch (typeof expression) { case "boolean": case "number": case "string": + // Set up expression object and fall through expression = {$:expression}; + // jshint -W086 case "object": + // jshint +W086 for (var key in expression) { if (key == '$') { (function() { if (!expression[key]) return; - var path = key + var path = key; predicates.push(function(value) { return search(value, expression[path]); }); })(); } else { @@ -22585,11 +23465,11 @@ if (predicates.check(value)) { filtered.push(value); } } return filtered; - } + }; } /** * @ngdoc filter * @name ng.filter:currency @@ -22732,17 +23612,17 @@ number = Math.round(number * pow) / pow; var fraction = ('' + number).split(DECIMAL_SEP); var whole = fraction[0]; fraction = fraction[1] || ''; - var pos = 0, + var i, pos = 0, lgroup = pattern.lgSize, group = pattern.gSize; if (whole.length >= (lgroup + group)) { pos = whole.length - lgroup; - for (var i = 0; i < pos; i++) { + for (i = 0; i < pos; i++) { if ((pos - i)%group === 0 && i !== 0) { formatedText += groupSep; } formatedText += whole.charAt(i); } @@ -22952,11 +23832,11 @@ tzHour = int(match[9] + match[10]); tzMin = int(match[9] + match[11]); } dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); var h = int(match[4]||0) - tzHour; - var m = int(match[5]||0) - tzMin + var m = int(match[5]||0) - tzMin; var s = int(match[6]||0); var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); timeSetter.call(date, h, m, s, ms); return date; } @@ -23073,13 +23953,10 @@ * @description * Creates a new array or string containing only a specified number of elements. The elements * are taken from either the beginning or the end of the source array or string, as specified by * the value and sign (positive or negative) of `limit`. * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * * @param {Array|string} input Source array or string to be limited. * @param {string|number} limit The length of the returned array or string. If the `limit` number * is positive, `limit` number of items from the beginning of the source array/string are copied. * If the number is negative, `limit` number of items from the end of the source array/string * are copied. The `limit` will be trimmed if it exceeds `array.length` @@ -23163,24 +24040,21 @@ for (; i<n; i++) { out.push(input[i]); } return out; - } + }; } /** * @ngdoc function * @name ng.filter:orderBy * @function * * @description * Orders a specified `array` by the `expression` predicate. * - * Note: this function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * * @param {Array} array The array to sort. * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be * used by the comparator to determine the order of elements. * * Can be one of: @@ -23301,18 +24175,18 @@ return v1 < v2 ? -1 : 1; } else { return t1 < t2 ? -1 : 1; } } - } + }; } function ngDirective(directive) { if (isFunction(directive)) { directive = { link: directive - } + }; } directive.restrict = directive.restrict || 'AC'; return valueFn(directive); } @@ -23353,11 +24227,11 @@ // if we have no href url, then don't navigate anywhere. if (!element.attr('href')) { event.preventDefault(); } }); - } + }; } }); /** * @ngdoc directive @@ -23706,10 +24580,11 @@ } }; }; }); +/* global -nullFormCtrl */ var nullFormCtrl = { $addControl: noop, $removeControl: noop, $setValidity: noop, $setDirty: noop, @@ -23911,11 +24786,11 @@ * @description * Nestable alias of {@link ng.directive:form `form`} directive. HTML * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a * sub-group of controls needs to be determined. * - * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into * related scope, under this name. * */ /** @@ -23970,11 +24845,11 @@ * or {@link ng.directive:ngClick ngClick} directives. * This is because of the following form submission rules in the HTML specification: * * - If a form has only one input field then hitting enter in this field triggers form submit * (`ngSubmit`) - * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter + * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter * doesn't trigger submit * - if a form has one or more input fields and one or more buttons or input[type=submit] then * hitting enter in any of the input fields will trigger the click handler on the *first* button or * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) * @@ -24071,10 +24946,18 @@ }; var formDirective = formDirectiveFactory(); var ngFormDirective = formDirectiveFactory(true); +/* global + + -VALID_CLASS, + -INVALID_CLASS, + -PRISTINE_CLASS, + -DIRTY_CLASS +*/ + var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; var inputType = { @@ -24099,12 +24982,11 @@ * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the - * input. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. * * @example <doc:example> <doc:source> <script> @@ -24607,12 +25489,12 @@ ctrl.$formatters.push(function(value) { return ctrl.$isEmpty(value) ? '' : '' + value; }); if (attr.min) { - var min = parseFloat(attr.min); var minValidator = function(value) { + var min = parseFloat(attr.min); if (!ctrl.$isEmpty(value) && value < min) { ctrl.$setValidity('min', false); return undefined; } else { ctrl.$setValidity('min', true); @@ -24623,12 +25505,12 @@ ctrl.$parsers.push(minValidator); ctrl.$formatters.push(minValidator); } if (attr.max) { - var max = parseFloat(attr.max); var maxValidator = function(value) { + var max = parseFloat(attr.max); if (!ctrl.$isEmpty(value) && value > max) { ctrl.$setValidity('max', false); return undefined; } else { ctrl.$setValidity('max', true); @@ -24889,11 +25771,11 @@ * @property {*} $modelValue The value in the model, that the control is bound to. * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. Each function is called, in turn, passing the value through to the next. Used to sanitize / convert the value as well as validation. For validation, the parsers should update the validity state using - {@link ng.directive:ngModel.NgModelController#$setValidity $setValidity()}, + {@link ng.directive:ngModel.NgModelController#methods_$setValidity $setValidity()}, and return `undefined` for invalid values. * * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever the model value changes. Each function is called, in turn, passing the value through to the @@ -25002,34 +25884,35 @@ * ## Isolated Scope Pitfall * * Note that if you have a directive with an isolated scope, you cannot require `ngModel` * since the model value will be looked up on the isolated scope rather than the outer scope. * When the directive updates the model value, calling `ngModel.$setViewValue()` the property - * on the outer scope will not be updated. + * on the outer scope will not be updated. However you can get around this by using $parent. * - * Here is an example of this situation. You'll notice that even though both 'input' and 'div' - * seem to be attached to the same model, they are not kept in synch. + * Here is an example of this situation. You'll notice that the first div is not updating the input. + * However the second div can update the input properly. * * <example module="badIsolatedDirective"> <file name="script.js"> - angular.module('badIsolatedDirective', []).directive('bad', function() { - return { - require: 'ngModel', - scope: { }, - template: '<input ng-model="innerModel">', - link: function(scope, element, attrs, ngModel) { - scope.$watch('innerModel', function(value) { - console.log(value); - ngModel.$setViewValue(value); - }); - } - }; + angular.module('badIsolatedDirective', []).directive('isolate', function() { + return { + require: 'ngModel', + scope: { }, + template: '<input ng-model="innerModel">', + link: function(scope, element, attrs, ngModel) { + scope.$watch('innerModel', function(value) { + console.log(value); + ngModel.$setViewValue(value); + }); + } + }; }); </file> <file name="index.html"> - <input ng-model="someModel"> - <div bad ng-model="someModel"></div> + <input ng-model="someModel"/> + <div isolate ng-model="someModel"></div> + <div isolate ng-model="$parent.someModel"></div> </file> * </example> * * */ @@ -25070,14 +25953,14 @@ * @name { ng.directive:ngModel.NgModelController#$isEmpty * @methodOf ng.directive:ngModel.NgModelController * * @description * This is called when we need to determine if the value of the input is empty. - * + * * For instance, the required directive does this to work out if the input has data or not. * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. - * + * * You can override this for input directives whose concept of being empty is different to the * default. The `checkboxInputType` directive does this because in its case a value of `false` * implies empty. */ this.$isEmpty = function(value) { @@ -25118,11 +26001,14 @@ * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` * class and can be bound to as `{{someForm.someControl.$error.myError}}` . * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). */ this.$setValidity = function(validationErrorKey, isValid) { + // Purposeful use of ! here to cast isValid to boolean in case it is undefined + // jshint -W018 if ($error[validationErrorKey] === !isValid) return; + // jshint +W018 if (isValid) { if ($error[validationErrorKey]) invalidCount--; if (!invalidCount) { toggleValidCss(true); @@ -25198,11 +26084,11 @@ try { listener(); } catch(e) { $exceptionHandler(e); } - }) + }); } }; // model -> value var ctrl = this; @@ -25281,11 +26167,11 @@ var modelCtrl = ctrls[0], formCtrl = ctrls[1] || nullFormCtrl; formCtrl.$addControl(modelCtrl); - element.on('$destroy', function() { + scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } }; }; @@ -25547,12 +26433,12 @@ * expression changes. * * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like * `{{ expression }}` which is similar but less verbose. * - * It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily - * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an + * It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily + * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an * element attribute, it makes the bindings invisible to the user while the page is loading. * * An alternative solution to this problem would be using the * {@link ng.directive:ngCloak ngCloak} directive. * @@ -25584,10 +26470,13 @@ </doc:example> */ var ngBindDirective = ngDirective(function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBind); scope.$watch(attr.ngBind, function ngBindWatchAction(value) { + // We are purposefully using == here rather than === because we want to + // catch when value is "null or undefined" + // jshint -W041 element.text(value == undefined ? '' : value); }); }); @@ -25645,11 +26534,11 @@ var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); element.addClass('ng-binding').data('$binding', interpolateFn); attr.$observe('ngBindTemplate', function(value) { element.text(value); }); - } + }; }]; /** * @ngdoc directive @@ -25659,18 +26548,41 @@ * Creates a binding that will innerHTML the result of evaluating the `expression` into the current * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in * core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to - * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example + * an explicitly trusted value via {@link ng.$sce#methods_trustAsHtml $sce.trustAsHtml}. See the example * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. * * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you * will have an exception (instead of an exploit.) * * @element ANY * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. + * + * @example + * Try it here: enter text in text box and watch the greeting change. + <doc:example module="ngBindHtmlExample" deps="angular-sanitize.js" > + <doc:source> + <script> + angular.module('ngBindHtmlExample', ['ngSanitize']) + + .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) { + $scope.myHTML = 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'; + }]); + </script> + <div ng-controller="ngBindHtmlCtrl"> + <p ng-bind-html="myHTML"></p> + </div> + </doc:source> + <doc:scenario> + it('should check ng-bind-html', function() { + expect(using('.doc-example-live').binding('myHTML')). + toBe('I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'); + }); + </doc:scenario> + </doc:example> */ var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { return function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBindHtml); @@ -25687,21 +26599,22 @@ name = 'ngClass' + name; return function() { return { restrict: 'AC', link: function(scope, element, attr) { - var oldVal = undefined; + var oldVal; scope.$watch(attr[name], ngClassWatchAction, true); attr.$observe('class', function(value) { ngClassWatchAction(scope.$eval(attr[name])); }); if (name !== 'ngClass') { scope.$watch('$index', function($index, old$index) { + // jshint bitwise: false var mod = $index & 1; if (mod !== old$index & 1) { if (mod === selector) { addClass(scope.$eval(attr[name])); } else { @@ -25744,11 +26657,11 @@ }); return classes.join(' '); } return classVal; - }; + } } }; }; } @@ -25775,11 +26688,11 @@ * of the evaluation can be a string representing space delimited class * names, an array, or a map of class names to boolean values. In the case of a map, the * names of the properties whose values are truthy will be added as css classes to the * element. * - * @example Example that demostrates basic bindings via ngClass directive. + * @example Example that demonstrates basic bindings via ngClass directive. <example> <file name="index.html"> <p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p> <input type="checkbox" ng-model="bold"> bold <input type="checkbox" ng-model="strike"> strike @@ -25840,30 +26753,22 @@ <example animations="true"> <file name="index.html"> <input type="button" value="set" ng-click="myVar='my-class'"> <input type="button" value="clear" ng-click="myVar=''"> <br> - <span ng-class="myVar">Sample Text</span> + <span class="base-class" ng-class="myVar">Sample Text</span> </file> <file name="style.css"> - .my-class-add, .my-class-remove { + .base-class { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } - .my-class, - .my-class-add.my-class-add-active { + .base-class.my-class { color: red; font-size:3em; } - - .my-class-remove.my-class-remove-active { - font-size:1.0em; - color:black; - } </file> <file name="scenario.js"> it('should check ng-class', function() { expect(element('.doc-example-live span').prop('className')).not(). toMatch(/my-class/); @@ -25882,14 +26787,14 @@ </example> ## ngClass and pre-existing CSS3 Transitions/Animations The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. - Therefore, if any CSS3 Transition/Animation styles (outside of ngAnimate) are set on the element, then, if a ngClass animation - is triggered, the ngClass animation will be skipped so that ngAnimate can allow for the pre-existing transition or animation to - take over. This restriction allows for ngClass to still work with standard CSS3 Transitions/Animations that are defined - outside of ngAnimate. + Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder + any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure + to view the step by step details of {@link ngAnimate.$animate#methods_addclass $animate.addClass} and + {@link ngAnimate.$animate#methods_removeclass $animate.removeClass}. */ var ngClassDirective = classDirective('', true); /** * @ngdoc directive @@ -26000,11 +26905,12 @@ * The directive can be applied to the `<body>` element, but the preferred usage is to apply * multiple `ngCloak` directives to small portions of the page to permit progressive rendering * of the browser view. * * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and - * `angular.min.js`: + * `angular.min.js`. + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). * * <pre> * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { * display: none !important; * } @@ -26063,11 +26969,14 @@ * are accessed through bindings. * * View — The template (HTML with data bindings) that is rendered into the View. * * Controller — The `ngController` directive specifies a Controller class; the class contains business * logic behind the application to decorate the scope with functions and values * - * Note that an alternative way to define controllers is via the {@link ngRoute.$route $route} service. + * Note that you can also attach controllers to the DOM by declaring it in a route definition + * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller + * again using `ng-controller` in the template itself. This will cause the controller to be attached + * and executed twice. * * @element ANY * @scope * @param {expression} ngController Name of a globally accessible constructor function or an * {@link guide/expression expression} that on the current scope evaluates to a @@ -26218,29 +27127,34 @@ }]; /** * @ngdoc directive * @name ng.directive:ngCsp - * @priority 1000 * * @element html * @description * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. - * + * * This is necessary when developing things like Google Chrome Extensions. - * + * * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). * For us to be compatible, we just need to implement the "getterFn" in $parse without violating * any of these restrictions. - * + * * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will * be raised. - * + * + * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically + * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). + * To make those directives work in CSP mode, include the `angular-csp.css` manually. + * * In order to use this feature put the `ngCsp` directive on the root element of the application. - * + * + * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* + * * @example * This example shows how to apply the `ngCsp` directive to the `html` tag. <pre> <!doctype html> <html ng-app ng-csp> @@ -26248,18 +27162,13 @@ ... </html> </pre> */ -var ngCspDirective = ['$sniffer', function($sniffer) { - return { - priority: 1000, - compile: function() { - $sniffer.csp = true; - } - }; -}]; +// ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap +// the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute +// anywhere in the current doc /** * @ngdoc directive * @name ng.directive:ngClick * @@ -26298,17 +27207,21 @@ forEach( 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), function(name) { var directiveName = directiveNormalize('ng-' + name); ngEventDirectives[directiveName] = ['$parse', function($parse) { - return function(scope, element, attr) { - var fn = $parse(attr[directiveName]); - element.on(lowercase(name), function(event) { - scope.$apply(function() { - fn(scope, {$event:event}); - }); - }); + return { + compile: function($element, attr) { + var fn = $parse(attr[directiveName]); + return function(scope, element, attr) { + element.on(lowercase(name), function(event) { + scope.$apply(function() { + fn(scope, {$event:event}); + }); + }); + }; + } }; }]; } ); @@ -26638,11 +27551,11 @@ * @element ANY * @scope * @priority 600 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then * the element is removed from the DOM tree. If it is truthy a copy of the compiled - * eleent is added to the DOM tree. + * element is added to the DOM tree. * * @example <example animations="true"> <file name="index.html"> Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> @@ -26656,24 +27569,25 @@ background:white; border:1px solid black; padding:10px; } + /&#42; + The transition styles can also be placed on the CSS base class above + &#42;/ .animate-if.ng-enter, .animate-if.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } .animate-if.ng-enter, .animate-if.ng-leave.ng-leave-active { opacity:0; } - .animate-if.ng-enter.ng-enter-active, - .animate-if.ng-leave { + .animate-if.ng-leave, + .animate-if.ng-enter.ng-enter-active { opacity:1; } </file> </example> */ @@ -26681,33 +27595,43 @@ return { transclude: 'element', priority: 600, terminal: true, restrict: 'A', + $$tlb: true, compile: function (element, attr, transclude) { return function ($scope, $element, $attr) { - var childElement, childScope; + var block, childScope; $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { - if (childElement) { - $animate.leave(childElement); - childElement = undefined; - } - if (childScope) { - childScope.$destroy(); - childScope = undefined; - } + if (toBoolean(value)) { + childScope = $scope.$new(); transclude(childScope, function (clone) { - childElement = clone; + block = { + startNode: clone[0], + endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ') + }; $animate.enter(clone, $element.parent(), $element); }); + + } else { + + if (childScope) { + childScope.$destroy(); + childScope = null; + } + + if (block) { + $animate.leave(getBlockElements(block)); + block = null; + } } }); - } + }; } - } + }; }]; /** * @ngdoc directive * @name ng.directive:ngInclude @@ -26715,14 +27639,14 @@ * * @description * Fetches, compiles and includes an external HTML fragment. * * By default, the template URL is restricted to the same domain and protocol as the - * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * application document. This is done by calling {@link ng.$sce#methods_getTrustedResourceUrl * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols - * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or - * {@link ng.$sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link + * you may either {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist them} or + * {@link ng.$sce#methods_trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link * ng.$sce Strict Contextual Escaping}. * * In addition, the browser's * {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest * Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing @@ -26757,12 +27681,12 @@ <select ng-model="template" ng-options="t.name for t in templates"> <option value="">(blank)</option> </select> url of the template: <tt>{{template.url}}</tt> <hr/> - <div class="example-animate-container"> - <div class="include-example" ng-include="template.url"></div> + <div class="slide-animate-container"> + <div class="slide-animate" ng-include="template.url"></div> </div> </div> </file> <file name="script.js"> function Ctrl($scope) { @@ -26777,26 +27701,24 @@ </file> <file name="template2.html"> Content of template2.html </file> <file name="animations.css"> - .example-animate-container { + .slide-animate-container { position:relative; background:white; border:1px solid black; height:40px; overflow:hidden; } - .example-animate-container > div { + .slide-animate { padding:10px; } - .include-example.ng-enter, .include-example.ng-leave { + .slide-animate.ng-enter, .slide-animate.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; position:absolute; top:0; left:0; @@ -26804,21 +27726,21 @@ bottom:0; display:block; padding:10px; } - .include-example.ng-enter { + .slide-animate.ng-enter { top:-50px; } - .include-example.ng-enter.ng-enter-active { + .slide-animate.ng-enter.ng-enter-active { top:0; } - .include-example.ng-leave { + .slide-animate.ng-leave { top:0; } - .include-example.ng-leave.ng-leave-active { + .slide-animate.ng-leave.ng-leave-active { top:50px; } </file> <file name="scenario.js"> it('should load template1.html', function() { @@ -26884,10 +27806,15 @@ currentElement = null; } }; scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { + var afterAnimation = function() { + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + }; var thisChangeId = ++changeCounter; if (src) { $http.get(src, {cache: $templateCache}).success(function(response) { if (thisChangeId !== changeCounter) return; @@ -26898,17 +27825,12 @@ currentScope = newScope; currentElement = clone; currentElement.html(response); - $animate.enter(currentElement, null, $element); + $animate.enter(currentElement, null, $element, afterAnimation); $compile(currentElement.contents())(currentScope); - - if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - currentScope.$emit('$includeContentLoaded'); scope.$eval(onloadExp); }); }).error(function() { if (thisChangeId === changeCounter) cleanupLastIncludeContent(); @@ -26933,11 +27855,11 @@ * current scope. * * <div class="alert alert-error"> * The only appropriate use of `ngInit` for aliasing special properties of * {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you - * should use {@link guide/dev_guide.mvc.understanding_controller controllers} rather than `ngInit` + * should use {@link guide/controller controllers} rather than `ngInit` * to initialize values on a scope. * </div> * * @element ANY * @param {expression} ngInit {@link guide/expression Expression} to eval. @@ -26973,11 +27895,11 @@ compile: function() { return { pre: function(scope, element, attrs) { scope.$eval(attrs.ngInit); } - } + }; } }); /** * @ngdoc directive @@ -26987,11 +27909,11 @@ * * @description * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current * DOM element. This is useful if the element contains what appears to be Angular directives and * bindings but which should be ignored by Angular. This could be the case if you have a site that - * displays snippets of code. for instance. + * displays snippets of code, for instance. * * @element ANY * * @example * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, @@ -27364,53 +28286,39 @@ .example-animate-container { background:white; border:1px solid black; list-style:none; margin:0; - padding:0; + padding:0 10px; } - .example-animate-container > li { - padding:10px; + .animate-repeat { + line-height:40px; list-style:none; + box-sizing:border-box; } + .animate-repeat.ng-move, .animate-repeat.ng-enter, - .animate-repeat.ng-leave, - .animate-repeat.ng-move { + .animate-repeat.ng-leave { -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -o-transition:all linear 0.5s; transition:all linear 0.5s; } + .animate-repeat.ng-leave.ng-leave-active, + .animate-repeat.ng-move, .animate-repeat.ng-enter { - line-height:0; opacity:0; - padding-top:0; - padding-bottom:0; + max-height:0; } + + .animate-repeat.ng-leave, + .animate-repeat.ng-move.ng-move-active, .animate-repeat.ng-enter.ng-enter-active { - line-height:20px; opacity:1; - padding:10px; + max-height:40px; } - - .animate-repeat.ng-leave { - opacity:1; - line-height:20px; - padding:10px; - } - .animate-repeat.ng-leave.ng-leave-active { - opacity:0; - line-height:0; - padding-top:0; - padding-bottom:0; - } - - .animate-repeat.ng-move { } - .animate-repeat.ng-move.ng-move-active { } </file> <file name="scenario.js"> it('should render initial data set', function() { var r = using('.doc-example-live').repeater('ul li'); expect(r.count()).toBe(10); @@ -27438,10 +28346,11 @@ var ngRepeatMinErr = minErr('ngRepeat'); return { transclude: 'element', priority: 1000, terminal: true, + $$tlb: true, compile: function(element, attr, linker) { return function($scope, $element, $attr){ var expression = $attr.ngRepeat; var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, @@ -27467,14 +28376,14 @@ return trackByExpGetter($scope, hashFnLocals); }; } else { trackByIdArrayFn = function(key, value) { return hashKey(value); - } + }; trackByIdObjFn = function(key) { return key; - } + }; } match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); if (!match) { throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", @@ -27532,11 +28441,11 @@ key = (collection === collectionKeys) ? index : collectionKeys[index]; value = collection[key]; trackById = trackByIdFn(key, value, index); assertNotHasOwnProperty(trackById, '`track by` id'); if(lastBlockMap.hasOwnProperty(trackById)) { - block = lastBlockMap[trackById] + block = lastBlockMap[trackById]; delete lastBlockMap[trackById]; nextBlockMap[trackById] = block; nextBlockOrder[index] = block; } else if (nextBlockMap.hasOwnProperty(trackById)) { // restore lastBlockMap @@ -27580,13 +28489,11 @@ nextNode = previousNode; do { nextNode = nextNode.nextSibling; } while(nextNode && nextNode[NG_REMOVED]); - if (block.startNode == nextNode) { - // do nothing - } else { + if (block.startNode != nextNode) { // existing item which got moved $animate.move(getBlockElements(block), null, jqLite(previousNode)); } previousNode = block.endNode; } else { @@ -27598,11 +28505,13 @@ if (keyIdentifier) childScope[keyIdentifier] = key; childScope.$index = index; childScope.$first = (index === 0); childScope.$last = (index === (arrayLength - 1)); childScope.$middle = !(childScope.$first || childScope.$last); - childScope.$odd = !(childScope.$even = index%2==0); + // jshint bitwise: false + childScope.$odd = !(childScope.$even = (index&1) === 0); + // jshint bitwise: true if (!block.startNode) { linker(childScope, function(clone) { clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); $animate.enter(clone, null, jqLite(previousNode)); @@ -27617,27 +28526,10 @@ lastBlockMap = nextBlockMap; }); }; } }; - - function getBlockElements(block) { - if (block.startNode === block.endNode) { - return jqLite(block.startNode); - } - - var element = block.startNode; - var elements = [element]; - - do { - element = element.nextSibling; - if (!element) break; - elements.push(element); - } while (element !== block.endNode); - - return jqLite(elements); - } }]; /** * @ngdoc directive * @name ng.directive:ngShow @@ -27645,10 +28537,11 @@ * @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}). * * <pre> * <!-- when $scope.myValue is truthy (element is visible) --> * <div ng-show="myValue"></div> * @@ -27735,35 +28628,31 @@ <span class="icon-thumbs-down"></span> I hide when your checkbox is checked. </div> </div> </file> <file name="animations.css"> - .animate-show.ng-hide-add, - .animate-show.ng-hide-remove { + .animate-show { -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -o-transition:all linear 0.5s; transition:all linear 0.5s; - display:block!important; + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; } - .animate-show.ng-hide-add.ng-hide-add-active, + .animate-show.ng-hide-add, .animate-show.ng-hide-remove { + display:block!important; + } + + .animate-show.ng-hide { line-height:0; opacity:0; padding:0 10px; } - .animate-show.ng-hide-add, - .animate-show.ng-hide-remove.ng-hide-remove-active { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - .check-element { padding:10px; border:1px solid black; background:white; } @@ -27797,10 +28686,11 @@ * @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}). * * <pre> * <!-- when $scope.myValue is truthy (element is hidden) --> * <div ng-hide="myValue"></div> * @@ -27887,35 +28777,31 @@ <span class="icon-thumbs-down"></span> I hide when your checkbox is checked. </div> </div> </file> <file name="animations.css"> - .animate-hide.ng-hide-add, - .animate-hide.ng-hide-remove { + .animate-hide { -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -o-transition:all linear 0.5s; transition:all linear 0.5s; - display:block!important; + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; } - .animate-hide.ng-hide-add.ng-hide-add-active, + .animate-hide.ng-hide-add, .animate-hide.ng-hide-remove { + display:block!important; + } + + .animate-hide.ng-hide { line-height:0; opacity:0; padding:0 10px; } - .animate-hide.ng-hide-add, - .animate-hide.ng-hide-remove.ng-hide-remove-active { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - .check-element { padding:10px; border:1px solid black; background:white; } @@ -28040,13 +28926,13 @@ </select> <tt>selection={{selection}}</tt> <hr/> <div class="animate-switch-container" ng-switch on="selection"> - <div ng-switch-when="settings">Settings Div</div> - <div ng-switch-when="home">Home Span</div> - <div ng-switch-default>default</div> + <div class="animate-switch" ng-switch-when="settings">Settings Div</div> + <div class="animate-switch" ng-switch-when="home">Home Span</div> + <div class="animate-switch" ng-switch-default>default</div> </div> </div> </file> <file name="script.js"> function Ctrl($scope) { @@ -28061,41 +28947,33 @@ border:1px solid black; height:40px; overflow:hidden; } - .animate-switch-container > div { + .animate-switch { padding:10px; } - .animate-switch-container > .ng-enter, - .animate-switch-container > .ng-leave { + .animate-switch.ng-animate { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; position:absolute; top:0; left:0; right:0; bottom:0; } - .animate-switch-container > .ng-enter { + .animate-switch.ng-leave.ng-leave-active, + .animate-switch.ng-enter { top:-50px; } - .animate-switch-container > .ng-enter.ng-enter-active { + .animate-switch.ng-leave, + .animate-switch.ng-enter.ng-enter-active { top:0; } - - .animate-switch-container > .ng-leave { - top:0; - } - .animate-switch-container > .ng-leave.ng-leave-active { - top:50px; - } </file> <file name="scenario.js"> it('should start in settings', function() { expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/); }); @@ -28147,11 +29025,11 @@ }); }); } }); } - } + }; }]; var ngSwitchWhenDirective = ngDirective({ transclude: 'element', priority: 800, @@ -28390,11 +29268,11 @@ <select ng-model="color" ng-options="c.name for c in colors"></select><br> Color (null allowed): <span class="nullable"> <select ng-model="color" ng-options="c.name for c in colors"> - <option value="">-- chose color --</option> + <option value="">-- choose color --</option> </select> </span><br/> Color grouped by shade: <select ng-model="color" ng-options="c.name group by c.shade for c in colors"> @@ -28420,14 +29298,16 @@ </doc:scenario> </doc:example> */ var ngOptionsDirective = valueFn({ terminal: true }); +// jshint maxlen: false var selectDirective = ['$compile', '$parse', function($compile, $parse) { //0000111110000000000022220000000000000000000000333300000000000000444444444444444000000000555555555555555000000066666666666666600000000000000007777000000000000000000088888 var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/, nullModelCtrl = {$setViewValue: noop}; +// jshint maxlen: 100 return { restrict: 'E', require: ['select', '?ngModel'], controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { @@ -28504,11 +29384,11 @@ optGroupTemplate =jqLite(document.createElement('optgroup')), unknownOption = optionTemplate.clone(); // find "null" option for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) { - if (children[i].value == '') { + if (children[i].value === '') { emptyOption = nullOption = children.eq(i); break; } } @@ -28527,20 +29407,20 @@ attr.$observe('required', function() { requiredValidator(ngModelCtrl.$viewValue); }); } - if (optionsExp) Options(scope, element, ngModelCtrl); - else if (multiple) Multiple(scope, element, ngModelCtrl); - else Single(scope, element, ngModelCtrl, selectCtrl); + if (optionsExp) setupAsOptions(scope, element, ngModelCtrl); + else if (multiple) setupAsMultiple(scope, element, ngModelCtrl); + else setupAsSingle(scope, element, ngModelCtrl, selectCtrl); //////////////////////////// - function Single(scope, selectElement, ngModelCtrl, selectCtrl) { + function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) { ngModelCtrl.$render = function() { var viewValue = ngModelCtrl.$viewValue; if (selectCtrl.hasOption(viewValue)) { if (unknownOption.parent()) unknownOption.remove(); @@ -28561,11 +29441,11 @@ ngModelCtrl.$setViewValue(selectElement.val()); }); }); } - function Multiple(scope, selectElement, ctrl) { + function setupAsMultiple(scope, selectElement, ctrl) { var lastView; ctrl.$render = function() { var items = new HashMap(ctrl.$viewValue); forEach(selectElement.find('option'), function(option) { option.selected = isDefined(items.get(option.value)); @@ -28592,16 +29472,18 @@ ctrl.$setViewValue(array); }); }); } - function Options(scope, selectElement, ctrl) { + function setupAsOptions(scope, selectElement, ctrl) { var match; if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { throw ngOptionsMinErr('iexp', - "Expected expression in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '{0}'. Element: {1}", + "Expected expression in form of " + + "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + + " but got '{0}'. Element: {1}", optionsExp, startingTag(selectElement)); } var displayFn = $parse(match[2] || match[1]), valueName = match[4] || match[6], @@ -28609,13 +29491,14 @@ groupByFn = $parse(match[3] || ''), valueFn = $parse(match[2] ? match[1] : valueName), valuesFn = $parse(match[7]), track = match[8], trackFn = track ? $parse(match[8]) : null, - // This is an array of array of existing option groups in DOM. We try to reuse these if possible - // optionGroupsCache[0] is the options with no option group - // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element + // This is an array of array of existing option groups in DOM. + // We try to reuse these if possible + // - optionGroupsCache[0] is the options with no option group + // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element optionGroupsCache = [[{element: selectElement, label:''}]]; if (nullOption) { // compile the element since there might be bindings in it $compile(nullOption)(scope); @@ -28665,11 +29548,11 @@ } } else { key = selectElement.val(); if (key == '?') { value = undefined; - } else if (key == ''){ + } else if (key === ''){ value = null; } else { if (trackFn) { for (trackIndex = 0; trackIndex < collection.length; trackIndex++) { locals[valueName] = collection[trackIndex]; @@ -28693,11 +29576,12 @@ // TODO(vojta): can't we optimize this ? scope.$watch(render); function render() { - var optionGroups = {'':[]}, // Temporary location for the option groups before we render them + // Temporary location for the option groups before we render them + var optionGroups = {'':[]}, optionGroupNames = [''], optionGroupName, optionGroup, option, existingParent, existingOptions, existingOption, @@ -28742,11 +29626,13 @@ if (!(optionGroup = optionGroups[optionGroupName])) { optionGroup = optionGroups[optionGroupName] = []; optionGroupNames.push(optionGroupName); } if (multiple) { - selected = selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals)) !== undefined; + selected = isDefined( + selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals)) + ); } else { if (trackFn) { var modelCast = {}; modelCast[valueName] = modelValue; selected = trackFn(scope, modelCast) === trackFn(scope, locals); @@ -28754,13 +29640,16 @@ selected = modelValue === valueFn(scope, locals); } selectedSet = selectedSet || selected; // see if at least one item is selected } label = displayFn(scope, locals); // what will be seen by the user - label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values + + // doing displayFn(scope, locals) || '' overwrites zero values + label = isDefined(label) ? label : ''; optionGroup.push({ - id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index), // either the index into array or key from object + // either the index into array or key from object + id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index), label: label, selected: selected // determine if we should be selected }); } if (!multiple) { @@ -28955,10 +29844,11 @@ * @param {function()} fn Factory function(), return a function for * the statement. */ angular.scenario.dsl = angular.scenario.dsl || function(name, fn) { angular.scenario.dsl[name] = function() { + /* jshint -W040 *//* The dsl binds `this` for us when calling chained functions */ function executeStatement(statement, args) { var result = statement.apply(this, args); if (angular.isFunction(result) || result instanceof angular.scenario.Future) return result; var self = this; @@ -29186,19 +30076,19 @@ bindExp = bindExp.replace(/\s/g, ''); match = function (actualExp) { if (actualExp) { actualExp = actualExp.replace(/\s/g, ''); if (actualExp == bindExp) return true; - if (actualExp.indexOf(bindExp) == 0) { + if (actualExp.indexOf(bindExp) === 0) { return actualExp.charAt(bindExp.length) == '|'; } } - } + }; } else if (bindExp) { match = function(actualExp) { return actualExp && bindExp.exec(actualExp); - } + }; } else { match = function(actualExp) { return !!actualExp; }; } @@ -29206,11 +30096,11 @@ if (this.is(bindSelector)) { selection = selection.add(this); } function push(value) { - if (value == undefined) { + if (value === undefined) { value = ''; } else if (typeof value != 'string') { value = angular.toJson(value); } result.push('' + value); @@ -29265,12 +30155,12 @@ * Triggers a browser event. Attempts to choose the right event if one is * not specified. * * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement * @param {string} eventType Optional event type - * @param {Object=} eventData An optional object which contains additional event data (such as x,y coordinates, keys, etc...) that - * are passed into the event when triggered + * @param {Object=} eventData An optional object which contains additional event data (such as x,y + * coordinates, keys, etc...) that are passed into the event when triggered */ window.browserTrigger = function browserTrigger(element, eventType, eventData) { if (element && !element.nodeName) element = element[0]; if (!element) return; @@ -29369,12 +30259,12 @@ } else { evnt = document.createEvent('MouseEvents'); x = x || 0; y = y || 0; - evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'), pressed('alt'), - pressed('shift'), pressed('meta'), 0, element); + evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'), + pressed('alt'), pressed('shift'), pressed('meta'), 0, element); } /* we're unable to change the timeStamp value directly so this * is only here to allow for testing where the timeStamp value is * read */ @@ -29400,11 +30290,11 @@ delete angular['ff-684208-preventDefault']; return finalProcessDefault; } - } + }; }()); /** * Represents the application currently being tested and abstracts usage * of iframes or separate windows. @@ -29473,19 +30363,14 @@ try { var $window = self.getWindow_(); if ($window.angular) { // Disable animations - - // TODO(i): this doesn't disable javascript animations - // we don't need that for our tests, but it should be done $window.angular.resumeBootstrap([['$provide', function($provide) { - $provide.decorator('$sniffer', function($delegate) { - $delegate.transitions = false; - $delegate.animations = false; - return $delegate; - }); + return ['$animate', function($animate) { + $animate.enabled(false); + }]; }]]); } self.executeAction(loadFn); } catch (e) { @@ -29812,11 +30697,11 @@ self.emit('SpecEnd', it); }); runner.on('StepBegin', function(spec, step) { var it = self.getSpec(spec.id); - var step = new angular.scenario.ObjectModel.Step(step.name); + step = new angular.scenario.ObjectModel.Step(step.name); it.steps.push(step); // forward the event self.emit('StepBegin', it, step); }); @@ -29886,12 +30771,13 @@ * * @param {string} eventName Name of the event to fire. */ angular.scenario.ObjectModel.prototype.emit = function(eventName) { var self = this, - args = Array.prototype.slice.call(arguments, 1), - eventName = eventName.toLowerCase(); + args = Array.prototype.slice.call(arguments, 1); + + eventName = eventName.toLowerCase(); if (this.listeners[eventName]) { angular.forEach(this.listeners[eventName], function(listener) { listener.apply(self, args); }); @@ -30239,11 +31125,12 @@ /** * Executes a spec which is an it block with associated before/after functions * based on the describe nesting. * * @param {Object} spec A spec object - * @param {function()} specDone function that is called when the spec finishes. Function(error, index) + * @param {function()} specDone function that is called when the spec finishes, + * of the form `Function(error, index)` */ angular.scenario.SpecRunner.prototype.run = function(spec, specDone) { var self = this; this.spec = spec; @@ -30546,16 +31433,17 @@ * Usage: * binding(name) returns the value of the first matching binding */ angular.scenario.dsl('binding', function() { return function(name) { - return this.addFutureAction("select binding '" + name + "'", function($window, $document, done) { - var values = $document.elements().bindings($window.angular.element, name); - if (!values.length) { - return done("Binding selector '" + name + "' did not match."); - } - done(null, values[0]); + return this.addFutureAction("select binding '" + name + "'", + function($window, $document, done) { + var values = $document.elements().bindings($window.angular.element, name); + if (!values.length) { + return done("Binding selector '" + name + "' did not match."); + } + done(null, values[0]); }); }; }); /** @@ -30568,32 +31456,35 @@ angular.scenario.dsl('input', function() { var chain = {}; var supportInputEvent = 'oninput' in document.createElement('div') && msie != 9; chain.enter = function(value, event) { - return this.addFutureAction("input '" + this.name + "' enter '" + value + "'", function($window, $document, done) { - var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input'); - input.val(value); - input.trigger(event || (supportInputEvent ? 'input' : 'change')); - done(); + return this.addFutureAction("input '" + this.name + "' enter '" + value + "'", + function($window, $document, done) { + var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input'); + input.val(value); + input.trigger(event || (supportInputEvent ? 'input' : 'change')); + done(); }); }; chain.check = function() { - return this.addFutureAction("checkbox '" + this.name + "' toggle", function($window, $document, done) { - var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':checkbox'); - input.trigger('click'); - done(); + return this.addFutureAction("checkbox '" + this.name + "' toggle", + function($window, $document, done) { + var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':checkbox'); + input.trigger('click'); + done(); }); }; chain.select = function(value) { - return this.addFutureAction("radio button '" + this.name + "' toggle '" + value + "'", function($window, $document, done) { - var input = $document. - elements('[ng\\:model="$1"][value="$2"]', this.name, value).filter(':radio'); - input.trigger('click'); - done(); + return this.addFutureAction("radio button '" + this.name + "' toggle '" + value + "'", + function($window, $document, done) { + var input = $document. + elements('[ng\\:model="$1"][value="$2"]', this.name, value).filter(':radio'); + input.trigger('click'); + done(); }); }; chain.val = function() { return this.addFutureAction("return input val", function($window, $document, done) { @@ -30611,37 +31502,41 @@ /** * Usage: * repeater('#products table', 'Product List').count() number of rows * repeater('#products table', 'Product List').row(1) all bindings in row as an array - * repeater('#products table', 'Product List').column('product.name') all values across all rows in an array + * repeater('#products table', 'Product List').column('product.name') all values across all rows + * in an array */ angular.scenario.dsl('repeater', function() { var chain = {}; chain.count = function() { - return this.addFutureAction("repeater '" + this.label + "' count", function($window, $document, done) { - try { - done(null, $document.elements().length); - } catch (e) { - done(null, 0); - } + return this.addFutureAction("repeater '" + this.label + "' count", + function($window, $document, done) { + try { + done(null, $document.elements().length); + } catch (e) { + done(null, 0); + } }); }; chain.column = function(binding) { - return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'", function($window, $document, done) { - done(null, $document.elements().bindings($window.angular.element, binding)); + return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'", + function($window, $document, done) { + done(null, $document.elements().bindings($window.angular.element, binding)); }); }; chain.row = function(index) { - return this.addFutureAction("repeater '" + this.label + "' row '" + index + "'", function($window, $document, done) { - var matches = $document.elements().slice(index, index + 1); - if (!matches.length) - return done('row ' + index + ' out of bounds'); - done(null, matches.bindings($window.angular.element)); + return this.addFutureAction("repeater '" + this.label + "' row '" + index + "'", + function($window, $document, done) { + var matches = $document.elements().slice(index, index + 1); + if (!matches.length) + return done('row ' + index + ' out of bounds'); + done(null, matches.bindings($window.angular.element)); }); }; return function(selector, label) { this.dsl.using(selector, label); @@ -30656,40 +31551,42 @@ */ angular.scenario.dsl('select', function() { var chain = {}; chain.option = function(value) { - return this.addFutureAction("select '" + this.name + "' option '" + value + "'", function($window, $document, done) { - var select = $document.elements('select[ng\\:model="$1"]', this.name); - var option = select.find('option[value="' + value + '"]'); - if (option.length) { - select.val(value); - } else { - option = select.find('option').filter(function(){ - return _jQuery(this).text() === value; - }); - if (!option.length) { - option = select.find('option:contains("' + value + '")'); - } + return this.addFutureAction("select '" + this.name + "' option '" + value + "'", + function($window, $document, done) { + var select = $document.elements('select[ng\\:model="$1"]', this.name); + var option = select.find('option[value="' + value + '"]'); if (option.length) { - select.val(option.val()); + select.val(value); } else { - return done("option '" + value + "' not found"); + option = select.find('option').filter(function(){ + return _jQuery(this).text() === value; + }); + if (!option.length) { + option = select.find('option:contains("' + value + '")'); + } + if (option.length) { + select.val(option.val()); + } else { + return done("option '" + value + "' not found"); + } } - } - select.trigger('change'); - done(); + select.trigger('change'); + done(); }); }; chain.options = function() { var values = arguments; - return this.addFutureAction("select '" + this.name + "' options '" + values + "'", function($window, $document, done) { - var select = $document.elements('select[multiple][ng\\:model="$1"]', this.name); - select.val(values); - select.trigger('change'); - done(); + return this.addFutureAction("select '" + this.name + "' options '" + values + "'", + function($window, $document, done) { + var select = $document.elements('select[multiple][ng\\:model="$1"]', this.name); + select.val(values); + select.trigger('change'); + done(); }); }; return function(name) { this.name = name; @@ -30717,87 +31614,95 @@ 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset' ]; var chain = {}; chain.count = function() { - return this.addFutureAction("element '" + this.label + "' count", function($window, $document, done) { - try { - done(null, $document.elements().length); - } catch (e) { - done(null, 0); - } + return this.addFutureAction("element '" + this.label + "' count", + function($window, $document, done) { + try { + done(null, $document.elements().length); + } catch (e) { + done(null, 0); + } }); }; chain.click = function() { - return this.addFutureAction("element '" + this.label + "' click", function($window, $document, done) { - var elements = $document.elements(); - var href = elements.attr('href'); - var eventProcessDefault = elements.trigger('click')[0]; + return this.addFutureAction("element '" + this.label + "' click", + function($window, $document, done) { + var elements = $document.elements(); + var href = elements.attr('href'); + var eventProcessDefault = elements.trigger('click')[0]; - if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { - this.application.navigateTo(href, function() { + if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + this.application.navigateTo(href, function() { + done(); + }, done); + } else { done(); - }, done); - } else { - done(); - } + } }); }; chain.dblclick = function() { - return this.addFutureAction("element '" + this.label + "' dblclick", function($window, $document, done) { - var elements = $document.elements(); - var href = elements.attr('href'); - var eventProcessDefault = elements.trigger('dblclick')[0]; + return this.addFutureAction("element '" + this.label + "' dblclick", + function($window, $document, done) { + var elements = $document.elements(); + var href = elements.attr('href'); + var eventProcessDefault = elements.trigger('dblclick')[0]; - if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { - this.application.navigateTo(href, function() { + if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + this.application.navigateTo(href, function() { + done(); + }, done); + } else { done(); - }, done); - } else { - done(); - } + } }); }; chain.mouseover = function() { - return this.addFutureAction("element '" + this.label + "' mouseover", function($window, $document, done) { - var elements = $document.elements(); - elements.trigger('mouseover'); - done(); + return this.addFutureAction("element '" + this.label + "' mouseover", + function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mouseover'); + done(); }); }; chain.mousedown = function() { - return this.addFutureAction("element '" + this.label + "' mousedown", function($window, $document, done) { - var elements = $document.elements(); - elements.trigger('mousedown'); - done(); + return this.addFutureAction("element '" + this.label + "' mousedown", + function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mousedown'); + done(); }); }; chain.mouseup = function() { - return this.addFutureAction("element '" + this.label + "' mouseup", function($window, $document, done) { - var elements = $document.elements(); - elements.trigger('mouseup'); - done(); + return this.addFutureAction("element '" + this.label + "' mouseup", + function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mouseup'); + done(); }); }; chain.query = function(fn) { - return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) { - fn.call(this, $document.elements(), done); + return this.addFutureAction('element ' + this.label + ' custom query', + function($window, $document, done) { + fn.call(this, $document.elements(), done); }); }; angular.forEach(KEY_VALUE_METHODS, function(methodName) { chain[methodName] = function(name, value) { var args = arguments, futureName = (args.length == 1) ? "element '" + this.label + "' get " + methodName + " '" + name + "'" - : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" + value + "'"; + : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" + + value + "'"; return this.addFutureAction(futureName, function($window, $document, done) { var element = $document.elements(); done(null, element[methodName].apply(element, args)); }); @@ -30805,11 +31710,11 @@ }); angular.forEach(VALUE_METHODS, function(methodName) { chain[methodName] = function(value) { var args = arguments, - futureName = (args.length == 0) + futureName = (args.length === 0) ? "element '" + this.label + "' " + methodName : "element '" + this.label + "' set " + methodName + " to '" + value + "'"; return this.addFutureAction(futureName, function($window, $document, done) { var element = $document.elements(); @@ -31124,7 +32029,8 @@ angular.scenario.setUpAndRun(config); }); } })(window, document); -angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n</style>'); -angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>'); + +!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n\n/* The styles below ensure that the CSS transition will ALWAYS\n * animate and close. A nasty bug occurs with CSS transitions where\n * when the active class isn\'t set, or if the active class doesn\'t\n * contain any styles to transition to, then, if ngAnimate is used,\n * it will appear as if the webpage is broken due to the forever hanging\n * animations. The clip (!ie) and zoom (ie) CSS properties are used\n * since they trigger a transition without making the browser\n * animate anything and they\'re both highly underused CSS properties */\n.ng-animate-start { clip:rect(0, auto, auto, 0); -ms-zoom:1.0001; }\n.ng-animate-active { clip:rect(-1px, auto, auto, 0); -ms-zoom:1; }\n</style>'); +!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>'); \ No newline at end of file