diff options
Diffstat (limited to 'js/vendor/angular/angular.js')
-rwxr-xr-x[-rw-r--r--] | js/vendor/angular/angular.js | 1430 |
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 |