Commit 1669496b authored by Pascal Hartig's avatar Pascal Hartig

AngularJS: architecture example updated to 1.0.7

parent b7d77e8a
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
"name": "todomvc-angular", "name": "todomvc-angular",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"angular": "~1.0.5", "angular": "~1.0.7",
"todomvc-common": "~0.1.4" "todomvc-common": "~0.1.4"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "~1.0.5" "angular-mocks": "~1.0.5"
} }
} }
\ No newline at end of file
/** /**
* @license AngularJS v1.0.5 * @license AngularJS v1.0.7
* (c) 2010-2012 Google, Inc. http://angularjs.org * (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
...@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase() ...@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
var manualLowercase = function(s) { var manualLowercase = function(s) {
return isString(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; : s;
}; };
var manualUppercase = function(s) { var manualUppercase = function(s) {
return isString(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; : s;
}; };
...@@ -52,8 +52,6 @@ if ('i' !== 'I'.toLowerCase()) { ...@@ -52,8 +52,6 @@ if ('i' !== 'I'.toLowerCase()) {
uppercase = manualUppercase; uppercase = manualUppercase;
} }
function fromCharCode(code) {return String.fromCharCode(code);}
var /** 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]), msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
...@@ -69,6 +67,29 @@ var /** holds major version number for IE or NaN for real browsers */ ...@@ -69,6 +67,29 @@ var /** holds major version number for IE or NaN for real browsers */
nodeName_, nodeName_,
uid = ['0', '0', '0']; 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 * @ngdoc function
* @name angular.forEach * @name angular.forEach
...@@ -96,30 +117,6 @@ var /** holds major version number for IE or NaN for real browsers */ ...@@ -96,30 +117,6 @@ var /** holds major version number for IE or NaN for real browsers */
* @param {Object=} context Object to become context (`this`) for the iterator function. * @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`. * @returns {Object|Array} Reference to `obj`.
*/ */
/**
* @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)
}
}
function forEach(obj, iterator, context) { function forEach(obj, iterator, context) {
var key; var key;
if (obj) { if (obj) {
...@@ -203,6 +200,21 @@ function nextUid() { ...@@ -203,6 +200,21 @@ function nextUid() {
return uid.join(''); 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 * @ngdoc function
* @name angular.extend * @name angular.extend
...@@ -214,8 +226,10 @@ function nextUid() { ...@@ -214,8 +226,10 @@ function nextUid() {
* *
* @param {Object} dst Destination object. * @param {Object} dst Destination object.
* @param {...Object} src Source object(s). * @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/ */
function extend(dst) { function extend(dst) {
var h = dst.$$hashKey;
forEach(arguments, function(obj){ forEach(arguments, function(obj){
if (obj !== dst) { if (obj !== dst) {
forEach(obj, function(value, key){ forEach(obj, function(value, key){
...@@ -223,6 +237,8 @@ function extend(dst) { ...@@ -223,6 +237,8 @@ function extend(dst) {
}); });
} }
}); });
setHashKey(dst,h);
return dst; return dst;
} }
...@@ -577,12 +593,14 @@ function copy(source, destination){ ...@@ -577,12 +593,14 @@ function copy(source, destination){
destination.push(copy(source[i])); destination.push(copy(source[i]));
} }
} else { } else {
var h = destination.$$hashKey;
forEach(destination, function(value, key){ forEach(destination, function(value, key){
delete destination[key]; delete destination[key];
}); });
for ( var key in source) { for ( var key in source) {
destination[key] = copy(source[key]); destination[key] = copy(source[key]);
} }
setHashKey(destination,h);
} }
} }
return destination; return destination;
...@@ -622,7 +640,7 @@ function shallowCopy(src, dst) { ...@@ -622,7 +640,7 @@ function shallowCopy(src, dst) {
* During a property comparision, properties of `function` type and properties with names * During a property comparision, properties of `function` type and properties with names
* that begin with `$` are ignored. * 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 {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare. * @param {*} o2 Object or value to compare.
...@@ -682,7 +700,7 @@ function sliceArgs(args, startIndex) { ...@@ -682,7 +700,7 @@ function sliceArgs(args, startIndex) {
* *
* @description * @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for * 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). * known as [function currying](http://en.wikipedia.org/wiki/Currying).
* *
* @param {Object} self Context which `fn` should be evaluated in. * @param {Object} self Context which `fn` should be evaluated in.
...@@ -861,7 +879,7 @@ function encodeUriQuery(val, pctEncodeSpaces) { ...@@ -861,7 +879,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
replace(/%3A/gi, ':'). replace(/%3A/gi, ':').
replace(/%24/g, '$'). replace(/%24/g, '$').
replace(/%2C/gi, ','). replace(/%2C/gi, ',').
replace((pctEncodeSpaces ? null : /%20/g), '+'); replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
} }
...@@ -875,7 +893,7 @@ function encodeUriQuery(val, pctEncodeSpaces) { ...@@ -875,7 +893,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
* *
* @description * @description
* *
* Use this directive to auto-bootstrap on application. Only * Use this directive to auto-bootstrap an application. Only
* one directive can be used per HTML document. The directive * one directive can be used per HTML document. The directive
* designates the root of the application and is typically placed * designates the root of the application and is typically placed
* at the root of the page. * at the root of the page.
...@@ -950,22 +968,38 @@ function angularInit(element, bootstrap) { ...@@ -950,22 +968,38 @@ function angularInit(element, bootstrap) {
* @returns {AUTO.$injector} Returns the newly created injector for this app. * @returns {AUTO.$injector} Returns the newly created injector for this app.
*/ */
function bootstrap(element, modules) { function bootstrap(element, modules) {
element = jqLite(element); var resumeBootstrapInternal = function() {
modules = modules || []; element = jqLite(element);
modules.unshift(['$provide', function($provide) { modules = modules || [];
$provide.value('$rootElement', element); modules.unshift(['$provide', function($provide) {
}]); $provide.value('$rootElement', element);
modules.unshift('ng'); }]);
var injector = createInjector(modules); modules.unshift('ng');
injector.invoke( var injector = createInjector(modules);
['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
scope.$apply(function() { function(scope, element, compile, injector) {
element.data('$injector', injector); scope.$apply(function() {
compile(element)(scope); element.data('$injector', injector);
}); compile(element)(scope);
}] });
); }]
return injector; );
return injector;
};
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return resumeBootstrapInternal();
}
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
resumeBootstrapInternal();
};
} }
var SNAKE_CASE_REGEXP = /[A-Z]/g; var SNAKE_CASE_REGEXP = /[A-Z]/g;
...@@ -998,7 +1032,7 @@ function bindJQuery() { ...@@ -998,7 +1032,7 @@ function bindJQuery() {
} }
/** /**
* throw error of the argument is falsy. * throw error if the argument is falsy.
*/ */
function assertArg(arg, name, reason) { function assertArg(arg, name, reason) {
if (!arg) { if (!arg) {
...@@ -1279,11 +1313,11 @@ function setupModuleLoader(window) { ...@@ -1279,11 +1313,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/ */
var version = { var version = {
full: '1.0.5', // all of these placeholder strings will be replaced by rake's full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
major: 1, // compile task major: 1, // package task
minor: 0, minor: 0,
dot: 5, dot: 7,
codeName: 'flatulent-propulsion' codeName: 'monochromatic-rainbow'
}; };
...@@ -1428,18 +1462,18 @@ function publishExternalAPI(angular){ ...@@ -1428,18 +1462,18 @@ function publishExternalAPI(angular){
* - [after()](http://api.jquery.com/after/) * - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/) * - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/) * - [attr()](http://api.jquery.com/attr/)
* - [bind()](http://api.jquery.com/bind/) * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
* - [children()](http://api.jquery.com/children/) * - [children()](http://api.jquery.com/children/) - Does not support selectors
* - [clone()](http://api.jquery.com/clone/) * - [clone()](http://api.jquery.com/clone/)
* - [contents()](http://api.jquery.com/contents/) * - [contents()](http://api.jquery.com/contents/)
* - [css()](http://api.jquery.com/css/) * - [css()](http://api.jquery.com/css/)
* - [data()](http://api.jquery.com/data/) * - [data()](http://api.jquery.com/data/)
* - [eq()](http://api.jquery.com/eq/) * - [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/) * - [hasClass()](http://api.jquery.com/hasClass/)
* - [html()](http://api.jquery.com/html/) * - [html()](http://api.jquery.com/html/)
* - [next()](http://api.jquery.com/next/) * - [next()](http://api.jquery.com/next/) - Does not support selectors
* - [parent()](http://api.jquery.com/parent/) * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
* - [prepend()](http://api.jquery.com/prepend/) * - [prepend()](http://api.jquery.com/prepend/)
* - [prop()](http://api.jquery.com/prop/) * - [prop()](http://api.jquery.com/prop/)
* - [ready()](http://api.jquery.com/ready/) * - [ready()](http://api.jquery.com/ready/)
...@@ -1451,7 +1485,7 @@ function publishExternalAPI(angular){ ...@@ -1451,7 +1485,7 @@ function publishExternalAPI(angular){
* - [text()](http://api.jquery.com/text/) * - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/) * - [toggleClass()](http://api.jquery.com/toggleClass/)
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers. * - [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/) * - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/) * - [wrap()](http://api.jquery.com/wrap/)
* *
...@@ -1998,23 +2032,43 @@ forEach({ ...@@ -1998,23 +2032,43 @@ forEach({
if (!eventFns) { if (!eventFns) {
if (type == 'mouseenter' || type == 'mouseleave') { 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[type] = [];
events.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"}
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 { } else {
addEventListenerFn(element, type, handle); addEventListenerFn(element, type, handle);
events[type] = []; events[type] = [];
...@@ -2330,7 +2384,7 @@ function annotate(fn) { ...@@ -2330,7 +2384,7 @@ function annotate(fn) {
} }
} else if (isArray(fn)) { } else if (isArray(fn)) {
last = fn.length - 1; last = fn.length - 1;
assertArgFn(fn[last], 'fn') assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last); $inject = fn.slice(0, last);
} else { } else {
assertArgFn(fn, 'fn', true); assertArgFn(fn, 'fn', true);
...@@ -2364,19 +2418,19 @@ function annotate(fn) { ...@@ -2364,19 +2418,19 @@ function annotate(fn) {
* # Injection Function Annotation * # Injection Function Annotation
* *
* JavaScript does not have annotations, and annotations are needed for dependency injection. The * 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> * <pre>
* // inferred (only works if code not minified/obfuscated) * // inferred (only works if code not minified/obfuscated)
* $inject.invoke(function(serviceA){}); * $injector.invoke(function(serviceA){});
* *
* // annotated * // annotated
* function explicit(serviceA) {}; * function explicit(serviceA) {};
* explicit.$inject = ['serviceA']; * explicit.$inject = ['serviceA'];
* $inject.invoke(explicit); * $injector.invoke(explicit);
* *
* // inline * // inline
* $inject.invoke(['serviceA', function(serviceA){}]); * $injector.invoke(['serviceA', function(serviceA){}]);
* </pre> * </pre>
* *
* ## Inference * ## Inference
...@@ -2493,7 +2547,7 @@ function annotate(fn) { ...@@ -2493,7 +2547,7 @@ function annotate(fn) {
* // ... * // ...
* }; * };
* tmpFn.$inject = ['$compile', '$rootScope']; * tmpFn.$inject = ['$compile', '$rootScope'];
* injector.invoke(tempFn); * injector.invoke(tmpFn);
* *
* // To better support inline function the inline annotation is supported * // To better support inline function the inline annotation is supported
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
...@@ -2522,7 +2576,7 @@ function annotate(fn) { ...@@ -2522,7 +2576,7 @@ function annotate(fn) {
* @description * @description
* *
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance. * 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 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. * a service. The Provider can have additional methods which would allow for configuration of the provider.
...@@ -2546,7 +2600,7 @@ function annotate(fn) { ...@@ -2546,7 +2600,7 @@ function annotate(fn) {
* *
* beforeEach(module(function($provide) { * beforeEach(module(function($provide) {
* $provide.provider('greet', GreetProvider); * $provide.provider('greet', GreetProvider);
* }); * }));
* *
* it('should greet', inject(function(greet) { * it('should greet', inject(function(greet) {
* expect(greet('angular')).toEqual('Hello angular!'); * expect(greet('angular')).toEqual('Hello angular!');
...@@ -2559,9 +2613,7 @@ function annotate(fn) { ...@@ -2559,9 +2613,7 @@ function annotate(fn) {
* inject(function(greet) { * inject(function(greet) {
* expect(greet('angular')).toEqual('Ahoj angular!'); * expect(greet('angular')).toEqual('Ahoj angular!');
* }); * });
* )}; * });
*
* });
* </pre> * </pre>
*/ */
...@@ -2655,7 +2707,7 @@ function annotate(fn) { ...@@ -2655,7 +2707,7 @@ function annotate(fn) {
* *
* @param {string} name The name of the service to decorate. * @param {string} name The name of the service to decorate.
* @param {function()} decorator This function will be invoked when the service needs to be * @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: * injector.invoke} method and is therefore fully injectable. Local injection arguments:
* *
* * `$delegate` - The original service instance, which can be monkey patched, configured, * * `$delegate` - The original service instance, which can be monkey patched, configured,
...@@ -2855,6 +2907,8 @@ function createInjector(modulesToLoad) { ...@@ -2855,6 +2907,8 @@ function createInjector(modulesToLoad) {
var Constructor = function() {}, var Constructor = function() {},
instance, returnedValue; 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; Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor(); instance = new Constructor();
returnedValue = invoke(Type, instance, locals); returnedValue = invoke(Type, instance, locals);
...@@ -2870,6 +2924,7 @@ function createInjector(modulesToLoad) { ...@@ -2870,6 +2924,7 @@ function createInjector(modulesToLoad) {
}; };
} }
} }
/** /**
* @ngdoc function * @ngdoc function
* @name ng.$anchorScroll * @name ng.$anchorScroll
...@@ -3234,7 +3289,13 @@ function Browser(window, document, $log, $sniffer) { ...@@ -3234,7 +3289,13 @@ function Browser(window, document, $log, $sniffer) {
cookie = cookieArray[i]; cookie = cookieArray[i];
index = cookie.indexOf('='); index = cookie.indexOf('=');
if (index > 0) { //ignore nameless cookies 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));
}
} }
} }
} }
...@@ -3298,6 +3359,7 @@ function $BrowserProvider(){ ...@@ -3298,6 +3359,7 @@ function $BrowserProvider(){
return new Browser($window, $document, $log, $sniffer); return new Browser($window, $document, $log, $sniffer);
}]; }];
} }
/** /**
* @ngdoc object * @ngdoc object
* @name ng.$cacheFactory * @name ng.$cacheFactory
...@@ -3625,7 +3687,7 @@ function $CompileProvider($provide) { ...@@ -3625,7 +3687,7 @@ function $CompileProvider($provide) {
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ', MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/; urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
/** /**
...@@ -3827,7 +3889,7 @@ function $CompileProvider($provide) { ...@@ -3827,7 +3889,7 @@ function $CompileProvider($provide) {
function compile($compileNodes, transcludeFn, maxPriority) { function compile($compileNodes, transcludeFn, maxPriority) {
if (!($compileNodes instanceof jqLite)) { if (!($compileNodes instanceof jqLite)) {
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it. // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
$compileNodes = jqLite($compileNodes); $compileNodes = jqLite($compileNodes);
} }
// We can not compile top level text elements since text nodes can be merged and we will // We can not compile top level text elements since text nodes can be merged and we will
...@@ -3879,7 +3941,7 @@ function $CompileProvider($provide) { ...@@ -3879,7 +3941,7 @@ function $CompileProvider($provide) {
* functions return values - the linking functions - are combined into a composite linking * functions return values - the linking functions - are combined into a composite linking
* function, which is the a linking function for the node. * function, which is the a linking function for the node.
* *
* @param {NodeList} nodeList an array of nodes to compile * @param {NodeList} nodeList an array of nodes or NodeList to compile
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope. * scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
...@@ -3902,7 +3964,7 @@ function $CompileProvider($provide) { ...@@ -3902,7 +3964,7 @@ function $CompileProvider($provide) {
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement) ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
: null; : null;
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length) childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
? null ? null
: compileNodes(nodeList[i].childNodes, : compileNodes(nodeList[i].childNodes,
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn); nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
...@@ -4038,9 +4100,9 @@ function $CompileProvider($provide) { ...@@ -4038,9 +4100,9 @@ function $CompileProvider($provide) {
/** /**
* Once the directives have been collected their compile functions is executed. This method * Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application * is responsible for inlining directive templates as well as terminating the application
* of the directives if the terminal directive has been reached.. * of the directives if the terminal directive has been reached.
* *
* @param {Array} directives Array of collected directives to execute their compile function. * @param {Array} directives Array of collected directives to execute their compile function.
* this needs to be pre-sorted by priority order. * this needs to be pre-sorted by priority order.
...@@ -4048,11 +4110,11 @@ function $CompileProvider($provide) { ...@@ -4048,11 +4110,11 @@ function $CompileProvider($provide) {
* @param {Object} templateAttrs The shared attribute function * @param {Object} templateAttrs The shared attribute function
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope. * scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement} $rootElement If we are working on the root of the compile tree then this * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
* argument has the root jqLite array so that we can replace widgets on it. * argument has the root jqLite array so that we can replace nodes on it.
* @returns linkFn * @returns linkFn
*/ */
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) { function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
var terminalPriority = -Number.MAX_VALUE, var terminalPriority = -Number.MAX_VALUE,
preLinkFns = [], preLinkFns = [],
postLinkFns = [], postLinkFns = [],
...@@ -4106,7 +4168,7 @@ function $CompileProvider($provide) { ...@@ -4106,7 +4168,7 @@ function $CompileProvider($provide) {
$compileNode = templateAttrs.$$element = $compileNode = templateAttrs.$$element =
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
compileNode = $compileNode[0]; compileNode = $compileNode[0];
replaceWith($rootElement, jqLite($template[0]), compileNode); replaceWith(jqCollection, jqLite($template[0]), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority); childTranscludeFn = compile($template, transcludeFn, terminalPriority);
} else { } else {
$template = jqLite(JQLiteClone(compileNode)).contents(); $template = jqLite(JQLiteClone(compileNode)).contents();
...@@ -4130,7 +4192,7 @@ function $CompileProvider($provide) { ...@@ -4130,7 +4192,7 @@ function $CompileProvider($provide) {
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
} }
replaceWith($rootElement, $compileNode, compileNode); replaceWith(jqCollection, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}}; var newTemplateAttrs = {$attr: {}};
...@@ -4158,7 +4220,7 @@ function $CompileProvider($provide) { ...@@ -4158,7 +4220,7 @@ function $CompileProvider($provide) {
assertNoDuplicate('template', templateDirective, directive, $compileNode); assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive; templateDirective = directive;
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace, nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
childTranscludeFn); childTranscludeFn);
ii = directives.length; ii = directives.length;
} else if (directive.compile) { } else if (directive.compile) {
...@@ -4291,7 +4353,7 @@ function $CompileProvider($provide) { ...@@ -4291,7 +4353,7 @@ function $CompileProvider($provide) {
parentGet = $parse(attrs[attrName]); parentGet = $parse(attrs[attrName]);
scope[scopeName] = function(locals) { scope[scopeName] = function(locals) {
return parentGet(parentScope, locals); return parentGet(parentScope, locals);
} };
break; break;
} }
...@@ -4461,7 +4523,7 @@ function $CompileProvider($provide) { ...@@ -4461,7 +4523,7 @@ function $CompileProvider($provide) {
directives.unshift(derivedSyncDirective); directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn); afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn); afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
while(linkQueue.length) { while(linkQueue.length) {
...@@ -4726,7 +4788,7 @@ function $ControllerProvider() { ...@@ -4726,7 +4788,7 @@ function $ControllerProvider() {
* @description * @description
* `$controller` service is responsible for instantiating controllers. * `$controller` service is responsible for instantiating controllers.
* *
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
* a service, so that one can override this service with {@link https://gist.github.com/1649788 * a service, so that one can override this service with {@link https://gist.github.com/1649788
* BC version}. * BC version}.
*/ */
...@@ -4779,7 +4841,7 @@ function $DocumentProvider(){ ...@@ -4779,7 +4841,7 @@ function $DocumentProvider(){
* *
*/ */
function $ExceptionHandlerProvider() { function $ExceptionHandlerProvider() {
this.$get = ['$log', function($log){ this.$get = ['$log', function($log) {
return function(exception, cause) { return function(exception, cause) {
$log.error.apply($log, arguments); $log.error.apply($log, arguments);
}; };
...@@ -4967,7 +5029,7 @@ function $InterpolateProvider() { ...@@ -4967,7 +5029,7 @@ function $InterpolateProvider() {
}]; }];
} }
var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/, var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/, PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
HASH_MATCH = PATH_MATCH, HASH_MATCH = PATH_MATCH,
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
...@@ -5046,7 +5108,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) { ...@@ -5046,7 +5108,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
var match = matchUrl(url); var match = matchUrl(url);
// already hashbang url // already hashbang url
if (decodeURIComponent(match.path) == basePath) { if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
match.hash.indexOf(hashPrefix) === 0) {
return url; return url;
// convert html5 url -> hashbang url // convert html5 url -> hashbang url
} else { } else {
...@@ -5543,6 +5606,10 @@ function $LocationProvider(){ ...@@ -5543,6 +5606,10 @@ function $LocationProvider(){
// update $location when $browser url changes // update $location when $browser url changes
$browser.onUrlChange(function(newUrl) { $browser.onUrlChange(function(newUrl) {
if ($location.absUrl() != newUrl) { if ($location.absUrl() != newUrl) {
if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
$browser.url($location.absUrl());
return;
}
$rootScope.$evalAsync(function() { $rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl(); var oldUrl = $location.absUrl();
...@@ -5851,10 +5918,10 @@ function lex(text, csp){ ...@@ -5851,10 +5918,10 @@ function lex(text, csp){
function readIdent() { function readIdent() {
var ident = "", var ident = "",
start = index, start = index,
lastDot, peekIndex, methodName; lastDot, peekIndex, methodName, ch;
while (index < text.length) { while (index < text.length) {
var ch = text.charAt(index); ch = text.charAt(index);
if (ch == '.' || isIdent(ch) || isNumber(ch)) { if (ch == '.' || isIdent(ch) || isNumber(ch)) {
if (ch == '.') lastDot = index; if (ch == '.') lastDot = index;
ident += ch; ident += ch;
...@@ -5868,7 +5935,7 @@ function lex(text, csp){ ...@@ -5868,7 +5935,7 @@ function lex(text, csp){
if (lastDot) { if (lastDot) {
peekIndex = index; peekIndex = index;
while(peekIndex < text.length) { while(peekIndex < text.length) {
var ch = text.charAt(peekIndex); ch = text.charAt(peekIndex);
if (ch == '(') { if (ch == '(') {
methodName = ident.substr(lastDot - start + 1); methodName = ident.substr(lastDot - start + 1);
ident = ident.substr(0, lastDot - start); ident = ident.substr(0, lastDot - start);
...@@ -6121,8 +6188,8 @@ function parser(text, json, $filter, csp){ ...@@ -6121,8 +6188,8 @@ function parser(text, json, $filter, csp){
text.substring(0, token.index) + "] can not be assigned to", token); text.substring(0, token.index) + "] can not be assigned to", token);
} }
right = logicalOR(); right = logicalOR();
return function(self, locals){ return function(scope, locals){
return left.assign(self, right(self, locals), locals); return left.assign(scope, right(scope, locals), locals);
}; };
} else { } else {
return left; return left;
...@@ -6239,12 +6306,12 @@ function parser(text, json, $filter, csp){ ...@@ -6239,12 +6306,12 @@ function parser(text, json, $filter, csp){
var field = expect().text; var field = expect().text;
var getter = getterFn(field, csp); var getter = getterFn(field, csp);
return extend( return extend(
function(self, locals) { function(scope, locals, self) {
return getter(object(self, locals), locals); return getter(self || object(scope, locals), locals);
}, },
{ {
assign:function(self, value, locals) { assign:function(scope, value, locals) {
return setter(object(self, locals), field, value); return setter(object(scope, locals), field, value);
} }
} }
); );
...@@ -6285,14 +6352,14 @@ function parser(text, json, $filter, csp){ ...@@ -6285,14 +6352,14 @@ function parser(text, json, $filter, csp){
} while (expect(',')); } while (expect(','));
} }
consume(')'); consume(')');
return function(self, locals){ return function(scope, locals){
var args = [], var args = [],
context = contextGetter ? contextGetter(self, locals) : self; context = contextGetter ? contextGetter(scope, locals) : scope;
for ( var i = 0; i < argsFn.length; i++) { for ( var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self, locals)); args.push(argsFn[i](scope, locals));
} }
var fnPtr = fn(self, locals) || noop; var fnPtr = fn(scope, locals, context) || noop;
// IE stupidity! // IE stupidity!
return fnPtr.apply return fnPtr.apply
? fnPtr.apply(context, args) ? fnPtr.apply(context, args)
...@@ -6334,8 +6401,7 @@ function parser(text, json, $filter, csp){ ...@@ -6334,8 +6401,7 @@ function parser(text, json, $filter, csp){
var object = {}; var object = {};
for ( var i = 0; i < keyValues.length; i++) { for ( var i = 0; i < keyValues.length; i++) {
var keyValue = keyValues[i]; var keyValue = keyValues[i];
var value = keyValue.value(self, locals); object[keyValue.key] = keyValue.value(self, locals);
object[keyValue.key] = value;
} }
return object; return object;
}; };
...@@ -6457,7 +6523,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) { ...@@ -6457,7 +6523,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
} }
return pathVal; return pathVal;
}; };
}; }
function getterFn(path, csp) { function getterFn(path, csp) {
if (getterFnCache.hasOwnProperty(path)) { if (getterFnCache.hasOwnProperty(path)) {
...@@ -6472,7 +6538,7 @@ function getterFn(path, csp) { ...@@ -6472,7 +6538,7 @@ function getterFn(path, csp) {
fn = (pathKeysLength < 6) fn = (pathKeysLength < 6)
? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
: function(scope, locals) { : function(scope, locals) {
var i = 0, val var i = 0, val;
do { do {
val = cspSafeGetterFn( val = cspSafeGetterFn(
pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
...@@ -6685,7 +6751,7 @@ function $ParseProvider() { ...@@ -6685,7 +6751,7 @@ function $ParseProvider() {
* models and avoiding unnecessary browser repaints, which would result in flickering UI. * models and avoiding unnecessary browser repaints, which would result in flickering UI.
* - $q promises are recognized by the templating engine in angular, which means that in templates * - $q promises are recognized by the templating engine in angular, which means that in templates
* you can treat promises attached to a scope as if they were the resulting values. * you can treat promises attached to a scope as if they were the resulting values.
* - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks. * all the important functionality needed for common async tasks.
* *
* # Testing * # Testing
...@@ -6880,10 +6946,7 @@ function qFactory(nextTick, exceptionHandler) { ...@@ -6880,10 +6946,7 @@ function qFactory(nextTick, exceptionHandler) {
* the promise comes from a source that can't be trusted. * the promise comes from a source that can't be trusted.
* *
* @param {*} value Value or a promise * @param {*} value Value or a promise
* @returns {Promise} Returns a single promise that will be resolved with an array of values, * @returns {Promise} Returns a promise of the passed value or promise
* each value corresponding to the promise at the same index in the `promises` array. If any of
* the promises is resolved with a rejection, this resulting promise will be resolved with the
* same rejection.
*/ */
var when = function(value, callback, errback) { var when = function(value, callback, errback) {
var result = defer(), var result = defer(),
...@@ -7240,8 +7303,9 @@ function $RouteProvider(){ ...@@ -7240,8 +7303,9 @@ function $RouteProvider(){
* {@link ng.directive:ngView ngView} listens for the directive * {@link ng.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view. * to instantiate the controller and render the view.
* *
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information. * @param {Route} current Current route information.
* @param {Route} previous Previous route information. * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
*/ */
/** /**
...@@ -7339,7 +7403,7 @@ function $RouteProvider(){ ...@@ -7339,7 +7403,7 @@ function $RouteProvider(){
var next = parseRoute(), var next = parseRoute(),
last = $route.current; last = $route.current;
if (next && last && next.$route === last.$route if (next && last && next.$$route === last.$$route
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) { && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
last.params = next.params; last.params = next.params;
copy(last.params, $routeParams); copy(last.params, $routeParams);
...@@ -7418,7 +7482,7 @@ function $RouteProvider(){ ...@@ -7418,7 +7482,7 @@ function $RouteProvider(){
match = inherit(route, { match = inherit(route, {
params: extend({}, $location.search(), params), params: extend({}, $location.search(), params),
pathParams: params}); pathParams: params});
match.$route = route; match.$$route = route;
} }
}); });
// No route matched; fallback to "otherwise" route // No route matched; fallback to "otherwise" route
...@@ -7478,22 +7542,22 @@ function $RouteParamsProvider() { ...@@ -7478,22 +7542,22 @@ function $RouteParamsProvider() {
/** /**
* DESIGN NOTES * DESIGN NOTES
* *
* The design decisions behind the scope ware heavily favored for speed and memory consumption. * The design decisions behind the scope are heavily favored for speed and memory consumption.
* *
* The typical use of scope is to watch the expressions, which most of the time return the same * The typical use of scope is to watch the expressions, which most of the time return the same
* value as last time so we optimize the operation. * value as last time so we optimize the operation.
* *
* Closures construction is expensive from speed as well as memory: * Closures construction is expensive in terms of speed as well as memory:
* - no closures, instead ups prototypical inheritance for API * - No closures, instead use prototypical inheritance for API
* - Internal state needs to be stored on scope directly, which means that private state is * - Internal state needs to be stored on scope directly, which means that private state is
* exposed as $$____ properties * exposed as $$____ properties
* *
* Loop operations are optimized by using while(count--) { ... } * Loop operations are optimized by using while(count--) { ... }
* - this means that in order to keep the same order of execution as addition we have to add * - this means that in order to keep the same order of execution as addition we have to add
* items to the array at the begging (shift) instead of at the end (push) * items to the array at the beginning (shift) instead of at the end (push)
* *
* Child scopes are created and removed often * Child scopes are created and removed often
* - Using array would be slow since inserts in meddle are expensive so we use linked list * - Using an array would be slow since inserts in middle are expensive so we use linked list
* *
* There are few watches then a lot of observers. This is why you don't want the observer to be * There are few watches then a lot of observers. This is why you don't want the observer to be
* implemented in the same way as watch. Watch requires return of initialization function which * implemented in the same way as watch. Watch requires return of initialization function which
...@@ -7515,7 +7579,7 @@ function $RouteParamsProvider() { ...@@ -7515,7 +7579,7 @@ function $RouteParamsProvider() {
* @methodOf ng.$rootScopeProvider * @methodOf ng.$rootScopeProvider
* @description * @description
* *
* Sets the number of digest iteration the scope should attempt to execute before giving up and * Sets the number of digest iterations the scope should attempt to execute before giving up and
* assuming that the model is unstable. * assuming that the model is unstable.
* *
* The current default is 10 iterations. * The current default is 10 iterations.
...@@ -7795,7 +7859,7 @@ function $RootScopeProvider(){ ...@@ -7795,7 +7859,7 @@ function $RootScopeProvider(){
* @function * @function
* *
* @description * @description
* Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
* Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
* `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
* firing. This means that it is possible to get into an infinite loop. This function will throw * firing. This means that it is possible to get into an infinite loop. This function will throw
...@@ -8137,7 +8201,7 @@ function $RootScopeProvider(){ ...@@ -8137,7 +8201,7 @@ function $RootScopeProvider(){
* Afterwards, the event traverses upwards toward the root scope and calls all registered * Afterwards, the event traverses upwards toward the root scope and calls all registered
* listeners along the way. The event will stop propagating if one of the listeners cancels it. * listeners along the way. The event will stop propagating if one of the listeners cancels it.
* *
* Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service. * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
* *
* @param {string} name Event name to emit. * @param {string} name Event name to emit.
...@@ -8206,7 +8270,7 @@ function $RootScopeProvider(){ ...@@ -8206,7 +8270,7 @@ function $RootScopeProvider(){
* Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service. * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
* *
* @param {string} name Event name to emit. * @param {string} name Event name to broadcast.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners. * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
*/ */
...@@ -8352,10 +8416,23 @@ function $SnifferProvider() { ...@@ -8352,10 +8416,23 @@ function $SnifferProvider() {
* @example * @example
<doc:example> <doc:example>
<doc:source> <doc:source>
<input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" /> <script>
<button ng-click="$window.alert(greeting)">ALERT</button> function Ctrl($scope, $window) {
$scope.$window = $window;
$scope.greeting = 'Hello, World!';
}
</script>
<div ng-controller="Ctrl">
<input type="text" ng-model="greeting" />
<button ng-click="$window.alert(greeting)">ALERT</button>
</div>
</doc:source> </doc:source>
<doc:scenario> <doc:scenario>
it('should display the greeting in the input box', function() {
input('greeting').enter('Hello, E2E Tests');
// If we click the button it will block the test runner
// element(':button').click();
});
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
...@@ -8508,7 +8585,7 @@ function $HttpProvider() { ...@@ -8508,7 +8585,7 @@ function $HttpProvider() {
* *
* @description * @description
* The `$http` service is a core Angular service that facilitates communication with the remote * The `$http` service is a core Angular service that facilitates communication with the remote
* HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
* XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}. * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
* *
* For unit testing applications that use `$http` service, see * For unit testing applications that use `$http` service, see
...@@ -8518,13 +8595,13 @@ function $HttpProvider() { ...@@ -8518,13 +8595,13 @@ function $HttpProvider() {
* $resource} service. * $resource} service.
* *
* The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
* the $q service. While for simple usage patters this doesn't matter much, for advanced usage, * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
* it is important to familiarize yourself with these apis and guarantees they provide. * it is important to familiarize yourself with these APIs and the guarantees they provide.
* *
* *
* # General usage * # General usage
* The `$http` service is a function which takes a single argument — a configuration object — * The `$http` service is a function which takes a single argument — a configuration object —
* that is used to generate an http request and returns a {@link ng.$q promise} * that is used to generate an HTTP request and returns a {@link ng.$q promise}
* with two $http specific methods: `success` and `error`. * with two $http specific methods: `success` and `error`.
* *
* <pre> * <pre>
...@@ -8539,21 +8616,21 @@ function $HttpProvider() { ...@@ -8539,21 +8616,21 @@ function $HttpProvider() {
* }); * });
* </pre> * </pre>
* *
* Since the returned value of calling the $http function is a Promise object, you can also use * Since the returned value of calling the $http function is a `promise`, you can also use
* the `then` method to register callbacks, and these callbacks will receive a single argument – * the `then` method to register callbacks, and these callbacks will receive a single argument –
* an object representing the response. See the api signature and type info below for more * an object representing the response. See the API signature and type info below for more
* details. * details.
* *
* A response status code that falls in the [200, 300) range is considered a success status and * A response status code between 200 and 299 is considered a success status and
* will result in the success callback being called. Note that if the response is a redirect, * will result in the success callback being called. Note that if the response is a redirect,
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses. * called for such responses.
* *
* # Shortcut methods * # Shortcut methods
* *
* Since all invocation of the $http service require definition of the http method and url and * Since all invocations of the $http service require passing in an HTTP method and URL, and
* POST and PUT requests require response body/data to be provided as well, shortcut methods * POST/PUT requests require request data to be provided as well, shortcut methods
* were created to simplify using the api: * were created:
* *
* <pre> * <pre>
* $http.get('/someUrl').success(successCallback); * $http.get('/someUrl').success(successCallback);
...@@ -8572,25 +8649,25 @@ function $HttpProvider() { ...@@ -8572,25 +8649,25 @@ function $HttpProvider() {
* *
* # Setting HTTP Headers * # Setting HTTP Headers
* *
* The $http service will automatically add certain http headers to all requests. These defaults * The $http service will automatically add certain HTTP headers to all requests. These defaults
* can be fully configured by accessing the `$httpProvider.defaults.headers` configuration * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
* object, which currently contains this default configuration: * object, which currently contains this default configuration:
* *
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests): * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
* - `Accept: application/json, text/plain, * / *` * - `Accept: application/json, text/plain, * / *`
* - `X-Requested-With: XMLHttpRequest` * - `X-Requested-With: XMLHttpRequest`
* - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests) * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
* - `Content-Type: application/json` * - `Content-Type: application/json`
* - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests) * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
* - `Content-Type: application/json` * - `Content-Type: application/json`
* *
* To add or overwrite these defaults, simply add or remove a property from this configuration * To add or overwrite these defaults, simply add or remove a property from these configuration
* objects. To add headers for an HTTP method other than POST or PUT, simply add a new object * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
* with name equal to the lower-cased http method name, e.g. * with the lowercased HTTP method name as the key, e.g.
* `$httpProvider.defaults.headers.get['My-Header']='value'`. * `$httpProvider.defaults.headers.get['My-Header']='value'`.
* *
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
* fassion as described above. * fashion.
* *
* *
* # Transforming Requests and Responses * # Transforming Requests and Responses
...@@ -8600,32 +8677,36 @@ function $HttpProvider() { ...@@ -8600,32 +8677,36 @@ function $HttpProvider() {
* *
* Request transformations: * Request transformations:
* *
* - if the `data` property of the request config object contains an object, serialize it into * - If the `data` property of the request configuration object contains an object, serialize it into
* JSON format. * JSON format.
* *
* Response transformations: * Response transformations:
* *
* - if XSRF prefix is detected, strip it (see Security Considerations section below) * - If XSRF prefix is detected, strip it (see Security Considerations section below).
* - if json response is detected, deserialize it using a JSON parser * - If JSON response is detected, deserialize it using a JSON parser.
*
* To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
* `$httpProvider.defaults.transformResponse` properties. These properties are by default an
* array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
* transformation chain. You can also decide to completely override any default transformations by assigning your
* transformation functions to these properties directly without the array wrapper.
* *
* To override these transformation locally, specify transform functions as `transformRequest` * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
* and/or `transformResponse` properties of the config object. To globally override the default * `transformResponse` properties of the configuration object passed into `$http`.
* transforms, override the `$httpProvider.defaults.transformRequest` and
* `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`.
* *
* *
* # Caching * # Caching
* *
* To enable caching set the configuration property `cache` to `true`. When the cache is * To enable caching, set the configuration property `cache` to `true`. When the cache is
* enabled, `$http` stores the response from the server in local cache. Next time the * enabled, `$http` stores the response from the server in local cache. Next time the
* response is served from the cache without sending a request to the server. * response is served from the cache without sending a request to the server.
* *
* Note that even if the response is served from cache, delivery of the data is asynchronous in * Note that even if the response is served from cache, delivery of the data is asynchronous in
* the same way that real requests are. * the same way that real requests are.
* *
* If there are multiple GET requests for the same url that should be cached using the same * If there are multiple GET requests for the same URL that should be cached using the same
* cache, but the cache is not populated yet, only one request to the server will be made and * cache, but the cache is not populated yet, only one request to the server will be made and
* the remaining requests will be fulfilled using the response for the first request. * the remaining requests will be fulfilled using the response from the first request.
* *
* *
* # Response interceptors * # Response interceptors
...@@ -8677,7 +8758,7 @@ function $HttpProvider() { ...@@ -8677,7 +8758,7 @@ function $HttpProvider() {
* When designing web applications, consider security threats from: * When designing web applications, consider security threats from:
* *
* - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
* JSON Vulnerability} * JSON vulnerability}
* - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
* *
* Both server and the client must cooperate in order to eliminate these threats. Angular comes * Both server and the client must cooperate in order to eliminate these threats. Angular comes
...@@ -8687,8 +8768,8 @@ function $HttpProvider() { ...@@ -8687,8 +8768,8 @@ function $HttpProvider() {
* ## JSON Vulnerability Protection * ## JSON Vulnerability Protection
* *
* A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
* JSON Vulnerability} allows third party web-site to turn your JSON resource URL into * JSON vulnerability} allows third party website to turn your JSON resource URL into
* {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
* counter this your server can prefix all JSON requests with following string `")]}',\n"`. * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
* Angular will automatically strip the prefix before processing it as JSON. * Angular will automatically strip the prefix before processing it as JSON.
* *
...@@ -8709,19 +8790,19 @@ function $HttpProvider() { ...@@ -8709,19 +8790,19 @@ function $HttpProvider() {
* ## Cross Site Request Forgery (XSRF) Protection * ## Cross Site Request Forgery (XSRF) Protection
* *
* {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
* an unauthorized site can gain your user's private data. Angular provides following mechanism * an unauthorized site can gain your user's private data. Angular provides a mechanism
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
* runs on your domain could read the cookie, your server can be assured that the XHR came from * runs on your domain could read the cookie, your server can be assured that the XHR came from
* JavaScript running on your domain. * JavaScript running on your domain.
* *
* To take advantage of this, your server needs to set a token in a JavaScript readable session * To take advantage of this, your server needs to set a token in a JavaScript readable session
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
* server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
* that only JavaScript running on your domain could have read the token. The token must be * that only JavaScript running on your domain could have sent the request. The token must be
* unique for each user and must be verifiable by the server (to prevent the JavaScript making * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
* up its own tokens). We recommend that the token is a digest of your site's authentication * up its own tokens). We recommend that the token is a digest of your site's authentication
* cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}. * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
* *
* *
* @param {object} config Object describing the request to be made and how it should be * @param {object} config Object describing the request to be made and how it should be
...@@ -8899,7 +8980,7 @@ function $HttpProvider() { ...@@ -8899,7 +8980,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `GET` request * Shortcut method to perform `GET` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request * @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object * @param {Object=} config Optional configuration object
...@@ -8912,7 +8993,7 @@ function $HttpProvider() { ...@@ -8912,7 +8993,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `DELETE` request * Shortcut method to perform `DELETE` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request * @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object * @param {Object=} config Optional configuration object
...@@ -8925,7 +9006,7 @@ function $HttpProvider() { ...@@ -8925,7 +9006,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `HEAD` request * Shortcut method to perform `HEAD` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request * @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object * @param {Object=} config Optional configuration object
...@@ -8938,7 +9019,7 @@ function $HttpProvider() { ...@@ -8938,7 +9019,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `JSONP` request * Shortcut method to perform `JSONP` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request. * @param {string} url Relative or absolute URL specifying the destination of the request.
* Should contain `JSON_CALLBACK` string. * Should contain `JSON_CALLBACK` string.
...@@ -8953,7 +9034,7 @@ function $HttpProvider() { ...@@ -8953,7 +9034,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `POST` request * Shortcut method to perform `POST` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request * @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content * @param {*} data Request content
...@@ -8967,7 +9048,7 @@ function $HttpProvider() { ...@@ -8967,7 +9048,7 @@ function $HttpProvider() {
* @methodOf ng.$http * @methodOf ng.$http
* *
* @description * @description
* Shortcut method to perform `PUT` request * Shortcut method to perform `PUT` request.
* *
* @param {string} url Relative or absolute URL specifying the destination of the request * @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content * @param {*} data Request content
...@@ -9019,7 +9100,7 @@ function $HttpProvider() { ...@@ -9019,7 +9100,7 @@ function $HttpProvider() {
/** /**
* Makes the request * Makes the request.
* *
* !!! ACCESSES CLOSURE VARS: * !!! ACCESSES CLOSURE VARS:
* $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
...@@ -9129,6 +9210,7 @@ function $HttpProvider() { ...@@ -9129,6 +9210,7 @@ function $HttpProvider() {
}]; }];
} }
var XHR = window.XMLHttpRequest || function() { var XHR = window.XMLHttpRequest || function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
...@@ -9365,17 +9447,17 @@ function $TimeoutProvider() { ...@@ -9365,17 +9447,17 @@ function $TimeoutProvider() {
* block and delegates any exceptions to * block and delegates any exceptions to
* {@link ng.$exceptionHandler $exceptionHandler} service. * {@link ng.$exceptionHandler $exceptionHandler} service.
* *
* The return value of registering a timeout function is a promise which will be resolved when * The return value of registering a timeout function is a promise, which will be resolved when
* the timeout is reached and the timeout function is executed. * the timeout is reached and the timeout function is executed.
* *
* To cancel a the timeout request, call `$timeout.cancel(promise)`. * To cancel a timeout request, call `$timeout.cancel(promise)`.
* *
* In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
* synchronously flush the queue of deferred functions. * synchronously flush the queue of deferred functions.
* *
* @param {function()} fn A function, who's execution should be delayed. * @param {function()} fn A function, whose execution should be delayed.
* @param {number=} [delay=0] Delay in milliseconds. * @param {number=} [delay=0] Delay in milliseconds.
* @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
* promise will be resolved with is the return value of the `fn` function. * promise will be resolved with is the return value of the `fn` function.
...@@ -9415,7 +9497,7 @@ function $TimeoutProvider() { ...@@ -9415,7 +9497,7 @@ function $TimeoutProvider() {
* @methodOf ng.$timeout * @methodOf ng.$timeout
* *
* @description * @description
* Cancels a task associated with the `promise`. As a result of this the promise will be * Cancels a task associated with the `promise`. As a result of this, the promise will be
* resolved with a rejection. * resolved with a rejection.
* *
* @param {Promise=} promise Promise returned by the `$timeout` function. * @param {Promise=} promise Promise returned by the `$timeout` function.
...@@ -9441,7 +9523,7 @@ function $TimeoutProvider() { ...@@ -9441,7 +9523,7 @@ function $TimeoutProvider() {
* *
* Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
* achieve this a filter definition consists of a factory function which is annotated with dependencies and is * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
* responsible for creating a the filter function. * responsible for creating a filter function.
* *
* <pre> * <pre>
* // Filter registration * // Filter registration
...@@ -9503,7 +9585,7 @@ function $TimeoutProvider() { ...@@ -9503,7 +9585,7 @@ function $TimeoutProvider() {
* *
* The general syntax in templates is as follows: * The general syntax in templates is as follows:
* *
* {{ expression | [ filter_name ] }} * {{ expression [| filter_name[:parameter_value] ... ] }}
* *
* @param {String} name Name of the filter function to retrieve * @param {String} name Name of the filter function to retrieve
* @return {Function} the filter function * @return {Function} the filter function
...@@ -9579,22 +9661,22 @@ function $FilterProvider($provide) { ...@@ -9579,22 +9661,22 @@ function $FilterProvider($provide) {
Search: <input ng-model="searchText"> Search: <input ng-model="searchText">
<table id="searchTextResults"> <table id="searchTextResults">
<tr><th>Name</th><th>Phone</th><tr> <tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:searchText"> <tr ng-repeat="friend in friends | filter:searchText">
<td>{{friend.name}}</td> <td>{{friend.name}}</td>
<td>{{friend.phone}}</td> <td>{{friend.phone}}</td>
<tr> </tr>
</table> </table>
<hr> <hr>
Any: <input ng-model="search.$"> <br> Any: <input ng-model="search.$"> <br>
Name only <input ng-model="search.name"><br> Name only <input ng-model="search.name"><br>
Phone only <input ng-model="search.phone"å><br> Phone only <input ng-model="search.phone"><br>
<table id="searchObjResults"> <table id="searchObjResults">
<tr><th>Name</th><th>Phone</th><tr> <tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:search"> <tr ng-repeat="friend in friends | filter:search">
<td>{{friend.name}}</td> <td>{{friend.name}}</td>
<td>{{friend.phone}}</td> <td>{{friend.phone}}</td>
<tr> </tr>
</table> </table>
</doc:source> </doc:source>
<doc:scenario> <doc:scenario>
...@@ -9891,6 +9973,7 @@ function padNumber(num, digits, trim) { ...@@ -9891,6 +9973,7 @@ function padNumber(num, digits, trim) {
function dateGetter(name, size, offset, trim) { function dateGetter(name, size, offset, trim) {
offset = offset || 0;
return function(date) { return function(date) {
var value = date['get' + name](); var value = date['get' + name]();
if (offset > 0 || value > -offset) if (offset > 0 || value > -offset)
...@@ -9913,7 +9996,8 @@ function timeZoneGetter(date) { ...@@ -9913,7 +9996,8 @@ function timeZoneGetter(date) {
var zone = -1 * date.getTimezoneOffset(); var zone = -1 * date.getTimezoneOffset();
var paddedZone = (zone >= 0) ? "+" : ""; var paddedZone = (zone >= 0) ? "+" : "";
paddedZone += padNumber(zone / 60, 2) + padNumber(Math.abs(zone % 60), 2); paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
padNumber(Math.abs(zone % 60), 2);
return paddedZone; return paddedZone;
} }
...@@ -9979,7 +10063,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ ...@@ -9979,7 +10063,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* * `'ss'`: Second in minute, padded (00-59) * * `'ss'`: Second in minute, padded (00-59)
* * `'s'`: Second in minute (0-59) * * `'s'`: Second in minute (0-59)
* * `'a'`: am/pm marker * * `'a'`: am/pm marker
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200) * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
* *
* `format` string can also be one of the following predefined * `format` string can also be one of the following predefined
* {@link guide/i18n localizable formats}: * {@link guide/i18n localizable formats}:
...@@ -10000,7 +10084,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ ...@@ -10000,7 +10084,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* (e.g. `"h o''clock"`). * (e.g. `"h o''clock"`).
* *
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
* specified in the string input, the time is considered to be in the local timezone. * specified in the string input, the time is considered to be in the local timezone.
* @param {string=} format Formatting rules (see Description). If not specified, * @param {string=} format Formatting rules (see Description). If not specified,
...@@ -10291,12 +10375,12 @@ function limitToFilter(){ ...@@ -10291,12 +10375,12 @@ function limitToFilter(){
(<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th> (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th> <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th> <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
<tr> </tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse"> <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td> <td>{{friend.name}}</td>
<td>{{friend.phone}}</td> <td>{{friend.phone}}</td>
<td>{{friend.age}}</td> <td>{{friend.age}}</td>
<tr> </tr>
</table> </table>
</div> </div>
</doc:source> </doc:source>
...@@ -11118,8 +11202,8 @@ var inputType = { ...@@ -11118,8 +11202,8 @@ var inputType = {
* *
* @param {string} ngModel Assignable angular expression to data-bind to. * @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published. * @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less then `min`. * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`. * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
* @param {string=} required Sets `required` validation error key if the value is not entered. * @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
...@@ -11431,6 +11515,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { ...@@ -11431,6 +11515,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
} else { } else {
var timeout; var timeout;
var deferListener = function() {
if (!timeout) {
timeout = $browser.defer(function() {
listener();
timeout = null;
});
}
};
element.bind('keydown', function(event) { element.bind('keydown', function(event) {
var key = event.keyCode; var key = event.keyCode;
...@@ -11438,16 +11531,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { ...@@ -11438,16 +11531,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// command modifiers arrows // command modifiers arrows
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
if (!timeout) { deferListener();
timeout = $browser.defer(function() {
listener();
timeout = null;
});
}
}); });
// if user paste into input using mouse, we need "change" event to catch it // if user paste into input using mouse, we need "change" event to catch it
element.bind('change', listener); element.bind('change', listener);
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
if ($sniffer.hasEvent('paste')) {
element.bind('paste cut', deferListener);
}
} }
...@@ -11746,7 +11839,7 @@ function checkboxInputType(scope, element, attr, ctrl) { ...@@ -11746,7 +11839,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
<tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br> <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
<tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br> <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
<tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br> <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
<tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br> <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br> <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
<tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br> <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
...@@ -12009,7 +12102,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ ...@@ -12009,7 +12102,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* For example {@link ng.directive:input input} or * For example {@link ng.directive:input input} or
* {@link ng.directive:select select} directives call it. * {@link ng.directive:select select} directives call it.
* *
* It internally calls all `formatters` and if resulted value is valid, updates the model and * It internally calls all `parsers` and if resulted value is valid, updates the model and
* calls all registered change listeners. * calls all registered change listeners.
* *
* @param {string} value Value from the view. * @param {string} value Value from the view.
...@@ -12315,7 +12408,7 @@ var ngValueDirective = function() { ...@@ -12315,7 +12408,7 @@ var ngValueDirective = function() {
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
* `{{ expression }}` which is similar but less verbose. * `{{ expression }}` which is similar but less verbose.
* *
* Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
* it's desirable to put bindings into template that is momentarily displayed by the browser in its * it's desirable to put bindings into template that is momentarily displayed by the browser in its
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
* bindings invisible to the user while the page is loading. * bindings invisible to the user while the page is loading.
...@@ -12456,9 +12549,9 @@ function classDirective(name, selector) { ...@@ -12456,9 +12549,9 @@ function classDirective(name, selector) {
if (name !== 'ngClass') { if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) { scope.$watch('$index', function($index, old$index) {
var mod = $index % 2; var mod = $index & 1;
if (mod !== old$index % 2) { if (mod !== old$index & 1) {
if (mod == selector) { if (mod === selector) {
addClass(scope.$eval(attr[name])); addClass(scope.$eval(attr[name]));
} else { } else {
removeClass(scope.$eval(attr[name])); removeClass(scope.$eval(attr[name]));
...@@ -12470,12 +12563,12 @@ function classDirective(name, selector) { ...@@ -12470,12 +12563,12 @@ function classDirective(name, selector) {
function ngClassWatchAction(newVal) { function ngClassWatchAction(newVal) {
if (selector === true || scope.$index % 2 === selector) { if (selector === true || scope.$index % 2 === selector) {
if (oldVal && (newVal !== oldVal)) { if (oldVal && !equals(newVal,oldVal)) {
removeClass(oldVal); removeClass(oldVal);
} }
addClass(newVal); addClass(newVal);
} }
oldVal = newVal; oldVal = copy(newVal);
} }
...@@ -12601,7 +12694,7 @@ var ngClassOddDirective = classDirective('Odd', 0); ...@@ -12601,7 +12694,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
* @name ng.directive:ngClassEven * @name ng.directive:ngClassEven
* *
* @description * @description
* The `ngClassOdd` and `ngClassEven` works exactly as * The `ngClassOdd` and `ngClassEven` directives work exactly as
* {@link ng.directive:ngClass ngClass}, except it works in * {@link ng.directive:ngClass ngClass}, except it works in
* conjunction with `ngRepeat` and takes affect only on odd (even) rows. * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
* *
...@@ -12659,7 +12752,7 @@ var ngClassEvenDirective = classDirective('Even', 1); ...@@ -12659,7 +12752,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
* `angular.min.js` files. Following is the css rule: * `angular.min.js` files. Following is the css rule:
* *
* <pre> * <pre>
* [ng\:cloak], [ng-cloak], .ng-cloak { * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
* display: none; * display: none;
* } * }
* </pre> * </pre>
...@@ -12718,8 +12811,7 @@ var ngCloakDirective = ngDirective({ ...@@ -12718,8 +12811,7 @@ var ngCloakDirective = ngDirective({
* * Controller — The `ngController` directive specifies a Controller class; the class has * * Controller — The `ngController` directive specifies a Controller class; the class has
* methods that typically express the business logic behind the application. * methods that typically express the business logic behind the application.
* *
* Note that an alternative way to define controllers is via the `{@link ng.$route}` * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
* service.
* *
* @element ANY * @element ANY
* @scope * @scope
...@@ -12810,16 +12902,32 @@ var ngControllerDirective = [function() { ...@@ -12810,16 +12902,32 @@ var ngControllerDirective = [function() {
* @name ng.directive:ngCsp * @name ng.directive:ngCsp
* @priority 1000 * @priority 1000
* *
* @element html
* @description * @description
* Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
* This directive should be used on the root element of the application (typically the `<html>` *
* element or other element with the {@link ng.directive:ngApp ngApp} * This is necessary when developing things like Google Chrome Extensions.
* directive). *
* * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
* If enabled the performance of template expression evaluator will suffer slightly, so don't enable * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
* this mode unless you need it. * any of these restrictions.
* *
* @element html * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
* it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
* be raised.
*
* In order to use this feature put `ngCsp` directive on the root element of the application.
*
* @example
* This example shows how to apply the `ngCsp` directive to the `html` tag.
<pre>
<!doctype html>
<html ng-app ng-csp>
...
...
</html>
</pre>
*/ */
var ngCspDirective = ['$sniffer', function($sniffer) { var ngCspDirective = ['$sniffer', function($sniffer) {
...@@ -13444,7 +13552,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp ...@@ -13444,7 +13552,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
if (!isNaN(value)) { if (!isNaN(value)) {
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
//check it against pluralization rules in $locale service //check it against pluralization rules in $locale service
if (!whens[value]) value = $locale.pluralCat(value - offset); if (!(value in whens)) value = $locale.pluralCat(value - offset);
return whensExpFns[value](scope, element, true); return whensExpFns[value](scope, element, true);
} else { } else {
return ''; return '';
...@@ -13552,7 +13660,7 @@ var ngRepeatDirective = ngDirective({ ...@@ -13552,7 +13660,7 @@ var ngRepeatDirective = ngDirective({
// Same as lastOrder but it has the current state. It will become the // Same as lastOrder but it has the current state. It will become the
// lastOrder on the next iteration. // lastOrder on the next iteration.
nextOrder = new HashQueueMap(), nextOrder = new HashQueueMap(),
arrayLength, arrayBound,
childScope, childScope,
key, value, // key/value of iteration key, value, // key/value of iteration
array, array,
...@@ -13573,7 +13681,7 @@ var ngRepeatDirective = ngDirective({ ...@@ -13573,7 +13681,7 @@ var ngRepeatDirective = ngDirective({
array = collection || []; array = collection || [];
} }
arrayLength = array.length; arrayBound = array.length-1;
// we are not using forEach for perf reasons (trying to avoid #call) // we are not using forEach for perf reasons (trying to avoid #call)
for (index = 0, length = array.length; index < length; index++) { for (index = 0, length = array.length; index < length; index++) {
...@@ -13610,7 +13718,7 @@ var ngRepeatDirective = ngDirective({ ...@@ -13610,7 +13718,7 @@ var ngRepeatDirective = ngDirective({
childScope.$index = index; childScope.$index = index;
childScope.$first = (index === 0); childScope.$first = (index === 0);
childScope.$last = (index === (arrayLength - 1)); childScope.$last = (index === arrayBound);
childScope.$middle = !(childScope.$first || childScope.$last); childScope.$middle = !(childScope.$first || childScope.$last);
if (!last) { if (!last) {
...@@ -13777,11 +13885,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) { ...@@ -13777,11 +13885,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
* @description * @description
* Conditionally change the DOM structure. * Conditionally change the DOM structure.
* *
* @usageContent * @usage
* <ANY ng-switch-when="matchValue1">...</ANY> * <ANY ng-switch="expression">
* <ANY ng-switch-when="matchValue1">...</ANY>
* <ANY ng-switch-when="matchValue2">...</ANY> * <ANY ng-switch-when="matchValue2">...</ANY>
* ... * ...
* <ANY ng-switch-default>...</ANY> * <ANY ng-switch-default>...</ANY>
* </ANY>
* *
* @scope * @scope
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>. * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
...@@ -14175,7 +14285,8 @@ var scriptDirective = ['$templateCache', function($templateCache) { ...@@ -14175,7 +14285,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
* `select` model to be bound to a non-string value. This is because an option element can currently * `select` model to be bound to a non-string value. This is because an option element can currently
* be bound to string values only. * be bound to string values only.
* *
* @param {string} name assignable expression to data-bind to. * @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is entered. * @param {string=} required The control is considered valid only if value is entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
...@@ -14270,7 +14381,7 @@ var scriptDirective = ['$templateCache', function($templateCache) { ...@@ -14270,7 +14381,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
var ngOptionsDirective = valueFn({ terminal: true }); var ngOptionsDirective = valueFn({ terminal: true });
var selectDirective = ['$compile', '$parse', function($compile, $parse) { var selectDirective = ['$compile', '$parse', function($compile, $parse) {
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777 //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/, var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
nullModelCtrl = {$setViewValue: noop}; nullModelCtrl = {$setViewValue: noop};
...@@ -14542,10 +14653,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -14542,10 +14653,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (multiple) { if (multiple) {
selectedSet = new HashMap(modelValue); selectedSet = new HashMap(modelValue);
} else if (modelValue === null || nullOption) {
// if we are not multiselect, and we are null then we have to add the nullOption
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
selectedSet = true;
} }
// We now build up the list of options we need (we merge later) // We now build up the list of options we need (we merge later)
...@@ -14570,9 +14677,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -14570,9 +14677,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
selected: selected // determine if we should be selected selected: selected // determine if we should be selected
}); });
} }
if (!multiple && !selectedSet) { if (!multiple) {
// nothing was selected, we have to insert the undefined item if (nullOption || modelValue === null) {
optionGroups[''].unshift({id:'?', label:'', selected:true}); // insert null option if we have a placeholder, or the model is null
optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
} else if (!selectedSet) {
// option could not be found, we have to insert the undefined item
optionGroups[''].unshift({id:'?', label:'', selected:true});
}
} }
// Now we need to update the list of DOM nodes to match the optionGroups we computed above // Now we need to update the list of DOM nodes to match the optionGroups we computed above
...@@ -14616,7 +14728,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -14616,7 +14728,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (existingOption.id !== option.id) { if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id); lastElement.val(existingOption.id = option.id);
} }
if (existingOption.element.selected !== option.selected) { // lastElement.prop('selected') provided by jQuery has side-effects
if (lastElement[0].selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected)); lastElement.prop('selected', (existingOption.selected = option.selected));
} }
} else { } else {
...@@ -14719,6 +14832,7 @@ var styleDirective = valueFn({ ...@@ -14719,6 +14832,7 @@ var styleDirective = valueFn({
restrict: 'E', restrict: 'E',
terminal: true terminal: true
}); });
//try to bind to jquery now so that one can write angular.element().read() //try to bind to jquery now so that one can write angular.element().read()
//but we will rebind on bootstrap again. //but we will rebind on bootstrap again.
bindJQuery(); bindJQuery();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment