You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2015/05/03 20:18:27 UTC
[33/51] [abbrv] struts git commit: Migrate angularjs version in maven
angularjs archetype from 1.2 to latest stable version 1.3
http://git-wip-us.apache.org/repos/asf/struts/blob/d586fd50/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular.js
index 59f7a96..e488352 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.2.23
+ * @license AngularJS v1.3.15
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -30,138 +30,129 @@
* should all be static strings, not variables or general expressions.
*
* @param {string} module The namespace to use for the new minErr instance.
+ * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
+ * error from returned function, for cases when a particular type of error is useful.
* @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
*/
-function minErr(module) {
- return function () {
+function minErr(module, ErrorConstructor) {
+ ErrorConstructor = ErrorConstructor || Error;
+ return function() {
var code = arguments[0],
prefix = '[' + (module ? module + ':' : '') + code + '] ',
template = arguments[1],
templateArgs = arguments,
- stringify = function (obj) {
- if (typeof obj === 'function') {
- return obj.toString().replace(/ \{[\s\S]*$/, '');
- } else if (typeof obj === 'undefined') {
- return 'undefined';
- } else if (typeof obj !== 'string') {
- return JSON.stringify(obj);
- }
- return obj;
- },
+
message, i;
- message = prefix + template.replace(/\{\d+\}/g, function (match) {
+ message = prefix + template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1), arg;
if (index + 2 < templateArgs.length) {
- arg = templateArgs[index + 2];
- if (typeof arg === 'function') {
- return arg.toString().replace(/ ?\{[\s\S]*$/, '');
- } else if (typeof arg === 'undefined') {
- return 'undefined';
- } else if (typeof arg !== 'string') {
- return toJson(arg);
- }
- return arg;
+ return toDebugString(templateArgs[index + 2]);
}
return match;
});
- message = message + '\nhttp://errors.angularjs.org/1.2.23/' +
+ message = message + '\nhttp://errors.angularjs.org/1.3.15/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
- message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
- encodeURIComponent(stringify(arguments[i]));
+ message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
+ encodeURIComponent(toDebugString(arguments[i]));
}
-
- return new Error(message);
+ return new ErrorConstructor(message);
};
}
/* We need to tell jshint what variables are being exported */
/* global angular: true,
- msie: true,
- jqLite: true,
- jQuery: true,
- slice: true,
- push: true,
- toString: true,
- ngMinErr: true,
- angularModule: true,
- nodeName_: true,
- uid: 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,
- isElement: true,
- makeMap: true,
- map: true,
- size: true,
- includes: true,
- indexOf: true,
- arrayRemove: true,
- isLeafNode: true,
- copy: true,
- shallowCopy: true,
- equals: true,
- csp: true,
- concat: true,
- sliceArgs: true,
- bind: true,
- toJsonReplacer: true,
- toJson: true,
- fromJson: true,
- toBoolean: true,
- startingTag: true,
- tryDecodeURIComponent: true,
- parseKeyValue: true,
- toKeyValue: true,
- encodeUriSegment: true,
- encodeUriQuery: true,
- angularInit: true,
- bootstrap: true,
- snake_case: true,
- bindJQuery: true,
- assertArg: true,
- assertArgFn: true,
- assertNotHasOwnProperty: true,
- getter: true,
- getBlockElements: true,
- hasOwnProperty: 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,
+ isFormData: 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,
*/
////////////////////////////////////
@@ -181,6 +172,8 @@ function minErr(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';
@@ -195,7 +188,7 @@ var VALIDITY_STATE_PROPERTY = 'validity';
* @param {string} string String to be converted to lowercase.
* @returns {string} Lowercased string.
*/
-var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
+var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
@@ -208,7 +201,7 @@ var hasOwnProperty = Object.prototype.hasOwnProperty;
* @param {string} string String to be converted to uppercase.
* @returns {string} Uppercased string.
*/
-var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
+var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
var manualLowercase = function(s) {
@@ -234,11 +227,12 @@ if ('i' !== 'I'.toLowerCase()) {
}
-var /** holds major version number for IE or NaN for real browsers */
- msie,
+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'),
@@ -246,17 +240,13 @@ var /** holds major version number for IE or NaN for real browsers */
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
- nodeName_,
- uid = ['0', '0', '0'];
+ uid = 0;
/**
- * IE 11 changed the format of the UserAgent string.
- * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
+ * documentMode is an IE-only property
+ * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
*/
-msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
-if (isNaN(msie)) {
- msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
-}
+msie = document.documentMode;
/**
@@ -272,7 +262,7 @@ function isArrayLike(obj) {
var length = obj.length;
- if (obj.nodeType === 1 && length) {
+ if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
return true;
}
@@ -288,13 +278,18 @@ function isArrayLike(obj) {
*
* @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.
+ * 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 = [];
@@ -309,27 +304,31 @@ function isArrayLike(obj) {
* @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;
+ 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);
+ iterator.call(context, obj[key], key, obj);
}
}
} else if (isArray(obj) || isArrayLike(obj)) {
- for (key = 0; key < obj.length; key++) {
- iterator.call(context, obj[key], key);
+ 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.forEach(iterator, context, obj);
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
- iterator.call(context, obj[key], key);
+ iterator.call(context, obj[key], key, obj);
}
}
}
@@ -338,18 +337,12 @@ function forEach(obj, iterator, context) {
}
function sortedKeys(obj) {
- var keys = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- keys.push(key);
- }
- }
- return keys.sort();
+ return Object.keys(obj).sort();
}
function forEachSorted(obj, iterator, context) {
var keys = sortedKeys(obj);
- for ( var i = 0; i < keys.length; i++) {
+ for (var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
@@ -366,33 +359,17 @@ function reverseParams(iteratorFn) {
}
/**
- * 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.
+ * 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 {string} an unique alpha-numeric string
+ * @returns {number} 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('');
+ return ++uid;
}
@@ -404,8 +381,7 @@ function nextUid() {
function setHashKey(obj, h) {
if (h) {
obj.$$hashKey = h;
- }
- else {
+ } else {
delete obj.$$hashKey;
}
}
@@ -417,8 +393,10 @@ function setHashKey(obj, h) {
* @kind 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.
+ * 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).
@@ -426,15 +404,19 @@ function setHashKey(obj, h) {
*/
function extend(dst) {
var h = dst.$$hashKey;
- forEach(arguments, function(obj) {
- if (obj !== dst) {
- forEach(obj, function(value, key) {
- dst[key] = value;
- });
+
+ 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);
+ setHashKey(dst, h);
return dst;
}
@@ -444,7 +426,7 @@ function int(str) {
function inherit(parent, extra) {
- return extend(new (extend(function() {}, {prototype:parent}))(), extra);
+ return extend(Object.create(parent), extra);
}
/**
@@ -482,6 +464,8 @@ noop.$inject = [];
return (transformationFn || angular.identity)(value);
};
```
+ * @param {*} value to be returned.
+ * @returns {*} the value passed in.
*/
function identity($) {return $;}
identity.$inject = [];
@@ -501,7 +485,7 @@ function valueFn(value) {return function() {return value;};}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
-function isUndefined(value){return typeof value === 'undefined';}
+function isUndefined(value) {return typeof value === 'undefined';}
/**
@@ -516,7 +500,7 @@ function isUndefined(value){return typeof value === 'undefined';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
-function isDefined(value){return typeof value !== 'undefined';}
+function isDefined(value) {return typeof value !== 'undefined';}
/**
@@ -532,7 +516,10 @@ function isDefined(value){return typeof value !== 'undefined';}
* @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';}
+function isObject(value) {
+ // http://jsperf.com/isobject4
+ return value !== null && typeof value === 'object';
+}
/**
@@ -547,7 +534,7 @@ function isObject(value){return value != null && typeof value === 'object';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
-function isString(value){return typeof value === 'string';}
+function isString(value) {return typeof value === 'string';}
/**
@@ -559,10 +546,16 @@ function isString(value){return typeof value === 'string';}
* @description
* Determines if a reference is a `Number`.
*
+ * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
+ *
+ * If you wish to exclude these then you can use the native
+ * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
+ * method.
+ *
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
-function isNumber(value){return typeof value === 'number';}
+function isNumber(value) {return typeof value === 'number';}
/**
@@ -594,14 +587,7 @@ function isDate(value) {
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
-var isArray = (function() {
- if (!isFunction(Array.isArray)) {
- return function(value) {
- return toString.call(value) === '[object Array]';
- };
- }
- return Array.isArray;
-})();
+var isArray = Array.isArray;
/**
* @ngdoc function
@@ -615,7 +601,7 @@ var isArray = (function() {
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
-function isFunction(value){return typeof value === 'function';}
+function isFunction(value) {return typeof value === 'function';}
/**
@@ -638,7 +624,7 @@ function isRegExp(value) {
* @returns {boolean} True if `obj` is a window obj.
*/
function isWindow(obj) {
- return obj && obj.document && obj.location && obj.alert && obj.setInterval;
+ return obj && obj.window === obj;
}
@@ -652,6 +638,11 @@ function isFile(obj) {
}
+function isFormData(obj) {
+ return toString.call(obj) === '[object FormData]';
+}
+
+
function isBlob(obj) {
return toString.call(obj) === '[object Blob]';
}
@@ -667,19 +658,17 @@ function isPromiseLike(obj) {
}
-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\s*/, '').replace(/\s\s*$/, '') : value;
- };
- }
- return function(value) {
- return isString(value) ? value.trim() : value;
- };
-})();
+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');
+};
/**
@@ -706,93 +695,27 @@ function isElement(node) {
*/
function makeMap(str) {
var obj = {}, items = str.split(","), i;
- for ( i = 0; i < items.length; i++ )
- obj[ items[i] ] = true;
+ for (i = 0; i < items.length; i++)
+ obj[items[i]] = true;
return obj;
}
-if (msie < 9) {
- nodeName_ = function(element) {
- element = element.nodeName ? element : element[0];
- return (element.scopeName && element.scopeName != 'HTML')
- ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
- };
-} else {
- nodeName_ = function(element) {
- return element.nodeName ? element.nodeName : element[0].nodeName;
- };
-}
-
-
-function map(obj, iterator, context) {
- var results = [];
- forEach(obj, function(value, index, list) {
- results.push(iterator.call(context, value, index, list));
- });
- return results;
-}
-
-
-/**
- * @description
- * Determines the number of elements in an array, the number of properties an object has, or
- * the length of a string.
- *
- * Note: This function is used to augment the Object type in Angular expressions. See
- * {@link angular.Object} for more information about Angular arrays.
- *
- * @param {Object|Array|string} obj Object, array, or string to inspect.
- * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
- * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
- */
-function size(obj, ownPropsOnly) {
- var count = 0, key;
-
- if (isArray(obj) || isString(obj)) {
- return obj.length;
- } else if (isObject(obj)) {
- for (key in obj)
- if (!ownPropsOnly || obj.hasOwnProperty(key))
- count++;
- }
-
- return count;
+function nodeName_(element) {
+ return lowercase(element.nodeName || (element[0] && element[0].nodeName));
}
-
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;
+ return Array.prototype.indexOf.call(array, obj) != -1;
}
function arrayRemove(array, value) {
- var index = indexOf(array, value);
- if (index >=0)
+ var index = array.indexOf(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
@@ -803,7 +726,7 @@ function isLeafNode (node) {
* 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)
+ * * If a destination is provided, all of its elements (for arrays) 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.
@@ -868,7 +791,8 @@ function copy(source, destination, stackSource, stackDest) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isObject(source)) {
- destination = copy(source, {}, stackSource, stackDest);
+ var emptyObject = Object.create(Object.getPrototypeOf(source));
+ destination = copy(source, emptyObject, stackSource, stackDest);
}
}
} else {
@@ -879,7 +803,7 @@ function copy(source, destination, stackSource, stackDest) {
stackDest = stackDest || [];
if (isObject(source)) {
- var index = indexOf(stackSource, source);
+ var index = stackSource.indexOf(source);
if (index !== -1) return stackDest[index];
stackSource.push(source);
@@ -889,7 +813,7 @@ function copy(source, destination, stackSource, stackDest) {
var result;
if (isArray(source)) {
destination.length = 0;
- for ( var i = 0; i < source.length; i++) {
+ for (var i = 0; i < source.length; i++) {
result = copy(source[i], null, stackSource, stackDest);
if (isObject(source[i])) {
stackSource.push(source[i]);
@@ -906,13 +830,15 @@ function copy(source, destination, stackSource, stackDest) {
delete destination[key];
});
}
- for ( var key in source) {
- result = copy(source[key], null, stackSource, stackDest);
- if (isObject(source[key])) {
- stackSource.push(source[key]);
- stackDest.push(result);
+ 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;
}
- destination[key] = result;
}
setHashKey(destination,h);
}
@@ -922,20 +848,22 @@ function copy(source, destination, stackSource, stackDest) {
}
/**
- * Creates a shallow copy of an object, an array or a primitive
+ * 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; i < src.length; i++) {
+ 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 (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
+ if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
@@ -984,25 +912,26 @@ function equals(o1, o2) {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) == o2.length) {
- for(key=0; key<length; key++) {
+ 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 (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime());
- } else if (isRegExp(o1) && isRegExp(o2)) {
- return o1.toString() == o2.toString();
+ return equals(o1.getTime(), o2.getTime());
+ } else if (isRegExp(o1)) {
+ return isRegExp(o2) ? o1.toString() == o2.toString() : false;
} else {
- if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
+ if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
+ isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
keySet = {};
- for(key in o1) {
+ 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) {
+ for (key in o2) {
if (!keySet.hasOwnProperty(key) &&
key.charAt(0) !== '$' &&
o2[key] !== undefined &&
@@ -1070,7 +999,7 @@ function bind(self, fn) {
return curryArgs.length
? function() {
return arguments.length
- ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
+ ? fn.apply(self, concat(curryArgs, arguments, 0))
: fn.apply(self, curryArgs);
}
: function() {
@@ -1088,7 +1017,7 @@ function bind(self, fn) {
function toJsonReplacer(key, value) {
var val = value;
- if (typeof key === 'string' && key.charAt(0) === '$') {
+ if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
@@ -1109,16 +1038,20 @@ function toJsonReplacer(key, value) {
* @kind function
*
* @description
- * Serializes input into a JSON-formatted string. Properties with leading $ characters will be
+ * 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.
+ * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.
+ * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).
* @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);
+ if (!isNumber(pretty)) {
+ pretty = pretty ? 2 : null;
+ }
+ return JSON.stringify(obj, toJsonReplacer, pretty);
}
@@ -1132,7 +1065,7 @@ function toJson(obj, pretty) {
* Deserializes a JSON string.
*
* @param {string} json JSON string to deserialize.
- * @returns {Object|Array|string|number} Deserialized thingy.
+ * @returns {Object|Array|string|number} Deserialized JSON string.
*/
function fromJson(json) {
return isString(json)
@@ -1141,18 +1074,6 @@ function fromJson(json) {
}
-function toBoolean(value) {
- if (typeof value === 'function') {
- value = true;
- } else if (value && value.length !== 0) {
- var v = lowercase("" + value);
- value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
- } else {
- value = false;
- }
- return value;
-}
-
/**
* @returns {string} Returns the string representation of the element.
*/
@@ -1162,16 +1083,14 @@ function startingTag(element) {
// 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) {}
- // As Per DOM Standards
- var TEXT_NODE = 3;
+ } catch (e) {}
var elemHtml = jqLite('<div>').append(element).html();
try {
- return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
+ return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
- } catch(e) {
+ } catch (e) {
return lowercase(elemHtml);
}
@@ -1191,7 +1110,7 @@ function startingTag(element) {
function tryDecodeURIComponent(value) {
try {
return decodeURIComponent(value);
- } catch(e) {
+ } catch (e) {
// Ignore any invalid uri component
}
}
@@ -1204,14 +1123,14 @@ function tryDecodeURIComponent(value) {
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue) {
- if ( keyValue ) {
+ if (keyValue) {
key_value = keyValue.replace(/\+/g,'%20').split('=');
key = tryDecodeURIComponent(key_value[0]);
- if ( isDefined(key) ) {
+ 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])) {
+ } else if (isArray(obj[key])) {
obj[key].push(val);
} else {
obj[key] = [obj[key],val];
@@ -1275,9 +1194,23 @@ function encodeUriQuery(val, pctEncodeSpaces) {
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
@@ -1287,6 +1220,11 @@ function encodeUriQuery(val, pctEncodeSpaces) {
* @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
*
@@ -1300,7 +1238,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
* {@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
+ * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
* should contain the application code needed or have dependencies on other modules that will
* contain the code. See {@link angular.module} for more information.
*
@@ -1308,7 +1246,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
* 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.
+ * `ngApp` is the easiest, and most common way to bootstrap an application.
*
<example module="ngAppDemo">
<file name="index.html">
@@ -1324,48 +1262,109 @@ function encodeUriQuery(val, pctEncodeSpaces) {
</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 elements = [element],
- appElement,
+ var appElement,
module,
- names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
- NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
+ config = {};
- function append(element) {
- element && elements.push(element);
- }
+ // The element `element` has priority over any other element
+ forEach(ngAttrPrefixes, function(prefix) {
+ var name = prefix + 'app';
- forEach(names, function(name) {
- names[name] = true;
- append(document.getElementById(name));
- name = name.replace(':', '\\:');
- if (element.querySelectorAll) {
- forEach(element.querySelectorAll('.' + name), append);
- forEach(element.querySelectorAll('.' + name + '\\:'), append);
- forEach(element.querySelectorAll('[' + name + ']'), append);
+ if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
+ appElement = element;
+ module = element.getAttribute(name);
}
});
+ forEach(ngAttrPrefixes, function(prefix) {
+ var name = prefix + 'app';
+ var candidate;
- forEach(elements, function(element) {
- if (!appElement) {
- var className = ' ' + element.className + ' ';
- var match = NG_APP_CLASS_REGEXP.exec(className);
- if (match) {
- appElement = element;
- module = (match[2] || '').replace(/\s+/g, ',');
- } else {
- forEach(element.attributes, function(attr) {
- if (!appElement && names[attr.name]) {
- appElement = element;
- module = attr.value;
- }
- });
- }
+ if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
+ appElement = candidate;
+ module = candidate.getAttribute(name);
}
});
if (appElement) {
- bootstrap(appElement, module ? [module] : []);
+ config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
+ bootstrap(appElement, module ? [module] : [], config);
}
}
@@ -1378,7 +1377,7 @@ function angularInit(element, bootstrap) {
*
* See: {@link guide/bootstrap Bootstrap}
*
- * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
+ * 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
@@ -1386,44 +1385,45 @@ function angularInit(element, bootstrap) {
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
* multiple instances of Angular try to work on the DOM.
*
- * <example name="multi-bootstrap" module="multi-bootstrap">
- * <file name="index.html">
- * <script src="../../../angular.js"></script>
- * <div ng-controller="BrokenTable">
- * <table>
- * <tr>
- * <th ng-repeat="heading in headings">{{heading}}</th>
- * </tr>
- * <tr ng-repeat="filling in fillings">
- * <td ng-repeat="fill in filling">{{fill}}</td>
- * </tr>
- * </table>
+ * ```html
+ * <!doctype html>
+ * <html>
+ * <body>
+ * <div ng-controller="WelcomeController">
+ * {{greeting}}
* </div>
- * </file>
- * <file name="controller.js">
- * var app = angular.module('multi-bootstrap', [])
*
- * .controller('BrokenTable', function($scope) {
- * $scope.headings = ['One', 'Two', 'Three'];
- * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
- * });
- * </file>
- * <file name="protractor.js" type="protractor">
- * it('should only insert one table cell for each item in $scope.fillings', function() {
- * expect(element.all(by.css('td')).count())
- * .toBe(9);
- * });
- * </file>
- * </example>
+ * <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.
+ * function that will be invoked by the injector as a `config` 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) {
+function bootstrap(element, modules, config) {
+ if (!isObject(config)) config = {};
+ var defaultConfig = {
+ strictDi: false
+ };
+ config = extend(defaultConfig, config);
var doBootstrap = function() {
element = jqLite(element);
@@ -1440,10 +1440,18 @@ function bootstrap(element, 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);
- injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
- function(scope, element, compile, injector, animate) {
+ 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);
@@ -1453,8 +1461,14 @@ function bootstrap(element, modules) {
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();
}
@@ -1464,8 +1478,44 @@ function bootstrap(element, modules) {
forEach(extraModules, function(module) {
modules.push(module);
});
- doBootstrap();
+ return doBootstrap();
};
+
+ if (isFunction(angular.resumeDeferredBootstrap)) {
+ angular.resumeDeferredBootstrap();
+ }
+}
+
+/**
+ * @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) {
+ var injector = angular.element(rootElement).injector();
+ if (!injector) {
+ throw ngMinErr('test',
+ 'no injector found for element argument to getTestability');
+ }
+ return injector.get('$$testability');
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -1476,11 +1526,21 @@ function snake_case(name, separator) {
});
}
+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.1+ for on()/off() support.
+ // 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, {
@@ -1490,15 +1550,33 @@ function bindJQuery() {
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
- // Method signature:
- // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
- jqLitePatchJQueryRemove('remove', true, true, false);
- jqLitePatchJQueryRemove('empty', false, false, false);
- jqLitePatchJQueryRemove('html', false, false, true);
+
+ // 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;
}
/**
@@ -1562,27 +1640,46 @@ function getter(obj, path, bindFnToScope) {
/**
* Return the DOM siblings between the first and last node in the given array.
* @param {Array} array like object
- * @returns {DOMElement} object containing the elements
+ * @returns {jqLite} jqLite collection containing the nodes
*/
-function getBlockElements(nodes) {
- var startNode = nodes[0],
- endNode = nodes[nodes.length - 1];
- if (startNode === endNode) {
- return jqLite(startNode);
- }
-
- var element = startNode;
- var elements = [element];
+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 {
- element = element.nextSibling;
- if (!element) break;
- elements.push(element);
- } while (element !== endNode);
+ node = node.nextSibling;
+ if (!node) break;
+ blockNodes.push(node);
+ } while (node !== endNode);
- return jqLite(elements);
+ 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;
+
/**
* @ngdoc type
* @name angular.Module
@@ -1683,14 +1780,18 @@ function setupModuleLoader(window) {
var invokeQueue = [];
/** @type {!Array.<Function>} */
+ var configBlocks = [];
+
+ /** @type {!Array.<Function>} */
var runBlocks = [];
- var config = invokeLater('$injector', 'invoke');
+ var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** @type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
+ _configBlocks: configBlocks,
_runBlocks: runBlocks,
/**
@@ -1801,7 +1902,7 @@ function setupModuleLoader(window) {
* })
* ```
*
- * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
+ * See {@link ng.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information.
*/
animation: invokeLater('$animateProvider', 'register'),
@@ -1851,7 +1952,7 @@ function setupModuleLoader(window) {
* @description
* Use this method to register work which needs to be performed on module loading.
* For more about how to configure services, see
- * {@link providers#providers_provider-recipe Provider Recipe}.
+ * {@link providers#provider-recipe Provider Recipe}.
*/
config: config,
@@ -1875,7 +1976,7 @@ function setupModuleLoader(window) {
config(configFn);
}
- return moduleInstance;
+ return moduleInstance;
/**
* @param {string} provider
@@ -1883,9 +1984,10 @@ function setupModuleLoader(window) {
* @param {String=} insertMethod
* @returns {angular.Module}
*/
- function invokeLater(provider, method, insertMethod) {
+ function invokeLater(provider, method, insertMethod, queue) {
+ if (!queue) queue = invokeQueue;
return function() {
- invokeQueue[insertMethod || 'push']([provider, method, arguments]);
+ queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
@@ -1895,80 +1997,119 @@ function setupModuleLoader(window) {
}
+/* global: toDebugString: true */
+
+function serializeObject(obj) {
+ var seen = [];
+
+ return JSON.stringify(obj, function(key, val) {
+ val = toJsonReplacer(key, val);
+ if (isObject(val)) {
+
+ if (seen.indexOf(val) >= 0) return '<<already seen>>';
+
+ seen.push(val);
+ }
+ return val;
+ });
+}
+
+function toDebugString(obj) {
+ if (typeof obj === 'function') {
+ return obj.toString().replace(/ \{[\s\S]*$/, '');
+ } else if (typeof obj === 'undefined') {
+ return 'undefined';
+ } else if (typeof obj !== 'string') {
+ return serializeObject(obj);
+ }
+ return obj;
+}
+
/* global angularModule: true,
version: true,
$LocaleProvider,
$CompileProvider,
- htmlAnchorDirective,
- inputDirective,
- inputDirective,
- formDirective,
- scriptDirective,
- selectDirective,
- styleDirective,
- optionDirective,
- ngBindDirective,
- ngBindHtmlDirective,
- ngBindTemplateDirective,
- ngClassDirective,
- ngClassEvenDirective,
- ngClassOddDirective,
- ngCspDirective,
- ngCloakDirective,
- ngControllerDirective,
- ngFormDirective,
- ngHideDirective,
- ngIfDirective,
- ngIncludeDirective,
- ngIncludeFillContentDirective,
- ngInitDirective,
- ngNonBindableDirective,
- ngPluralizeDirective,
- ngRepeatDirective,
- ngShowDirective,
- ngStyleDirective,
- ngSwitchDirective,
- ngSwitchWhenDirective,
- ngSwitchDefaultDirective,
- ngOptionsDirective,
- ngTranscludeDirective,
- ngModelDirective,
- ngListDirective,
- ngChangeDirective,
- requiredDirective,
- requiredDirective,
- ngValueDirective,
- ngAttributeAliasDirectives,
- ngEventDirectives,
-
- $AnchorScrollProvider,
- $AnimateProvider,
- $BrowserProvider,
- $CacheFactoryProvider,
- $ControllerProvider,
- $DocumentProvider,
- $ExceptionHandlerProvider,
- $FilterProvider,
- $InterpolateProvider,
- $IntervalProvider,
- $HttpProvider,
- $HttpBackendProvider,
- $LocationProvider,
- $LogProvider,
- $ParseProvider,
- $RootScopeProvider,
- $QProvider,
- $$SanitizeUriProvider,
- $SceProvider,
- $SceDelegateProvider,
- $SnifferProvider,
- $TemplateCacheProvider,
- $TimeoutProvider,
- $$RAFProvider,
- $$AsyncCallbackProvider,
- $WindowProvider
+ htmlAnchorDirective,
+ inputDirective,
+ inputDirective,
+ formDirective,
+ scriptDirective,
+ selectDirective,
+ styleDirective,
+ optionDirective,
+ ngBindDirective,
+ ngBindHtmlDirective,
+ ngBindTemplateDirective,
+ ngClassDirective,
+ ngClassEvenDirective,
+ ngClassOddDirective,
+ ngCspDirective,
+ ngCloakDirective,
+ ngControllerDirective,
+ ngFormDirective,
+ ngHideDirective,
+ ngIfDirective,
+ ngIncludeDirective,
+ ngIncludeFillContentDirective,
+ ngInitDirective,
+ ngNonBindableDirective,
+ ngPluralizeDirective,
+ ngRepeatDirective,
+ ngShowDirective,
+ ngStyleDirective,
+ ngSwitchDirective,
+ ngSwitchWhenDirective,
+ ngSwitchDefaultDirective,
+ ngOptionsDirective,
+ ngTranscludeDirective,
+ ngModelDirective,
+ ngListDirective,
+ ngChangeDirective,
+ patternDirective,
+ patternDirective,
+ requiredDirective,
+ requiredDirective,
+ minlengthDirective,
+ minlengthDirective,
+ maxlengthDirective,
+ maxlengthDirective,
+ ngValueDirective,
+ ngModelOptionsDirective,
+ ngAttributeAliasDirectives,
+ ngEventDirectives,
+
+ $AnchorScrollProvider,
+ $AnimateProvider,
+ $BrowserProvider,
+ $CacheFactoryProvider,
+ $ControllerProvider,
+ $DocumentProvider,
+ $ExceptionHandlerProvider,
+ $FilterProvider,
+ $InterpolateProvider,
+ $IntervalProvider,
+ $HttpProvider,
+ $HttpBackendProvider,
+ $LocationProvider,
+ $LogProvider,
+ $ParseProvider,
+ $RootScopeProvider,
+ $QProvider,
+ $$QProvider,
+ $$SanitizeUriProvider,
+ $SceProvider,
+ $SceDelegateProvider,
+ $SnifferProvider,
+ $TemplateCacheProvider,
+ $TemplateRequestProvider,
+ $$TestabilityProvider,
+ $TimeoutProvider,
+ $$RAFProvider,
+ $$AsyncCallbackProvider,
+ $WindowProvider,
+ $$jqLiteProvider
*/
@@ -1987,15 +2128,15 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.2.23', // all of these placeholder strings will be replaced by grunt's
+ full: '1.3.15', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
- minor: 2,
- dot: 23,
- codeName: 'superficial-malady'
+ minor: 3,
+ dot: 15,
+ codeName: 'locality-filtration'
};
-function publishExternalAPI(angular){
+function publishExternalAPI(angular) {
extend(angular, {
'bootstrap': bootstrap,
'copy': copy,
@@ -2022,8 +2163,10 @@ function publishExternalAPI(angular){
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0},
+ 'getTestability': getTestability,
'$$minErr': minErr,
- '$$csp': csp
+ '$$csp': csp,
+ 'reloadWithDebugInfo': reloadWithDebugInfo
});
angularModule = setupModuleLoader(window);
@@ -2075,9 +2218,16 @@ function publishExternalAPI(angular){
ngModel: ngModelDirective,
ngList: ngListDirective,
ngChange: ngChangeDirective,
+ pattern: patternDirective,
+ ngPattern: patternDirective,
required: requiredDirective,
ngRequired: requiredDirective,
- ngValue: ngValueDirective
+ minlength: minlengthDirective,
+ ngMinlength: minlengthDirective,
+ maxlength: maxlengthDirective,
+ ngMaxlength: maxlengthDirective,
+ ngValue: ngValueDirective,
+ ngModelOptions: ngModelOptionsDirective
}).
directive({
ngInclude: ngIncludeFillContentDirective
@@ -2102,23 +2252,39 @@ function publishExternalAPI(angular){
$parse: $ParseProvider,
$rootScope: $RootScopeProvider,
$q: $QProvider,
+ $$q: $$QProvider,
$sce: $SceProvider,
$sceDelegate: $SceDelegateProvider,
$sniffer: $SnifferProvider,
$templateCache: $TemplateCacheProvider,
+ $templateRequest: $TemplateRequestProvider,
+ $$testability: $$TestabilityProvider,
$timeout: $TimeoutProvider,
$window: $WindowProvider,
$$rAF: $$RAFProvider,
- $$asyncCallback : $$AsyncCallbackProvider
+ $$asyncCallback: $$AsyncCallbackProvider,
+ $$jqLite: $$jqLiteProvider
});
}
]);
}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Any commits to this file should be reviewed with security in mind. *
+ * Changes to this file can potentially create security vulnerabilities. *
+ * An approval from 2 Core members with history of modifying *
+ * this file is required. *
+ * *
+ * Does the change somehow allow for arbitrary javascript to be executed? *
+ * Or allows for someone to change the prototype of built-in objects? *
+ * Or gives undesired access to variables likes document or window? *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
/* global JQLitePrototype: true,
addEventListenerFn: true,
removeEventListenerFn: true,
- BOOLEAN_ATTR: true
+ BOOLEAN_ATTR: true,
+ ALIASED_ATTR: true,
*/
//////////////////////////////////
@@ -2153,13 +2319,14 @@ function publishExternalAPI(angular){
* - [`addClass()`](http://api.jquery.com/addClass/)
* - [`after()`](http://api.jquery.com/after/)
* - [`append()`](http://api.jquery.com/append/)
- * - [`attr()`](http://api.jquery.com/attr/)
+ * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
* - [`bind()`](http://api.jquery.com/bind/) - 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/)
+ * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`
* - [`data()`](http://api.jquery.com/data/)
+ * - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/)
* - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
@@ -2200,10 +2367,12 @@ function publishExternalAPI(angular){
* `'ngModel'`).
* - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
- * element or its parent.
+ * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
+ * be enabled.
* - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
* current element. This getter should be used only on elements that contain a directive which starts a new isolate
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
+ * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
@@ -2215,17 +2384,17 @@ JQLite.expando = 'ng339';
var jqCache = JQLite.cache = {},
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); });
+ addEventListenerFn = function(element, type, fn) {
+ element.addEventListener(type, fn, false);
+ },
+ removeEventListenerFn = function(element, type, fn) {
+ element.removeEventListener(type, fn, false);
+ };
/*
* !!! This is an undocumented "private" function !!!
*/
-var jqData = JQLite._data = function(node) {
+JQLite._data = function(node) {
//jQuery always returns an object on cache miss
return this.cache[node[this.expando]] || {};
};
@@ -2235,6 +2404,7 @@ function jqNextId() { return ++jqId; }
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
+var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
var jqLiteMinErr = minErr('jqLite');
/**
@@ -2250,49 +2420,6 @@ function camelCase(name) {
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) {
- // jshint -W040
- 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);
- }
-}
-
var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
var HTML_REGEXP = /<|&#?\w+;/;
var TAG_NAME_REGEXP = /<([\w:]+)/;
@@ -2312,26 +2439,32 @@ wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
+
function jqLiteIsTextNode(html) {
return !HTML_REGEXP.test(html);
}
+function jqLiteAcceptsData(node) {
+ // The window object can accept data but has no nodeType
+ // Otherwise we are only interested in elements (1) and documents (9)
+ var nodeType = node.nodeType;
+ return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
+}
+
function jqLiteBuildFragment(html, context) {
- var elem, tmp, tag, wrap,
+ var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
- nodes = [], i, j, jj;
+ nodes = [], i;
if (jqLiteIsTextNode(html)) {
// Convert non-html into a text node
nodes.push(context.createTextNode(html));
} else {
- tmp = fragment.appendChild(context.createElement('div'));
// Convert html into DOM nodes
+ tmp = tmp || fragment.appendChild(context.createElement("div"));
tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
wrap = wrapMap[tag] || wrapMap._default;
- tmp.innerHTML = '<div> </div>' +
- wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
- tmp.removeChild(tmp.firstChild);
+ tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
// Descend through wrappers to the right content
i = wrap[0];
@@ -2339,7 +2472,7 @@ function jqLiteBuildFragment(html, context) {
tmp = tmp.lastChild;
}
- for (j=0, jj=tmp.childNodes.length; j<jj; ++j) nodes.push(tmp.childNodes[j]);
+ nodes = concat(nodes, tmp.childNodes);
tmp = fragment.firstChild;
tmp.textContent = "";
@@ -2348,7 +2481,11 @@ function jqLiteBuildFragment(html, context) {
// Remove wrapper from fragment
fragment.textContent = "";
fragment.innerHTML = ""; // Clear inner HTML
- return nodes;
+ forEach(nodes, function(node) {
+ fragment.appendChild(node);
+ });
+
+ return fragment;
}
function jqLiteParseHTML(html, context) {
@@ -2359,7 +2496,11 @@ function jqLiteParseHTML(html, context) {
return [context.createElement(parsed[1])];
}
- return jqLiteBuildFragment(html, context);
+ if ((parsed = jqLiteBuildFragment(html, context))) {
+ return parsed.childNodes;
+ }
+
+ return [];
}
/////////////////////////////////////////////
@@ -2367,20 +2508,22 @@ function JQLite(element) {
if (element instanceof JQLite) {
return element;
}
+
+ var argIsString;
+
if (isString(element)) {
element = trim(element);
+ argIsString = true;
}
if (!(this instanceof JQLite)) {
- if (isString(element) && element.charAt(0) != '<') {
+ if (argIsString && 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)) {
+ if (argIsString) {
jqLiteAddNodes(this, jqLiteParseHTML(element));
- var fragment = jqLite(document.createDocumentFragment());
- fragment.append(this);
} else {
jqLiteAddNodes(this, element);
}
@@ -2390,50 +2533,63 @@ function jqLiteClone(element) {
return element.cloneNode(true);
}
-function jqLiteDealoc(element){
- jqLiteRemoveData(element);
- for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
- jqLiteDealoc(children[i]);
+function jqLiteDealoc(element, onlyDescendants) {
+ if (!onlyDescendants) jqLiteRemoveData(element);
+
+ if (element.querySelectorAll) {
+ var descendants = element.querySelectorAll('*');
+ for (var i = 0, l = descendants.length; i < l; i++) {
+ jqLiteRemoveData(descendants[i]);
+ }
}
}
function jqLiteOff(element, type, fn, unsupported) {
if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
- var events = jqLiteExpandoStore(element, 'events'),
- handle = jqLiteExpandoStore(element, 'handle');
+ var expandoStore = jqLiteExpandoStore(element);
+ var events = expandoStore && expandoStore.events;
+ var handle = expandoStore && expandoStore.handle;
if (!handle) return; //no listeners registered
- if (isUndefined(type)) {
- forEach(events, function(eventHandler, type) {
- removeEventListenerFn(element, type, eventHandler);
+ if (!type) {
+ for (type in events) {
+ if (type !== '$destroy') {
+ removeEventListenerFn(element, type, handle);
+ }
delete events[type];
- });
+ }
} else {
forEach(type.split(' '), function(type) {
- if (isUndefined(fn)) {
- removeEventListenerFn(element, type, events[type]);
- delete events[type];
- } else {
- arrayRemove(events[type] || [], fn);
+ if (isDefined(fn)) {
+ var listenerFns = events[type];
+ arrayRemove(listenerFns || [], fn);
+ if (listenerFns && listenerFns.length > 0) {
+ return;
+ }
}
+
+ removeEventListenerFn(element, type, handle);
+ delete events[type];
});
}
}
function jqLiteRemoveData(element, name) {
- var expandoId = element.ng339,
- expandoStore = jqCache[expandoId];
+ var expandoId = element.ng339;
+ var expandoStore = expandoId && jqCache[expandoId];
if (expandoStore) {
if (name) {
- delete jqCache[expandoId].data[name];
+ delete expandoStore.data[name];
return;
}
if (expandoStore.handle) {
- expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
+ if (expandoStore.events.$destroy) {
+ expandoStore.handle({}, '$destroy');
+ }
jqLiteOff(element);
}
delete jqCache[expandoId];
@@ -2441,43 +2597,42 @@ function jqLiteRemoveData(element, name) {
}
}
-function jqLiteExpandoStore(element, key, value) {
+
+function jqLiteExpandoStore(element, createIfNecessary) {
var expandoId = element.ng339,
- expandoStore = jqCache[expandoId || -1];
+ expandoStore = expandoId && jqCache[expandoId];
- if (isDefined(value)) {
- if (!expandoStore) {
- element.ng339 = expandoId = jqNextId();
- expandoStore = jqCache[expandoId] = {};
- }
- expandoStore[key] = value;
- } else {
- return expandoStore && expandoStore[key];
+ if (createIfNecessary && !expandoStore) {
+ element.ng339 = expandoId = jqNextId();
+ expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
}
+
+ return expandoStore;
}
+
function jqLiteData(element, key, value) {
- var data = jqLiteExpandoStore(element, 'data'),
- isSetter = isDefined(value),
- keyDefined = !isSetter && isDefined(key),
- isSimpleGetter = keyDefined && !isObject(key);
+ if (jqLiteAcceptsData(element)) {
- if (!data && !isSimpleGetter) {
- jqLiteExpandoStore(element, 'data', data = {});
- }
+ var isSimpleSetter = isDefined(value);
+ var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
+ var massGetter = !key;
+ var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
+ var data = expandoStore && expandoStore.data;
- if (isSetter) {
- data[key] = value;
- } else {
- if (keyDefined) {
- if (isSimpleGetter) {
- // don't create data in this case.
- return data && data[key];
+ if (isSimpleSetter) { // data('key', value)
+ data[key] = value;
+ } else {
+ if (massGetter) { // data()
+ return data;
} else {
- extend(data, key);
+ if (isSimpleGetter) { // data('key')
+ // don't force creation of expandoStore if it doesn't exist yet
+ return data && data[key];
+ } else { // mass-setter: data({key1: val1, key2: val2})
+ extend(data, key);
+ }
}
- } else {
- return data;
}
}
}
@@ -2485,7 +2640,7 @@ function jqLiteData(element, key, value) {
function jqLiteHasClass(element, selector) {
if (!element.getAttribute) return false;
return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
- indexOf( " " + selector + " " ) > -1);
+ indexOf(" " + selector + " ") > -1);
}
function jqLiteRemoveClass(element, cssClasses) {
@@ -2516,25 +2671,41 @@ function jqLiteAddClass(element, cssClasses) {
}
}
+
function jqLiteAddNodes(root, elements) {
+ // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
+
if (elements) {
- elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements))
- ? elements
- : [ elements ];
- for(var i=0; i < elements.length; i++) {
- root.push(elements[i]);
+
+ // if a Node (the most common case)
+ if (elements.nodeType) {
+ root[root.length++] = elements;
+ } else {
+ var length = elements.length;
+
+ // if an Array or NodeList and not a Window
+ if (typeof length === 'number' && elements.window !== elements) {
+ if (length) {
+ for (var i = 0; i < length; i++) {
+ root[root.length++] = elements[i];
+ }
+ }
+ } else {
+ root[root.length++] = elements;
+ }
}
}
}
+
function jqLiteController(element, name) {
- return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
+ return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
}
function jqLiteInheritedData(element, name, value) {
// if element is the document object work with the html element instead
// this makes $(document).scope() possible
- if(element.nodeType == 9) {
+ if (element.nodeType == NODE_TYPE_DOCUMENT) {
element = element.documentElement;
}
var names = isArray(name) ? name : [name];
@@ -2547,25 +2718,43 @@ function jqLiteInheritedData(element, name, value) {
// If dealing with a document fragment node with a host element, and no parent, use the host
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
// to lookup parent controllers.
- element = element.parentNode || (element.nodeType === 11 && element.host);
+ element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
}
}
function jqLiteEmpty(element) {
- for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
- jqLiteDealoc(childNodes[i]);
- }
+ jqLiteDealoc(element, true);
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
-//////////////////////////////////////////
-// Functions which are declared directly.
-//////////////////////////////////////////
-var JQLitePrototype = JQLite.prototype = {
- ready: function(fn) {
- var fired = false;
+function jqLiteRemove(element, keepData) {
+ if (!keepData) jqLiteDealoc(element);
+ var parent = element.parentNode;
+ if (parent) parent.removeChild(element);
+}
+
+
+function jqLiteDocumentLoaded(action, win) {
+ win = win || window;
+ if (win.document.readyState === 'complete') {
+ // Force the action to be run async for consistent behaviour
+ // from the action's point of view
+ // i.e. it will definitely not be in a $apply
+ win.setTimeout(action);
+ } else {
+ // No need to unbind this handler as load is only ever called once
+ jqLite(win).on('load', action);
+ }
+}
+
+//////////////////////////////////////////
+// Functions which are declared directly.
+//////////////////////////////////////////
+var JQLitePrototype = JQLite.prototype = {
+ ready: function(fn) {
+ var fired = false;
function trigger() {
if (fired) return;
@@ -2573,8 +2762,8 @@ var JQLitePrototype = JQLite.prototype = {
fn();
}
- // check if document already is loaded
- if (document.readyState === 'complete'){
+ // check if document is already loaded
+ if (document.readyState === 'complete') {
setTimeout(trigger);
} else {
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
@@ -2586,7 +2775,7 @@ var JQLitePrototype = JQLite.prototype = {
},
toString: function() {
var value = [];
- forEach(this, function(e){ value.push('' + e);});
+ forEach(this, function(e) { value.push('' + e);});
return '[' + value.join(', ') + ']';
},
@@ -2611,15 +2800,27 @@ forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','),
});
var BOOLEAN_ELEMENTS = {};
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
- BOOLEAN_ELEMENTS[uppercase(value)] = true;
+ BOOLEAN_ELEMENTS[value] = true;
});
+var ALIASED_ATTR = {
+ 'ngMinlength': 'minlength',
+ 'ngMaxlength': 'maxlength',
+ 'ngMin': 'min',
+ 'ngMax': 'max',
+ 'ngPattern': 'pattern'
+};
function getBooleanAttrName(element, name) {
// check dom last since we will most likely fail on name
var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
// booleanAttr is here twice to minimize DOM access
- return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
+ return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
+}
+
+function getAliasedAttrName(element, name) {
+ var nodeName = element.nodeName;
+ return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
}
forEach({
@@ -2649,7 +2850,7 @@ forEach({
return jqLiteInheritedData(element, '$injector');
},
- removeAttr: function(element,name) {
+ removeAttr: function(element, name) {
element.removeAttribute(name);
},
@@ -2661,26 +2862,11 @@ forEach({
if (isDefined(value)) {
element.style[name] = value;
} else {
- var val;
-
- if (msie <= 8) {
- // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
- val = element.currentStyle && element.currentStyle[name];
- if (val === '') val = 'auto';
- }
-
- val = val || element.style[name];
-
- if (msie <= 8) {
- // jquery weirdness :-/
- val = (val === '') ? undefined : val;
- }
-
- return val;
+ return element.style[name];
}
},
- attr: function(element, name, value){
+ attr: function(element, name, value) {
var lowercasedName = lowercase(name);
if (BOOLEAN_ATTR[lowercasedName]) {
if (isDefined(value)) {
@@ -2693,7 +2879,7 @@ forEach({
}
} else {
return (element[name] ||
- (element.attributes.getNamedItem(name)|| noop).specified)
+ (element.attributes.getNamedItem(name) || noop).specified)
? lowercasedName
: undefined;
}
@@ -2717,31 +2903,23 @@ forEach({
},
text: (function() {
- var NODE_TYPE_TEXT_PROPERTY = [];
- if (msie < 9) {
- NODE_TYPE_TEXT_PROPERTY[1] = 'innerText'; /** Element **/
- NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue'; /** Text **/
- } else {
- NODE_TYPE_TEXT_PROPERTY[1] = /** Element **/
- NODE_TYPE_TEXT_PROPERTY[3] = 'textContent'; /** Text **/
- }
getText.$dv = '';
return getText;
function getText(element, value) {
- var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType];
if (isUndefined(value)) {
- return textProp ? element[textProp] : '';
+ var nodeType = element.nodeType;
+ return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
}
- element[textProp] = value;
+ element.textContent = value;
}
})(),
val: function(element, value) {
if (isUndefined(value)) {
- if (nodeName_(element) === 'SELECT' && element.multiple) {
+ if (element.multiple && nodeName_(element) === 'select') {
var result = [];
- forEach(element.options, function (option) {
+ forEach(element.options, function(option) {
if (option.selected) {
result.push(option.value || option.text);
}
@@ -2757,14 +2935,12 @@ forEach({
if (isUndefined(value)) {
return element.innerHTML;
}
- for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
- jqLiteDealoc(childNodes[i]);
- }
+ jqLiteDealoc(element, true);
element.innerHTML = value;
},
empty: jqLiteEmpty
-}, function(fn, name){
+}, function(fn, name) {
/**
* Properties: writes return selection, reads return first value
*/
@@ -2816,57 +2992,50 @@ forEach({
});
function createEventHandler(element, events) {
- var eventHandler = function (event, type) {
- if (!event.preventDefault) {
- event.preventDefault = function() {
- event.returnValue = false; //ie
- };
- }
+ var eventHandler = function(event, type) {
+ // jQuery specific api
+ event.isDefaultPrevented = function() {
+ return event.defaultPrevented;
+ };
- if (!event.stopPropagation) {
- event.stopPropagation = function() {
- event.cancelBubble = true; //ie
- };
- }
+ var eventFns = events[type || event.type];
+ var eventFnsLength = eventFns ? eventFns.length : 0;
- if (!event.target) {
- event.target = event.srcElement || document;
- }
+ if (!eventFnsLength) return;
+
+ if (isUndefined(event.immediatePropagationStopped)) {
+ var originalStopImmediatePropagation = event.stopImmediatePropagation;
+ event.stopImmediatePropagation = function() {
+ event.immediatePropagationStopped = true;
+
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ }
- if (isUndefined(event.defaultPrevented)) {
- var prevent = event.preventDefault;
- event.preventDefault = function() {
- event.defaultPrevented = true;
- prevent.call(event);
+ if (originalStopImmediatePropagation) {
+ originalStopImmediatePropagation.call(event);
+ }
};
- event.defaultPrevented = false;
}
- event.isDefaultPrevented = function() {
- return event.defaultPrevented || event.returnValue === false;
+ event.isImmediatePropagationStopped = function() {
+ return event.immediatePropagationStopped === true;
};
// Copy event handlers in case event handlers array is modified during execution.
- var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
-
- forEach(eventHandlersCopy, function(fn) {
- fn.call(element, event);
- });
+ if ((eventFnsLength > 1)) {
+ eventFns = shallowCopy(eventFns);
+ }
- // Remove monkey-patched methods (IE),
- // as they would cause memory leaks in IE8.
- if (msie <= 8) {
- // IE7/8 does not allow to delete property on native object
- event.preventDefault = null;
- event.stopPropagation = null;
- event.isDefaultPrevented = null;
- } else {
- // It shouldn't affect normal browsers (native methods are defined on prototype).
- delete event.preventDefault;
- delete event.stopPropagation;
- delete event.isDefaultPrevented;
+ for (var i = 0; i < eventFnsLength; i++) {
+ if (!event.isImmediatePropagationStopped()) {
+ eventFns[i].call(element, event);
+ }
}
};
+
+ // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
+ // events on `element`
eventHandler.elem = element;
return eventHandler;
}
@@ -2879,68 +3048,56 @@ function createEventHandler(element, events) {
forEach({
removeData: jqLiteRemoveData,
- dealoc: jqLiteDealoc,
-
- on: function onFn(element, type, fn, unsupported){
+ on: function jqLiteOn(element, type, fn, unsupported) {
if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
- var events = jqLiteExpandoStore(element, 'events'),
- handle = jqLiteExpandoStore(element, 'handle');
+ // Do not add event handlers to non-elements because they will not be cleaned up.
+ if (!jqLiteAcceptsData(element)) {
+ return;
+ }
+
+ var expandoStore = jqLiteExpandoStore(element, true);
+ var events = expandoStore.events;
+ var handle = expandoStore.handle;
- if (!events) jqLiteExpandoStore(element, 'events', events = {});
- if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
+ if (!handle) {
+ handle = expandoStore.handle = createEventHandler(element, events);
+ }
+
+ // http://jsperf.com/string-indexof-vs-split
+ var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
+ var i = types.length;
- forEach(type.split(' '), function(type){
+ while (i--) {
+ type = types[i];
var eventFns = events[type];
if (!eventFns) {
- if (type == 'mouseenter' || type == 'mouseleave') {
- var contains = document.body.contains || document.body.compareDocumentPosition ?
- function( a, b ) {
- // jshint bitwise: false
- var adown = a.nodeType === 9 ? a.documentElement : a,
- bup = b && b.parentNode;
- return a === bup || !!( bup && bup.nodeType === 1 && (
- adown.contains ?
- adown.contains( bup ) :
- a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
- ));
- } :
- function( a, b ) {
- if ( b ) {
- while ( (b = b.parentNode) ) {
- if ( b === a ) {
- return true;
- }
- }
- }
- return false;
- };
-
- events[type] = [];
+ events[type] = [];
+ if (type === 'mouseenter' || type === 'mouseleave') {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
- var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"};
- onFn(element, eventmap[type], function(event) {
+ jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
var target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
- if ( !related || (related !== target && !contains(target, related)) ){
+ if (!related || (related !== target && !target.contains(related))) {
handle(event, type);
}
});
} else {
- addEventListenerFn(element, type, handle);
- events[type] = [];
+ if (type !== '$destroy') {
+ addEventListenerFn(element, type, handle);
+ }
}
eventFns = events[type];
}
eventFns.push(fn);
- });
+ }
},
off: jqLiteOff,
@@ -2961,7 +3118,7 @@ forEach({
replaceWith: function(element, replaceNode) {
var index, parent = element.parentNode;
jqLiteDealoc(element);
- forEach(new JQLite(replaceNode), function(node){
+ forEach(new JQLite(replaceNode), function(node) {
if (index) {
parent.insertBefore(node, index.nextSibling);
} else {
@@ -2973,8 +3130,8 @@ forEach({
children: function(element) {
var children = [];
- forEach(element.childNodes, function(element){
- if (element.nodeType === 1)
+ forEach(element.childNodes, function(element) {
+ if (element.nodeType === NODE_TYPE_ELEMENT)
children.push(element);
});
return children;
@@ -2985,24 +3142,28 @@ forEach({
},
append: function(element, node) {
- forEach(new JQLite(node), function(child){
- if (element.nodeType === 1 || element.nodeType === 11) {
- element.appendChild(child);
- }
- });
+ var nodeType = element.nodeType;
+ if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
+
+ node = new JQLite(node);
+
+ for (var i = 0, ii = node.length; i < ii; i++) {
+ var child = node[i];
+ element.appendChild(child);
+ }
},
prepend: function(element, node) {
- if (element.nodeType === 1) {
+ if (element.nodeType === NODE_TYPE_ELEMENT) {
var index = element.firstChild;
- forEach(new JQLite(node), function(child){
+ forEach(new JQLite(node), function(child) {
element.insertBefore(child, index);
});
}
},
wrap: function(element, wrapNode) {
- wrapNode = jqLite(wrapNode)[0];
+ wrapNode = jqLite(wrapNode).eq(0).clone()[0];
var parent = element.parentNode;
if (parent) {
parent.replaceChild(wrapNode, element);
@@ -3010,18 +3171,21 @@ forEach({
wrapNode.appendChild(element);
},
- remove: function(element) {
- jqLiteDealoc(element);
- var parent = element.parentNode;
- if (parent) parent.removeChild(element);
+ remove: jqLiteRemove,
+
+ detach: function(element) {
+ jqLiteRemove(element, true);
},
after: function(element, newElement) {
var index = element, parent = element.parentNode;
- forEach(new JQLite(newElement), function(node){
+ newElement = new JQLite(newElement);
+
+ for (var i = 0, ii = newElement.length; i < ii; i++) {
+ var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
<TRUNCATED>