summaryrefslogtreecommitdiffstats
path: root/js/vendor/angular/angular.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/vendor/angular/angular.js')
-rwxr-xr-x[-rw-r--r--]js/vendor/angular/angular.js1430
1 files changed, 992 insertions, 438 deletions
diff --git a/js/vendor/angular/angular.js b/js/vendor/angular/angular.js
index 0b8b39aae..719bc648e 100644..100755
--- a/js/vendor/angular/angular.js
+++ b/js/vendor/angular/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.0.4
+ * @license AngularJS v1.0.8
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
var manualLowercase = function(s) {
return isString(s)
- ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
+ ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
: s;
};
var manualUppercase = function(s) {
return isString(s)
- ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
+ ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
: s;
};
@@ -52,11 +52,8 @@ if ('i' !== 'I'.toLowerCase()) {
uppercase = manualUppercase;
}
-function fromCharCode(code) {return String.fromCharCode(code);}
-
-var Error = window.Error,
- /** holds major version number for IE or NaN for real browsers */
+var /** holds major version number for IE or NaN for real browsers */
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
@@ -70,6 +67,29 @@ var Error = window.Error,
nodeName_,
uid = ['0', '0', '0'];
+
+/**
+ * @private
+ * @param {*} obj
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
+ */
+function isArrayLike(obj) {
+ if (!obj || (typeof obj.length !== 'number')) return false;
+
+ // We have on object which has length property. Should we treat it as array?
+ if (typeof obj.hasOwnProperty != 'function' &&
+ typeof obj.constructor != 'function') {
+ // This is here for IE8: it is a bogus object treat it as array;
+ return true;
+ } else {
+ return obj instanceof JQLite || // JQLite
+ (jQuery && obj instanceof jQuery) || // jQuery
+ toString.call(obj) !== '[object Object]' || // some browser native object
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
+ }
+}
+
+
/**
* @ngdoc function
* @name angular.forEach
@@ -108,7 +128,7 @@ function forEach(obj, iterator, context) {
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context);
- } else if (isObject(obj) && isNumber(obj.length)) {
+ } else if (isArrayLike(obj)) {
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
@@ -153,7 +173,7 @@ 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 the nextId
+ * the number string gets longer over time, and it can also overflow, where as the nextId
* will grow much slower, it is a string, and it will never overflow.
*
* @returns an unique alpha-numeric string
@@ -180,6 +200,21 @@ function nextUid() {
return uid.join('');
}
+
+/**
+ * Set or clear the hashkey for an object.
+ * @param obj object
+ * @param h the hashkey (!truthy to delete the hashkey)
+ */
+function setHashKey(obj, h) {
+ if (h) {
+ obj.$$hashKey = h;
+ }
+ else {
+ delete obj.$$hashKey;
+ }
+}
+
/**
* @ngdoc function
* @name angular.extend
@@ -191,8 +226,10 @@ function nextUid() {
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
+ * @returns {Object} Reference to `dst`.
*/
function extend(dst) {
+ var h = dst.$$hashKey;
forEach(arguments, function(obj){
if (obj !== dst) {
forEach(obj, function(value, key){
@@ -200,6 +237,8 @@ function extend(dst) {
});
}
});
+
+ setHashKey(dst,h);
return dst;
}
@@ -243,7 +282,7 @@ noop.$inject = [];
*
<pre>
function transformer(transformationFn, value) {
- return (transformationFn || identity)(value);
+ return (transformationFn || angular.identity)(value);
};
</pre>
*/
@@ -371,6 +410,18 @@ function isFunction(value){return typeof value == 'function';}
/**
+ * Determines if a value is a regular expression object.
+ *
+ * @private
+ * @param {*} value Reference to check.
+ * @returns {boolean} True if `value` is a `RegExp`.
+ */
+function isRegExp(value) {
+ return toString.apply(value) == '[object RegExp]';
+}
+
+
+/**
* Checks if `obj` is a window object.
*
* @private
@@ -397,9 +448,20 @@ function isBoolean(value) {
}
-function trim(value) {
- return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
-}
+var trim = (function() {
+ // native trim is way faster: http://jsperf.com/angular-trim-test
+ // but IE doesn't have it... :-(
+ // TODO: we should move this into IE/ES5 polyfill
+ if (!String.prototype.trim) {
+ return function(value) {
+ return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
+ };
+ }
+ return function(value) {
+ return isString(value) ? value.trim() : value;
+ };
+})();
+
/**
* @ngdoc function
@@ -542,6 +604,8 @@ function copy(source, destination){
destination = copy(source, []);
} else if (isDate(source)) {
destination = new Date(source.getTime());
+ } else if (isRegExp(source)) {
+ destination = new RegExp(source.source);
} else if (isObject(source)) {
destination = copy(source, {});
}
@@ -549,19 +613,19 @@ function copy(source, destination){
} else {
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
if (isArray(source)) {
- while(destination.length) {
- destination.pop();
- }
+ destination.length = 0;
for ( var i = 0; i < source.length; i++) {
destination.push(copy(source[i]));
}
} else {
+ var h = destination.$$hashKey;
forEach(destination, function(value, key){
delete destination[key];
});
for ( var key in source) {
destination[key] = copy(source[key]);
}
+ setHashKey(destination,h);
}
}
return destination;
@@ -589,7 +653,7 @@ function shallowCopy(src, dst) {
* @function
*
* @description
- * Determines if two objects or two values are equivalent. Supports value types, arrays and
+ * 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:
@@ -597,11 +661,14 @@ function shallowCopy(src, dst) {
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
+ * * Both values represent the same regular expression (In JavasScript,
+ * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
+ * representation matches).
*
* During a property comparision, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
- * Scope and DOMWindow objects are being compared only be identify (`===`).
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
@@ -615,6 +682,7 @@ function equals(o1, o2) {
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;
@@ -623,8 +691,10 @@ function equals(o1, o2) {
}
} else if (isDate(o1)) {
return isDate(o2) && 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)) return false;
+ 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;
@@ -661,7 +731,7 @@ function sliceArgs(args, startIndex) {
*
* @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
*
* @param {Object} self Context which `fn` should be evaluated in.
@@ -713,13 +783,15 @@ function toJsonReplacer(key, value) {
* @function
*
* @description
- * Serializes input into a JSON-formatted string.
+ * 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} Jsonified string representing `obj`.
+ * @returns {string|undefined} Jsonified string representing `obj`.
*/
function toJson(obj, pretty) {
+ if (typeof obj === 'undefined') return undefined;
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
}
@@ -762,25 +834,53 @@ function startingTag(element) {
// are not allowed to have children. So we just ignore it.
element.html('');
} catch(e) {}
- return jqLite('<div>').append(element).html().
- match(/^(<[^>]+>)/)[1].
- replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
+ // As Per DOM Standards
+ var TEXT_NODE = 3;
+ var elemHtml = jqLite('<div>').append(element).html();
+ try {
+ return element[0].nodeType === TEXT_NODE ? 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)>
*/
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue){
- if (keyValue) {
+ if ( keyValue ) {
key_value = keyValue.split('=');
- key = decodeURIComponent(key_value[0]);
- obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
+ key = tryDecodeURIComponent(key_value[0]);
+ if ( isDefined(key) ) {
+ obj[key] = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
+ }
}
});
return obj;
@@ -831,7 +931,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
- replace((pctEncodeSpaces ? null : /%20/g), '+');
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
@@ -845,10 +945,14 @@ function encodeUriQuery(val, pctEncodeSpaces) {
*
* @description
*
- * Use this directive to auto-bootstrap on application. Only
- * one directive can be used per HTML document. The directive
+ * Use this directive to auto-bootstrap an application. Only
+ * one ngApp directive can be used per HTML document. The directive
* designates the root of the application and is typically placed
- * ot the root of the page.
+ * at the root of the page.
+ *
+ * The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an
+ * HTML document you must manually bootstrap them using {@link angular.bootstrap}.
+ * Applications cannot be nested.
*
* In the example below if the `ngApp` directive would not be placed
* on the `html` element then the document would not be compiled
@@ -915,27 +1019,46 @@ function angularInit(element, bootstrap) {
*
* See: {@link guide/bootstrap Bootstrap}
*
+ * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
+ * They must use {@link api/ng.directive:ngApp ngApp}.
+ *
* @param {Element} element DOM element which is the root of angular application.
* @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
* @returns {AUTO.$injector} Returns the newly created injector for this app.
*/
function bootstrap(element, modules) {
- element = jqLite(element);
- modules = modules || [];
- modules.unshift(['$provide', function($provide) {
- $provide.value('$rootElement', element);
- }]);
- modules.unshift('ng');
- var injector = createInjector(modules);
- injector.invoke(
- ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
- scope.$apply(function() {
- element.data('$injector', injector);
- compile(element)(scope);
- });
- }]
- );
- return injector;
+ var doBootstrap = function() {
+ element = jqLite(element);
+ modules = modules || [];
+ modules.unshift(['$provide', function($provide) {
+ $provide.value('$rootElement', element);
+ }]);
+ modules.unshift('ng');
+ var injector = createInjector(modules);
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
+ function(scope, element, compile, injector) {
+ scope.$apply(function() {
+ element.data('$injector', injector);
+ compile(element)(scope);
+ });
+ }]
+ );
+ return injector;
+ };
+
+ var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
+
+ 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();
+ };
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -958,9 +1081,10 @@ function bindJQuery() {
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
- JQLitePatchJQueryRemove('remove', true);
- JQLitePatchJQueryRemove('empty');
- JQLitePatchJQueryRemove('html');
+ // Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
+ JQLitePatchJQueryRemove('remove', true, true, false);
+ JQLitePatchJQueryRemove('empty', false, false, false);
+ JQLitePatchJQueryRemove('html', false, false, true);
} else {
jqLite = JQLite;
}
@@ -968,7 +1092,7 @@ function bindJQuery() {
}
/**
- * throw error of the argument is falsy.
+ * throw error if the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
@@ -988,6 +1112,33 @@ function assertArgFn(arg, name, acceptArrayAnnotation) {
}
/**
+ * 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=true} bindFnToScope
+ * @returns 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;
+}
+
+/**
* @ngdoc interface
* @name angular.Module
* @description
@@ -1017,8 +1168,8 @@ function setupModuleLoader(window) {
*
* # Module
*
- * A module is a collocation of services, directives, filters, and configuration information. Module
- * is used to configure the {@link AUTO.$injector $injector}.
+ * A module is a collection of services, directives, filters, and configuration information.
+ * `angular.module` is used to configure the {@link AUTO.$injector $injector}.
*
* <pre>
* // Create a new module
@@ -1249,11 +1400,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.0.4', // all of these placeholder strings will be replaced by rake's
- major: 1, // compile task
+ full: '1.0.8', // all of these placeholder strings will be replaced by grunt's
+ major: 1, // package task
minor: 0,
- dot: 4,
- codeName: 'bewildering-hair'
+ dot: 8,
+ codeName: 'bubble-burst'
};
@@ -1322,7 +1473,6 @@ function publishExternalAPI(angular){
ngPluralize: ngPluralizeDirective,
ngRepeat: ngRepeatDirective,
ngShow: ngShowDirective,
- ngSubmit: ngSubmitDirective,
ngStyle: ngStyleDirective,
ngSwitch: ngSwitchDirective,
ngSwitchWhen: ngSwitchWhenDirective,
@@ -1392,24 +1542,25 @@ function publishExternalAPI(angular){
* Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
* raw DOM references.
*
- * ## Angular's jQuery lite provides the following methods:
+ * ## Angular's jqLite
+ * Angular's lite version of jQuery provides only the following jQuery methods:
*
* - [addClass()](http://api.jquery.com/addClass/)
* - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/)
- * - [bind()](http://api.jquery.com/bind/)
- * - [children()](http://api.jquery.com/children/)
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
* - [clone()](http://api.jquery.com/clone/)
* - [contents()](http://api.jquery.com/contents/)
* - [css()](http://api.jquery.com/css/)
* - [data()](http://api.jquery.com/data/)
* - [eq()](http://api.jquery.com/eq/)
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [hasClass()](http://api.jquery.com/hasClass/)
* - [html()](http://api.jquery.com/html/)
- * - [next()](http://api.jquery.com/next/)
- * - [parent()](http://api.jquery.com/parent/)
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
* - [prepend()](http://api.jquery.com/prepend/)
* - [prop()](http://api.jquery.com/prop/)
* - [ready()](http://api.jquery.com/ready/)
@@ -1421,12 +1572,18 @@ function publishExternalAPI(angular){
* - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/)
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
- * - [unbind()](http://api.jquery.com/unbind/)
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
* - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/)
*
- * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
+ * ## jQuery/jqLite Extras
+ * Angular also provides the following additional methods and events to both jQuery and jqLite:
*
+ * ### Events
+ * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
+ * on all DOM nodes being removed. This can be used to clean up and 3rd party bindings to the DOM
+ * element before it is removed.
+ * ### Methods
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
* retrieves controller associated with the `ngController` directive. If `name` is provided as
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
@@ -1473,37 +1630,38 @@ function camelCase(name) {
/////////////////////////////////////////////
// jQuery mutation patch
//
-// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
+// 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) {
+function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
var originalJqFn = jQuery.fn[name];
originalJqFn = originalJqFn.$original || originalJqFn;
removePatch.$original = originalJqFn;
jQuery.fn[name] = removePatch;
- function removePatch() {
- var list = [this],
+ function removePatch(param) {
+ var list = filterElems && param ? [this.filter(param)] : [this],
fireEvent = dispatchThis,
set, setIndex, setLength,
- element, childIndex, childLength, children,
- fns, events;
-
- 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]));
+ 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]));
+ }
}
}
}
@@ -1563,7 +1721,7 @@ function JQLiteUnbind(element, type, fn) {
removeEventListenerFn(element, type, events[type]);
delete events[type];
} else {
- arrayRemove(events[type], fn);
+ arrayRemove(events[type] || [], fn);
}
}
}
@@ -1837,6 +1995,15 @@ forEach({
val: function(element, value) {
if (isUndefined(value)) {
+ if (nodeName_(element) === 'SELECT' && element.multiple) {
+ var result = [];
+ forEach(element.options, function (option) {
+ if (option.selected) {
+ result.push(option.value || option.text);
+ }
+ });
+ return result.length === 0 ? null : result;
+ }
return element.value;
}
element.value = value;
@@ -1968,23 +2135,43 @@ forEach({
if (!eventFns) {
if (type == 'mouseenter' || type == 'mouseleave') {
- var counter = 0;
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
+ function( a, b ) {
+ 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.mouseenter = [];
- events.mouseleave = [];
+ events[type] = [];
+
+ // 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"}
+ bindFn(element, eventmap[type], function(event) {
+ var ret, 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)) ){
+ handle(event, type);
+ }
- bindFn(element, 'mouseover', function(event) {
- counter++;
- if (counter == 1) {
- handle(event, 'mouseenter');
- }
- });
- bindFn(element, 'mouseout', function(event) {
- counter --;
- if (counter == 0) {
- handle(event, 'mouseleave');
- }
});
+
} else {
addEventListenerFn(element, type, handle);
events[type] = [];
@@ -2034,12 +2221,7 @@ forEach({
if (element.nodeType === 1) {
var index = element.firstChild;
forEach(new JQLite(node), function(child){
- if (index) {
- element.insertBefore(child, index);
- } else {
- element.appendChild(child);
- index = child;
- }
+ element.insertBefore(child, index);
});
}
},
@@ -2300,7 +2482,7 @@ function annotate(fn) {
}
} else if (isArray(fn)) {
last = fn.length - 1;
- assertArgFn(fn[last], 'fn')
+ assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
@@ -2334,19 +2516,19 @@ function annotate(fn) {
* # Injection Function Annotation
*
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
*
* <pre>
* // inferred (only works if code not minified/obfuscated)
- * $inject.invoke(function(serviceA){});
+ * $injector.invoke(function(serviceA){});
*
* // annotated
* function explicit(serviceA) {};
* explicit.$inject = ['serviceA'];
- * $inject.invoke(explicit);
+ * $injector.invoke(explicit);
*
* // inline
- * $inject.invoke(['serviceA', function(serviceA){}]);
+ * $injector.invoke(['serviceA', function(serviceA){}]);
* </pre>
*
* ## Inference
@@ -2430,7 +2612,7 @@ function annotate(fn) {
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
* are supported.
*
- * # The `$injector` property
+ * # The `$inject` property
*
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
* services to be injected into the function.
@@ -2463,7 +2645,7 @@ function annotate(fn) {
* // ...
* };
* tmpFn.$inject = ['$compile', '$rootScope'];
- * injector.invoke(tempFn);
+ * injector.invoke(tmpFn);
*
* // To better support inline function the inline annotation is supported
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -2492,7 +2674,7 @@ function annotate(fn) {
* @description
*
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
- * The providers share the same name as the instance they create with the `Provider` suffixed to them.
+ * The providers share the same name as the instance they create with `Provider` suffixed to them.
*
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
* a service. The Provider can have additional methods which would allow for configuration of the provider.
@@ -2516,7 +2698,7 @@ function annotate(fn) {
*
* beforeEach(module(function($provide) {
* $provide.provider('greet', GreetProvider);
- * });
+ * }));
*
* it('should greet', inject(function(greet) {
* expect(greet('angular')).toEqual('Hello angular!');
@@ -2529,9 +2711,7 @@ function annotate(fn) {
* inject(function(greet) {
* expect(greet('angular')).toEqual('Ahoj angular!');
* });
- * )};
- *
- * });
+ * });
* </pre>
*/
@@ -2625,7 +2805,7 @@ function annotate(fn) {
*
* @param {string} name The name of the service to decorate.
* @param {function()} decorator This function will be invoked when the service needs to be
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
*
* * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -2825,6 +3005,8 @@ function createInjector(modulesToLoad) {
var Constructor = function() {},
instance, returnedValue;
+ // Check if Type is annotated and use just the given function at n-1 as parameter
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals);
@@ -2840,6 +3022,7 @@ function createInjector(modulesToLoad) {
};
}
}
+
/**
* @ngdoc function
* @name ng.$anchorScroll
@@ -3031,7 +3214,8 @@ function Browser(window, document, $log, $sniffer) {
//////////////////////////////////////////////////////////////
var lastBrowserUrl = location.href,
- baseElement = document.find('base');
+ baseElement = document.find('base'),
+ replacedUrl = null;
/**
* @name ng.$browser#url
@@ -3066,14 +3250,21 @@ function Browser(window, document, $log, $sniffer) {
baseElement.attr('href', baseElement.attr('href'));
}
} else {
- if (replace) location.replace(url);
- else location.href = url;
+ if (replace) {
+ location.replace(url);
+ replacedUrl = url;
+ } else {
+ location.href = url;
+ replacedUrl = null;
+ }
}
return self;
// getter
} else {
- // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
- return location.href.replace(/%27/g,"'");
+ // - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
+ // location.href synchronously
+ // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
+ return replacedUrl || location.href.replace(/%27/g,"'");
}
};
@@ -3144,7 +3335,7 @@ function Browser(window, document, $log, $sniffer) {
*/
self.baseHref = function() {
var href = baseElement.attr('href');
- return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
+ return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
};
//////////////////////////////////////////////////////////////
@@ -3204,7 +3395,13 @@ function Browser(window, document, $log, $sniffer) {
cookie = cookieArray[i];
index = cookie.indexOf('=');
if (index > 0) { //ignore nameless cookies
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
+ var name = unescape(cookie.substring(0, index));
+ // the first value that is seen for a cookie is the most
+ // specific one. values for the same cookie name that
+ // follow are for less specific paths.
+ if (lastCookies[name] === undefined) {
+ lastCookies[name] = unescape(cookie.substring(index + 1));
+ }
}
}
}
@@ -3268,12 +3465,26 @@ function $BrowserProvider(){
return new Browser($window, $document, $log, $sniffer);
}];
}
+
/**
* @ngdoc object
* @name ng.$cacheFactory
*
* @description
- * Factory that constructs cache objects.
+ * Factory that constructs cache objects and gives access to them.
+ *
+ * <pre>
+ *
+ * var cache = $cacheFactory('cacheId');
+ * expect($cacheFactory.get('cacheId')).toBe(cache);
+ * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
+ *
+ * cache.put("key", "value");
+ * cache.put("another key", "another value");
+ *
+ * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
+ *
+ * </pre>
*
*
* @param {string} cacheId Name or id of the newly created cache.
@@ -3405,6 +3616,16 @@ function $CacheFactoryProvider() {
}
+ /**
+ * @ngdoc method
+ * @name ng.$cacheFactory#info
+ * @methodOf ng.$cacheFactory
+ *
+ * @description
+ * Get information about all the of the caches that have been created
+ *
+ * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
+ */
cacheFactory.info = function() {
var info = {};
forEach(caches, function(cache, cacheId) {
@@ -3414,6 +3635,17 @@ function $CacheFactoryProvider() {
};
+ /**
+ * @ngdoc method
+ * @name ng.$cacheFactory#get
+ * @methodOf ng.$cacheFactory
+ *
+ * @description
+ * Get access to a cache object by the `cacheId` used when it was created.
+ *
+ * @param {string} cacheId Name or id of a cache to access.
+ * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
+ */
cacheFactory.get = function(cacheId) {
return caches[cacheId];
};
@@ -3428,8 +3660,44 @@ function $C