mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
This commit contained broken tests and was not ready to be merged.
(reverted from commit 814c9847e8)
1591 lines
45 KiB
JavaScript
1591 lines
45 KiB
JavaScript
'use strict';
|
|
|
|
/* We need to tell jshint what variables are being exported */
|
|
/* global angular: true,
|
|
msie: true,
|
|
jqLite: true,
|
|
jQuery: true,
|
|
slice: true,
|
|
splice: true,
|
|
push: true,
|
|
toString: true,
|
|
ngMinErr: true,
|
|
angularModule: true,
|
|
uid: true,
|
|
REGEX_STRING_REGEXP: true,
|
|
VALIDITY_STATE_PROPERTY: true,
|
|
|
|
lowercase: true,
|
|
uppercase: true,
|
|
manualLowercase: true,
|
|
manualUppercase: true,
|
|
nodeName_: true,
|
|
isArrayLike: true,
|
|
forEach: true,
|
|
sortedKeys: true,
|
|
forEachSorted: true,
|
|
reverseParams: true,
|
|
nextUid: true,
|
|
setHashKey: true,
|
|
extend: true,
|
|
int: true,
|
|
inherit: true,
|
|
noop: true,
|
|
identity: true,
|
|
valueFn: true,
|
|
isUndefined: true,
|
|
isDefined: true,
|
|
isObject: true,
|
|
isString: true,
|
|
isNumber: true,
|
|
isDate: true,
|
|
isArray: true,
|
|
isFunction: true,
|
|
isRegExp: true,
|
|
isWindow: true,
|
|
isScope: true,
|
|
isFile: true,
|
|
isBlob: true,
|
|
isBoolean: true,
|
|
isPromiseLike: true,
|
|
trim: true,
|
|
escapeForRegexp: true,
|
|
isElement: true,
|
|
makeMap: true,
|
|
includes: true,
|
|
arrayRemove: true,
|
|
copy: true,
|
|
shallowCopy: true,
|
|
equals: true,
|
|
csp: true,
|
|
concat: true,
|
|
sliceArgs: true,
|
|
bind: true,
|
|
toJsonReplacer: true,
|
|
toJson: true,
|
|
fromJson: true,
|
|
startingTag: true,
|
|
tryDecodeURIComponent: true,
|
|
parseKeyValue: true,
|
|
toKeyValue: true,
|
|
encodeUriSegment: true,
|
|
encodeUriQuery: true,
|
|
angularInit: true,
|
|
bootstrap: true,
|
|
getTestability: true,
|
|
snake_case: true,
|
|
bindJQuery: true,
|
|
assertArg: true,
|
|
assertArgFn: true,
|
|
assertNotHasOwnProperty: true,
|
|
getter: true,
|
|
getBlockNodes: true,
|
|
hasOwnProperty: true,
|
|
createMap: true,
|
|
|
|
NODE_TYPE_ELEMENT: true,
|
|
NODE_TYPE_TEXT: true,
|
|
NODE_TYPE_COMMENT: true,
|
|
NODE_TYPE_DOCUMENT: true,
|
|
NODE_TYPE_DOCUMENT_FRAGMENT: true,
|
|
*/
|
|
|
|
////////////////////////////////////
|
|
|
|
/**
|
|
* @ngdoc module
|
|
* @name ng
|
|
* @module ng
|
|
* @description
|
|
*
|
|
* # ng (core module)
|
|
* The ng module is loaded by default when an AngularJS application is started. The module itself
|
|
* contains the essential components for an AngularJS application to function. The table below
|
|
* lists a high level breakdown of each of the services/factories, filters, directives and testing
|
|
* components available within this core module.
|
|
*
|
|
* <div doc-module-components="ng"></div>
|
|
*/
|
|
|
|
var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
|
|
|
|
// The name of a form control's ValidityState property.
|
|
// This is used so that it's possible for internal tests to create mock ValidityStates.
|
|
var VALIDITY_STATE_PROPERTY = 'validity';
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.lowercase
|
|
* @module ng
|
|
* @kind 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;};
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.uppercase
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description Converts the specified string to uppercase.
|
|
* @param {string} string String to be converted to uppercase.
|
|
* @returns {string} Uppercased string.
|
|
*/
|
|
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
|
|
|
|
|
|
var manualLowercase = function(s) {
|
|
/* jshint bitwise: false */
|
|
return isString(s)
|
|
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
|
|
: s;
|
|
};
|
|
var manualUppercase = function(s) {
|
|
/* jshint bitwise: false */
|
|
return isString(s)
|
|
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
|
|
: s;
|
|
};
|
|
|
|
|
|
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
|
|
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
|
|
// with correct but slower alternatives.
|
|
if ('i' !== 'I'.toLowerCase()) {
|
|
lowercase = manualLowercase;
|
|
uppercase = manualUppercase;
|
|
}
|
|
|
|
|
|
var
|
|
msie, // holds major version number for IE, or NaN if UA is not IE.
|
|
jqLite, // delay binding since jQuery could be loaded after us.
|
|
jQuery, // delay binding
|
|
slice = [].slice,
|
|
splice = [].splice,
|
|
push = [].push,
|
|
toString = Object.prototype.toString,
|
|
ngMinErr = minErr('ng'),
|
|
|
|
/** @name angular */
|
|
angular = window.angular || (window.angular = {}),
|
|
angularModule,
|
|
uid = 0;
|
|
|
|
/**
|
|
* documentMode is an IE-only property
|
|
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
|
|
*/
|
|
msie = document.documentMode;
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @param {*} obj
|
|
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
|
|
* String ...)
|
|
*/
|
|
function isArrayLike(obj) {
|
|
if (obj == null || isWindow(obj)) {
|
|
return false;
|
|
}
|
|
|
|
var length = obj.length;
|
|
|
|
if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
|
|
return true;
|
|
}
|
|
|
|
return isString(obj) || isArray(obj) || length === 0 ||
|
|
typeof length === 'number' && length > 0 && (length - 1) in obj;
|
|
}
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.forEach
|
|
* @module ng
|
|
* @kind 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, obj)`, where `value`
|
|
* is the value of an object property or an array element, `key` is the object property key or
|
|
* array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
|
|
*
|
|
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
|
|
* using the `hasOwnProperty` method.
|
|
*
|
|
* Unlike ES262's
|
|
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
|
|
* Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
|
|
* return the value provided.
|
|
*
|
|
```js
|
|
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, length;
|
|
if (obj) {
|
|
if (isFunction(obj)) {
|
|
for (key in obj) {
|
|
// Need to check if hasOwnProperty exists,
|
|
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
|
|
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
|
|
iterator.call(context, obj[key], key, obj);
|
|
}
|
|
}
|
|
} else if (isArray(obj) || isArrayLike(obj)) {
|
|
var isPrimitive = typeof obj !== 'object';
|
|
for (key = 0, length = obj.length; key < length; key++) {
|
|
if (isPrimitive || key in obj) {
|
|
iterator.call(context, obj[key], key, obj);
|
|
}
|
|
}
|
|
} else if (obj.forEach && obj.forEach !== forEach) {
|
|
obj.forEach(iterator, context, obj);
|
|
} else {
|
|
for (key in obj) {
|
|
if (obj.hasOwnProperty(key)) {
|
|
iterator.call(context, obj[key], key, obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
function sortedKeys(obj) {
|
|
return Object.keys(obj).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.
|
|
*
|
|
* Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
|
|
* we hit number precision issues in JavaScript.
|
|
*
|
|
* Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
|
|
*
|
|
* @returns {number} an unique alpha-numeric string
|
|
*/
|
|
function nextUid() {
|
|
return ++uid;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
|
|
* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
|
|
* by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
|
|
* Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
|
|
*
|
|
* @param {Object} dst Destination object.
|
|
* @param {...Object} src Source object(s).
|
|
* @returns {Object} Reference to `dst`.
|
|
*/
|
|
function extend(dst) {
|
|
var h = dst.$$hashKey;
|
|
|
|
for (var i = 1, ii = arguments.length; i < ii; i++) {
|
|
var obj = arguments[i];
|
|
if (obj) {
|
|
var keys = Object.keys(obj);
|
|
for (var j = 0, jj = keys.length; j < jj; j++) {
|
|
var key = keys[j];
|
|
dst[key] = obj[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
setHashKey(dst, h);
|
|
return dst;
|
|
}
|
|
|
|
function int(str) {
|
|
return parseInt(str, 10);
|
|
}
|
|
|
|
|
|
function inherit(parent, extra) {
|
|
return extend(Object.create(parent), extra);
|
|
}
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.noop
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* A function that performs no operations. This function can be useful when writing code in the
|
|
* functional style.
|
|
```js
|
|
function foo(callback) {
|
|
var result = calculateResult();
|
|
(callback || angular.noop)(result);
|
|
}
|
|
```
|
|
*/
|
|
function noop() {}
|
|
noop.$inject = [];
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.identity
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* A function that returns its first argument. This function is useful when writing code in the
|
|
* functional style.
|
|
*
|
|
```js
|
|
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
|
|
* @module ng
|
|
* @kind 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
|
|
* @module ng
|
|
* @kind 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
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
|
|
* considered to be objects. Note that JavaScript arrays are objects.
|
|
*
|
|
* @param {*} value Reference to check.
|
|
* @returns {boolean} True if `value` is an `Object` but not `null`.
|
|
*/
|
|
function isObject(value) {
|
|
// http://jsperf.com/isobject4
|
|
return value !== null && typeof value === 'object';
|
|
}
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.isString
|
|
* @module ng
|
|
* @kind 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
|
|
* @module ng
|
|
* @kind 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
|
|
* @module ng
|
|
* @kind 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.call(value) === '[object Date]';
|
|
}
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.isArray
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Determines if a reference is an `Array`.
|
|
*
|
|
* @param {*} value Reference to check.
|
|
* @returns {boolean} True if `value` is an `Array`.
|
|
*/
|
|
var isArray = Array.isArray;
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.isFunction
|
|
* @module ng
|
|
* @kind 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.call(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.window === obj;
|
|
}
|
|
|
|
|
|
function isScope(obj) {
|
|
return obj && obj.$evalAsync && obj.$watch;
|
|
}
|
|
|
|
|
|
function isFile(obj) {
|
|
return toString.call(obj) === '[object File]';
|
|
}
|
|
|
|
|
|
function isBlob(obj) {
|
|
return toString.call(obj) === '[object Blob]';
|
|
}
|
|
|
|
|
|
function isBoolean(value) {
|
|
return typeof value === 'boolean';
|
|
}
|
|
|
|
|
|
function isPromiseLike(obj) {
|
|
return obj && isFunction(obj.then);
|
|
}
|
|
|
|
|
|
var trim = function(value) {
|
|
return isString(value) ? value.trim() : value;
|
|
};
|
|
|
|
// Copied from:
|
|
// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
|
|
// Prereq: s is a string.
|
|
var escapeForRegexp = function(s) {
|
|
return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
|
|
replace(/\x08/g, '\\x08');
|
|
};
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.isElement
|
|
* @module ng
|
|
* @kind 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.prop && node.attr && 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;
|
|
}
|
|
|
|
|
|
function nodeName_(element) {
|
|
return lowercase(element.nodeName || (element[0] && element[0].nodeName));
|
|
}
|
|
|
|
function includes(array, obj) {
|
|
return Array.prototype.indexOf.call(array, obj) != -1;
|
|
}
|
|
|
|
function arrayRemove(array, value) {
|
|
var index = array.indexOf(value);
|
|
if (index >= 0)
|
|
array.splice(index, 1);
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.copy
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Creates a deep copy of `source`, which should be an object or an array.
|
|
*
|
|
* * If no destination is supplied, a copy of the object or array is created.
|
|
* * If a destination is provided, all of its elements (for array) or properties (for objects)
|
|
* are deleted and then all elements/properties from the source are copied to it.
|
|
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
|
|
* * If `source` is identical to 'destination' an exception will be thrown.
|
|
*
|
|
* @param {*} source The source that will be used to make a copy.
|
|
* Can be any type, including primitives, `null`, and `undefined`.
|
|
* @param {(Object|Array)=} destination Destination into which the source is copied. If
|
|
* provided, must be of the same type as `source`.
|
|
* @returns {*} The copy or updated `destination`, if `destination` was specified.
|
|
*
|
|
* @example
|
|
<example module="copyExample">
|
|
<file name="index.html">
|
|
<div ng-controller="ExampleController">
|
|
<form novalidate class="simple-form">
|
|
Name: <input type="text" ng-model="user.name" /><br />
|
|
E-mail: <input type="email" ng-model="user.email" /><br />
|
|
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
|
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
|
<button ng-click="reset()">RESET</button>
|
|
<button ng-click="update(user)">SAVE</button>
|
|
</form>
|
|
<pre>form = {{user | json}}</pre>
|
|
<pre>master = {{master | json}}</pre>
|
|
</div>
|
|
|
|
<script>
|
|
angular.module('copyExample', [])
|
|
.controller('ExampleController', ['$scope', function($scope) {
|
|
$scope.master= {};
|
|
|
|
$scope.update = function(user) {
|
|
// Example with 1 argument
|
|
$scope.master= angular.copy(user);
|
|
};
|
|
|
|
$scope.reset = function() {
|
|
// Example with 2 arguments
|
|
angular.copy($scope.master, $scope.user);
|
|
};
|
|
|
|
$scope.reset();
|
|
}]);
|
|
</script>
|
|
</file>
|
|
</example>
|
|
*/
|
|
function copy(source, destination, stackSource, stackDest) {
|
|
if (isWindow(source) || isScope(source)) {
|
|
throw ngMinErr('cpws',
|
|
"Can't copy! Making copies of Window or Scope instances is not supported.");
|
|
}
|
|
|
|
if (!destination) {
|
|
destination = source;
|
|
if (source) {
|
|
if (isArray(source)) {
|
|
destination = copy(source, [], stackSource, stackDest);
|
|
} else if (isDate(source)) {
|
|
destination = new Date(source.getTime());
|
|
} else if (isRegExp(source)) {
|
|
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
|
destination.lastIndex = source.lastIndex;
|
|
} else if (isObject(source)) {
|
|
var emptyObject = Object.create(Object.getPrototypeOf(source));
|
|
destination = copy(source, emptyObject, stackSource, stackDest);
|
|
}
|
|
}
|
|
} else {
|
|
if (source === destination) throw ngMinErr('cpi',
|
|
"Can't copy! Source and destination are identical.");
|
|
|
|
stackSource = stackSource || [];
|
|
stackDest = stackDest || [];
|
|
|
|
if (isObject(source)) {
|
|
var index = stackSource.indexOf(source);
|
|
if (index !== -1) return stackDest[index];
|
|
|
|
stackSource.push(source);
|
|
stackDest.push(destination);
|
|
}
|
|
|
|
var result;
|
|
if (isArray(source)) {
|
|
destination.length = 0;
|
|
for (var i = 0; i < source.length; i++) {
|
|
result = copy(source[i], null, stackSource, stackDest);
|
|
if (isObject(source[i])) {
|
|
stackSource.push(source[i]);
|
|
stackDest.push(result);
|
|
}
|
|
destination.push(result);
|
|
}
|
|
} else {
|
|
var h = destination.$$hashKey;
|
|
if (isArray(destination)) {
|
|
destination.length = 0;
|
|
} else {
|
|
forEach(destination, function(value, key) {
|
|
delete destination[key];
|
|
});
|
|
}
|
|
for (var key in source) {
|
|
if (source.hasOwnProperty(key)) {
|
|
result = copy(source[key], null, stackSource, stackDest);
|
|
if (isObject(source[key])) {
|
|
stackSource.push(source[key]);
|
|
stackDest.push(result);
|
|
}
|
|
destination[key] = result;
|
|
}
|
|
}
|
|
setHashKey(destination,h);
|
|
}
|
|
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
/**
|
|
* Creates a shallow copy of an object, an array or a primitive.
|
|
*
|
|
* Assumes that there are no proto properties for objects.
|
|
*/
|
|
function shallowCopy(src, dst) {
|
|
if (isArray(src)) {
|
|
dst = dst || [];
|
|
|
|
for (var i = 0, ii = src.length; i < ii; i++) {
|
|
dst[i] = src[i];
|
|
}
|
|
} else if (isObject(src)) {
|
|
dst = dst || {};
|
|
|
|
for (var key in src) {
|
|
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
|
dst[key] = src[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return dst || src;
|
|
}
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.equals
|
|
* @module ng
|
|
* @kind 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 are equal by
|
|
* comparing them with `angular.equals`.
|
|
* * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
|
|
* * Both values represent the same regular expression (In JavaScript,
|
|
* /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 < length; key++) {
|
|
if (!equals(o1[key], o2[key])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
} else if (isDate(o1)) {
|
|
if (!isDate(o2)) return false;
|
|
return equals(o1.getTime(), o2.getTime());
|
|
} else if (isRegExp(o1) && isRegExp(o2)) {
|
|
return o1.toString() == o2.toString();
|
|
} else {
|
|
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
|
|
keySet = {};
|
|
for (key in o1) {
|
|
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
|
if (!equals(o1[key], o2[key])) return false;
|
|
keySet[key] = true;
|
|
}
|
|
for (key in o2) {
|
|
if (!keySet.hasOwnProperty(key) &&
|
|
key.charAt(0) !== '$' &&
|
|
o2[key] !== undefined &&
|
|
!isFunction(o2[key])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var csp = function() {
|
|
if (isDefined(csp.isActive_)) return csp.isActive_;
|
|
|
|
var active = !!(document.querySelector('[ng-csp]') ||
|
|
document.querySelector('[data-ng-csp]'));
|
|
|
|
if (!active) {
|
|
try {
|
|
/* jshint -W031, -W054 */
|
|
new Function('');
|
|
/* jshint +W031, +W054 */
|
|
} catch (e) {
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
return (csp.isActive_ = active);
|
|
};
|
|
|
|
|
|
|
|
function concat(array1, array2, index) {
|
|
return array1.concat(slice.call(array2, index));
|
|
}
|
|
|
|
function sliceArgs(args, startIndex) {
|
|
return slice.call(args, startIndex || 0);
|
|
}
|
|
|
|
|
|
/* jshint -W101 */
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.bind
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
|
|
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
|
|
* known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
|
|
* distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
|
|
*
|
|
* @param {Object} self Context which `fn` should be evaluated in.
|
|
* @param {function()} fn Function to be bound.
|
|
* @param {...*} args Optional arguments to be prebound to the `fn` function call.
|
|
* @returns {function()} Function that wraps the `fn` with all the specified bindings.
|
|
*/
|
|
/* jshint +W101 */
|
|
function bind(self, fn) {
|
|
var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
|
|
if (isFunction(fn) && !(fn instanceof RegExp)) {
|
|
return curryArgs.length
|
|
? function() {
|
|
return arguments.length
|
|
? fn.apply(self, concat(curryArgs, arguments, 0))
|
|
: fn.apply(self, curryArgs);
|
|
}
|
|
: function() {
|
|
return arguments.length
|
|
? fn.apply(self, arguments)
|
|
: fn.call(self);
|
|
};
|
|
} else {
|
|
// in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
|
|
return fn;
|
|
}
|
|
}
|
|
|
|
|
|
function toJsonReplacer(key, value) {
|
|
var val = value;
|
|
|
|
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
|
|
val = undefined;
|
|
} else if (isWindow(value)) {
|
|
val = '$WINDOW';
|
|
} else if (value && document === value) {
|
|
val = '$DOCUMENT';
|
|
} else if (isScope(value)) {
|
|
val = '$SCOPE';
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.toJson
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
|
|
* stripped since angular uses this notation internally.
|
|
*
|
|
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
|
|
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
|
* @returns {string|undefined} JSON-ified string representing `obj`.
|
|
*/
|
|
function toJson(obj, pretty) {
|
|
if (typeof obj === 'undefined') return undefined;
|
|
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
|
|
}
|
|
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.fromJson
|
|
* @module ng
|
|
* @kind function
|
|
*
|
|
* @description
|
|
* Deserializes a JSON string.
|
|
*
|
|
* @param {string} json JSON string to deserialize.
|
|
* @returns {Object|Array|string|number} Deserialized thingy.
|
|
*/
|
|
function fromJson(json) {
|
|
return isString(json)
|
|
? JSON.parse(json)
|
|
: json;
|
|
}
|
|
|
|
|
|
/**
|
|
* @returns {string} Returns the string representation of the element.
|
|
*/
|
|
function startingTag(element) {
|
|
element = jqLite(element).clone();
|
|
try {
|
|
// turns out IE does not let you set .html() on elements which
|
|
// are not allowed to have children. So we just ignore it.
|
|
element.empty();
|
|
} catch (e) {}
|
|
var elemHtml = jqLite('<div>').append(element).html();
|
|
try {
|
|
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
|
|
elemHtml.
|
|
match(/^(<[^>]+>)/)[1].
|
|
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
|
} catch (e) {
|
|
return lowercase(elemHtml);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Tries to decode the URI component without throwing an exception.
|
|
*
|
|
* @private
|
|
* @param str value potential URI component to check.
|
|
* @returns {boolean} True if `value` can be decoded
|
|
* with the decodeURIComponent function.
|
|
*/
|
|
function tryDecodeURIComponent(value) {
|
|
try {
|
|
return decodeURIComponent(value);
|
|
} catch (e) {
|
|
// Ignore any invalid uri component
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses an escaped url query string into key-value pairs.
|
|
* @returns {Object.<string,boolean|Array>}
|
|
*/
|
|
function parseKeyValue(/**string*/keyValue) {
|
|
var obj = {}, key_value, key;
|
|
forEach((keyValue || "").split('&'), function(keyValue) {
|
|
if (keyValue) {
|
|
key_value = keyValue.replace(/\+/g,'%20').split('=');
|
|
key = tryDecodeURIComponent(key_value[0]);
|
|
if (isDefined(key)) {
|
|
var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
|
|
if (!hasOwnProperty.call(obj, key)) {
|
|
obj[key] = val;
|
|
} else if (isArray(obj[key])) {
|
|
obj[key].push(val);
|
|
} else {
|
|
obj[key] = [obj[key],val];
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return obj;
|
|
}
|
|
|
|
function toKeyValue(obj) {
|
|
var parts = [];
|
|
forEach(obj, function(value, key) {
|
|
if (isArray(value)) {
|
|
forEach(value, function(arrayValue) {
|
|
parts.push(encodeUriQuery(key, true) +
|
|
(arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
|
|
});
|
|
} else {
|
|
parts.push(encodeUriQuery(key, true) +
|
|
(value === true ? '' : '=' + encodeUriQuery(value, true)));
|
|
}
|
|
});
|
|
return parts.length ? parts.join('&') : '';
|
|
}
|
|
|
|
|
|
/**
|
|
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
|
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
|
* segments:
|
|
* segment = *pchar
|
|
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
* pct-encoded = "%" HEXDIG HEXDIG
|
|
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
|
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
|
* / "*" / "+" / "," / ";" / "="
|
|
*/
|
|
function encodeUriSegment(val) {
|
|
return encodeUriQuery(val, true).
|
|
replace(/%26/gi, '&').
|
|
replace(/%3D/gi, '=').
|
|
replace(/%2B/gi, '+');
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
|
|
* method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
|
|
* encoded per http://tools.ietf.org/html/rfc3986:
|
|
* query = *( pchar / "/" / "?" )
|
|
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
|
* pct-encoded = "%" HEXDIG HEXDIG
|
|
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
|
* / "*" / "+" / "," / ";" / "="
|
|
*/
|
|
function encodeUriQuery(val, pctEncodeSpaces) {
|
|
return encodeURIComponent(val).
|
|
replace(/%40/gi, '@').
|
|
replace(/%3A/gi, ':').
|
|
replace(/%24/g, '$').
|
|
replace(/%2C/gi, ',').
|
|
replace(/%3B/gi, ';').
|
|
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
|
}
|
|
|
|
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
|
|
|
|
function getNgAttribute(element, ngAttr) {
|
|
var attr, i, ii = ngAttrPrefixes.length;
|
|
element = jqLite(element);
|
|
for (i = 0; i < ii; ++i) {
|
|
attr = ngAttrPrefixes[i] + ngAttr;
|
|
if (isString(attr = element.attr(attr))) {
|
|
return attr;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ngApp
|
|
* @module ng
|
|
*
|
|
* @element ANY
|
|
* @param {angular.Module} ngApp an optional application
|
|
* {@link angular.module module} name to load.
|
|
* @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
|
|
* created in "strict-di" mode. This means that the application will fail to invoke functions which
|
|
* do not use explicit function annotation (and are thus unsuitable for minification), as described
|
|
* in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
|
|
* tracking down the root of these bugs.
|
|
*
|
|
* @description
|
|
*
|
|
* Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
|
|
* designates the **root element** of the application and is typically placed near the root element
|
|
* of the page - e.g. on the `<body>` or `<html>` tags.
|
|
*
|
|
* Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
|
|
* found in the document will be used to define the root element to auto-bootstrap as an
|
|
* application. To run multiple applications in an HTML document you must manually bootstrap them using
|
|
* {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
|
|
*
|
|
* You can specify an **AngularJS module** to be used as the root module for the application. This
|
|
* module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
|
|
* should contain the application code needed or have dependencies on other modules that will
|
|
* contain the code. See {@link angular.module} for more information.
|
|
*
|
|
* In the example below if the `ngApp` directive were not placed on the `html` element then the
|
|
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
|
|
* would not be resolved to `3`.
|
|
*
|
|
* `ngApp` is the easiest, and most common, way to bootstrap an application.
|
|
*
|
|
<example module="ngAppDemo">
|
|
<file name="index.html">
|
|
<div ng-controller="ngAppDemoController">
|
|
I can add: {{a}} + {{b}} = {{ a+b }}
|
|
</div>
|
|
</file>
|
|
<file name="script.js">
|
|
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
|
|
$scope.a = 1;
|
|
$scope.b = 2;
|
|
});
|
|
</file>
|
|
</example>
|
|
*
|
|
* Using `ngStrictDi`, you would see something like this:
|
|
*
|
|
<example ng-app-included="true">
|
|
<file name="index.html">
|
|
<div ng-app="ngAppStrictDemo" ng-strict-di>
|
|
<div ng-controller="GoodController1">
|
|
I can add: {{a}} + {{b}} = {{ a+b }}
|
|
|
|
<p>This renders because the controller does not fail to
|
|
instantiate, by using explicit annotation style (see
|
|
script.js for details)
|
|
</p>
|
|
</div>
|
|
|
|
<div ng-controller="GoodController2">
|
|
Name: <input ng-model="name"><br />
|
|
Hello, {{name}}!
|
|
|
|
<p>This renders because the controller does not fail to
|
|
instantiate, by using explicit annotation style
|
|
(see script.js for details)
|
|
</p>
|
|
</div>
|
|
|
|
<div ng-controller="BadController">
|
|
I can add: {{a}} + {{b}} = {{ a+b }}
|
|
|
|
<p>The controller could not be instantiated, due to relying
|
|
on automatic function annotations (which are disabled in
|
|
strict mode). As such, the content of this section is not
|
|
interpolated, and there should be an error in your web console.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</file>
|
|
<file name="script.js">
|
|
angular.module('ngAppStrictDemo', [])
|
|
// BadController will fail to instantiate, due to relying on automatic function annotation,
|
|
// rather than an explicit annotation
|
|
.controller('BadController', function($scope) {
|
|
$scope.a = 1;
|
|
$scope.b = 2;
|
|
})
|
|
// Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
|
|
// due to using explicit annotations using the array style and $inject property, respectively.
|
|
.controller('GoodController1', ['$scope', function($scope) {
|
|
$scope.a = 1;
|
|
$scope.b = 2;
|
|
}])
|
|
.controller('GoodController2', GoodController2);
|
|
function GoodController2($scope) {
|
|
$scope.name = "World";
|
|
}
|
|
GoodController2.$inject = ['$scope'];
|
|
</file>
|
|
<file name="style.css">
|
|
div[ng-controller] {
|
|
margin-bottom: 1em;
|
|
-webkit-border-radius: 4px;
|
|
border-radius: 4px;
|
|
border: 1px solid;
|
|
padding: .5em;
|
|
}
|
|
div[ng-controller^=Good] {
|
|
border-color: #d6e9c6;
|
|
background-color: #dff0d8;
|
|
color: #3c763d;
|
|
}
|
|
div[ng-controller^=Bad] {
|
|
border-color: #ebccd1;
|
|
background-color: #f2dede;
|
|
color: #a94442;
|
|
margin-bottom: 0;
|
|
}
|
|
</file>
|
|
</example>
|
|
*/
|
|
function angularInit(element, bootstrap) {
|
|
var appElement,
|
|
module,
|
|
config = {};
|
|
|
|
// The element `element` has priority over any other element
|
|
forEach(ngAttrPrefixes, function(prefix) {
|
|
var name = prefix + 'app';
|
|
|
|
if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
|
|
appElement = element;
|
|
module = element.getAttribute(name);
|
|
}
|
|
});
|
|
forEach(ngAttrPrefixes, function(prefix) {
|
|
var name = prefix + 'app';
|
|
var candidate;
|
|
|
|
if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
|
|
appElement = candidate;
|
|
module = candidate.getAttribute(name);
|
|
}
|
|
});
|
|
if (appElement) {
|
|
config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
|
|
bootstrap(appElement, module ? [module] : [], config);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.bootstrap
|
|
* @module ng
|
|
* @description
|
|
* Use this function to manually start up angular application.
|
|
*
|
|
* See: {@link guide/bootstrap Bootstrap}
|
|
*
|
|
* Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
|
|
* They must use {@link ng.directive:ngApp ngApp}.
|
|
*
|
|
* Angular will detect if it has been loaded into the browser more than once and only allow the
|
|
* first loaded script to be bootstrapped and will report a warning to the browser console for
|
|
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
|
|
* multiple instances of Angular try to work on the DOM.
|
|
*
|
|
* ```html
|
|
* <!doctype html>
|
|
* <html>
|
|
* <body>
|
|
* <div ng-controller="WelcomeController">
|
|
* {{greeting}}
|
|
* </div>
|
|
*
|
|
* <script src="angular.js"></script>
|
|
* <script>
|
|
* var app = angular.module('demo', [])
|
|
* .controller('WelcomeController', function($scope) {
|
|
* $scope.greeting = 'Welcome!';
|
|
* });
|
|
* angular.bootstrap(document, ['demo']);
|
|
* </script>
|
|
* </body>
|
|
* </html>
|
|
* ```
|
|
*
|
|
* @param {DOMElement} element DOM element which is the root of angular application.
|
|
* @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
|
|
* Each item in the array should be the name of a predefined module or a (DI annotated)
|
|
* function that will be invoked by the injector as a run block.
|
|
* See: {@link angular.module modules}
|
|
* @param {Object=} config an object for defining configuration options for the application. The
|
|
* following keys are supported:
|
|
*
|
|
* * `strictDi` - disable automatic function annotation for the application. This is meant to
|
|
* assist in finding bugs which break minified code. Defaults to `false`.
|
|
*
|
|
* @returns {auto.$injector} Returns the newly created injector for this app.
|
|
*/
|
|
function bootstrap(element, modules, config) {
|
|
if (!isObject(config)) config = {};
|
|
var defaultConfig = {
|
|
strictDi: false
|
|
};
|
|
config = extend(defaultConfig, config);
|
|
var doBootstrap = function() {
|
|
element = jqLite(element);
|
|
|
|
if (element.injector()) {
|
|
var tag = (element[0] === document) ? 'document' : startingTag(element);
|
|
//Encode angle brackets to prevent input from being sanitized to empty string #8683
|
|
throw ngMinErr(
|
|
'btstrpd',
|
|
"App Already Bootstrapped with this Element '{0}'",
|
|
tag.replace(/</,'<').replace(/>/,'>'));
|
|
}
|
|
|
|
modules = modules || [];
|
|
modules.unshift(['$provide', function($provide) {
|
|
$provide.value('$rootElement', element);
|
|
}]);
|
|
|
|
if (config.debugInfoEnabled) {
|
|
// Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
|
|
modules.push(['$compileProvider', function($compileProvider) {
|
|
$compileProvider.debugInfoEnabled(true);
|
|
}]);
|
|
}
|
|
|
|
modules.unshift('ng');
|
|
var injector = createInjector(modules, config.strictDi);
|
|
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
|
|
function bootstrapApply(scope, element, compile, injector) {
|
|
scope.$apply(function() {
|
|
element.data('$injector', injector);
|
|
compile(element)(scope);
|
|
});
|
|
}]
|
|
);
|
|
return injector;
|
|
};
|
|
|
|
var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
|
|
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
|
|
|
if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
|
|
config.debugInfoEnabled = true;
|
|
window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
|
|
}
|
|
|
|
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
|
return doBootstrap();
|
|
}
|
|
|
|
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
|
angular.resumeBootstrap = function(extraModules) {
|
|
forEach(extraModules, function(module) {
|
|
modules.push(module);
|
|
});
|
|
doBootstrap();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.reloadWithDebugInfo
|
|
* @module ng
|
|
* @description
|
|
* Use this function to reload the current application with debug information turned on.
|
|
* This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
|
|
*
|
|
* See {@link ng.$compileProvider#debugInfoEnabled} for more.
|
|
*/
|
|
function reloadWithDebugInfo() {
|
|
window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
|
|
window.location.reload();
|
|
}
|
|
|
|
/**
|
|
* @name angular.getTestability
|
|
* @module ng
|
|
* @description
|
|
* Get the testability service for the instance of Angular on the given
|
|
* element.
|
|
* @param {DOMElement} element DOM element which is the root of angular application.
|
|
*/
|
|
function getTestability(rootElement) {
|
|
return angular.element(rootElement).injector().get('$$testability');
|
|
}
|
|
|
|
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
|
function snake_case(name, separator) {
|
|
separator = separator || '_';
|
|
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
|
|
return (pos ? separator : '') + letter.toLowerCase();
|
|
});
|
|
}
|
|
|
|
var bindJQueryFired = false;
|
|
var skipDestroyOnNextJQueryCleanData;
|
|
function bindJQuery() {
|
|
var originalCleanData;
|
|
|
|
if (bindJQueryFired) {
|
|
return;
|
|
}
|
|
|
|
// bind to jQuery if present;
|
|
jQuery = window.jQuery;
|
|
// Use jQuery if it exists with proper functionality, otherwise default to us.
|
|
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
|
|
// Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
|
|
// versions. It will not work for sure with jQuery <1.7, though.
|
|
if (jQuery && jQuery.fn.on) {
|
|
jqLite = jQuery;
|
|
extend(jQuery.fn, {
|
|
scope: JQLitePrototype.scope,
|
|
isolateScope: JQLitePrototype.isolateScope,
|
|
controller: JQLitePrototype.controller,
|
|
injector: JQLitePrototype.injector,
|
|
inheritedData: JQLitePrototype.inheritedData
|
|
});
|
|
|
|
// All nodes removed from the DOM via various jQuery APIs like .remove()
|
|
// are passed through jQuery.cleanData. Monkey-patch this method to fire
|
|
// the $destroy event on all removed nodes.
|
|
originalCleanData = jQuery.cleanData;
|
|
jQuery.cleanData = function(elems) {
|
|
var events;
|
|
if (!skipDestroyOnNextJQueryCleanData) {
|
|
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
|
events = jQuery._data(elem, "events");
|
|
if (events && events.$destroy) {
|
|
jQuery(elem).triggerHandler('$destroy');
|
|
}
|
|
}
|
|
} else {
|
|
skipDestroyOnNextJQueryCleanData = false;
|
|
}
|
|
originalCleanData(elems);
|
|
};
|
|
} else {
|
|
jqLite = JQLite;
|
|
}
|
|
|
|
angular.element = jqLite;
|
|
|
|
// Prevent double-proxying.
|
|
bindJQueryFired = true;
|
|
}
|
|
|
|
/**
|
|
* throw error if the argument is falsy.
|
|
*/
|
|
function assertArg(arg, name, reason) {
|
|
if (!arg) {
|
|
throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
function assertArgFn(arg, name, acceptArrayAnnotation) {
|
|
if (acceptArrayAnnotation && isArray(arg)) {
|
|
arg = arg[arg.length - 1];
|
|
}
|
|
|
|
assertArg(isFunction(arg), name, 'not a function, got ' +
|
|
(arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
|
return arg;
|
|
}
|
|
|
|
/**
|
|
* throw error if the name given is hasOwnProperty
|
|
* @param {String} name the name to test
|
|
* @param {String} context the context in which the name is used, such as module or directive
|
|
*/
|
|
function assertNotHasOwnProperty(name, context) {
|
|
if (name === 'hasOwnProperty') {
|
|
throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the value accessible from the object by path. Any undefined traversals are ignored
|
|
* @param {Object} obj starting object
|
|
* @param {String} path path to traverse
|
|
* @param {boolean} [bindFnToScope=true]
|
|
* @returns {Object} value as accessible by path
|
|
*/
|
|
//TODO(misko): this function needs to be removed
|
|
function getter(obj, path, bindFnToScope) {
|
|
if (!path) return obj;
|
|
var keys = path.split('.');
|
|
var key;
|
|
var lastInstance = obj;
|
|
var len = keys.length;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
key = keys[i];
|
|
if (obj) {
|
|
obj = (lastInstance = obj)[key];
|
|
}
|
|
}
|
|
if (!bindFnToScope && isFunction(obj)) {
|
|
return bind(lastInstance, obj);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Return the DOM siblings between the first and last node in the given array.
|
|
* @param {Array} array like object
|
|
* @returns {jqLite} jqLite collection containing the nodes
|
|
*/
|
|
function getBlockNodes(nodes) {
|
|
// TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
|
|
// collection, otherwise update the original collection.
|
|
var node = nodes[0];
|
|
var endNode = nodes[nodes.length - 1];
|
|
var blockNodes = [node];
|
|
|
|
do {
|
|
node = node.nextSibling;
|
|
if (!node) break;
|
|
blockNodes.push(node);
|
|
} while (node !== endNode);
|
|
|
|
return jqLite(blockNodes);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new object without a prototype. This object is useful for lookup without having to
|
|
* guard against prototypically inherited properties via hasOwnProperty.
|
|
*
|
|
* Related micro-benchmarks:
|
|
* - http://jsperf.com/object-create2
|
|
* - http://jsperf.com/proto-map-lookup/2
|
|
* - http://jsperf.com/for-in-vs-object-keys2
|
|
*
|
|
* @returns {Object}
|
|
*/
|
|
function createMap() {
|
|
return Object.create(null);
|
|
}
|
|
|
|
var NODE_TYPE_ELEMENT = 1;
|
|
var NODE_TYPE_TEXT = 3;
|
|
var NODE_TYPE_COMMENT = 8;
|
|
var NODE_TYPE_DOCUMENT = 9;
|
|
var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
|