/** * @license AngularJS v1.2.2 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document, undefined) {'use strict'; /** * @description * * This object provides a utility for producing rich Error messages within * Angular. It can be called as follows: * * var exampleMinErr = minErr('example'); * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); * * The above creates an instance of minErr in the example namespace. The * resulting error will have a namespaced error code of example.one. The * resulting error will replace {0} with the value of foo, and {1} with the * value of bar. The object is not restricted in the number of arguments it can * take. * * If fewer arguments are specified than necessary for interpolation, the extra * interpolation markers will be preserved in the final string. * * Since data will be parsed statically during a build step, some restrictions * are applied with respect to how minErr instances are created and called. * Instances should have names of the form namespaceMinErr for a minErr created * using minErr('namespace') . Error codes, namespaces and template strings * should all be static strings, not variables or general expressions. * * @param {string} module The namespace to use for the new minErr instance. * @returns {function(string, string, ...): Error} instance */ function minErr(module) { return function () { var code = arguments[0], prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, stringify = function (obj) { if (typeof obj === 'function') { return obj.toString().replace(/ \{[\s\S]*$/, ''); } else if (typeof obj === 'undefined') { return 'undefined'; } else if (typeof obj !== 'string') { return JSON.stringify(obj); } return obj; }, message, i; message = prefix + template.replace(/\{\d+\}/g, function (match) { var index = +match.slice(1, -1), arg; if (index + 2 < templateArgs.length) { arg = templateArgs[index + 2]; if (typeof arg === 'function') { return arg.toString().replace(/ ?\{[\s\S]*$/, ''); } else if (typeof arg === 'undefined') { return 'undefined'; } else if (typeof arg !== 'string') { return toJson(arg); } return arg; } return match; }); message = message + '\nhttp://errors.angularjs.org/1.2.2/' + (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + encodeURIComponent(stringify(arguments[i])); } 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 * @function * * @description Converts the specified string to lowercase. * @param {string} string String to be converted to lowercase. * @returns {string} Lowercased string. */ var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; /** * @ngdoc function * @name angular.uppercase * @function * * @description Converts the specified string to uppercase. * @param {string} string String to be converted to uppercase. * @returns {string} Uppercased string. */ 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; }; // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods // with correct but slower alternatives. if ('i' !== 'I'.toLowerCase()) { lowercase = manualLowercase; uppercase = manualUppercase; } var /** holds major version number for IE or NaN for real browsers */ msie, jqLite, // delay binding since jQuery could be loaded after us. jQuery, // delay binding slice = [].slice, push = [].push, toString = Object.prototype.toString, ngMinErr = minErr('ng'), _angular = window.angular, /** @name angular */ angular = window.angular || (window.angular = {}), angularModule, nodeName_, uid = ['0', '0', '0']; /** * IE 11 changed the format of the UserAgent string. * See http://msdn.microsoft.com/en-us/library/ms537503.aspx */ msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); if (isNaN(msie)) { msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); } /** * @private * @param {*} obj * @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; } var length = obj.length; if (obj.nodeType === 1 && length) { return true; } return isString(obj) || isArray(obj) || length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj; } /** * @ngdoc function * @name angular.forEach * @function * * @description * Invokes the `iterator` function once for each item in `obj` collection, which can be either an * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` * is the value of an object property or an array element and `key` is the object property key or * array element index. Specifying a `context` for the function is optional. * * Note: this function was previously known as `angular.foreach`. *
var values = {name: 'misko', gender: 'male'}; var log = []; angular.forEach(values, function(value, key){ this.push(key + ': ' + value); }, log); expect(log).toEqual(['name: misko', 'gender:male']);* * @param {Object|Array} obj Object to iterate over. * @param {Function} iterator Iterator function. * @param {Object=} context Object to become context (`this`) for the iterator function. * @returns {Object|Array} Reference to `obj`. */ function forEach(obj, iterator, context) { var key; if (obj) { if (isFunction(obj)){ for (key in obj) { if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key); } } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context); } else if (isArrayLike(obj)) { for (key = 0; key < obj.length; key++) iterator.call(context, obj[key], key); } else { for (key in obj) { if (obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key); } } } } return obj; } function sortedKeys(obj) { var keys = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return keys.sort(); } function forEachSorted(obj, iterator, context) { var keys = sortedKeys(obj); for ( var i = 0; i < keys.length; i++) { iterator.call(context, obj[keys[i]], keys[i]); } return keys; } /** * 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); }; } /** * 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 * the number string gets longer over time, and it can also overflow, where as the nextId * will grow much slower, it is a string, and it will never overflow. * * @returns an unique alpha-numeric string */ function nextUid() { var index = uid.length; var digit; while(index) { index--; digit = uid[index].charCodeAt(0); if (digit == 57 /*'9'*/) { uid[index] = 'A'; return uid.join(''); } if (digit == 90 /*'Z'*/) { uid[index] = '0'; } else { uid[index] = String.fromCharCode(digit + 1); return uid.join(''); } } uid.unshift('0'); return uid.join(''); } /** * Set or clear the hashkey for an object. * @param obj object * @param h the hashkey (!truthy to delete the hashkey) */ function setHashKey(obj, h) { if (h) { obj.$$hashKey = h; } else { delete obj.$$hashKey; } } /** * @ngdoc function * @name angular.extend * @function * * @description * Extends the destination object `dst` by copying all of the properties from the `src` object(s) * to `dst`. You can specify multiple `src` objects. * * @param {Object} dst Destination object. * @param {...Object} src Source object(s). * @returns {Object} Reference to `dst`. */ function extend(dst) { var h = dst.$$hashKey; forEach(arguments, function(obj){ if (obj !== dst) { forEach(obj, function(value, key){ dst[key] = value; }); } }); setHashKey(dst,h); return dst; } function int(str) { return parseInt(str, 10); } function inherit(parent, extra) { return extend(new (extend(function() {}, {prototype:parent}))(), extra); } /** * @ngdoc function * @name angular.noop * @function * * @description * A function that performs no operations. This function can be useful when writing code in the * functional style.
function foo(callback) { var result = calculateResult(); (callback || angular.noop)(result); }*/ function noop() {} noop.$inject = []; /** * @ngdoc function * @name angular.identity * @function * * @description * A function that returns its first argument. This function is useful when writing code in the * functional style. *
function transformer(transformationFn, value) { return (transformationFn || angular.identity)(value); };*/ function identity($) {return $;} identity.$inject = []; function valueFn(value) {return function() {return value;};} /** * @ngdoc function * @name angular.isUndefined * @function * * @description * Determines if a reference is undefined. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is undefined. */ function isUndefined(value){return typeof value == 'undefined';} /** * @ngdoc function * @name angular.isDefined * @function * * @description * Determines if a reference is defined. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is defined. */ function isDefined(value){return typeof value != 'undefined';} /** * @ngdoc function * @name angular.isObject * @function * * @description * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not * considered to be objects. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is an `Object` but not `null`. */ function isObject(value){return value != null && typeof value == 'object';} /** * @ngdoc function * @name angular.isString * @function * * @description * Determines if a reference is a `String`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `String`. */ function isString(value){return typeof value == 'string';} /** * @ngdoc function * @name angular.isNumber * @function * * @description * Determines if a reference is a `Number`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Number`. */ function isNumber(value){return typeof value == 'number';} /** * @ngdoc function * @name angular.isDate * @function * * @description * Determines if a value is a date. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Date`. */ function isDate(value){ return toString.apply(value) == '[object Date]'; } /** * @ngdoc function * @name angular.isArray * @function * * @description * Determines if a reference is an `Array`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is an `Array`. */ function isArray(value) { return toString.apply(value) == '[object Array]'; } /** * @ngdoc function * @name angular.isFunction * @function * * @description * Determines if a reference is a `Function`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Function`. */ function isFunction(value){return typeof value == 'function';} /** * Determines if a value is a regular expression object. * * @private * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `RegExp`. */ function isRegExp(value) { return toString.apply(value) == '[object RegExp]'; } /** * Checks if `obj` is a window object. * * @private * @param {*} obj Object to check * @returns {boolean} True if `obj` is a window obj. */ function isWindow(obj) { return obj && obj.document && obj.location && obj.alert && obj.setInterval; } function isScope(obj) { return obj && obj.$evalAsync && obj.$watch; } function isFile(obj) { return toString.apply(obj) === '[object File]'; } function isBoolean(value) { return typeof value == 'boolean'; } var trim = (function() { // native trim is way faster: http://jsperf.com/angular-trim-test // but IE doesn't have it... :-( // TODO: we should move this into IE/ES5 polyfill if (!String.prototype.trim) { return function(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }; } return function(value) { return isString(value) ? value.trim() : value; }; })(); /** * @ngdoc function * @name angular.isElement * @function * * @description * Determines if a reference is a DOM element (or wrapped jQuery element). * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). */ function isElement(node) { return node && (node.nodeName // we are a direct element || (node.on && node.find)); // we have an on and find method part of jQuery API } /** * @param str 'key1,key2,...' * @returns {object} in the form of {key1:true, key2:true, ...} */ function makeMap(str){ var obj = {}, items = str.split(","), i; for ( i = 0; i < items.length; i++ ) obj[ items[i] ] = true; return obj; } if (msie < 9) { nodeName_ = function(element) { element = element.nodeName ? element : element[0]; return (element.scopeName && element.scopeName != 'HTML') ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; }; } else { nodeName_ = function(element) { return element.nodeName ? element.nodeName : element[0].nodeName; }; } function map(obj, iterator, context) { var results = []; forEach(obj, function(value, index, list) { results.push(iterator.call(context, value, index, list)); }); return results; } /** * @description * Determines the number of elements in an array, the number of properties an object has, or * the length of a string. * * Note: This function is used to augment the Object type in Angular expressions. See * {@link angular.Object} for more information about Angular arrays. * * @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 count = 0, key; if (isArray(obj) || isString(obj)) { return obj.length; } else if (isObject(obj)){ for (key in obj) if (!ownPropsOnly || obj.hasOwnProperty(key)) count++; } return count; } function includes(array, obj) { return indexOf(array, obj) != -1; } function indexOf(array, obj) { if (array.indexOf) return array.indexOf(obj); for ( var i = 0; i < array.length; i++) { if (obj === array[i]) return i; } return -1; } function arrayRemove(array, value) { var index = indexOf(array, value); if (index >=0) array.splice(index, 1); return value; } function isLeafNode (node) { if (node) { switch (node.nodeName) { case "OPTION": case "PRE": case "TITLE": return true; } } return false; } /** * @ngdoc function * @name angular.copy * @function * * @description * Creates a deep copy of `source`, which should be an object or an array. * * * If no destination is supplied, a copy of the object or array is created. * * 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. * * @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. * * @example
form = {{user | json}}
master = {{master | json}}
* // Create a new module * var myModule = angular.module('myModule', []); * * // register a new service * myModule.value('appName', 'MyCoolApp'); * * // configure existing services inside initialization blocks. * myModule.config(function($locationProvider) { * // Configure existing providers * $locationProvider.hashPrefix('!'); * }); ** * Then you can create an injector and load your modules like this: * *
* var injector = angular.injector(['ng', 'MyModule']) ** * 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.
* module.animation('.animation-name', function($inject1, $inject2) { * return { * eventName : function(element, done) { * //code to run the animation * //once complete, then run done() * return function cancellationFunction(element) { * //code to cancel the animation * } * } * } * }) ** * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and * {@link ngAnimate ngAnimate module} for more information. */ animation: invokeLater('$animateProvider', 'register'), /** * @ngdoc method * @name angular.Module#filter * @methodOf angular.Module * @param {string} name Filter name. * @param {Function} filterFactory Factory function for creating new instance of filter. * @description * See {@link ng.$filterProvider#register $filterProvider.register()}. */ filter: invokeLater('$filterProvider', 'register'), /** * @ngdoc method * @name angular.Module#controller * @methodOf angular.Module * @param {string|Object} name Controller name, or an object map of controllers where the * keys are the names and the values are the constructors. * @param {Function} constructor Controller constructor function. * @description * See {@link ng.$controllerProvider#register $controllerProvider.register()}. */ controller: invokeLater('$controllerProvider', 'register'), /** * @ngdoc method * @name angular.Module#directive * @methodOf angular.Module * @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#methods_directive $compileProvider.directive()}. */ directive: invokeLater('$compileProvider', 'directive'), /** * @ngdoc method * @name angular.Module#config * @methodOf angular.Module * @param {Function} configFn Execute this function on module load. Useful for service * configuration. * @description * Use this method to register work which needs to be performed on module loading. */ config: config, /** * @ngdoc method * @name angular.Module#run * @methodOf angular.Module * @param {Function} initializationFn Execute this function after injector creation. * Useful for application initialization. * @description * Use this method to register work which should be performed when the injector is done * loading all modules. */ run: function(block) { runBlocks.push(block); return this; } }; if (configFn) { config(configFn); } return moduleInstance; /** * @param {string} provider * @param {string} method * @param {String=} insertMethod * @returns {angular.Module} */ 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 * following properties: * * - `full` – `{string}` – Full version string, such as "0.9.18". * - `major` – `{number}` – Major version number, such as "0". * - `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.2', // all of these placeholder strings will be replaced by grunt's major: 1, // package task minor: 2, dot: 2, codeName: 'consciousness-inertia' }; function publishExternalAPI(angular){ extend(angular, { 'bootstrap': bootstrap, 'copy': copy, 'extend': extend, 'equals': equals, 'element': jqLite, 'forEach': forEach, 'injector': createInjector, 'noop':noop, 'bind':bind, 'toJson': toJson, 'fromJson': fromJson, 'identity':identity, 'isUndefined': isUndefined, 'isDefined': isDefined, 'isString': isString, 'isFunction': isFunction, 'isObject': isObject, 'isNumber': isNumber, 'isElement': isElement, 'isArray': isArray, 'version': version, 'isDate': isDate, 'lowercase': lowercase, 'uppercase': uppercase, 'callbacks': {counter: 0}, '$$minErr': minErr, '$$csp': csp }); angularModule = setupModuleLoader(window); try { angularModule('ngLocale'); } catch (e) { angularModule('ngLocale', []).provider('$locale', $LocaleProvider); } angularModule('ng', ['ngLocale'], ['$provide', function ngModule($provide) { $provide.provider('$compile', $CompileProvider). directive({ a: htmlAnchorDirective, input: inputDirective, textarea: inputDirective, form: formDirective, script: scriptDirective, select: selectDirective, style: styleDirective, option: optionDirective, ngBind: ngBindDirective, ngBindHtml: ngBindHtmlDirective, ngBindTemplate: ngBindTemplateDirective, ngClass: ngClassDirective, ngClassEven: ngClassEvenDirective, ngClassOdd: ngClassOddDirective, ngCloak: ngCloakDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, ngInclude: ngIncludeDirective, ngInit: ngInitDirective, ngNonBindable: ngNonBindableDirective, ngPluralize: ngPluralizeDirective, ngRepeat: ngRepeatDirective, ngShow: ngShowDirective, ngStyle: ngStyleDirective, ngSwitch: ngSwitchDirective, ngSwitchWhen: ngSwitchWhenDirective, ngSwitchDefault: ngSwitchDefaultDirective, ngOptions: ngOptionsDirective, ngTransclude: ngTranscludeDirective, ngModel: ngModelDirective, ngList: ngListDirective, ngChange: ngChangeDirective, required: requiredDirective, ngRequired: requiredDirective, ngValue: ngValueDirective }). directive(ngAttributeAliasDirectives). directive(ngEventDirectives); $provide.provider({ $anchorScroll: $AnchorScrollProvider, $animate: $AnimateProvider, $browser: $BrowserProvider, $cacheFactory: $CacheFactoryProvider, $controller: $ControllerProvider, $document: $DocumentProvider, $exceptionHandler: $ExceptionHandlerProvider, $filter: $FilterProvider, $interpolate: $InterpolateProvider, $interval: $IntervalProvider, $http: $HttpProvider, $httpBackend: $HttpBackendProvider, $location: $LocationProvider, $log: $LogProvider, $parse: $ParseProvider, $rootScope: $RootScopeProvider, $q: $QProvider, $sce: $SceProvider, $sceDelegate: $SceDelegateProvider, $sniffer: $SnifferProvider, $templateCache: $TemplateCacheProvider, $timeout: $TimeoutProvider, $window: $WindowProvider }); } ]); } /* global -JQLitePrototype, -addEventListenerFn, -removeEventListenerFn, -BOOLEAN_ATTR */ ////////////////////////////////// //JQLite ////////////////////////////////// /** * @ngdoc function * @name angular.element * @function * * @description * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. * * If jQuery is available, `angular.element` is an alias for the * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." * *
* // create an injector * var $injector = angular.injector(['ng']); * * // use the injector to kick off your application * // use the type inference to auto inject arguments, or use implicit injection * $injector.invoke(function($rootScope, $compile, $document){ * $compile($document)($rootScope); * $rootScope.$digest(); * }); **/ /** * @ngdoc overview * @name AUTO * @description * * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. */ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var $injectorMinErr = minErr('$injector'); function annotate(fn) { var $inject, fnText, argDecl, last; if (typeof fn == 'function') { if (!($inject = fn.$inject)) { $inject = []; if (fn.length) { fnText = fn.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS); forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ arg.replace(FN_ARG, function(all, underscore, name){ $inject.push(name); }); }); } fn.$inject = $inject; } } else if (isArray(fn)) { last = fn.length - 1; assertArgFn(fn[last], 'fn'); $inject = fn.slice(0, last); } else { assertArgFn(fn, 'fn', true); } return $inject; } /////////////////////////////////////// /** * @ngdoc object * @name AUTO.$injector * @function * * @description * * `$injector` is used to retrieve object instances as defined by * {@link AUTO.$provide provider}, instantiate types, invoke methods, * and load modules. * * The following always holds true: * *
* var $injector = angular.injector(); * expect($injector.get('$injector')).toBe($injector); * expect($injector.invoke(function($injector){ * return $injector; * }).toBe($injector); ** * # Injection Function Annotation * * JavaScript does not have annotations, and annotations are needed for dependency injection. The * following are all valid ways of annotating function with injection arguments and are equivalent. * *
* // inferred (only works if code not minified/obfuscated) * $injector.invoke(function(serviceA){}); * * // annotated * function explicit(serviceA) {}; * explicit.$inject = ['serviceA']; * $injector.invoke(explicit); * * // inline * $injector.invoke(['serviceA', function(serviceA){}]); ** * ## 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. * * ## `$inject` Annotation * By adding a `$inject` property onto a function the injection parameters can be specified. * * ## Inline * As an array of injection names, where the last item in the array is the function to call. */ /** * @ngdoc method * @name AUTO.$injector#get * @methodOf AUTO.$injector * * @description * Return an instance of the service. * * @param {string} name The name of the instance to retrieve. * @return {*} The instance. */ /** * @ngdoc method * @name AUTO.$injector#invoke * @methodOf AUTO.$injector * * @description * 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. * @returns {*} the value returned by the invoked `fn` function. */ /** * @ngdoc method * @name AUTO.$injector#has * @methodOf AUTO.$injector * * @description * Allows the user to query if the particular service exist. * * @param {string} Name of the service to query. * @returns {boolean} returns true if injector has given service. */ /** * @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. * * @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. * @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. * * # 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. *
* // Given * function MyController($scope, $route) { * // ... * } * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); ** * 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. *
* // Given * var MyController = function(obfuscatedScope, obfuscatedRoute) { * // ... * } * // Define function dependencies * MyController.$inject = ['$scope', '$route']; * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); ** * # 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: * *
* // We wish to write this (not minification / obfuscation safe) * injector.invoke(function($compile, $rootScope) { * // ... * }); * * // We are forced to write break inlining * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { * // ... * }; * tmpFn.$inject = ['$compile', '$rootScope']; * injector.invoke(tmpFn); * * // To better support inline function the inline annotation is supported * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { * // ... * }]); * * // Therefore * expect(injector.annotate( * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) * ).toEqual(['$compile', '$rootScope']); ** * @param {function|Array.
* // Define the eventTracker provider * function EventTrackerProvider() { * var trackingUrl = '/track'; * * // A provider method for configuring where the tracked events should been saved * this.setTrackingUrl = function(url) { * trackingUrl = url; * }; * * // The service factory function * this.$get = ['$http', function($http) { * var trackedEvents = {}; * return { * // Call this to track an event * event: function(event) { * var count = trackedEvents[event] || 0; * count += 1; * trackedEvents[event] = count; * return count; * }, * // Call this to save the tracked events to the trackingUrl * save: function() { * $http.post(trackingUrl, trackedEvents); * } * }; * }]; * } * * describe('eventTracker', function() { * var postSpy; * * beforeEach(module(function($provide) { * // Register the eventTracker provider * $provide.provider('eventTracker', EventTrackerProvider); * })); * * beforeEach(module(function(eventTrackerProvider) { * // Configure eventTracker provider * eventTrackerProvider.setTrackingUrl('/custom-track'); * })); * * it('tracks events', inject(function(eventTracker) { * expect(eventTracker.event('login')).toEqual(1); * expect(eventTracker.event('login')).toEqual(2); * })); * * it('saves to the tracking url', inject(function(eventTracker, $http) { * postSpy = spyOn($http, 'post'); * eventTracker.event('login'); * eventTracker.save(); * expect(postSpy).toHaveBeenCalled(); * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); * })); * }); **/ /** * @ngdoc method * @name AUTO.$provide#factory * @methodOf AUTO.$provide * @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.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})`. * @returns {Object} registered provider instance * * @example * Here is an example of registering a service *
* $provide.factory('ping', ['$http', function($http) { * return function ping() { * return $http.send('/ping'); * }; * }]); ** You would then inject and use this service like this: *
* someModule.controller('Ctrl', ['ping', function(ping) { * ping(); * }]); **/ /** * @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. * 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#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#methods_service $provide.service(class)} that is defined as a CoffeeScript class. *
* class Ping * constructor: (@$http)-> * send: ()=> * @$http.get('/ping') * * $provide.service('ping', ['$http', Ping]) ** You would then inject and use this service like this: *
* someModule.controller 'Ctrl', ['ping', (ping)-> * ping.send() * ] **/ /** * @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**. * * 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. *
* $provide.value('ADMIN_USER', 'admin'); * * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); * * $provide.value('halfOf', function(value) { * return value / 2; * }); **/ /** * @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}. * * @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: *
* $provide.constant('SHARD_HEIGHT', 306); * * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); * * $provide.constant('double', function(value) { * return value * 2; * }); **/ /** * @ngdoc method * @name AUTO.$provide#decorator * @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. * * @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. * Local injection arguments: * * * `$delegate` - The original service instance, which can be monkey patched, configured, * decorated or delegated to. * * @example * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting * calls to {@link ng.$log#error $log.warn()}. *
* $provider.decorator('$log', ['$delegate', function($delegate) { * $delegate.warn = $delegate.error; * return $delegate; * }]); **/ function createInjector(modulesToLoad) { var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], loadedModules = new HashMap(), providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, providerInjector = (providerCache.$injector = createInternalInjector(providerCache, function() { throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); })), instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) { var provider = providerInjector.get(servicename + providerSuffix); return instanceInjector.invoke(provider.$get, provider); })); forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); return instanceInjector; //////////////////////////////////// // $provider //////////////////////////////////// function supportObject(delegate) { return function(key, value) { if (isObject(key)) { forEach(key, reverseParams(delegate)); } else { return delegate(key, value); } }; } function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { provider_ = providerInjector.instantiate(provider_); } if (!provider_.$get) { throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); } return providerCache[name + providerSuffix] = provider_; } function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } function value(name, val) { return factory(name, valueFn(val)); } function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; } function decorator(serviceName, decorFn) { var origProvider = providerInjector.get(serviceName + providerSuffix), orig$get = origProvider.$get; origProvider.$get = function() { var origInstance = instanceInjector.invoke(orig$get, origProvider); return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); }; } //////////////////////////////////// // Module Loading //////////////////////////////////// function loadModules(modulesToLoad){ var runBlocks = [], moduleFn, invokeQueue, i, ii; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; loadedModules.put(module, true); try { if (isString(module)) { moduleFn = angularModule(module); runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); 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]); } } else if (isFunction(module)) { runBlocks.push(providerInjector.invoke(module)); } else if (isArray(module)) { runBlocks.push(providerInjector.invoke(module)); } else { assertArgFn(module, 'module'); } } 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 // 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); } }); return runBlocks; } //////////////////////////////////// // internal Injector //////////////////////////////////// function createInternalInjector(cache, factory) { function getService(serviceName) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- ')); } return cache[serviceName]; } else { try { path.unshift(serviceName); cache[serviceName] = INSTANTIATING; return cache[serviceName] = factory(serviceName); } finally { path.shift(); } } } function invoke(fn, self, locals){ var args = [], $inject = annotate(fn), length, i, 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); } args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key) ); } if (!fn.$inject) { // this means that we must be an array. fn = fn[length]; } // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke switch (self ? -1 : args.length) { case 0: return fn(); case 1: return fn(args[0]); case 2: return fn(args[0], args[1]); case 3: return fn(args[0], args[1], args[2]); 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]); default: return fn.apply(self, args); } } function instantiate(Type, locals) { var Constructor = function() {}, instance, returnedValue; // Check if Type is annotated and use just the given function at n-1 as parameter // 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) || isFunction(returnedValue) ? returnedValue : instance; } return { invoke: invoke, instantiate: instantiate, get: getService, annotate: annotate, has: function(name) { return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); } }; } } /** * @ngdoc function * @name ng.$anchorScroll * @requires $window * @requires $location * @requires $rootScope * * @description * When called, it checks current value of `$location.hash()` and scroll to related element, * according to rules specified in * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}. * * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. * * @example
* return { * eventFn : function(element, done) { * //code to run the animation * //once complete, then run done() * return function cancellationFunction() { * //code to cancel the animation * } * } * } ** * @param {string} name The name of the animation. * @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); this.$$selectors[name.substr(1)] = key; $provide.factory(key, factory); }; 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. * * $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}. */ 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). * @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 */ enter : function(element, parent, after, done) { if (after) { after.after(element); } else { if (!parent || !parent[0]) { parent = after.parent(); } parent.append(element); } 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). * @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 */ 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 */ 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 * @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 */ addClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { 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 * @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 */ removeClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { jqLiteRemoveClass(element, className); }); done && $timeout(done, 0, false); }, enabled : noop }; }]; }]; /** * ! This is a private undocumented service ! * * @name ng.$browser * @requires $log * @description * This object has two goals: * * - hide all the global state in the browser caused by the window object * - abstract away all the browser specific features and inconsistencies * * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` * service, which can be used for convenient testing of the application without the interaction with * the real browser apis. */ /** * @param {object} window The global window object. * @param {object} document jQuery wrapped document. * @param {function()} XHR XMLHttpRequest constructor. * @param {object} $log console.log or an object with the same interface. * @param {object} $sniffer $sniffer service */ function Browser(window, document, $log, $sniffer) { var self = this, rawDocument = document[0], location = window.location, history = window.history, setTimeout = window.setTimeout, clearTimeout = window.clearTimeout, pendingDeferIds = {}; self.isMock = false; var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; // TODO(vojta): remove this temporary api self.$$completeOutstandingRequest = completeOutstandingRequest; self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; /** * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. */ function completeOutstandingRequest(fn) { try { fn.apply(null, sliceArgs(arguments, 1)); } finally { outstandingRequestCount--; if (outstandingRequestCount === 0) { while(outstandingRequestCallbacks.length) { try { outstandingRequestCallbacks.pop()(); } catch (e) { $log.error(e); } } } } } /** * @private * Note: this method is used only by scenario runner * TODO(vojta): prefix this method with $$ ? * @param {function()} callback Function that will be called when no outstanding request */ self.notifyWhenNoOutstandingRequests = function(callback) { // force browser to execute all pollFns - this is needed so that cookies and other pollers fire // at some deterministic time in respect to the test runner's actions. Leaving things up to the // regular poller would result in flaky tests. forEach(pollFns, function(pollFn){ pollFn(); }); if (outstandingRequestCount === 0) { callback(); } else { outstandingRequestCallbacks.push(callback); } }; ////////////////////////////////////////////////////////////// // Poll Watcher API ////////////////////////////////////////////////////////////// var pollFns = [], pollTimeout; /** * @name ng.$browser#addPollFn * @methodOf ng.$browser * * @param {function()} fn Poll function to add * * @description * Adds a function to the list of functions that poller periodically executes, * and starts polling if not started yet. * * @returns {function()} the added function */ self.addPollFn = function(fn) { if (isUndefined(pollTimeout)) startPoller(100, setTimeout); pollFns.push(fn); return fn; }; /** * @param {number} interval How often should browser call poll functions (ms) * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. * * @description * Configures the poller to run in the specified intervals, using the specified * setTimeout fn and kicks it off. */ function startPoller(interval, setTimeout) { (function check() { forEach(pollFns, function(pollFn){ pollFn(); }); pollTimeout = setTimeout(check, interval); })(); } ////////////////////////////////////////////////////////////// // URL API ////////////////////////////////////////////////////////////// var lastBrowserUrl = location.href, baseElement = document.find('base'), newLocation = null; /** * @name ng.$browser#url * @methodOf ng.$browser * * @description * GETTER: * Without any argument, this method just returns current value of location.href. * * SETTER: * With at least one argument, this method sets url to new value. * If html5 history api supported, pushState/replaceState is used, otherwise * location.href/location.replace is used. * Returns its own instance to allow chaining * * NOTE: this api is intended for use only by the $location service. Please use the * {@link ng.$location $location service} to change url. * * @param {string} url New url (when used as setter) * @param {boolean=} replace Should new url replace current history record ? */ self.url = function(url, replace) { // Android Browser BFCache causes location reference to become stale. if (location !== window.location) location = window.location; // setter if (url) { if (lastBrowserUrl == url) return; lastBrowserUrl = url; if ($sniffer.history) { if (replace) history.replaceState(null, '', url); else { history.pushState(null, '', url); // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 baseElement.attr('href', baseElement.attr('href')); } } else { newLocation = url; if (replace) { location.replace(url); } else { location.href = url; } } return self; // getter } else { // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href // methods not updating location.href synchronously. // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 return newLocation || location.href.replace(/%27/g,"'"); } }; var urlChangeListeners = [], urlChangeInit = false; function fireUrlChange() { newLocation = null; if (lastBrowserUrl == self.url()) return; lastBrowserUrl = self.url(); forEach(urlChangeListeners, function(listener) { listener(self.url()); }); } /** * @name ng.$browser#onUrlChange * @methodOf ng.$browser * @TODO(vojta): refactor to use node's syntax for events * * @description * Register callback function that will be called, when url changes. * * It's only called when the url is changed by outside of angular: * - user types different url into address bar * - user clicks on history (forward/back) button * - user clicks on a link * * It's not called when url is changed by $browser.url() method * * The listener gets called with new url as parameter. * * NOTE: this api is intended for use only by the $location service. Please use the * {@link ng.$location $location service} to monitor url changes in angular apps. * * @param {function(string)} listener Listener function to be called when url changes. * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. */ self.onUrlChange = function(callback) { if (!urlChangeInit) { // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) // don't fire popstate when user change the address bar and don't fire hashchange when url // changed by push/replaceState // html5 history api - popstate event if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange); // hashchange event if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange); // polling else self.addPollFn(fireUrlChange); urlChangeInit = true; } urlChangeListeners.push(callback); return callback; }; ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// /** * @name ng.$browser#baseHref * @methodOf ng.$browser * * @description * Returns current
* * var cache = $cacheFactory('cacheId'); * 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}); * ** * * @param {string} cacheId Name or id of the newly created cache. * @param {object=} options Options object that specifies the cache behavior. Properties: * * - `{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. * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. * - `{void}` `removeAll()` — Removes all cached values. * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * */ function $CacheFactoryProvider() { this.$get = function() { var caches = {}; function cacheFactory(cacheId, options) { if (cacheId in caches) { throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); } var size = 0, stats = extend({}, options, {id: cacheId}), data = {}, capacity = (options && options.capacity) || Number.MAX_VALUE, lruHash = {}, freshEnd = null, staleEnd = null; return caches[cacheId] = { put: function(key, value) { var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); refresh(lruEntry); if (isUndefined(value)) return; if (!(key in data)) size++; data[key] = value; if (size > capacity) { this.remove(staleEnd.key); } return value; }, get: function(key) { var lruEntry = lruHash[key]; if (!lruEntry) return; refresh(lruEntry); return data[key]; }, remove: function(key) { var lruEntry = lruHash[key]; if (!lruEntry) return; if (lruEntry == freshEnd) freshEnd = lruEntry.p; if (lruEntry == staleEnd) staleEnd = lruEntry.n; link(lruEntry.n,lruEntry.p); delete lruHash[key]; delete data[key]; size--; }, removeAll: function() { data = {}; size = 0; lruHash = {}; freshEnd = staleEnd = null; }, destroy: function() { data = null; stats = null; lruHash = null; delete caches[cacheId]; }, info: function() { return extend({}, stats, {size: size}); } }; /** * makes the `entry` the freshEnd of the LRU linked list */ function refresh(entry) { if (entry != freshEnd) { if (!staleEnd) { staleEnd = entry; } else if (staleEnd == entry) { staleEnd = entry.n; } link(entry.n, entry.p); link(entry, freshEnd); freshEnd = entry; freshEnd.n = null; } } /** * bidirectionally links two entries of the LRU linked list */ function link(nextEntry, prevEntry) { if (nextEntry != prevEntry) { if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify } } } /** * @ngdoc method * @name ng.$cacheFactory#info * @methodOf ng.$cacheFactory * * @description * Get information about all the of the caches that have been created * * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` */ cacheFactory.info = function() { var info = {}; forEach(caches, function(cache, cacheId) { info[cacheId] = cache.info(); }); return info; }; /** * @ngdoc method * @name ng.$cacheFactory#get * @methodOf ng.$cacheFactory * * @description * Get access to a cache object by the `cacheId` used when it was created. * * @param {string} cacheId Name or id of a cache to access. * @returns {object} Cache object identified by the cacheId or undefined if no such cache. */ cacheFactory.get = function(cacheId) { return caches[cacheId]; }; return cacheFactory; }; } /** * @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. * * Adding via the `script` tag: *
* * * * * ... * ** * **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: * *
* var myApp = angular.module('myApp', []); * myApp.run(function($templateCache) { * $templateCache.put('templateId.html', 'This is the content of the template'); * }); ** * To retrieve the template later, simply use it in your HTML: *
* ** * or get it via Javascript: *
* $templateCache.get('templateId.html') ** * See {@link ng.$cacheFactory $cacheFactory}. * */ function $TemplateCacheProvider() { this.$get = ['$cacheFactory', function($cacheFactory) { return $cacheFactory('templates'); }]; } /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! * * DOM-related variables: * * - "node" - DOM Node * - "element" - DOM Element or Node * - "$node" or "$element" - jqLite-wrapped node or element * * * Compiler related stuff: * * - "linkFn" - linking fn of a single directive * - "nodeLinkFn" - function that aggregates all linking fns for a particular node * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) */ /** * @ngdoc function * @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. * * The compilation is a process of walking the DOM tree and matching DOM elements to * {@link ng.$compileProvider#methods_directive directives}. * *
* var myModule = angular.module(...); * * myModule.directive('directiveName', function factory(injectables) { * var directiveDefinitionObject = { * priority: 0, * template: '', // 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; * }); ** *
* 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) { ... } * }); ** * * * ### 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. Pre-link functions * are also run in priority order, but post-link functions are run in reverse order. 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 `
* function compile(tElement, tAttrs, transclude) { ... } ** * 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` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` * *
* function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } ** * 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. * * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. * The scope can be overridden by an optional first argument. This is the same as the `$transclude` * parameter of directive controllers. * `function([scope], cloneLinkingFn)`. * * * #### 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. * * * ### 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`. * *
* 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); * }); * } ** * Below is an example using `$compileProvider`. * *
* var element = $compile('* * - if on the other hand, you need the element to be cloned, the view reference from the original * example would not point to the clone, but rather to the original template that was cloned. In * this case, you can access the clone via the cloneAttachFn: *{{total}}
')(scope); *
* var templateHTML = angular.element('* * * For information on how the compiler works, see the * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. */ var $compileMinErr = minErr('$compile'); /** * @ngdoc service * @name ng.$compileProvider * @function * * @description */ $CompileProvider.$inject = ['$provide']; function $CompileProvider($provide) { var hasDirectives = {}, Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//; // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes // The assumption is that future DOM event attribute names will begin with // 'on' and be composed of only English letters. var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; /** * @ngdoc function * @name ng.$compileProvider#directive * @methodOf ng.$compileProvider * @function * * @description * Register a new directive with the compiler. * * @param {string|Object} name Name of the directive in camel-case (i.e.{{total}}
'), * scope = ....; * * var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) { * //attach the clone to DOM document at the right place * }); * * //now we have reference to the cloned DOM via `clone` *
ngBind
which
* will match as ng-bind
), or an object map of directives where the keys are the
* names and the values are the factories.
* @param {function|Array} directiveFactory An injectable directive factory function. See
* {@link guide/directive} for more info.
* @returns {ng.$compileProvider} Self for chaining.
*/
this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertArg(directiveFactory, 'directiveFactory');
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
$provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
function($injector, $exceptionHandler) {
var directives = [];
forEach(hasDirectives[name], function(directiveFactory, index) {
try {
var directive = $injector.invoke(directiveFactory);
if (isFunction(directive)) {
directive = { compile: valueFn(directive) };
} else if (!directive.compile && directive.link) {
directive.compile = valueFn(directive.link);
}
directive.priority = directive.priority || 0;
directive.index = index;
directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'A';
directives.push(directive);
} catch (e) {
$exceptionHandler(e);
}
});
return directives;
}]);
}
hasDirectives[name].push(directiveFactory);
} else {
forEach(name, reverseParams(registerDirective));
}
return this;
};
/**
* @ngdoc function
* @name ng.$compileProvider#aHrefSanitizationWhitelist
* @methodOf ng.$compileProvider
* @function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
* 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.
*/
this.aHrefSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
aHrefSanitizationWhitelist = regexp;
return this;
}
return aHrefSanitizationWhitelist;
};
/**
* @ngdoc function
* @name ng.$compileProvider#imgSrcSanitizationWhitelist
* @methodOf ng.$compileProvider
* @function
*
* @description
* 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.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this.imgSrcSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
imgSrcSanitizationWhitelist = regexp;
return this;
}
return imgSrcSanitizationWhitelist;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
'$controller', '$rootScope', '$document', '$sce', '$animate',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
$controller, $rootScope, $document, $sce, $animate) {
var Attributes = function(element, attr) {
this.$$element = element;
this.$attr = attr || {};
};
Attributes.prototype = {
$normalize: directiveNormalize,
/**
* @ngdoc function
* @name ng.$compile.directive.Attributes#$addClass
* @methodOf ng.$compile.directive.Attributes
* @function
*
* @description
* Adds the CSS class value specified by the classVal parameter to the element. If animations
* are enabled then an animation will be triggered for the class addition.
*
* @param {string} classVal The className value that will be added to the element
*/
$addClass : function(classVal) {
if(classVal && classVal.length > 0) {
$animate.addClass(this.$$element, classVal);
}
},
/**
* @ngdoc function
* @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.
*
* @param {string} classVal The className value that will be removed from the element
*/
$removeClass : function(classVal) {
if(classVal && classVal.length > 0) {
$animate.removeClass(this.$$element, classVal);
}
},
/**
* @ngdoc function
* @name ng.$compile.directive.Attributes#$updateClass
* @methodOf ng.$compile.directive.Attributes
* @function
*
* @description
* Adds and removes the appropriate CSS class values to the element based on the difference
* between the new and old CSS class values (specified as newClasses and oldClasses).
*
* @param {string} newClasses The current CSS className value
* @param {string} oldClasses The former CSS className value
*/
$updateClass : function(newClasses, oldClasses) {
this.$removeClass(tokenDifference(oldClasses, newClasses));
this.$addClass(tokenDifference(newClasses, oldClasses));
},
/**
* Set a normalized attribute on the element in a way such that all directives
* can share the attribute. This function properly handles boolean attributes.
* @param {string} key Normalized key. (ie ngAttribute)
* @param {string|boolean} value The value to set. If `null` attribute will be deleted.
* @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
* Defaults to true.
* @param {string=} attrName Optional none normalized name. Defaults to key.
*/
$set: function(key, value, writeAttr, attrName) {
// TODO: decide whether or not to throw an error if "class"
//is set through this function since it may cause $updateClass to
//become unstable.
var booleanKey = getBooleanAttrName(this.$$element[0], key),
normalizedVal,
nodeName;
if (booleanKey) {
this.$$element.prop(key, value);
attrName = booleanKey;
}
this[key] = value;
// translate normalized key to actual key
if (attrName) {
this.$attr[key] = attrName;
} else {
attrName = this.$attr[key];
if (!attrName) {
this.$attr[key] = attrName = snake_case(key, '-');
}
}
nodeName = nodeName_(this.$$element);
// sanitize a[href] and img[src] values
if ((nodeName === 'A' && key === 'href') ||
(nodeName === 'IMG' && key === 'src')) {
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
if (!msie || msie >= 8 ) {
normalizedVal = urlResolve(value).href;
if (normalizedVal !== '') {
if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
(key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
this[key] = value = 'unsafe:' + normalizedVal;
}
}
}
}
if (writeAttr !== false) {
if (value === null || value === undefined) {
this.$$element.removeAttr(attrName);
} else {
this.$$element.attr(attrName, value);
}
}
// fire observers
var $$observers = this.$$observers;
$$observers && forEach($$observers[key], function(fn) {
try {
fn(value);
} catch (e) {
$exceptionHandler(e);
}
});
},
/**
* @ngdoc function
* @name ng.$compile.directive.Attributes#$observe
* @methodOf ng.$compile.directive.Attributes
* @function
*
* @description
* Observes an interpolated attribute.
*
* The observer function will be invoked once during the next `$digest` following
* compilation. The observer is then invoked whenever the interpolated value
* changes.
*
* @param {string} key Normalized key. (ie ngAttribute) .
* @param {function(interpolatedValue)} fn Function that will be called whenever
the interpolated value of the attribute changes.
* See the {@link guide/directive#Attributes Directives} guide for more info.
* @returns {function()} the `fn` parameter.
*/
$observe: function(key, fn) {
var attrs = this,
$$observers = (attrs.$$observers || (attrs.$$observers = {})),
listeners = ($$observers[key] || ($$observers[key] = []));
listeners.push(fn);
$rootScope.$evalAsync(function() {
if (!listeners.$$inter) {
// no one registered attribute interpolation function, so lets call it manually
fn(attrs[key]);
}
});
return fn;
}
};
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
: function denormalizeTemplate(template) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
},
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
return compile;
//================================
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.
$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
forEach($compileNodes, function(node, index){
if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
$compileNodes[index] = node = jqLite(node).wrap('').parent()[0];
}
});
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
assertArg(scope, 'scope');
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
// and sometimes changes the structure of the DOM.
var $linkNode = cloneConnectFn
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
: $compileNodes;
forEach(transcludeControllers, function(instance, name) {
$linkNode.data('$' + name + 'Controller', instance);
});
// Attach scope only to non-text nodes.
for(var i = 0, ii = $linkNode.length; i* angular.module('exceptionOverride', []).factory('$exceptionHandler', function () { * return function (exception, cause) { * exception.message += ' (caused by "' + cause + '")'; * throw exception; * }; * }); ** * This example will override the normal action of `$exceptionHandler`, to make angular * exceptions fail hard when they happen, instead of just logging to the console. * * @param {Error} exception Exception associated with the error. * @param {string=} cause optional information about the context in which * the error was thrown. * */ function $ExceptionHandlerProvider() { this.$get = ['$log', function($log) { return function(exception, cause) { $log.error.apply($log, arguments); }; }]; } /** * Parse headers into key value object * * @param {string} headers Raw headers as a string * @returns {Object} Parsed headers as key value object */ function parseHeaders(headers) { var parsed = {}, key, val, i; if (!headers) return parsed; forEach(headers.split('\n'), function(line) { i = line.indexOf(':'); key = lowercase(trim(line.substr(0, i))); val = trim(line.substr(i + 1)); if (key) { if (parsed[key]) { parsed[key] += ', ' + val; } else { parsed[key] = val; } } }); return parsed; } /** * Returns a function that provides access to parsed headers. * * Headers are lazy parsed when first requested. * @see parseHeaders * * @param {(string|Object)} headers Headers to provide access to. * @returns {function(string=)} Returns a getter function which if called with: * * - if called with single an argument returns a single header value or null * - if called with no arguments returns an object containing all headers. */ function headersGetter(headers) { var headersObj = isObject(headers) ? headers : undefined; return function(name) { if (!headersObj) headersObj = parseHeaders(headers); if (name) { return headersObj[lowercase(name)] || null; } return headersObj; }; } /** * Chain all given functions * * This function is used for both request and response transforming * * @param {*} data Data to transform. * @param {function(string=)} headers Http headers getter fn. * @param {(function|Array.
* $http({method: 'GET', url: '/someUrl'}). * success(function(data, status, headers, config) { * // this callback will be called asynchronously * // when the response is available * }). * error(function(data, status, headers, config) { * // called asynchronously if an error occurs * // or server returns response with an error status. * }); ** * Since the returned value of calling the $http function is a `promise`, you can also use * the `then` method to register callbacks, and these callbacks will receive a single argument – * an object representing the response. See the API signature and type info below for more * details. * * A response status code between 200 and 299 is considered a success status and * 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. * * ``` * $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. * * ``` * $httpBackend.expectGET(...); * $scope.$apply(function() { * $http.get(...); * }); * $httpBackend.flush(); * ``` * * # Shortcut methods * * Since all invocations of the $http service require passing in an HTTP method and URL, and * POST/PUT requests require request data to be provided as well, shortcut methods * were created: * *
* $http.get('/someUrl').success(successCallback); * $http.post('/someUrl', data).success(successCallback); ** * Complete list of shortcut methods: * * - {@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 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration * object, which currently contains this default configuration: * * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): * - `Accept: application/json, text/plain, * / *` * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) * - `Content-Type: application/json` * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) * - `Content-Type: application/json` * * 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' }. * * 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. * * 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 * 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`. * * * # Caching * * To enable caching, set the request configuration `cache` property to `true` (to use default * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). * When the cache is enabled, `$http` stores the response from the server in the specified * cache. The next time the same request is made, the response is served from the cache without * sending a request to the server. * * Note that even if the response is served from cache, delivery of the data is asynchronous in * the same way that real requests are. * * If there are multiple GET requests for the same URL that should be cached using the same * cache, but the cache is not populated yet, only one request to the server will be made and * the remaining requests will be fulfilled using the response from the first request. * * You can change the default cache to a new object (built with * {@link ng.$cacheFactory `$cacheFactory`}) by updating the * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set * their `cache` property to `true` will now use this cache object. * * If you set the default cache to `false` then only requests that specify their own custom * cache object will be cached. * * # Interceptors * * Before you start creating interceptors, be sure to understand the * {@link ng.$q $q and deferred/promise APIs}. * * For purposes of global error handling, authentication, or any kind of synchronous or * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be * able to intercept requests before they are handed to the server and * responses before they are handed over to the application code that * initiated these requests. The interceptors leverage the {@link ng.$q * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. * * The interceptors are service factories that are registered with the `$httpProvider` by * 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. * * *
* // register the interceptor as a service * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { * return { * // optional method * 'request': function(config) { * // do something on success * return config || $q.when(config); * }, * * // optional method * 'requestError': function(rejection) { * // do something on error * if (canRecover(rejection)) { * return responseOrNewPromise * } * return $q.reject(rejection); * }, * * * * // optional method * 'response': function(response) { * // do something on success * return response || $q.when(response); * }, * * // optional method * 'responseError': function(rejection) { * // do something on error * if (canRecover(rejection)) { * return responseOrNewPromise * } * return $q.reject(rejection); * }; * } * }); * * $httpProvider.interceptors.push('myHttpInterceptor'); * * * // register the interceptor via an anonymous factory * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { * return { * 'request': function(config) { * // same as above * }, * 'response': function(response) { * // same as above * } * }; * }); ** * # Response interceptors (DEPRECATED) * * Before you start creating interceptors, be sure to understand the * {@link ng.$q $q and deferred/promise APIs}. * * For purposes of global error handling, authentication or any kind of synchronous or * asynchronous preprocessing of received responses, it is desirable to be able to intercept * responses for http requests before they are handed over to the application code that * initiated these requests. The response interceptors leverage the {@link ng.$q * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. * * The interceptors are service factories that are registered with the $httpProvider by * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and * injected with dependencies (if specified) and returns the interceptor — a function that * takes a {@link ng.$q promise} and returns the original or a new promise. * *
* // register the interceptor as a service * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { * return function(promise) { * return promise.then(function(response) { * // do something on success * return response; * }, function(response) { * // do something on error * if (canRecover(response)) { * return responseOrNewPromise * } * return $q.reject(response); * }); * } * }); * * $httpProvider.responseInterceptors.push('myHttpInterceptor'); * * * // register the interceptor via an anonymous factory * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) { * return function(promise) { * // same as above * } * }); ** * * # Security Considerations * * When designing web applications, consider security threats from: * * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx * JSON vulnerability} * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} * * Both server and the client must cooperate in order to eliminate these threats. Angular comes * pre-configured with strategies that address these issues, but for this to work backend server * cooperation is required. * * ## JSON Vulnerability Protection * * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx * JSON vulnerability} allows third party website to turn your JSON resource URL into * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To * counter this your server can prefix all JSON requests with following string `")]}',\n"`. * Angular will automatically strip the prefix before processing it as JSON. * * For example if your server needs to return: *
* ['one','two'] ** * which is vulnerable to attack, your server can return: *
* )]}', * ['one','two'] ** * Angular will strip the prefix, before processing the JSON. * * * ## Cross Site Request Forgery (XSRF) Protection * * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which * an unauthorized site can gain your user's private data. Angular provides a mechanism * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only * JavaScript that runs on your domain could read the cookie, your server can be assured that * the XHR came from JavaScript running on your domain. The header will not be set for * cross-domain requests. * * 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. * * 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.