diff options
| author | Guillaume Horel <guillaume.horel@gmail.com> | 2012-04-14 19:19:48 -0400 |
|---|---|---|
| committer | Guillaume Horel <guillaume.horel@gmail.com> | 2012-04-14 19:19:48 -0400 |
| commit | be91b58f33a5f3fb3f84392a5498f3188a3c7efa (patch) | |
| tree | 68504d6244174bb7aa67cc1996ce8ae5bb849a81 /alias-angular/app/lib/angular | |
| parent | b78615962784bcbb9bfb4cab161bd3439996f0c9 (diff) | |
| download | alias-be91b58f33a5f3fb3f84392a5498f3188a3c7efa.tar.gz | |
update to angular rc5
Diffstat (limited to 'alias-angular/app/lib/angular')
| -rw-r--r-- | alias-angular/app/lib/angular/angular.js | 1733 |
1 files changed, 557 insertions, 1176 deletions
diff --git a/alias-angular/app/lib/angular/angular.js b/alias-angular/app/lib/angular/angular.js index 88a224d..b621c96 100644 --- a/alias-angular/app/lib/angular/angular.js +++ b/alias-angular/app/lib/angular/angular.js @@ -1,14 +1,14 @@ /** - * @license AngularJS v1.0.0rc3 + * @license AngularJS v1.0.0rc5 * (c) 2010-2012 AngularJS http://angularjs.org * License: MIT */ +(function(window, document, undefined) { 'use strict'; -(function(window, document, undefined){ //////////////////////////////////// -if (typeof document.getAttribute == $undefined) +if (typeof document.getAttribute == 'undefined') document.getAttribute = function() {}; /** @@ -58,14 +58,7 @@ if ('i' !== 'I'.toLowerCase()) { function fromCharCode(code) {return String.fromCharCode(code);} -var $boolean = 'boolean', - $console = 'console', - $length = 'length', - $name = 'name', - $object = 'object', - $string = 'string', - $undefined = 'undefined', - Error = window.Error, +var Error = window.Error, /** holds major version number for IE or NaN for real browsers */ msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]), jqLite, // delay binding since jQuery could be loaded after us. @@ -113,7 +106,7 @@ function forEach(obj, iterator, context) { if (obj) { if (isFunction(obj)){ for (key in obj) { - if (key != 'prototype' && key != $length && key != $name && obj.hasOwnProperty(key)) { + if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key); } } @@ -144,7 +137,7 @@ function sortedKeys(obj) { } function forEachSorted(obj, iterator, context) { - var keys = sortedKeys(obj) + var keys = sortedKeys(obj); for ( var i = 0; i < keys.length; i++) { iterator.call(context, obj[keys[i]], keys[i]); } @@ -275,7 +268,7 @@ function valueFn(value) {return function() {return value;};} * @param {*} value Reference to check. * @returns {boolean} True if `value` is undefined. */ -function isUndefined(value){return typeof value == $undefined;} +function isUndefined(value){return typeof value == 'undefined';} /** @@ -289,7 +282,7 @@ function isUndefined(value){return typeof value == $undefined;} * @param {*} value Reference to check. * @returns {boolean} True if `value` is defined. */ -function isDefined(value){return typeof value != $undefined;} +function isDefined(value){return typeof value != 'undefined';} /** @@ -304,7 +297,7 @@ function isDefined(value){return typeof value != $undefined;} * @param {*} value Reference to check. * @returns {boolean} True if `value` is an `Object` but not `null`. */ -function isObject(value){return value!=null && typeof value == $object;} +function isObject(value){return value != null && typeof value == 'object';} /** @@ -318,7 +311,7 @@ function isObject(value){return value!=null && typeof value == $object;} * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `String`. */ -function isString(value){return typeof value == $string;} +function isString(value){return typeof value == 'string';} /** @@ -403,8 +396,10 @@ function isFile(obj) { } -function isBoolean(value) {return typeof value == $boolean;} -function isTextNode(node) {return nodeName_(node) == '#text';} +function isBoolean(value) { + return typeof value == 'boolean'; +} + function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; @@ -439,26 +434,6 @@ function makeMap(str){ } - -/** - * HTML class which is the only class which can be used in ng-bind to inline HTML for security - * reasons. - * - * @constructor - * @param html raw (unsafe) html - * @param {string=} option If set to 'usafe', get method will return raw (unsafe/unsanitized) html - */ -function HTML(html, option) { - this.html = html; - this.get = lowercase(option) == 'unsafe' - ? valueFn(html) - : function htmlSanitize() { - var buf = []; - htmlParser(html, htmlSanitizeWriter(buf)); - return buf.join(''); - }; -} - if (msie < 9) { nodeName_ = function(element) { element = element.nodeName ? element : element[0]; @@ -471,12 +446,6 @@ if (msie < 9) { }; } -function isVisible(element) { - var rect = element[0].getBoundingClientRect(), - width = (rect.width || (rect.right||0 - rect.left||0)), - height = (rect.height || (rect.bottom||0 - rect.top||0)); - return width>0 && height>0; -} function map(obj, iterator, context) { var results = []; @@ -677,17 +646,6 @@ function equals(o1, o2) { return false; } -function setHtml(node, html) { - if (isLeafNode(node)) { - if (msie) { - node.innerText = html; - } else { - node.textContent = html; - } - } else { - node.innerHTML = html; - } -} function concat(array1, array2, index) { return array1.concat(slice.call(array2, index)); @@ -748,7 +706,7 @@ function toJsonReplacer(key, value) { } return val; -}; +} /** @@ -805,7 +763,7 @@ function startingTag(element) { // turns out IE does not let you set .html() on elements which // are not allowed to have children. So we just ignore it. element.html(''); - } catch(e) {}; + } catch(e) {} return jqLite('<div>').append(element).html().match(/^(<[^>]+>)/)[1]; } @@ -879,10 +837,10 @@ function encodeUriQuery(val, pctEncodeSpaces) { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-app + * @name angular.module.ng.$compileProvider.directive.ngApp * * @element ANY - * @param {angular.Module} ng-app on optional application + * @param {angular.Module} ngApp on optional application * {@link angular.module module} name to load. * * @description @@ -892,11 +850,11 @@ function encodeUriQuery(val, pctEncodeSpaces) { * designates the root of the application and is typically placed * ot the root of the page. * - * In the example below if the `ng-app` directive would not be placed + * In the example below if the `ngApp` directive would not be placed * on the `html` element then the document would not be compiled * and the `{{ 1+2 }}` would not be resolved to `3`. * - * `ng-app` is the easiest way to bootstrap an application. + * `ngApp` is the easiest way to bootstrap an application. * <doc:example> <doc:source> @@ -924,7 +882,7 @@ function angularInit(element, bootstrap) { forEach(element.querySelectorAll('.' + name), append); forEach(element.querySelectorAll('.' + name + '\\:'), append); forEach(element.querySelectorAll('[' + name + ']'), append); - }; + } }); forEach(elements, function(element) { @@ -958,8 +916,8 @@ function angularInit(element, bootstrap) { * See: {@link guide/dev_guide.bootstrap.manual_bootstrap Bootstrap} * * @param {Element} element DOM element which is the root of angular application. - * @param {Array<String,function>=} modules an array of module declarations. See: {@link angular.module modules} - * @param {angular.module.auta.$injector} the injector; + * @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules} + * @returns {angular.module.auto.$injector} Returns the newly created injector for this app. */ function bootstrap(element, modules) { element = jqLite(element); @@ -1011,9 +969,7 @@ function bindJQuery() { */ function assertArg(arg, name, reason) { if (!arg) { - var error = new Error("Argument '" + (name||'?') + "' is " + - (reason || "required")); - throw error; + throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required")); } return arg; } @@ -1082,7 +1038,7 @@ function setupModuleLoader(window) { * </pre> * * However it's more likely that you'll just use - * {@link angular.module.ng.$compileProvider.directive.ng-app ng-app} or + * {@link angular.module.ng.$compileProvider.directive.ngApp ngApp} or * {@link angular.bootstrap} to simplify this process for you. * * @param {!string} name The name of the module to create or retrieve. @@ -1290,11 +1246,11 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.0.0rc3', // all of these placeholder strings will be replaced by rake's + full: '1.0.0rc5', // all of these placeholder strings will be replaced by rake's major: 1, // compile task minor: 0, dot: 0, - codeName: 'barefoot-telepathy' + codeName: 'reality-distortion' }; @@ -1347,7 +1303,6 @@ function publishExternalAPI(angular){ style: styleDirective, option: optionDirective, ngBind: ngBindDirective, - ngBindHtml: ngBindHtmlDirective, ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective, ngBindTemplate: ngBindTemplateDirective, ngClass: ngClassDirective, @@ -1374,7 +1329,6 @@ function publishExternalAPI(angular){ ngModel: ngModelDirective, ngList: ngListDirective, ngChange: ngChangeDirective, - ngModelInstant: ngModelInstantDirective, required: requiredDirective, ngRequired: requiredDirective, ngValue: ngValueDirective @@ -1400,14 +1354,13 @@ function publishExternalAPI(angular){ $routeParams: $RouteParamsProvider, $rootScope: $RootScopeProvider, $q: $QProvider, - $sanitize: $SanitizeProvider, $sniffer: $SnifferProvider, $templateCache: $TemplateCacheProvider, $window: $WindowProvider }); } ]); -}; +} ////////////////////////////////// //JQLite @@ -1470,7 +1423,7 @@ function publishExternalAPI(angular){ * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite: * * - `controller(name)` - retrieves the controller of the current element or its parent. By default - * retrieves controller associated with the `ng-controller` directive. If `name` is provided as + * retrieves controller associated with the `ngController` directive. If `name` is provided as * camelCase directive name, then the controller for this directive will be retrieved (e.g. * `'ngModel'`). * - `injector()` - retrieves the injector of the current element or its parent. @@ -1496,24 +1449,6 @@ var jqCache = {}, function jqNextId() { return (jqId++); } -function getStyle(element) { - var current = {}, style = element[0].style, value, name, i; - if (typeof style.length == 'number') { - for(i = 0; i < style.length; i++) { - name = style[i]; - current[name] = style[name]; - } - } else { - for (name in style) { - value = style[name]; - if (1*name != name && name != 'cssText' && value && typeof value == 'string' && value !='false') - current[name] = value; - } - } - return current; -} - - var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; var MOZ_HACK_REGEXP = /^moz([A-Z])/; @@ -1955,7 +1890,7 @@ function createEventHandler(element) { }; eventHandler.fns = []; return eventHandler; -}; +} ////////////////////////////////////////// // Functions iterating traversal. @@ -3069,7 +3004,7 @@ function Browser(window, document, body, $log, $sniffer) { * @returns {Object} Hash of all cookies (if called without any parameter) */ self.cookies = function(name, value) { - var cookieLength, cookieArray, cookie, i, keyValue, index; + var cookieLength, cookieArray, cookie, i, index; if (name) { if (value === undefined) { @@ -3164,56 +3099,6 @@ function Browser(window, document, body, $log, $sniffer) { ////////////////////////////////////////////////////////////// /** - * @ngdoc method - * @name angular.module.ng.$browser#addCss - * @methodOf angular.module.ng.$browser - * - * @param {string} url Url to css file - * @description - * Adds a stylesheet tag to the head. - */ - self.addCss = function(url) { - var link = jqLite(rawDocument.createElement('link')); - link.attr('rel', 'stylesheet'); - link.attr('type', 'text/css'); - link.attr('href', url); - body.append(link); - }; - - - /** - * @ngdoc method - * @name angular.module.ng.$browser#addJs - * @methodOf angular.module.ng.$browser - * - * @param {string} url Url to js file - * - * @description - * Adds a script tag to the head. - */ - self.addJs = function(url, done) { - // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: - // - fetches local scripts via XHR and evals them - // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'); - - script.type = 'text/javascript'; - script.src = url; - - if (msie) { - script.onreadystatechange = function() { - /loaded|complete/.test(script.readyState) && done && done(); - }; - } else { - if (done) script.onload = script.onerror = done; - } - - body[0].appendChild(script); - - return script; - }; - - /** * Returns current <base href> * (always relative - without domain) * @@ -3405,7 +3290,7 @@ function $TemplateCacheProvider() { * instance functions into a single template function which is then returned. * * The template function can then be used once to produce the view or as it is the case with - * {@link angular.module.ng.$compileProvider.directive.ng-repeat repeater} many-times, in which + * {@link angular.module.ng.$compileProvider.directive.ngRepeat repeater} many-times, in which * case each call results in a view that is a DOM clone of the original template. * <doc:example module="compile"> @@ -3740,7 +3625,7 @@ function $CompileProvider($provide) { var linkingFns = [], directiveLinkingFn, childLinkingFn, directives, attrs, linkingFnFound; - for(var i = 0, ii = nodeList.length; i < ii; i++) { + for(var i = 0; i < nodeList.length; i++) { attrs = new Attributes(); // we must always refer to nodeList[i] since the nodes can be replaced underneath us. @@ -3764,10 +3649,6 @@ function $CompileProvider($provide) { return linkingFnFound ? linkingFn : null; /* nodesetLinkingFn */ function linkingFn(scope, nodeList, rootElement, boundTranscludeFn) { - if (linkingFns.length != nodeList.length * 2) { - throw Error('Template changed structure!'); - } - var childLinkingFn, directiveLinkingFn, node, childScope, childTransclusionFn; for(var i=0, n=0, ii=linkingFns.length; i<ii; n++) { @@ -3840,7 +3721,7 @@ function $CompileProvider($provide) { if (isBooleanAttr(node, nName)) { attrs[nName] = true; // presence means true } - addAttrInterpolateDirective(node, directives, value, nName) + addAttrInterpolateDirective(node, directives, value, nName); addDirective(directives, nName, 'A', maxPriority); } } @@ -5035,7 +4916,7 @@ LocationUrl.prototype = { if (paramValue === null) { delete this.$$search[search]; } else { - this.$$search[search] = encodeUriQuery(paramValue); + this.$$search[search] = paramValue; } } else { this.$$search = isString(search) ? parseKeyValue(search) : search; @@ -5149,7 +5030,7 @@ function $LocationProvider(){ } else { return hashPrefix; } - } + }; /** * @ngdoc property @@ -5200,19 +5081,16 @@ function $LocationProvider(){ elm = elm.parent(); } - var href = elm.attr('href'); - if (!href || isDefined(elm.attr('ng-ext-link')) || elm.attr('target')) return; - - // remove same domain from full url links (IE7 always returns full hrefs) - href = href.replace(absUrlPrefix, ''); - - // link to different domain (or base path) - if (href.substr(0, 4) == 'http') return; + var absHref = elm.prop('href'); - // remove pathPrefix from absolute links - href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href; + if (!absHref || + elm.attr('target') || + absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path + return; + } - currentUrl.url(href); + // update location with href without the prefix + currentUrl.url(absHref.substr(absUrlPrefix.length)); $rootScope.$apply(); event.preventDefault(); // hack to work around FF6 bug 684208 when scenario runner clicks on links @@ -5531,7 +5409,7 @@ function lex(text){ //check if this is not a method invocation and if it is back out to last dot if (lastDot) { - peekIndex = index + peekIndex = index; while(peekIndex < text.length) { var ch = text.charAt(peekIndex); if (ch == '(') { @@ -5639,7 +5517,8 @@ function parser(text, json, $filter){ functionCall = _functionCall, fieldAccess = _fieldAccess, objectIndex = _objectIndex, - filterChain = _filterChain + filterChain = _filterChain; + if(json){ // The extra level of aliasing is here, just in case the lexer misses something, so that // we prevent any accidental execution in JSON. @@ -5714,10 +5593,6 @@ function parser(text, json, $filter){ }; } - function hasTokens () { - return tokens.length > 0; - } - function statements() { var statements = []; while(true) { @@ -5867,21 +5742,6 @@ function parser(text, json, $filter){ } } - function _functionIdent(fnScope) { - var token = expect(); - var element = token.text.split('.'); - var instance = fnScope; - var key; - for ( var i = 0; i < element.length; i++) { - key = element[i]; - if (instance) - instance = instance[key]; - } - if (!isFunction(instance)) { - throwError("should be a function", token); - } - return instance; - } function primary() { var primary; @@ -6397,7 +6257,7 @@ function qFactory(nextTick, exceptionHandler) { then: function(callback, errback) { var result = defer(); nextTick(function() { - result.resolve(errback(reason)); + result.resolve((errback || defaultErrback)(reason)); }); return result.promise; } @@ -6542,8 +6402,8 @@ function $RouteProvider(){ * - `controller` – `{function()=}` – Controller fn that should be associated with newly * created scope. * - `template` – `{string=}` – path to an html template that should be used by - * {@link angular.module.ng.$compileProvider.directive.ng-view ng-view} or - * {@link angular.module.ng.$compileProvider.directive.ng-include ng-include} directives. + * {@link angular.module.ng.$compileProvider.directive.ngView ngView} or + * {@link angular.module.ng.$compileProvider.directive.ngInclude ngInclude} directives. * - `redirectTo` – {(string|function())=} – value to update * {@link angular.module.ng.$location $location} path with and trigger route redirection. * @@ -6563,15 +6423,13 @@ function $RouteProvider(){ * If the option is set to `false` and url in the browser changes, then * `$routeUpdate` event is broadcasted on the root scope. * - * @returns {Object} route object + * @returns {Object} self * * @description * Adds a new route definition to the `$route` service. */ this.when = function(path, route) { - var routeDef = routes[path]; - if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true}; - if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions? + routes[path] = extend({reloadOnSearch: true}, route); // create redirection for trailing slashes if (path) { @@ -6582,7 +6440,7 @@ function $RouteProvider(){ routes[redirectPath] = {redirectTo: path}; } - return routeDef; + return this; }; /** @@ -6595,9 +6453,11 @@ function $RouteProvider(){ * is matched. * * @param {Object} params Mapping information to be assigned to `$route.current`. + * @returns {Object} self */ this.otherwise = function(params) { this.when(null, params); + return this; }; @@ -6619,12 +6479,12 @@ function $RouteProvider(){ * * You can define routes through {@link angular.module.ng.$routeProvider $routeProvider}'s API. * - * The `$route` service is typically used in conjunction with {@link angular.module.ng.$compileProvider.directive.ng-view ng-view} + * The `$route` service is typically used in conjunction with {@link angular.module.ng.$compileProvider.directive.ngView ngView} * directive and the {@link angular.module.ng.$routeParams $routeParams} service. * * @example This example shows how changing the URL hash causes the `$route` to match a route against the - URL, and the `ng-view` pulls in the partial. + URL, and the `ngView` pulls in the partial. Note that this example is using {@link angular.module.ng.$compileProvider.directive.script inlined templates} to get it working on jsfiddle as well. @@ -6670,11 +6530,11 @@ function $RouteProvider(){ <div ng-controller="MainCntl"> Choose: - <a href="/Book/Moby">Moby</a> | - <a href="/Book/Moby/ch/1">Moby: Ch1</a> | - <a href="/Book/Gatsby">Gatsby</a> | - <a href="/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | - <a href="/Book/Scarlet">Scarlet Letter</a><br/> + <a href="Book/Moby">Moby</a> | + <a href="Book/Moby/ch/1">Moby: Ch1</a> | + <a href="Book/Gatsby">Gatsby</a> | + <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | + <a href="Book/Scarlet">Scarlet Letter</a><br/> <div ng-view></div> <hr /> @@ -6753,7 +6613,7 @@ function $RouteProvider(){ * Causes `$route` service to reload the current route even if * {@link angular.module.ng.$location $location} hasn't changed. * - * As a result of that, {@link angular.module.ng.$compileProvider.directive.ng-view ng-view} + * As a result of that, {@link angular.module.ng.$compileProvider.directive.ngView ngView} * creates new scope, reinstantiates the controller. */ reload: function() { @@ -6957,7 +6817,7 @@ function $RootScopeProvider(){ TTL = value; } return TTL; - } + }; this.$get = ['$injector', '$exceptionHandler', '$parse', function( $injector, $exceptionHandler, $parse) { @@ -7219,7 +7079,7 @@ function $RootScopeProvider(){ * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100. * * Usually you don't call `$digest()` directly in - * {@link angular.module.ng.$compileProvider.directive.ng-controller controllers} or in + * {@link angular.module.ng.$compileProvider.directive.ngController controllers} or in * {@link angular.module.ng.$compileProvider.directive directives}. * Instead a call to {@link angular.module.ng.$rootScope.Scope#$apply $apply()} (typically from within a * {@link angular.module.ng.$compileProvider.directive directives}) will force a `$digest()`. @@ -7352,7 +7212,7 @@ function $RootScopeProvider(){ * scope is eligible for garbage collection. * * The `$destroy()` is usually used by directives such as - * {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} for managing the + * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} for managing the * unrolling of the loop. * * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope. @@ -7661,386 +7521,6 @@ function $RootScopeProvider(){ }]; } -/* - * HTML Parser By Misko Hevery (misko@hevery.com) - * based on: HTML Parser By John Resig (ejohn.org) - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - * - * // Use like so: - * htmlParser(htmlString, { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * }); - * - */ - - - -/** - * @ngdoc service - * @name angular.module.ng.$sanitize - * @function - * - * @description - * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are - * then serialized back to properly escaped html string. This means that no unsafe input can make - * it into the returned string, however, since our parser is more strict than a typical browser - * parser, it's possible that some obscure input, which would be recognized as valid HTML by a - * browser, won't make it through the sanitizer. - * - * @param {string} html Html input. - * @returns {string} Sanitized html. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.snippet = - '<p style="color:blue">an html\n' + - '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + - 'snippet</p>'; - } - </script> - <div ng-controller="Ctrl"> - Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> - <table> - <tr> - <td>Filter</td> - <td>Source</td> - <td>Rendered</td> - </tr> - <tr id="html-filter"> - <td>html filter</td> - <td> - <pre><div ng-bind-html="snippet"><br/></div></pre> - </td> - <td> - <div ng-bind-html="snippet"></div> - </td> - </tr> - <tr id="escaped-html"> - <td>no filter</td> - <td><pre><div ng-bind="snippet"><br/></div></pre></td> - <td><div ng-bind="snippet"></div></td> - </tr> - <tr id="html-unsafe-filter"> - <td>unsafe html filter</td> - <td><pre><div ng-bind-html-unsafe="snippet"><br/></div></pre></td> - <td><div ng-bind-html-unsafe="snippet"></div></td> - </tr> - </table> - </div> - </doc:source> - <doc:scenario> - it('should sanitize the html snippet ', function() { - expect(using('#html-filter').element('div').html()). - toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); - }); - - it('should escape snippet without any filter', function() { - expect(using('#escaped-html').element('div').html()). - toBe("<p style=\"color:blue\">an html\n" + - "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + - "snippet</p>"); - }); - - it('should inline raw snippet if filtered as unsafe', function() { - expect(using('#html-unsafe-filter').element("div").html()). - toBe("<p style=\"color:blue\">an html\n" + - "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + - "snippet</p>"); - }); - - it('should update', function() { - input('snippet').enter('new <b>text</b>'); - expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>'); - expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>"); - expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>'); - }); - </doc:scenario> - </doc:example> - */ - -function $SanitizeProvider() { - this.$get = valueFn(function(html) { - var buf = []; - htmlParser(html, htmlSanitizeWriter(buf)); - return buf.join(''); - }); -}; - -// Regular Expressions for parsing tags and attributes -var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, - END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, - ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, - BEGIN_TAG_REGEXP = /^</, - BEGING_END_TAGE_REGEXP = /^<\s*\//, - COMMENT_REGEXP = /<!--(.*?)-->/g, - CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, - URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/, - NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) - - -// Good source of info about elements and attributes -// http://dev.w3.org/html5/spec/Overview.html#semantics -// http://simon.html5.org/html-elements - -// Safe Void Elements - HTML5 -// http://dev.w3.org/html5/spec/Overview.html#void-elements -var voidElements = makeMap("area,br,col,hr,img,wbr"); - -// Elements that you can, intentionally, leave open (and which close themselves) -// http://dev.w3.org/html5/spec/Overview.html#optional-tags -var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), - optionalEndTagInlineElements = makeMap("rp,rt"), - optionalEndTagElements = extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements); - -// Safe Block Elements - HTML5 -var blockElements = extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," + - "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," + - "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); - -// Inline Elements - HTML5 -var inlineElements = extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," + - "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," + - "span,strike,strong,sub,sup,time,tt,u,var")); - - -// Special Elements (can contain anything) -var specialElements = makeMap("script,style"); - -var validElements = extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements); - -//Attributes that have href and hence need to be sanitized -var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); -var validAttrs = extend({}, uriAttrs, makeMap( - 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ - 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ - 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ - 'scope,scrolling,shape,span,start,summary,target,title,type,'+ - 'valign,value,vspace,width')); - -/** - * @example - * htmlParser(htmlString, { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * }); - * - * @param {string} html string - * @param {object} handler - */ -function htmlParser( html, handler ) { - var index, chars, match, stack = [], last = html; - stack.last = function() { return stack[ stack.length - 1 ]; }; - - while ( html ) { - chars = true; - - // Make sure we're not in a script or style element - if ( !stack.last() || !specialElements[ stack.last() ] ) { - - // Comment - if ( html.indexOf("<!--") === 0 ) { - index = html.indexOf("-->"); - - if ( index >= 0 ) { - if (handler.comment) handler.comment( html.substring( 4, index ) ); - html = html.substring( index + 3 ); - chars = false; - } - - // end tag - } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { - match = html.match( END_TAG_REGEXP ); - - if ( match ) { - html = html.substring( match[0].length ); - match[0].replace( END_TAG_REGEXP, parseEndTag ); - chars = false; - } - - // start tag - } else if ( BEGIN_TAG_REGEXP.test(html) ) { - match = html.match( START_TAG_REGEXP ); - - if ( match ) { - html = html.substring( match[0].length ); - match[0].replace( START_TAG_REGEXP, parseStartTag ); - chars = false; - } - } - - if ( chars ) { - index = html.indexOf("<"); - - var text = index < 0 ? html : html.substring( 0, index ); - html = index < 0 ? "" : html.substring( index ); - - if (handler.chars) handler.chars( decodeEntities(text) ); - } - - } else { - html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){ - text = text. - replace(COMMENT_REGEXP, "$1"). - replace(CDATA_REGEXP, "$1"); - - if (handler.chars) handler.chars( decodeEntities(text) ); - - return ""; - }); - - parseEndTag( "", stack.last() ); - } - - if ( html == last ) { - throw "Parse Error: " + html; - } - last = html; - } - - // Clean up any remaining tags - parseEndTag(); - - function parseStartTag( tag, tagName, rest, unary ) { - tagName = lowercase(tagName); - if ( blockElements[ tagName ] ) { - while ( stack.last() && inlineElements[ stack.last() ] ) { - parseEndTag( "", stack.last() ); - } - } - - if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { - parseEndTag( "", tagName ); - } - - unary = voidElements[ tagName ] || !!unary; - - if ( !unary ) - stack.push( tagName ); - - var attrs = {}; - - rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) { - var value = doubleQuotedValue - || singleQoutedValue - || unqoutedValue - || ''; - - attrs[name] = decodeEntities(value); - }); - if (handler.start) handler.start( tagName, attrs, unary ); - } - - function parseEndTag( tag, tagName ) { - var pos = 0, i; - tagName = lowercase(tagName); - if ( tagName ) - // Find the closest opened tag of the same type - for ( pos = stack.length - 1; pos >= 0; pos-- ) - if ( stack[ pos ] == tagName ) - break; - - if ( pos >= 0 ) { - // Close all the open elements, up the stack - for ( i = stack.length - 1; i >= pos; i-- ) - if (handler.end) handler.end( stack[ i ] ); - - // Remove the open elements from the stack - stack.length = pos; - } - } -} - -/** - * decodes all entities into regular string - * @param value - * @returns {string} A string with decoded entities. - */ -var hiddenPre=document.createElement("pre"); -function decodeEntities(value) { - hiddenPre.innerHTML=value.replace(/</g,"<"); - return hiddenPre.innerText || hiddenPre.textContent || ''; -} - -/** - * Escapes all potentially dangerous characters, so that the - * resulting string can be safely inserted into attribute or - * element text. - * @param value - * @returns escaped text - */ -function encodeEntities(value) { - return value. - replace(/&/g, '&'). - replace(NON_ALPHANUMERIC_REGEXP, function(value){ - return '&#' + value.charCodeAt(0) + ';'; - }). - replace(/</g, '<'). - replace(/>/g, '>'); -} - -/** - * create an HTML/XML writer which writes to buffer - * @param {Array} buf use buf.jain('') to get out sanitized html string - * @returns {object} in the form of { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * } - */ -function htmlSanitizeWriter(buf){ - var ignore = false; - var out = bind(buf, buf.push); - return { - start: function(tag, attrs, unary){ - tag = lowercase(tag); - if (!ignore && specialElements[tag]) { - ignore = tag; - } - if (!ignore && validElements[tag] == true) { - out('<'); - out(tag); - forEach(attrs, function(value, key){ - var lkey=lowercase(key); - if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { - out(' '); - out(key); - out('="'); - out(encodeEntities(value)); - out('"'); - } - }); - out(unary ? '/>' : '>'); - } - }, - end: function(tag){ - tag = lowercase(tag); - if (!ignore && validElements[tag] == true) { - out('</'); - out(tag); - out('>'); - } - if (tag == ignore) { - ignore = false; - } - }, - chars: function(chars){ - if (!ignore) { - out(encodeEntities(chars)); - } - } - }; -} - /** * !!! This is an undocumented "private" service !!! * @@ -8053,13 +7533,23 @@ function htmlSanitizeWriter(buf){ * @description * This is very simple implementation of testing browser's features. */ -function $SnifferProvider(){ - this.$get = ['$window', function($window){ +function $SnifferProvider() { + this.$get = ['$window', function($window) { + var eventSupport = {}; + return { history: !!($window.history && $window.history.pushState), hashchange: 'onhashchange' in $window && // IE8 compatible mode lies - (!$window.document.documentMode || $window.document.documentMode > 7) + (!$window.document.documentMode || $window.document.documentMode > 7), + hasEvent: function(event) { + if (isUndefined(eventSupport[event])) { + var divElm = $window.document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + } }; }]; } @@ -8314,6 +7804,9 @@ function $HttpProvider() { * with name equal to the lower-cased http method name, e.g. * `$httpProvider.defaults.headers.get['My-Header']='value'`. * + * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar + * fassion as described above. + * * * # Transforming Requests and Responses * @@ -8466,6 +7959,9 @@ function $HttpProvider() { * {@link angular.module.ng.$cacheFactory $cacheFactory}, this cache will be used for * caching. * - **timeout** – `{number}` – timeout in milliseconds. + * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the + * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 + * requests with credentials} for more information. * * @returns {HttpPromise} Returns a {@link angular.module.ng.$q promise} object with the * standard `then` method and two http specific methods: `success` and `error`. The `then` @@ -8645,7 +8141,7 @@ function $HttpProvider() { * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object + * @returns {HttpPromise} Future object */ /** @@ -8659,7 +8155,7 @@ function $HttpProvider() { * @param {string} url Relative or absolute URL specifying the destination of the request. * Should contain `JSON_CALLBACK` string. * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object + * @returns {HttpPromise} Future object */ createShortMethods('get', 'delete', 'head', 'jsonp'); @@ -8688,10 +8184,23 @@ function $HttpProvider() { * @param {string} url Relative or absolute URL specifying the destination of the request * @param {*} data Request content * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object + * @returns {HttpPromise} Future object */ createShortMethodsWithData('post', 'put'); + /** + * @ngdoc property + * @name angular.module.ng.$http#defaults + * @propertyOf angular.module.ng.$http + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = $config; + return $http; @@ -8765,7 +8274,8 @@ function $HttpProvider() { // if we won't have the response in cache, send the request to the backend if (!cachedResp) { - $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout); + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials); } return promise; @@ -8859,13 +8369,13 @@ var XHR = window.XMLHttpRequest || function() { function $HttpBackendProvider() { this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, - $document[0].body, $window.location.protocol.replace(':', '')); + $document[0], $window.location.protocol.replace(':', '')); }]; } -function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locationProtocol) { +function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) { // TODO(vojta): fix the signature - return function(method, url, post, callback, headers, timeout) { + return function(method, url, post, callback, headers, timeout, withCredentials) { $browser.$$incOutstandingRequestCount(); url = url || $browser.url(); @@ -8875,7 +8385,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati callbacks[callbackId].data = data; }; - var script = $browser.addJs(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), function() { if (callbacks[callbackId].data) { completeRequest(callback, 200, callbacks[callbackId].data); @@ -8883,7 +8393,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati completeRequest(callback, -2); } delete callbacks[callbackId]; - body.removeChild(script); }); } else { var xhr = new XHR(); @@ -8904,6 +8413,10 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati } }; + if (withCredentials) { + xhr.withCredentials = true; + } + xhr.send(post || ''); if (timeout > 0) { @@ -8929,6 +8442,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati $browser.$$completeOutstandingRequest(noop); } }; + + function jsonpReq(url, done) { + // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), + doneWrapper = function() { + rawDocument.body.removeChild(script); + if (done) done(); + }; + + script.type = 'text/javascript'; + script.src = url; + + if (msie) { + script.onreadystatechange = function() { + if (/loaded|complete/.test(script.readyState)) doneWrapper(); + }; + } else { + script.onload = script.onerror = doneWrapper; + } + + rawDocument.body.appendChild(script); + } } /** @@ -9098,7 +8635,6 @@ function $FilterProvider($provide) { register('filter', filterFilter); register('json', jsonFilter); register('limitTo', limitToFilter); - register('linky', linkyFilter); register('lowercase', lowercaseFilter); register('number', numberFilter); register('orderBy', orderByFilter); @@ -9146,7 +8682,7 @@ function $FilterProvider($provide) { {name:'Adam', phone:'555-5678'}, {name:'Julie', phone:'555-8765'}]"></div> - Search: <input ng-model="searchText" ng-model-instant> + Search: <input ng-model="searchText"> <table id="searchTextResults"> <tr><th>Name</th><th>Phone</th><tr> <tr ng-repeat="friend in friends | filter:searchText"> @@ -9155,9 +8691,9 @@ function $FilterProvider($provide) { <tr> </table> <hr> - Any: <input ng-model="search.$" ng-model-instant> <br> - Name only <input ng-model="search.name" ng-model-instant><br> - Phone only <input ng-model="search.phone" ng-model-instant><br> + Any: <input ng-model="search.$"> <br> + Name only <input ng-model="search.name"><br> + Phone only <input ng-model="search.phone"å><br> <table id="searchObjResults"> <tr><th>Name</th><th>Phone</th><tr> <tr ng-repeat="friend in friends | filter:search"> @@ -9291,7 +8827,7 @@ function filterFilter() { } </script> <div ng-controller="Ctrl"> - <input type="number" ng-model="amount" ng-model-instant> <br> + <input type="number" ng-model="amount"> <br> default currency symbol ($): {{amount | currency}}<br> custom currency identifier (USD$): {{amount | currency:"USD$"}} </div> @@ -9342,7 +8878,7 @@ function currencyFilter($locale) { } </script> <div ng-controller="Ctrl"> - Enter number: <input ng-model='val' ng-model-instant><br> + Enter number: <input ng-model='val'><br> Default formatting: {{val | number}}<br> No fractions: {{val | number:0}}<br> Negative number: {{-val | number:4}} @@ -9612,7 +9148,7 @@ function dateFilter($locale) { parts = [], fn, match; - format = format || 'mediumDate' + format = format || 'mediumDate'; format = $locale.DATETIME_FORMATS[format] || format; if (isString(date)) { if (NUMBER_STRING.test(date)) { @@ -9666,7 +9202,6 @@ function dateFilter($locale) { * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. * @returns {string} JSON string. * - * @css ng-monospace Always applied to the encapsulating element. * * @example: <doc:example> @@ -9709,113 +9244,6 @@ var lowercaseFilter = valueFn(lowercase); */ var uppercaseFilter = valueFn(uppercase); - -/** - * @ngdoc filter - * @name angular.module.ng.$filter.linky - * @function - * - * @description - * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and - * plain email address links. - * - * @param {string} text Input text. - * @returns {string} Html-linkified text. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.snippet = - 'Pretty text with some links:\n'+ - 'http://angularjs.org/,\n'+ - 'mailto:us@somewhere.org,\n'+ - 'another@somewhere.org,\n'+ - 'and one more: ftp://127.0.0.1/.'; - } - </script> - <div ng-controller="Ctrl"> - Snippet: <textarea ng-model="snippet" ng-model-instant cols="60" rows="3"></textarea> - <table> - <tr> - <td>Filter</td> - <td>Source</td> - <td>Rendered</td> - </tr> - <tr id="linky-filter"> - <td>linky filter</td> - <td> - <pre><div ng-bind-html="snippet | linky"><br></div></pre> - </td> - <td> - <div ng-bind-html="snippet | linky"></div> - </td> - </tr> - <tr id="escaped-html"> - <td>no filter</td> - <td><pre><div ng-bind="snippet"><br></div></pre></td> - <td><div ng-bind="snippet"></div></td> - </tr> - </table> - </doc:source> - <doc:scenario> - it('should linkify the snippet with urls', function() { - expect(using('#linky-filter').binding('snippet | linky')). - toBe('Pretty text with some links: ' + - '<a href="http://angularjs.org/">http://angularjs.org/</a>, ' + - '<a href="mailto:us@somewhere.org">us@somewhere.org</a>, ' + - '<a href="mailto:another@somewhere.org">another@somewhere.org</a>, ' + - 'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.'); - }); - - it ('should not linkify snippet without the linky filter', function() { - expect(using('#escaped-html').binding('snippet')). - toBe("Pretty text with some links:\n" + - "http://angularjs.org/,\n" + - "mailto:us@somewhere.org,\n" + - "another@somewhere.org,\n" + - "and one more: ftp://127.0.0.1/."); - }); - - it('should update', function() { - input('snippet').enter('new http://link.'); - expect(using('#linky-filter').binding('snippet | linky')). - toBe('new <a href="http://link">http://link</a>.'); - expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); - }); - </doc:scenario> - </doc:example> - */ -function linkyFilter() { - var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, - MAILTO_REGEXP = /^mailto:/; - - return function(text) { - if (!text) return text; - var match; - var raw = text; - var html = []; - var writer = htmlSanitizeWriter(html); - var url; - var i; - while ((match = raw.match(LINKY_URL_REGEXP))) { - // We can not end in these as they are sometimes found at the end of the sentence - url = match[0]; - // if we did not match ftp/http/mailto then assume mailto - if (match[2] == match[3]) url = 'mailto:' + url; - i = match.index; - writer.chars(raw.substr(0, i)); - writer.start('a', {href:url}); - writer.chars(match[0].replace(MAILTO_REGEXP, '')); - writer.end('a'); - raw = raw.substring(i + match[0].length); - } - writer.chars(raw); - return html.join(''); - }; -}; - /** * @ngdoc function * @name angular.module.ng.$filter.limitTo @@ -9847,7 +9275,7 @@ function linkyFilter() { } </script> <div ng-controller="Ctrl"> - Limit {{numbers}} to: <input type="integer" ng-model="limit" ng-model-instant> + Limit {{numbers}} to: <input type="integer" ng-model="limit"> <p>Output: {{ numbers | limitTo:limit }}</p> </div> </doc:source> @@ -10046,14 +9474,14 @@ function ngDirective(directive) { } directive.restrict = directive.restrict || 'AC'; return valueFn(directive); -}; +} /* * Modifies the default behavior of html A tag, so that the default action is prevented when href * attribute is empty. * - * The reasoning for this change is to allow easy creation of action links with ng-click without - * changing the location or causing page reloads, e.g.: + * The reasoning for this change is to allow easy creation of action links with `ngClick` directive + * without changing the location or causing page reloads, e.g.: * <a href="" ng-click="model.$save()">Save</a> */ var htmlAnchorDirective = valueFn({ @@ -10078,16 +9506,15 @@ var htmlAnchorDirective = valueFn({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-href + * @name angular.module.ng.$compileProvider.directive.ngHref * @restrict A * * @description - * Using <angular/> markup like {{hash}} in an href attribute makes + * Using Angular markup like {{hash}} in an href attribute makes * the page open to a wrong URL, if the user clicks that link before * angular has a chance to replace the {{hash}} with actual URL, the * link will be broken and will most likely return a 404 error. - * The `ng-href` solves this problem by placing the `href` in the - * `ng-` namespace. + * The `ngHref` directive solves this problem. * * The buggy way to write it: * <pre> @@ -10100,7 +9527,7 @@ var htmlAnchorDirective = valueFn({ * </pre> * * @element A - * @param {template} ng-href any string which can contain `{{}}` markup. + * @param {template} ngHref any string which can contain `{{}}` markup. * * @example * This example uses `link` variable inside `href` attribute: @@ -10109,10 +9536,10 @@ var htmlAnchorDirective = valueFn({ <input ng-model="value" /><br /> <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> - <a id="link-3" ng-href="/{{'123'}}" ng-ext-link>link 3</a> (link, reload!)<br /> + <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> - <a id="link-6" ng-href="/{{value}}" ng-ext-link>link</a> (link, change hash) + <a id="link-6" ng-href="{{value}}">link</a> (link, change location) </doc:source> <doc:scenario> it('should execute ng-click but not reload when href without value', function() { @@ -10137,21 +9564,21 @@ var htmlAnchorDirective = valueFn({ it('should execute ng-click but not reload when href empty string and name specified', function() { element('#link-4').click(); expect(input('value').val()).toEqual('4'); - expect(element('#link-4').attr('href')).toBe(""); + expect(element('#link-4').attr('href')).toBe(''); }); it('should execute ng-click but not reload when no href but name specified', function() { element('#link-5').click(); expect(input('value').val()).toEqual('5'); - expect(element('#link-5').attr('href')).toBe(""); + expect(element('#link-5').attr('href')).toBe(''); }); it('should only change url when only ng-href', function() { input('value').enter('6'); - expect(element('#link-6').attr('href')).toBe("/6"); + expect(element('#link-6').attr('href')).toBe('6'); element('#link-6').click(); - expect(browser().window().path()).toEqual('/6'); + expect(browser().location().url()).toEqual('/6'); }); </doc:scenario> </doc:example> @@ -10159,15 +9586,14 @@ var htmlAnchorDirective = valueFn({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-src + * @name angular.module.ng.$compileProvider.directive.ngSrc * @restrict A * * @description - * Using <angular/> markup like `{{hash}}` in a `src` attribute doesn't + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until <angular/> replaces the expression inside - * `{{hash}}`. The `ng-src` attribute solves this problem by placing - * the `src` attribute in the `ng-` namespace. + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. * * The buggy way to write it: * <pre> @@ -10180,12 +9606,12 @@ var htmlAnchorDirective = valueFn({ * </pre> * * @element IMG - * @param {template} ng-src any string which can contain `{{}}` markup. + * @param {template} ngSrc any string which can contain `{{}}` markup. */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-disabled + * @name angular.module.ng.$compileProvider.directive.ngDisabled * @restrict A * * @description @@ -10200,7 +9626,7 @@ var htmlAnchorDirective = valueFn({ * The HTML specs do not require browsers to preserve the special attributes such as disabled. * (The presence of them means true and absence means false) * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-disabled. + * To solve this problem, we introduce the `ngDisabled` directive. * * @example <doc:example> @@ -10218,20 +9644,20 @@ var htmlAnchorDirective = valueFn({ </doc:example> * * @element INPUT - * @param {string} expression Angular expression that will be evaluated. + * @param {expression} ngDisabled Angular expression that will be evaluated. */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-checked + * @name angular.module.ng.$compileProvider.directive.ngChecked * @restrict A * * @description * The HTML specs do not require browsers to preserve the special attributes such as checked. * (The presence of them means true and absence means false) * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-checked. + * To solve this problem, we introduce the `ngChecked` directive. * @example <doc:example> <doc:source> @@ -10248,20 +9674,20 @@ var htmlAnchorDirective = valueFn({ </doc:example> * * @element INPUT - * @param {string} expression Angular expression that will be evaluated. + * @param {expression} ngChecked Angular expression that will be evaluated. */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-multiple + * @name angular.module.ng.$compileProvider.directive.ngMultiple * @restrict A * * @description * The HTML specs do not require browsers to preserve the special attributes such as multiple. * (The presence of them means true and absence means false) * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-multiple. + * To solve this problem, we introduce the `ngMultiple` directive. * * @example <doc:example> @@ -10284,20 +9710,20 @@ var htmlAnchorDirective = valueFn({ </doc:example> * * @element SELECT - * @param {string} expression Angular expression that will be evaluated. + * @param {expression} ngMultiple Angular expression that will be evaluated. */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-readonly + * @name angular.module.ng.$compileProvider.directive.ngReadonly * @restrict A * * @description * The HTML specs do not require browsers to preserve the special attributes such as readonly. * (The presence of them means true and absence means false) * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-readonly. + * To solve this problem, we introduce the `ngReadonly` directive. * @example <doc:example> <doc:source> @@ -10320,14 +9746,14 @@ var htmlAnchorDirective = valueFn({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-selected + * @name angular.module.ng.$compileProvider.directive.ngSelected * @restrict A * * @description * The HTML specs do not require browsers to preserve the special attributes such as selected. * (The presence of them means true and absence means false) * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-selected. + * To solve this problem, we introduced the `ngSelected` directive. * @example <doc:example> <doc:source> @@ -10360,11 +9786,11 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) { ngAttributeAliasDirectives[normalized] = function() { return { priority: 100, - compile: function(tpl, attr) { + compile: function() { return function(scope, element, attr) { attr.$$observers[attrName] = []; scope.$watch(attr[normalized], function(value) { - attr.$set(attrName, value); + attr.$set(attrName, !!value); }); }; } @@ -10378,13 +9804,21 @@ forEach(['src', 'href'], function(attrName) { var normalized = directiveNormalize('ng-' + attrName); ngAttributeAliasDirectives[normalized] = function() { return { - priority: 100, - compile: function(tpl, attr) { + priority: 99, // it needs to run after the attributes are interpolated + compile: function() { return function(scope, element, attr) { - attr.$$observers[attrName] = []; - attr.$observe(normalized, function(value) { + var value = attr[normalized]; + if (value == undefined) { + // undefined value means that the directive is being interpolated + // so just register observer + attr.$$observers[attrName] = []; + attr.$observe(normalized, function(value) { + attr.$set(attrName, value); + }); + } else { + // value present means that no interpolation, so copy to native attribute. attr.$set(attrName, value); - }); + } }; } }; @@ -10396,7 +9830,7 @@ var nullFormCtrl = { $removeControl: noop, $setValidity: noop, $setDirty: noop -} +}; /** * @ngdoc object @@ -10460,23 +9894,43 @@ function FormController(element, attrs) { if (control.$name && form[control.$name] === control) { delete form[control.$name]; } - forEach(errors, cleanupControlErrors, control); + forEach(errors, function(queue, validationToken) { + form.$setValidity(validationToken, true, control); + }); }; form.$setValidity = function(validationToken, isValid, control) { - if (isValid) { - cleanupControlErrors(errors[validationToken], validationToken, control); + var queue = errors[validationToken]; - if (!invalidCount) { - toggleValidCss(isValid); - form.$valid = true; - form.$invalid = false; + if (isValid) { + if (queue) { + arrayRemove(queue, control); + if (!queue.length) { + invalidCount--; + if (!invalidCount) { + toggleValidCss(isValid); + form.$valid = true; + form.$invalid = false; + } + errors[validationToken] = false; + toggleValidCss(true, validationToken); + parentForm.$setValidity(validationToken, true, form); + } } + } else { if (!invalidCount) { toggleValidCss(isValid); } - addControlError(validationToken, control); + if (queue) { + if (includes(queue, control)) return; + } else { + errors[validationToken] = queue = []; + invalidCount++; + toggleValidCss(false, validationToken); + parentForm.$setValidity(validationToken, false, form); + } + queue.push(control); form.$valid = false; form.$invalid = true; @@ -10489,37 +9943,12 @@ function FormController(element, attrs) { form.$pristine = false; }; - function cleanupControlErrors(queue, validationToken, control) { - if (queue) { - control = control || this; // so that we can be used in forEach; - arrayRemove(queue, control); - if (!queue.length) { - invalidCount--; - errors[validationToken] = false; - toggleValidCss(true, validationToken); - parentForm.$setValidity(validationToken, true, form); - } - } - } - - function addControlError(validationToken, control) { - var queue = errors[validationToken]; - if (queue) { - if (includes(queue, control)) return; - } else { - errors[validationToken] = queue = []; - invalidCount++; - toggleValidCss(false, validationToken); - parentForm.$setValidity(validationToken, false, form); - } - queue.push(control); - } } /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-form + * @name angular.module.ng.$compileProvider.directive.ngForm * @restrict EAC * * @description @@ -10527,7 +9956,7 @@ function FormController(element, attrs) { * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a * sub-group of controls needs to be determined. * - * @param {string=} ng-form|name Name of the form. If specified, the form controller will be published into + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into * related scope, under this name. * */ @@ -10544,11 +9973,11 @@ function FormController(element, attrs) { * If `name` attribute is specified, the form controller is published onto the current scope under * this name. * - * # Alias: {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`} + * # Alias: {@link angular.module.ng.$compileProvider.directive.ngForm `ngForm`} * * In angular forms can be nested. This means that the outer form is valid when all of the child * forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this - * reason angular provides {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`} alias + * reason angular provides {@link angular.module.ng.$compileProvider.directive.ngForm `ngForm`} alias * which behaves identical to `<form>` but allows form nesting. * * @@ -10572,19 +10001,20 @@ function FormController(element, attrs) { * You can use one of the following two ways to specify what javascript method should be called when * a form is submitted: * - * - ng-submit on the form element (add link to ng-submit) - * - ng-click on the first button or input field of type submit (input[type=submit]) + * - {@link angular.module.ng.$compileProvider.directive.ngSubmit ngSubmit} directive on the form element + * - {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) * - * To prevent double execution of the handler, use only one of ng-submit or ng-click. This is - * because of the following form submission rules coming from the html spec: + * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This + * is because of the following form submission rules coming from the html spec: * * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ng-submit`) + * (`ngSubmit`) * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter * doesn't trigger submit * - if a form has one or more input fields and one or more buttons or input[type=submit] then * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ng-click`) *and* a submit handler on the enclosing form (`ng-submit`) + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) * * @param {string=} name Name of the form. If specified, the form controller will be published into * related scope, under this name. @@ -10670,17 +10100,17 @@ var inputType = { * @description * Standard HTML text input with angular data binding. * - * @param {string} ng-model 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=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -10737,19 +10167,19 @@ var inputType = { * Text input with number validation and transformation. Sets the `number` validation * error if not a valid number. * - * @param {string} ng-model 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=} min Sets the `min` validation error key if the value entered is less then `min`. * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`. * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -10805,17 +10235,17 @@ var inputType = { * Text input with URL validation. Sets the `url` validation error key if the content is not a * valid URL. * - * @param {string} ng-model 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=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -10870,14 +10300,14 @@ var inputType = { * Text input with email validation. Sets the `email` validation error key if not a valid email * address. * - * @param {string} ng-model 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=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @@ -10932,10 +10362,10 @@ var inputType = { * @description * HTML radio button. * - * @param {string} ng-model Assignable angular expression to data-bind to. + * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string} value The value to which the expression should be set when selected. * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -10973,11 +10403,11 @@ var inputType = { * @description * HTML checkbox. * - * @param {string} ng-model 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=} ng-true-value The value to which the expression should be set when selected. - * @param {string=} ng-false-value The value to which the expression should be set when not selected. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngTrueValue The value to which the expression should be set when selected. + * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -11024,12 +10454,43 @@ function isEmpty(value) { } -function textInputType(scope, element, attr, ctrl) { - element.bind('blur', function() { - scope.$apply(function() { - ctrl.$setViewValue(trim(element.val())); +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + + var listener = function() { + var value = trim(element.val()); + + if (ctrl.$viewValue !== value) { + scope.$apply(function() { + ctrl.$setViewValue(value); + }); + } + }; + + // if the browser does support "input" event, we are fine + if ($sniffer.hasEvent('input')) { + element.bind('input', listener); + } else { + var timeout; + + element.bind('keydown', function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + if (!timeout) { + timeout = $browser.defer(function() { + listener(); + timeout = null; + }); + } }); - }); + + // if user paste into input using mouse, we need "change" event to catch it + element.bind('change', listener); + } + ctrl.$render = function() { element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); @@ -11103,10 +10564,10 @@ function textInputType(scope, element, attr, ctrl) { ctrl.$parsers.push(maxLengthValidator); ctrl.$formatters.push(maxLengthValidator); } -}; +} -function numberInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); ctrl.$parsers.push(function(value) { var empty = isEmpty(value); @@ -11167,8 +10628,8 @@ function numberInputType(scope, element, attr, ctrl) { }); } -function urlInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); var urlValidator = function(value) { if (isEmpty(value) || URL_REGEXP.test(value)) { @@ -11184,8 +10645,8 @@ function urlInputType(scope, element, attr, ctrl) { ctrl.$parsers.push(urlValidator); } -function emailInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); var emailValidator = function(value) { if (isEmpty(value) || EMAIL_REGEXP.test(value)) { @@ -11202,15 +10663,17 @@ function emailInputType(scope, element, attr, ctrl) { } function radioInputType(scope, element, attr, ctrl) { - // correct the name - element.attr('name', attr.id + '@' + attr.name); + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } element.bind('click', function() { if (element[0].checked) { scope.$apply(function() { ctrl.$setViewValue(attr.value); }); - }; + } }); ctrl.$render = function() { @@ -11257,17 +10720,17 @@ function checkboxInputType(scope, element, attr, ctrl) { * properties of this element are exactly the same as those of the * {@link angular.module.ng.$compileProvider.directive.input input element}. * - * @param {string} ng-model 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=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. */ @@ -11281,17 +10744,17 @@ function checkboxInputType(scope, element, attr, ctrl) { * HTML input element control with angular data-binding. Input control follows HTML5 input types * and polyfills the HTML5 validation behavior for older browsers. * - * @param {string} ng-model 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=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user + * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example @@ -11366,13 +10829,14 @@ function checkboxInputType(scope, element, attr, ctrl) { </doc:scenario> </doc:example> */ -var inputDirective = [function() { +var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { return { restrict: 'E', require: '?ngModel', link: function(scope, element, attr, ctrl) { if (ctrl) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl); + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, + $browser); } } }; @@ -11385,7 +10849,7 @@ var VALID_CLASS = 'ng-valid', /** * @ngdoc object - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController + * @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController * * @property {string} $viewValue Actual string value in the view. * @property {*} $modelValue The value in the model, that the control is bound to. @@ -11438,8 +10902,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e /** * @ngdoc function - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setValidity - * @methodOf angular.module.ng.$compileProvider.directive.ng-model.NgModelController + * @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$setValidity + * @methodOf angular.module.ng.$compileProvider.directive.ngModel.NgModelController * * @description * Change the validity state, and notifies the form when the control changes validity. (i.e. it @@ -11465,7 +10929,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e this.$invalid = false; } } else { - toggleValidCss(false) + toggleValidCss(false); this.$invalid = true; this.$valid = false; invalidCount++; @@ -11480,8 +10944,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e /** * @ngdoc function - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setViewValue - * @methodOf angular.module.ng.$compileProvider.directive.ng-model.NgModelController + * @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$setViewValue + * @methodOf angular.module.ng.$compileProvider.directive.ngModel.NgModelController * * @description * Read a value from view. @@ -11550,15 +11014,15 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-model + * @name angular.module.ng.$compileProvider.directive.ngModel * * @element input * * @description * Is directive that tells Angular to do two-way data binding. It works together with `input`, - * `select`, `textarea`. You can easily write your own directives to use `ng-model` as well. + * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. * - * `ng-model` is responsible for: + * `ngModel` is responsible for: * * - binding the view into the model, which other directives such as `input`, `textarea` or `select` * require, @@ -11567,7 +11031,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), * - register the control with parent {@link angular.module.ng.$compileProvider.directive.form form}. * - * For basic examples, how to use `ng-model`, see: + * For basic examples, how to use `ngModel`, see: * * - {@link angular.module.ng.$compileProvider.directive.input input} * - {@link angular.module.ng.$compileProvider.directive.input.text text} @@ -11605,14 +11069,14 @@ var ngModelDirective = [function() { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-change + * @name angular.module.ng.$compileProvider.directive.ngChange * @restrict E * * @description * Evaluate given expression when user changes the input. * The expression is not evaluated when the value change is coming from the model. * - * Note, this directive requires `ng-model` to be present. + * Note, this directive requires `ngModel` to be present. * * @element input * @@ -11661,69 +11125,6 @@ var ngChangeDirective = valueFn({ }); -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-model-instant - * - * @element input - * - * @description - * By default, Angular udpates the model only on `blur` event - when the input looses focus. - * If you want to update after every key stroke, use `ng-model-instant`. - * - * @example - * <doc:example> - * <doc:source> - * First name: <input type="text" ng-model="firstName" /><br /> - * Last name: <input type="text" ng-model="lastName" ng-model-instant /><br /> - * - * First name ({{firstName}}) is only updated on `blur` event, but the last name ({{lastName}}) - * is updated immediately, because of using `ng-model-instant`. - * </doc:source> - * <doc:scenario> - * it('should update first name on blur', function() { - * input('firstName').enter('santa', 'blur'); - * expect(binding('firstName')).toEqual('santa'); - * }); - * - * it('should update last name immediately', function() { - * input('lastName').enter('santa', 'keydown'); - * expect(binding('lastName')).toEqual('santa'); - * }); - * </doc:scenario> - * </doc:example> - */ -var ngModelInstantDirective = ['$browser', function($browser) { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var handler = function() { - scope.$apply(function() { - ctrl.$setViewValue(trim(element.val())); - }); - }; - - var timeout; - element.bind('keydown', function(event) { - var key = event.keyCode; - - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - if (!timeout) { - timeout = $browser.defer(function() { - handler(); - timeout = null; - }); - } - }); - - element.bind('change input', handler); - } - }; -}]; - - var requiredDirective = [function() { return { require: '?ngModel', @@ -11753,13 +11154,13 @@ var requiredDirective = [function() { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-list + * @name angular.module.ng.$compileProvider.directive.ngList * * @description * Text input that converts between comma-seperated string into an array of strings. * * @element input - * @param {string=} ng-list optional delimiter that should be used to split the value. If + * @param {string=} ngList optional delimiter that should be used to split the value. If * specified in form `/something/` then the value will be converted into a regular expression. * * @example @@ -11832,9 +11233,9 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; var ngValueDirective = [function() { return { priority: 100, - compile: function(tpl, attr) { - if (CONSTANT_VALUE_REGEXP.test(attr.ngValue)) { - return function(scope) { + compile: function(tpl, tplAttr) { + if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { + return function(scope, elm, attr) { attr.$set('value', scope.$eval(attr.ngValue)); }; } else { @@ -11851,19 +11252,27 @@ var ngValueDirective = [function() { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-bind + * @name angular.module.ng.$compileProvider.directive.ngBind * * @description - * The `ng-bind` attribute tells Angular to replace the text content of the specified HTML element + * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element * with the value of a given expression, and to update the text content when the value of that * expression changes. * - * Typically, you don't use `ng-bind` directly, but instead you use the double curly markup like - * `{{ expression }}` and let the Angular compiler transform it to - * `<span ng-bind="expression"></span>` when the template is compiled. + * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like + * `{{ expression }}` which is similar but less verbose. + * + * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when + * 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 make the + * bindings invisible to the user while the page is loading. + * + * An alternative solution to this problem would be using the + * {@link angular.module.ng.$compileProvider.directive.ngCloak ngCloak} directive. + * * * @element ANY - * @param {expression} ng-bind {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBind {@link guide/dev_guide.expressions Expression} to evaluate. * * @example * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. @@ -11875,7 +11284,7 @@ var ngValueDirective = [function() { } </script> <div ng-controller="Ctrl"> - Enter name: <input type="text" ng-model="name" ng-model-instant><br> + Enter name: <input type="text" ng-model="name"><br> Hello <span ng-bind="name"></span>! </div> </doc:source> @@ -11898,66 +11307,17 @@ var ngBindDirective = ngDirective(function(scope, element, attr) { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-bind-html-unsafe - * - * @description - * Creates a binding that will innerHTML the result of evaluating the `expression` into the current - * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if - * {@link angular.module.ng.$compileProvider.directive.ng-bind-html ng-bind-html} directive is too - * restrictive and when you absolutely trust the source of the content you are binding to. - * - * See {@link angular.module.ng.$sanitize $sanitize} docs for examples. - * - * @element ANY - * @param {expression} ng-bind-html-unsafe {@link guide/dev_guide.expressions Expression} to evaluate. - */ -var ngBindHtmlUnsafeDirective = ngDirective(function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); - scope.$watch(attr.ngBindHtmlUnsafe, function(value) { - element.html(value == undefined ? '' : value); - }); -}); - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-bind-html - * - * @description - * Creates a binding that will sanitize the result of evaluating the `expression` with the - * {@link angular.module.ng.$sanitize $sanitize} service and innerHTML the result into the current - * element. - * - * See {@link angular.module.ng.$sanitize $sanitize} docs for examples. - * - * @element ANY - * @param {expression} ng-bind-html {@link guide/dev_guide.expressions Expression} to evaluate. - */ -var ngBindHtmlDirective = ['$sanitize', function($sanitize) { - return function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBindHtml); - scope.$watch(attr.ngBindHtml, function(value) { - if (value = $sanitize(value)) { - element.html(value); - } - }); - } -}]; - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-bind-template + * @name angular.module.ng.$compileProvider.directive.ngBindTemplate * * @description - * The `ng-bind-template` attribute specifies that the element - * text should be replaced with the template in ng-bind-template. - * Unlike ng-bind the ng-bind-template can contain multiple `{{` `}}` + * The `ngBindTemplate` directive specifies that the element + * text should be replaced with the template in ngBindTemplate. + * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` * expressions. (This is required since some HTML elements * can not have SPAN elements such as TITLE, or OPTION to name a few.) * * @element ANY - * @param {string} ng-bind-template template of form + * @param {string} ngBindTemplate template of form * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. * * @example @@ -11971,8 +11331,8 @@ var ngBindHtmlDirective = ['$sanitize', function($sanitize) { } </script> <div ng-controller="Ctrl"> - Salutation: <input type="text" ng-model="salutation" ng-model-instant><br> - Name: <input type="text" ng-model="name" ng-model-instant><br> + Salutation: <input type="text" ng-model="salutation"><br> + Name: <input type="text" ng-model="name"><br> <pre ng-bind-template="{{salutation}} {{name}}!"></pre> </div> </doc:source> @@ -12003,6 +11363,31 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { } }]; + +/** + * @ngdoc directive + * @name angular.module.ng.$compileProvider.directive.ngBindHtmlUnsafe + * + * @description + * Creates a binding that will innerHTML the result of evaluating the `expression` into the current + * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if + * {@link angular.module.ng.$compileProvider.directive.ngBindHtml ngBindHtml} directive is too + * restrictive and when you absolutely trust the source of the content you are binding to. + * + * See {@link angular.module.ng.$sanitize $sanitize} docs for examples. + * + * @element ANY + * @param {expression} ngBindHtmlUnsafe {@link guide/dev_guide.expressions Expression} to evaluate. + */ +var ngBindHtmlUnsafeDirective = [function() { + return function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); + scope.$watch(attr.ngBindHtmlUnsafe, function(value) { + element.html(value || ''); + }); + }; +}]; + function classDirective(name, selector) { name = 'ngClass' + name; return ngDirective(function(scope, element, attr) { @@ -12022,10 +11407,10 @@ function classDirective(name, selector) { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-class + * @name angular.module.ng.$compileProvider.directive.ngClass * * @description - * The `ng-class` allows you to set CSS class on HTML element dynamically by databinding an + * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an * expression that represents all classes to be added. * * The directive won't add duplicate classes if a particular class was already set. @@ -12034,7 +11419,7 @@ function classDirective(name, selector) { * new classes are added. * * @element ANY - * @param {expression} ng-class {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClass {@link guide/dev_guide.expressions Expression} to eval. The result * of the evaluation can be a string representing space delimited class * names, an array, or a map of class names to boolean values. * @@ -12068,18 +11453,18 @@ var ngClassDirective = classDirective('', true); /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-class-odd + * @name angular.module.ng.$compileProvider.directive.ngClassOdd * * @description - * The `ng-class-odd` and `ng-class-even` works exactly as - * {@link angular.module.ng.$compileProvider.directive.ng-class ng-class}, except it works in conjunction with `ng-repeat` and - * takes affect only on odd (even) rows. + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link angular.module.ng.$compileProvider.directive.ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. * * This directive can be applied only within a scope of an - * {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}. + * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ng-class-odd {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClassOdd {@link guide/dev_guide.expressions Expression} to eval. The result * of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -12108,18 +11493,18 @@ var ngClassOddDirective = classDirective('Odd', 0); /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-class-even + * @name angular.module.ng.$compileProvider.directive.ngClassEven * * @description - * The `ng-class-odd` and `ng-class-even` works exactly as - * {@link angular.module.ng.$compileProvider.directive.ng-class ng-class}, except it works in - * conjunction with `ng-repeat` and takes affect only on odd (even) rows. + * The `ngClassOdd` and `ngClassEven` works exactly as + * {@link angular.module.ng.$compileProvider.directive.ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. * * This directive can be applied only within a scope of an - * {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}. + * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ng-class-even {@link guide/dev_guide.expressions Expression} to eval. The + * @param {expression} ngClassEven {@link guide/dev_guide.expressions Expression} to eval. The * result of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -12147,28 +11532,28 @@ var ngClassEvenDirective = classDirective('Even', 1); /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-cloak + * @name angular.module.ng.$compileProvider.directive.ngCloak * * @description - * The `ng-cloak` directive is used to prevent the Angular html template from being briefly + * The `ngCloak` directive is used to prevent the Angular html template from being briefly * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this * directive to avoid the undesirable flicker effect caused by the html template display. * * The directive can be applied to the `<body>` element, but typically a fine-grained application is * prefered in order to benefit from progressive rendering of the browser view. * - * `ng-cloak` works in cooperation with a css rule that is embedded within `angular.js` and + * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and * `angular.min.js` files. Following is the css rule: * * <pre> - * [ng\:cloak], .ng-cloak { + * [ng\:cloak], [ng-cloak], .ng-cloak { * display: none; * } * </pre> * * When this css rule is loaded by the browser, all html elements (including their children) that * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive - * during the compilation of the template it deletes the `ng-cloak` element attribute, which + * during the compilation of the template it deletes the `ngCloak` element attribute, which * makes the compiled element visible. * * For the best result, `angular.js` script must be loaded in the head section of the html file; @@ -12177,7 +11562,7 @@ var ngClassEvenDirective = classDirective('Even', 1); * * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css - * class `ng-cloak` in addition to `ng-cloak` directive as shown in the example below. + * class `ngCloak` in addition to `ngCloak` directive as shown in the example below. * * @element ANY * @@ -12207,17 +11592,17 @@ var ngCloakDirective = ngDirective({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-controller + * @name angular.module.ng.$compileProvider.directive.ngController * * @description - * The `ng-controller` directive assigns behavior to a scope. This is a key aspect of how angular + * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular * supports the principles behind the Model-View-Controller design pattern. * * MVC components in angular: * * * Model — The Model is data in scope properties; scopes are attached to the DOM. * * View — The template (HTML with data bindings) is rendered into the View. - * * Controller — The `ng-controller` 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. * * Note that an alternative way to define controllers is via the `{@link angular.module.ng.$route}` @@ -12225,7 +11610,7 @@ var ngCloakDirective = ngDirective({ * * @element ANY * @scope - * @param {expression} ng-controller Name of a globally accessible constructor function or an + * @param {expression} ngController Name of a globally accessible constructor function or an * {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a * constructor function. * @@ -12309,14 +11694,14 @@ var ngControllerDirective = [function() { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-click + * @name angular.module.ng.$compileProvider.directive.ngClick * * @description - * The ng-click allows you to specify custom behavior when + * The ngClick allows you to specify custom behavior when * element is clicked. * * @element ANY - * @param {expression} ng-click {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngClick {@link guide/dev_guide.expressions Expression} to evaluate upon * click. (Event object is available as `$event`) * * @example @@ -12362,118 +11747,118 @@ forEach( /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-dblclick + * @name angular.module.ng.$compileProvider.directive.ngDblclick * * @description - * The ng-dblclick allows you to specify custom behavior on dblclick event. + * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. * * @element ANY - * @param {expression} ng-dblclick {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngDblclick {@link guide/dev_guide.expressions Expression} to evaluate upon * dblclick. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mousedown + * @name angular.module.ng.$compileProvider.directive.ngMousedown * * @description - * The ng-mousedown allows you to specify custom behavior on mousedown event. + * The ngMousedown directive allows you to specify custom behavior on mousedown event. * * @element ANY - * @param {expression} ng-mousedown {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousedown {@link guide/dev_guide.expressions Expression} to evaluate upon * mousedown. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mouseup + * @name angular.module.ng.$compileProvider.directive.ngMouseup * * @description * Specify custom behavior on mouseup event. * * @element ANY - * @param {expression} ng-mouseup {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseup {@link guide/dev_guide.expressions Expression} to evaluate upon * mouseup. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mouseover + * @name angular.module.ng.$compileProvider.directive.ngMouseover * * @description * Specify custom behavior on mouseover event. * * @element ANY - * @param {expression} ng-mouseover {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseover {@link guide/dev_guide.expressions Expression} to evaluate upon * mouseover. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mouseenter + * @name angular.module.ng.$compileProvider.directive.ngMouseenter * * @description * Specify custom behavior on mouseenter event. * * @element ANY - * @param {expression} ng-mouseenter {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseenter {@link guide/dev_guide.expressions Expression} to evaluate upon * mouseenter. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mouseleave + * @name angular.module.ng.$compileProvider.directive.ngMouseleave * * @description * Specify custom behavior on mouseleave event. * * @element ANY - * @param {expression} ng-mouseleave {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseleave {@link guide/dev_guide.expressions Expression} to evaluate upon * mouseleave. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-mousemove + * @name angular.module.ng.$compileProvider.directive.ngMousemove * * @description * Specify custom behavior on mousemove event. * * @element ANY - * @param {expression} ng-mousemove {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousemove {@link guide/dev_guide.expressions Expression} to evaluate upon * mousemove. (Event object is available as `$event`) * * @example - * See {@link angular.module.ng.$compileProvider.directive.ng-click ng-click} + * See {@link angular.module.ng.$compileProvider.directive.ngClick ngClick} */ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-submit + * @name angular.module.ng.$compileProvider.directive.ngSubmit * * @description * Enables binding angular expressions to onsubmit events. @@ -12482,7 +11867,7 @@ forEach( * server and reloading the current page). * * @element form - * @param {expression} ng-submit {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngSubmit {@link guide/dev_guide.expressions Expression} to eval. * * @example <doc:example> @@ -12530,24 +11915,23 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-include - * @restrict EA + * @name angular.module.ng.$compileProvider.directive.ngInclude + * @restrict ECA * * @description * Fetches, compiles and includes an external HTML fragment. * * Keep in mind that Same Origin Policy applies to included resources - * (e.g. ng-include won't work for file:// access). + * (e.g. ngInclude won't work for cross-domain requests on all browsers and for + * file:// access on some browsers). * * @scope * - * @param {string} ng-include|src angular expression evaluating to URL. If the source is a string constant, + * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`. - * @param {Scope=} [scope=new_child_scope] optional expression which evaluates to an - * instance of angular.module.ng.$rootScope.Scope to set the HTML fragment to. * @param {string=} onload Expression to evaluate when a new partial is loaded. * - * @param {string=} autoscroll Whether `ng-include` should call {@link angular.module.ng.$anchorScroll + * @param {string=} autoscroll Whether `ngInclude` should call {@link angular.module.ng.$anchorScroll * $anchorScroll} to scroll the viewport after the content is loaded. * * - If the attribute is not set, disable scrolling. @@ -12595,79 +11979,74 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) { /** * @ngdoc event - * @name angular.module.ng.$compileProvider.directive.ng-include#$includeContentLoaded - * @eventOf angular.module.ng.$compileProvider.directive.ng-include - * @eventType emit on the current ng-include scope + * @name angular.module.ng.$compileProvider.directive.ngInclude#$includeContentLoaded + * @eventOf angular.module.ng.$compileProvider.directive.ngInclude + * @eventType emit on the current ngInclude scope * @description - * Emitted every time the ng-include content is reloaded. + * Emitted every time the ngInclude content is reloaded. */ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', function($http, $templateCache, $anchorScroll, $compile) { return { - restrict: 'EA', + restrict: 'ECA', + terminal: true, compile: function(element, attr) { - var srcExp = attr.ngInclude || attr.src, - scopeExp = attr.scope || '', + var srcExp = attr.ngInclude || attr.src, onloadExp = attr.onload || '', autoScrollExp = attr.autoscroll; - return function(scope, element, attr) { + return function(scope, element) { var changeCounter = 0, childScope; - function incrementChange() { changeCounter++;} - scope.$watch(srcExp, incrementChange); - scope.$watch(function() { - var includeScope = scope.$eval(scopeExp); - if (includeScope) return includeScope.$id; - }, incrementChange); - scope.$watch(function() {return changeCounter;}, function(newChangeCounter) { - var src = scope.$eval(srcExp), - useScope = scope.$eval(scopeExp); + var clearContent = function() { + if (childScope) { + childScope.$destroy(); + childScope = null; + } + + element.html(''); + }; + + scope.$watch(srcExp, function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + $http.get(src, {cache: $templateCache}).success(function(response) { + if (thisChangeId !== changeCounter) return; - function clearContent() { - // if this callback is still desired - if (newChangeCounter === changeCounter) { if (childScope) childScope.$destroy(); - childScope = null; - element.html(''); - } - } + childScope = scope.$new(); - if (src) { - $http.get(src, {cache: $templateCache}).success(function(response) { - // if this callback is still desired - if (newChangeCounter === changeCounter) { - element.html(response); - if (childScope) childScope.$destroy(); - childScope = useScope ? useScope : scope.$new(); - $compile(element.contents())(childScope); - if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - scope.$emit('$includeContentLoaded'); - scope.$eval(onloadExp); - } - }).error(clearContent); - } else { - clearContent(); - } + element.html(response); + $compile(element.contents())(childScope); + + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + + childScope.$emit('$includeContentLoaded'); + scope.$eval(onloadExp); + }).error(function() { + if (thisChangeId === changeCounter) clearContent(); + }); + } else clearContent(); }); }; } - } + }; }]; /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-init + * @name angular.module.ng.$compileProvider.directive.ngInit * * @description - * The `ng-init` attribute specifies initialization tasks to be executed + * The `ngInit` directive specifies initialization tasks to be executed * before the template enters execution mode during bootstrap. * * @element ANY - * @param {expression} ng-init {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngInit {@link guide/dev_guide.expressions Expression} to eval. * * @example <doc:example> @@ -12696,18 +12075,18 @@ var ngInitDirective = ngDirective({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-non-bindable + * @name angular.module.ng.$compileProvider.directive.ngNonBindable * @priority 1000 * * @description * Sometimes it is necessary to write code which looks like bindings but which should be left alone - * by angular. Use `ng-non-bindable` to make angular ignore a chunk of HTML. + * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML. * * @element ANY * * @example * In this example there are two location where a simple binding (`{{}}`) is present, but the one - * wrapped in `ng-non-bindable` is left alone. + * wrapped in `ngNonBindable` is left alone. * * @example <doc:example> @@ -12728,15 +12107,15 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-pluralize + * @name angular.module.ng.$compileProvider.directive.ngPluralize * @restrict EA * * @description * # Overview - * ng-pluralize is a directive that displays messages according to en-US localization rules. + * `ngPluralize` is a directive that displays messages according to en-US localization rules. * These rules are bundled with angular.js and the rules can be overridden - * (see {@link guide/dev_guide.i18n Angular i18n} dev guide). You configure ng-pluralize by - * specifying the mappings between + * (see {@link guide/dev_guide.i18n Angular i18n} dev guide). You configure ngPluralize directive + * by specifying the mappings between * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html * plural categories} and the strings to be displayed. * @@ -12750,8 +12129,8 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * explicit number rule for "3" matches the number 3. You will see the use of plural categories * and explicit number rules throughout later parts of this documentation. * - * # Configuring ng-pluralize - * You configure ng-pluralize by providing 2 attributes: `count` and `when`. + * # Configuring ngPluralize + * You configure ngPluralize by providing 2 attributes: `count` and `when`. * You can also provide an optional attribute, `offset`. * * The value of the `count` attribute can be either a string or an {@link guide/dev_guide.expressions @@ -12761,7 +12140,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * string to be displayed. The value of the attribute should be a JSON object so that Angular * can interpret it correctly. * - * The following example shows how to configure ng-pluralize: + * The following example shows how to configure ngPluralize: * * <pre> * <ng-pluralize count="personCount" @@ -12782,7 +12161,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder * for <span ng-non-bindable>{{numberExpression}}</span>. * - * # Configuring ng-pluralize with offset + * # Configuring ngPluralize with offset * The `offset` attribute allows further customization of pluralized text, which can result in * a better user experience. For example, instead of the message "4 people are viewing this document", * you might display "John, Kate and 2 others are viewing this document". @@ -12931,10 +12310,10 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-repeat + * @name angular.module.ng.$compileProvider.directive.ngRepeat * * @description - * The `ng-repeat` directive instantiates a template once per item from a collection. Each template + * The `ngRepeat` directive instantiates a template once per item from a collection. Each template * instance gets its own scope, where the given loop variable is set to the current collection item, * and `$index` is set to the item index or key. * @@ -12950,7 +12329,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp * @element ANY * @scope * @priority 1000 - * @param {repeat_expression} ng-repeat The expression indicating how to enumerate a collection. Two + * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two * formats are currently supported: * * * `variable in expression` – where variable is the user defined loop variable and `expression` @@ -12965,7 +12344,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp * * @example * This example initializes the scope to a list of names and - * then uses `ng-repeat` to display every person: + * then uses `ngRepeat` to display every person: <doc:example> <doc:source> <div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]"> @@ -12997,7 +12376,7 @@ var ngRepeatDirective = ngDirective({ var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), lhs, rhs, valueIdent, keyIdent; if (! match) { - throw Error("Expected ng-repeat in form of '_item_ in _collection_' but got '" + + throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '" + expression + "'."); } lhs = match[1]; @@ -13111,14 +12490,14 @@ var ngRepeatDirective = ngDirective({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-show + * @name angular.module.ng.$compileProvider.directive.ngShow * * @description - * The `ng-show` and `ng-hide` directives show or hide a portion of the DOM tree (HTML) + * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) * conditionally. * * @element ANY - * @param {expression} ng-show If the {@link guide/dev_guide.expressions expression} is truthy + * @param {expression} ngShow If the {@link guide/dev_guide.expressions expression} is truthy * then the element is shown or hidden respectively. * * @example @@ -13151,14 +12530,14 @@ var ngShowDirective = ngDirective(function(scope, element, attr){ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-hide + * @name angular.module.ng.$compileProvider.directive.ngHide * * @description - * The `ng-hide` and `ng-show` directives hide or show a portion + * The `ngHide` and `ngShow` directives hide or show a portion * of the HTML conditionally. * * @element ANY - * @param {expression} ng-hide If the {@link guide/dev_guide.expressions expression} truthy then + * @param {expression} ngHide If the {@link guide/dev_guide.expressions expression} truthy then * the element is shown or hidden respectively. * * @example @@ -13190,13 +12569,13 @@ var ngHideDirective = ngDirective(function(scope, element, attr){ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-style + * @name angular.module.ng.$compileProvider.directive.ngStyle * * @description - * The ng-style allows you to set CSS style on an HTML element conditionally. + * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. * * @element ANY - * @param {expression} ng-style {@link guide/dev_guide.expressions Expression} which evals to an + * @param {expression} ngStyle {@link guide/dev_guide.expressions Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * @@ -13231,26 +12610,26 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) { /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-switch + * @name angular.module.ng.$compileProvider.directive.ngSwitch * @restrict EA * * @description * Conditionally change the DOM structure. * * @usageContent - * <any ng-switch-when="matchValue1">...</any> - * <any ng-switch-when="matchValue2">...</any> + * <ANY ng-switch-when="matchValue1">...</ANY> + * <ANY ng-switch-when="matchValue2">...</ANY> * ... - * <any ng-switch-default>...</any> + * <ANY ng-switch-default>...</ANY> * * @scope - * @param {*} ng-switch|on expression to match against <tt>ng-switch-when</tt>. + * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>. * @paramDescription * On child elments add: * - * * `ng-switch-when`: the case statement to match against. If match then this + * * `ngSwitchWhen`: the case statement to match against. If match then this * case will be displayed. - * * `ng-switch-default`: the default case when no other casses match. + * * `ngSwitchDefault`: the default case when no other casses match. * * @example <doc:example> @@ -13342,7 +12721,7 @@ var ngSwitchDefaultDirective = ngDirective({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-transclude + * @name angular.module.ng.$compileProvider.directive.ngTransclude * * @description * Insert the transcluded DOM here. @@ -13399,12 +12778,12 @@ var ngTranscludeDirective = ngDirective({ /** * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-view + * @name angular.module.ng.$compileProvider.directive.ngView * @restrict ECA * * @description * # Overview - * `ng-view` is a directive that complements the {@link angular.module.ng.$route $route} service by + * `ngView` is a directive that complements the {@link angular.module.ng.$route $route} service by * including the rendered template of the current route into the main layout (`index.html`) file. * Every time the current route changes, the included view changes with it according to the * configuration of the `$route` service. @@ -13458,11 +12837,11 @@ var ngTranscludeDirective = ngDirective({ <div ng-controller="MainCntl"> Choose: - <a href="/Book/Moby">Moby</a> | - <a href="/Book/Moby/ch/1">Moby: Ch1</a> | - <a href="/Book/Gatsby">Gatsby</a> | - <a href="/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | - <a href="/Book/Scarlet">Scarlet Letter</a><br/> + <a href="Book/Moby">Moby</a> | + <a href="Book/Moby/ch/1">Moby: Ch1</a> | + <a href="Book/Gatsby">Gatsby</a> | + <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | + <a href="Book/Scarlet">Scarlet Letter</a><br/> <div ng-view></div> <hr /> @@ -13494,11 +12873,11 @@ var ngTranscludeDirective = ngDirective({ /** * @ngdoc event - * @name angular.module.ng.$compileProvider.directive.ng-view#$viewContentLoaded - * @eventOf angular.module.ng.$compileProvider.directive.ng-view - * @eventType emit on the current ng-view scope + * @name angular.module.ng.$compileProvider.directive.ngView#$viewContentLoaded + * @eventOf angular.module.ng.$compileProvider.directive.ngView + * @eventType emit on the current ngView scope * @description - * Emitted every time the ng-view content is reloaded. + * Emitted every time the ngView content is reloaded. */ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', '$controller', @@ -13512,42 +12891,44 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c lastScope, onloadExp = attr.onload || ''; - scope.$on('$afterRouteChange', function(event, next, previous) { - changeCounter++; - }); + scope.$on('$afterRouteChange', update); + update(); - scope.$watch(function() {return changeCounter;}, function(newChangeCounter) { - var template = $route.current && $route.current.template; - function destroyLastScope() { - if (lastScope) { - lastScope.$destroy(); - lastScope = null; - } + function destroyLastScope() { + if (lastScope) { + lastScope.$destroy(); + lastScope = null; } + } + + function update() { + var template = $route.current && $route.current.template, + thisChangeId = ++changeCounter; function clearContent() { // ignore callback if another route change occured since - if (newChangeCounter == changeCounter) { + if (thisChangeId === changeCounter) { element.html(''); + destroyLastScope(); } - destroyLastScope(); } if (template) { $http.get(template, {cache: $templateCache}).success(function(response) { // ignore callback if another route change occured since - if (newChangeCounter == changeCounter) { + if (thisChangeId === changeCounter) { element.html(response); destroyLastScope(); var link = $compile(element.contents()), - current = $route.current; + current = $route.current, + controller; lastScope = current.scope = scope.$new(); if (current.controller) { - element.contents(). - data('$ngControllerController', $controller(current.controller, {$scope: lastScope})); + controller = $controller(current.controller, {$scope: lastScope}); + element.contents().data('$ngControllerController', controller); } link(lastScope); @@ -13561,7 +12942,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c } else { clearContent(); } - }); + } } }; }]; @@ -13572,7 +12953,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c * * @description * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the - * template can be used by `ng-include`, `ng-view` or directive templates. + * template can be used by `ngInclude`, `ngView` or directive templates. * * @restrict E * @param {'text/ng-template'} type must be set to `'text/ng-template'` @@ -13616,34 +12997,34 @@ var scriptDirective = ['$templateCache', function($templateCache) { * @description * HTML `SELECT` element with angular data-binding. * - * # `ng-options` + * # `ngOptions` * - * Optionally `ng-options` attribute can be used to dynamically generate a list of `<option>` + * Optionally `ngOptions` attribute can be used to dynamically generate a list of `<option>` * elements for a `<select>` element using an array or an object obtained by evaluating the - * `ng-options` expression. + * `ngOptions` expression. *˝˝ * When an item in the select menu is select, the value of array element or object property - * represented by the selected option will be bound to the model identified by the `ng-model` attribute - * of the parent select element. + * represented by the selected option will be bound to the model identified by the `ngModel` + * directive of the parent select element. * * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can * be nested into the `<select>` element. This element will then represent `null` or "not selected" * option. See example below for demonstration. * - * Note: `ng-options` provides iterator facility for `<option>` element which must be used instead - * of {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}. `ng-repeat` is not suitable for use with - * `<option>` element because of the following reasons: + * Note: `ngOptions` provides iterator facility for `<option>` element which must be used instead + * of {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. `ngRepeat` is not + * suitable for use with `<option>` element because of the following reasons: * * * value attribute of the option element that we need to bind to requires a string, but the * source of data for the iteration might be in a form of array containing objects instead of * strings - * * {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} unrolls after the select binds causing - * incorect rendering on most browsers. + * * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} unrolls after the + * select binds causing incorect rendering on most browsers. * * binding to a value not in list confuses most browsers. * * @param {string} name assignable expression to data-bind to. * @param {string=} required The control is considered valid only if value is entered. - * @param {comprehension_expression=} ng-options in one of the following forms: + * @param {comprehension_expression=} ngOptions in one of the following forms: * * * for array data sources: * * `label` **`for`** `value` **`in`** `array` @@ -13790,7 +13171,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { }); }; - // we have to do it on each watch since ng-model watches reference, but + // we have to do it on each watch since ngModel watches reference, but // we need to work of an array, so we need to see if anything was inserted/removed scope.$watch(function() { if (!equals(lastView, ctrl.$viewValue)) { @@ -13817,7 +13198,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { throw Error( - "Expected ng-options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + + "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + " but got '" + optionsExp + "'."); } @@ -14029,7 +13410,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { while(optionGroupsCache.length > groupIndex) { optionGroupsCache.pop()[0].element.remove(); } - }; + } } } } |
