/** * @license AngularJS v1.2.0-rc.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 (isFunction(obj)) { return obj.toString().replace(/ \{[\s\S]*$/, ''); } else if (isUndefined(obj)) { return 'undefined'; } else if (!isString(obj)) { 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 (isFunction(arg)) { return arg.toString().replace(/ ?\{[\s\S]*$/, ''); } else if (isUndefined(arg)) { return 'undefined'; } else if (!isString(arg)) { return toJson(arg); } return arg; } return match; }); message = message + '\nhttp://errors.angularjs.org/' + version.full + '/' + (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); }; } //////////////////////////////////// /** * hasOwnProperty may be overwritten by a property of the same name, or entirely * absent from an object that does not inherit Object.prototype; this copy is * used instead */ var hasOwnPropertyFn = Object.prototype.hasOwnProperty; var hasOwnPropertyLocal = function(obj, key) { return hasOwnPropertyFn.call(obj, key); }; /** * @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) { return isString(s) ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) : s; }; var manualUppercase = function(s) { 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, ...) */ function isArrayLike(obj) { if (obj == null || isWindow(obj)) { return false; } var length = obj.length; if (obj.nodeType === 1 && length) { return true; } return isArray(obj) || !isFunction(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 size = 0, key;
if (isArray(obj) || isString(obj)) {
return obj.length;
} else if (isObject(obj)){
for (key in obj)
if (!ownPropsOnly || obj.hasOwnProperty(key))
size++;
}
return size;
}
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, `source` is returned.
*
* Note: this function is used to augment the Object type in Angular expressions. See
* {@link ng.$filter} for more information about Angular arrays.
*
* @param {*} source The source that will be used to make a copy.
* Can be any type, including primitives, `null`, and `undefined`.
* @param {(Object|Array)=} destination Destination into which the source is copied. If
* provided, must be of the same type as `source`.
* @returns {*} The copy or updated `destination`, if `destination` was specified.
*/
function copy(source, destination){
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws', "Can't copy! Making copies of Window or Scope instances is not supported.");
}
if (!destination) {
destination = source;
if (source) {
if (isArray(source)) {
destination = copy(source, []);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source);
} else if (isObject(source)) {
destination = copy(source, {});
}
}
} else {
if (source === destination) throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
if (isArray(source)) {
destination.length = 0;
for ( var i = 0; i < source.length; i++) {
destination.push(copy(source[i]));
}
} else {
var h = destination.$$hashKey;
forEach(destination, function(value, key){
delete destination[key];
});
for ( var key in source) {
destination[key] = copy(source[key]);
}
setHashKey(destination,h);
}
}
return destination;
}
/**
* Create a shallow copy of an object
*/
function shallowCopy(src, dst) {
dst = dst || {};
for(var key in src) {
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc function
* @name angular.equals
* @function
*
* @description
* Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and
* objects.
*
* Two objects or values are considered equivalent if at least one of the following is true:
*
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
* * Both values represent the same regular expression (In JavasScript,
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
* representation matches).
*
* During a property comparison, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
* Scope and DOMWindow objects are being compared only by identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
* @returns {boolean} True if arguments are equal.
*/
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
if (t1 == t2) {
if (t1 == 'object') {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) == o2.length) {
for(key=0; key
* // 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} name Controller name.
* @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} name directive name
* @param {Function} directiveFactory Factory function for creating new instance of
* directives.
* @description
* See {@link ng.$compileProvider#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;
}
}
});
};
});
}
/**
* @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.0-rc.2', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 2,
dot: 0,
codeName: 'barehand-atomsplitting'
};
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,
'$$minErr': minErr,
'version': version,
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0}
});
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,
ngCsp: ngCspDirective,
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,
$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,
$$urlUtils: $$UrlUtilsProvider
});
}
]);
}
//////////////////////////////////
//JQLite
//////////////////////////////////
/**
* @ngdoc function
* @name angular.element
* @function
*
* @description
* Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
* `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if
* jQuery is available, or a function that wraps the element or string in Angular's jQuery lite
* implementation (commonly referred to as jqLite).
*
* Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded`
* event fired.
*
* jqLite is a tiny, API-compatible subset of jQuery that allows
* Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality
* within a very small footprint, so only a subset of the jQuery API - methods, arguments and
* invocation styles - are supported.
*
* Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
* raw DOM references.
*
* ## Angular's jqLite
* Angular's lite version of jQuery provides only the following jQuery methods:
*
* - [addClass()](http://api.jquery.com/addClass/)
* - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/)
* - [bind()](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
* - [children()](http://api.jquery.com/children/) - Does not support selectors
* - [clone()](http://api.jquery.com/clone/)
* - [contents()](http://api.jquery.com/contents/)
* - [css()](http://api.jquery.com/css/)
* - [data()](http://api.jquery.com/data/)
* - [eq()](http://api.jquery.com/eq/)
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [hasClass()](http://api.jquery.com/hasClass/)
* - [html()](http://api.jquery.com/html/)
* - [next()](http://api.jquery.com/next/) - Does not support selectors
* - [on()](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
* - [off()](http://api.jquery.com/off/) - Does not support namespaces or selectors
* - [parent()](http://api.jquery.com/parent/) - Does not support selectors
* - [prepend()](http://api.jquery.com/prepend/)
* - [prop()](http://api.jquery.com/prop/)
* - [ready()](http://api.jquery.com/ready/)
* - [remove()](http://api.jquery.com/remove/)
* - [removeAttr()](http://api.jquery.com/removeAttr/)
* - [removeClass()](http://api.jquery.com/removeClass/)
* - [removeData()](http://api.jquery.com/removeData/)
* - [replaceWith()](http://api.jquery.com/replaceWith/)
* - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/)
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
* - [unbind()](http://api.jquery.com/off/) - Does not support namespaces
* - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/)
*
* ## jQuery/jqLite Extras
* Angular also provides the following additional methods and events to both jQuery and jqLite:
*
* ### Events
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
* on all DOM nodes being removed. This can be used to clean up and 3rd party bindings to the DOM
* element before it is removed.
* ### Methods
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
* retrieves controller associated with the `ngController` directive. If `name` is provided as
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
* `'ngModel'`).
* - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
* element or its parent.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
* @returns {Object} jQuery object.
*/
var jqCache = JQLite.cache = {},
jqName = JQLite.expando = 'ng-' + new Date().getTime(),
jqId = 1,
addEventListenerFn = (window.document.addEventListener
? function(element, type, fn) {element.addEventListener(type, fn, false);}
: function(element, type, fn) {element.attachEvent('on' + type, fn);}),
removeEventListenerFn = (window.document.removeEventListener
? function(element, type, fn) {element.removeEventListener(type, fn, false); }
: function(element, type, fn) {element.detachEvent('on' + type, fn); });
function jqNextId() { return ++jqId; }
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
var jqLiteMinErr = minErr('jqLite');
/**
* Converts snake_case to camelCase.
* Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize
*/
function camelCase(name) {
return name.
replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
}).
replace(MOZ_HACK_REGEXP, 'Moz$1');
}
/////////////////////////////////////////////
// jQuery mutation patch
//
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
// $destroy event on all DOM nodes being removed.
//
/////////////////////////////////////////////
function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
var originalJqFn = jQuery.fn[name];
originalJqFn = originalJqFn.$original || originalJqFn;
removePatch.$original = originalJqFn;
jQuery.fn[name] = removePatch;
function removePatch(param) {
var list = filterElems && param ? [this.filter(param)] : [this],
fireEvent = dispatchThis,
set, setIndex, setLength,
element, childIndex, childLength, children;
if (!getterIfNoArguments || param != null) {
while(list.length) {
set = list.shift();
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
element = jqLite(set[setIndex]);
if (fireEvent) {
element.triggerHandler('$destroy');
} else {
fireEvent = !fireEvent;
}
for(childIndex = 0, childLength = (children = element.children()).length;
childIndex < childLength;
childIndex++) {
list.push(jQuery(children[childIndex]));
}
}
}
}
return originalJqFn.apply(this, arguments);
}
}
/////////////////////////////////////////////
function JQLite(element) {
if (element instanceof JQLite) {
return element;
}
if (!(this instanceof JQLite)) {
if (isString(element) && element.charAt(0) != '<') {
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
}
return new JQLite(element);
}
if (isString(element)) {
var div = document.createElement('div');
// Read about the NoScope elements here:
// http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
div.innerHTML = '
* // 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. The function arguments come form the function annotation.
* @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.
* function GreetProvider() {
* var salutation = 'Hello';
*
* this.salutation = function(text) {
* salutation = text;
* };
*
* this.$get = function() {
* return function (name) {
* return salutation + ' ' + name + '!';
* };
* };
* }
*
* describe('Greeter', function(){
*
* beforeEach(module(function($provide) {
* $provide.provider('greet', GreetProvider);
* }));
*
* it('should greet', inject(function(greet) {
* expect(greet('angular')).toEqual('Hello angular!');
* }));
*
* it('should allow configuration of salutation', function() {
* module(function(greetProvider) {
* greetProvider.salutation('Ahoj');
* });
* inject(function(greet) {
* expect(greet('angular')).toEqual('Ahoj angular!');
* });
* });
*
*/
/**
* @ngdoc method
* @name AUTO.$provide#provider
* @methodOf AUTO.$provide
* @description
*
* Register a provider for a service. The providers can be retrieved and can have additional configuration methods.
*
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key.
* @param {(Object|function())} provider If the provider is:
*
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
* - `Constructor`: a new instance of the provider will be created using
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
*
* @returns {Object} registered provider instance
*/
/**
* @ngdoc method
* @name AUTO.$provide#factory
* @methodOf AUTO.$provide
* @description
*
* A short hand for configuring services if only `$get` method is required.
*
* @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
*/
/**
* @ngdoc method
* @name AUTO.$provide#service
* @methodOf AUTO.$provide
* @description
*
* A short hand for registering service of given class.
*
* @param {string} name The name of the instance.
* @param {Function} constructor A class (constructor function) that will be instantiated.
* @returns {Object} registered provider instance
*/
/**
* @ngdoc method
* @name AUTO.$provide#value
* @methodOf AUTO.$provide
* @description
*
* A short hand for configuring services if the `$get` method is a constant.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
* @returns {Object} registered provider instance
*/
/**
* @ngdoc method
* @name AUTO.$provide#constant
* @methodOf AUTO.$provide
* @description
*
* A constant value, but unlike {@link AUTO.$provide#value value} it can be injected
* into configuration function (other modules) and it is not interceptable by
* {@link AUTO.$provide#decorator decorator}.
*
* @param {string} name The name of the constant.
* @param {*} value The constant value.
* @returns {Object} registered instance
*/
/**
* @ngdoc method
* @name AUTO.$provide#decorator
* @methodOf AUTO.$provide
* @description
*
* Decoration of service, allows the decorator to intercept the service instance creation. The
* returned instance may be the original instance, or a new instance which delegates to the
* original instance.
*
* @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. 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.
*/
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_) {
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, value) { return factory(name, valueFn(value)); }
function constant(name, value) {
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 = [];
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
try {
if (isString(module)) {
var moduleFn = angularModule(module);
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
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.
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) ? 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 scroll whenever it changes to match any anchor.
* This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
*/
function $AnchorScrollProvider() {
var autoScrollingEnabled = true;
this.disableAutoScrolling = function() {
autoScrollingEnabled = false;
};
this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
var document = $window.document;
// helper function to get first anchor from a NodeList
// can't use filter.filter, as it accepts only instances of Array
// and IE can't convert NodeList to an array using [].slice
// TODO(vojta): use filter if we change it to accept lists as well
function getFirstAnchor(list) {
var result = null;
forEach(list, function(element) {
if (!result && lowercase(element.nodeName) === 'a') result = element;
});
return result;
}
function scroll() {
var hash = $location.hash(), elm;
// empty hash, scroll to the top of the page
if (!hash) $window.scrollTo(0, 0);
// element with given id
else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
// first anchor with given name :-D
else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
// no element and hash == 'top', scroll to the top of the page
else if (hash === 'top') $window.scrollTo(0, 0);
}
// does not scroll when user clicks on anchor link that is currently on
// (no url change, no $location.hash() change), browser native does scroll
if (autoScrollingEnabled) {
$rootScope.$watch(function autoScrollWatch() {return $location.hash();},
function autoScrollWatchAction() {
$rootScope.$evalAsync(scroll);
});
}
return scroll;
}];
}
var $animateMinErr = minErr('$animate');
/**
* @ngdoc object
* @name ng.$animateProvider
*
* @description
* Default implementation of $animate that doesn't perform any animations, instead just synchronously performs DOM
* updates and calls done() callbacks.
*
* In order to enable animations the ngAnimate module has to be loaded.
*
* To see the functional implementation check out src/ngAnimate/animate.js
*/
var $AnimateProvider = ['$provide', function($provide) {
this.$$selectors = {};
/**
* @ngdoc function
* @name ng.$animateProvider#register
* @methodOf ng.$animateProvider
*
* @description
* Registers a new injectable animation factory function. The factory function produces the animation object which
* contains callback functions for each event that is expected to be animated.
*
* * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` must be called once the
* element animation is complete. If a function is returned then the animation service will use this function to
* cancel the animation whenever a cancel event is triggered.
*
*
*
* 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, 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) {
var afterNode = after && after[after.length - 1];
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
// IE does not like undefined so we have to pass null.
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
forEach(element, function(node) {
parentNode.insertBefore(node, afterNextSibling);
});
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 it's 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(' ') : '';
element.addClass(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(' ') : '';
element.removeClass(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'),
replacedUrl = 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) {
// 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 {
if (replace) {
location.replace(url);
replacedUrl = url;
} else {
location.href = url;
replacedUrl = null;
}
}
return self;
// getter
} else {
// - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
// location.href synchronously
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return replacedUrl || location.href.replace(/%27/g,"'");
}
};
var urlChangeListeners = [],
urlChangeInit = false;
function fireUrlChange() {
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
//////////////////////////////////////////////////////////////
/**
* 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");
*
* expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
*
*
*
*
* @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 trying to match DOM elements to
* {@link ng.$compileProvider#directive directives}. For each match it
* executes corresponding template function and collects the
* instance functions into a single template function which is then returned.
*
* The template function can then be used once to produce the view or as it is the case with
* {@link ng.directive:ngRepeat repeater} many-times, in which
* case each call results in a view that is a DOM clone of the original template.
*
* var element = $compile('{{total}}
')(scope);
*
*
* - 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:
*
* var templateHTML = angular.element('{{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`
*
*
*
* 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|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} name Name of the directive in camel-case. (ie ngBind which will match as
* ng-bind).
* @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) {
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) {
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.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', '$$urlUtils', '$animate',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
$controller, $rootScope, $document, $sce, $$urlUtils, $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);
}
},
/**
* 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) {
//special case for class attribute addition + removal
//so that class changes can tap into the animation
//hooks provided by the $animate service
if(key == 'class') {
value = value || '';
var current = this.$$element.attr('class') || '';
this.$removeClass(tokenDifference(current, value).join(' '));
this.$addClass(tokenDifference(value, current).join(' '));
} else {
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: $$urlUtils.resolve() doesn't support IE < 8 so we don't sanitize for that case.
if (!msie || msie >= 8 ) {
normalizedVal = $$urlUtils.resolve(value);
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);
}
});
function tokenDifference(str1, str2) {
var values = [],
tokens1 = str1.split(/\s+/),
tokens2 = str2.split(/\s+/);
outer:
for(var i=0;i
* $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.
*
* # 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#get $http.get}
* - {@link ng.$http#head $http.head}
* - {@link ng.$http#post $http.post}
* - {@link ng.$http#put $http.put}
* - {@link ng.$http#delete $http.delete}
* - {@link ng.$http#jsonp $http.jsonp}
*
*
* # 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'`.
*
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
* fashion.
*
*
* # 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 configuration property `cache` to `true`. When the cache is
* enabled, `$http` stores the response from the server in local cache. Next time 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.
*
* A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
* To skip it, set configuration property `cache` to `false`.
*
*
* # 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.