diff --git a/app/lib/angular/angular-cookies.js b/app/lib/angular/angular-cookies.js index 7f01b5e..7bd613c 100644 --- a/app/lib/angular/angular-cookies.js +++ b/app/lib/angular/angular-cookies.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ diff --git a/app/lib/angular/angular-cookies.min.js b/app/lib/angular/angular-cookies.min.js index ffe89e4..66e5ebd 100644 --- a/app/lib/angular/angular-cookies.min.js +++ b/app/lib/angular/angular-cookies.min.js @@ -1,5 +1,5 @@ /* - AngularJS v1.0.0rc10 + AngularJS v1.0.0rc11 (c) 2010-2012 Google, Inc. http://angularjs.org License: MIT */ diff --git a/app/lib/angular/angular-loader.js b/app/lib/angular/angular-loader.js index 98e78d0..8bb99eb 100644 --- a/app/lib/angular/angular-loader.js +++ b/app/lib/angular/angular-loader.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ diff --git a/app/lib/angular/angular-loader.min.js b/app/lib/angular/angular-loader.min.js index eef8782..ed38538 100644 --- a/app/lib/angular/angular-loader.min.js +++ b/app/lib/angular/angular-loader.min.js @@ -1,5 +1,5 @@ /* - AngularJS v1.0.0rc10 + AngularJS v1.0.0rc11 (c) 2010-2012 Google, Inc. http://angularjs.org License: MIT */ diff --git a/app/lib/angular/angular-resource.js b/app/lib/angular/angular-resource.js index 6093947..dc272c2 100644 --- a/app/lib/angular/angular-resource.js +++ b/app/lib/angular/angular-resource.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ diff --git a/app/lib/angular/angular-resource.min.js b/app/lib/angular/angular-resource.min.js index 0962b06..c3c3d36 100644 --- a/app/lib/angular/angular-resource.min.js +++ b/app/lib/angular/angular-resource.min.js @@ -1,5 +1,5 @@ /* - AngularJS v1.0.0rc10 + AngularJS v1.0.0rc11 (c) 2010-2012 Google, Inc. http://angularjs.org License: MIT */ diff --git a/app/lib/angular/angular-sanitize.js b/app/lib/angular/angular-sanitize.js index aae2c98..db2bbaa 100644 --- a/app/lib/angular/angular-sanitize.js +++ b/app/lib/angular/angular-sanitize.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ @@ -411,7 +411,7 @@ angular.module('ngSanitize', []).value('$sanitize', $sanitize); * See {@link angular.module.ngSanitize.$sanitize $sanitize} docs for examples. * * @element ANY - * @param {expression} ngBindHtml {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. */ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) { return function(scope, element, attr) { diff --git a/app/lib/angular/angular-sanitize.min.js b/app/lib/angular/angular-sanitize.min.js index ca032d6..15d7a0f 100644 --- a/app/lib/angular/angular-sanitize.min.js +++ b/app/lib/angular/angular-sanitize.min.js @@ -1,5 +1,5 @@ /* - AngularJS v1.0.0rc10 + AngularJS v1.0.0rc11 (c) 2010-2012 Google, Inc. http://angularjs.org License: MIT */ diff --git a/app/lib/angular/angular.js b/app/lib/angular/angular.js index 839b228..9f54d26 100644 --- a/app/lib/angular/angular.js +++ b/app/lib/angular/angular.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ @@ -761,7 +761,9 @@ function startingTag(element) { // are not allowed to have children. So we just ignore it. element.html(''); } catch(e) {} - return jqLite('
').append(element).html().match(/^(<[^>]+>)/)[1]; + return jqLite('
').append(element).html(). + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); } @@ -910,7 +912,7 @@ function angularInit(element, bootstrap) { * @description * Use this function to manually start up angular application. * - * See: {@link guide/dev_guide.bootstrap.manual_bootstrap Bootstrap} + * See: {@link guide/bootstrap Bootstrap} * * @param {Element} element DOM element which is the root of angular application. * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} @@ -919,10 +921,13 @@ function angularInit(element, bootstrap) { function bootstrap(element, modules) { element = jqLite(element); modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); modules.unshift('ng'); var injector = createInjector(modules); injector.invoke( - ['$rootScope', '$compile', '$injector', function(scope, compile, injector){ + ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){ scope.$apply(function() { element.data('$injector', injector); compile(element)(scope); @@ -1243,11 +1248,11 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.0.0rc10', // all of these placeholder strings will be replaced by rake's + full: '1.0.0rc11', // all of these placeholder strings will be replaced by rake's major: 1, // compile task minor: 0, dot: 0, - codeName: 'tesseract-giftwrapping' + codeName: 'promise-resolution' }; @@ -1726,8 +1731,12 @@ forEach('input,select,option,textarea,button,form'.split(','), function(value) { BOOLEAN_ELEMENTS[uppercase(value)] = true; }); -function isBooleanAttr(element, name) { - return BOOLEAN_ELEMENTS[element.nodeName] && BOOLEAN_ATTR[name.toLowerCase()]; +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } forEach({ @@ -1920,11 +1929,7 @@ function createEventHandler(element, events) { }; forEach(events[type || event.type], function(fn) { - try { - fn.call(element, event); - } catch (e) { - // Not much to do here since jQuery ignores these anyway - } + fn.call(element, event); }); // Remove monkey-patched methods (IE), @@ -2217,7 +2222,7 @@ HashQueueMap.prototype = { * * @description * Creates an injector function that can be used for retrieving services as well as for - * dependency injection (see {@link guide/dev_guide.di dependency injection}). + * dependency injection (see {@link guide/di dependency injection}). * * @param {Array.} modules A list of module functions or their aliases. See @@ -2252,19 +2257,32 @@ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(.+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -function inferInjectionArgs(fn) { - assertArgFn(fn); - if (!fn.$inject) { - var args = fn.$inject = []; - var fnText = fn.toString().replace(STRIP_COMMENTS, ''); - var argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ - arg.replace(FN_ARG, function(all, underscore, name){ - args.push(name); +function annotate(fn) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn == 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ + arg.replace(FN_ARG, function(all, underscore, name){ + $inject.push(name); + }); }); - }); + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn') + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); } - return fn.$inject; + return $inject; } /////////////////////////////////////// @@ -2345,7 +2363,7 @@ function inferInjectionArgs(fn) { * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. - * @return the value returned by the invoked `fn` function. + * @returns {*} the value returned by the invoked `fn` function. */ /** @@ -2359,9 +2377,90 @@ function inferInjectionArgs(fn) { * @param {function} Type Annotated constructor function. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. - * @return new instance of `Type`. + * @returns {Object} new instance of `Type`. */ +/** + * @ngdoc method + * @name angular.module.AUTO.$injector#annotate + * @methodOf angular.module.AUTO.$injector + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is used by the injector + * to determine which services need to be injected into the function when the function is invoked. There are three + * ways in which the function can be annotated with the needed dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting + * the function into a string using `toString()` method and extracting the argument names. + *
+ *   // Given
+ *   function MyController($scope, $route) {
+ *     // ...
+ *   }
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies + * are supported. + * + * # The `$injector` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of + * services to be injected into the function. + *
+ *   // Given
+ *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
+ *     // ...
+ *   }
+ *   // Define function dependencies
+ *   MyController.$inject = ['$scope', '$route'];
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very + * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives + * minification is a better choice: + * + *
+ *   // We wish to write this (not minification / obfuscation safe)
+ *   injector.invoke(function($compile, $rootScope) {
+ *     // ...
+ *   });
+ *
+ *   // We are forced to write break inlining
+ *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
+ *     // ...
+ *   };
+ *   tmpFn.$inject = ['$compile', '$rootScope'];
+ *   injector.invoke(tempFn);
+ *
+ *   // To better support inline function the inline annotation is supported
+ *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
+ *     // ...
+ *   }]);
+ *
+ *   // Therefore
+ *   expect(injector.annotate(
+ *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
+ *    ).toEqual(['$compile', '$rootScope']);
+ * 
+ * + * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described + * above. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + /** * @ngdoc object @@ -2664,23 +2763,11 @@ function createInjector(modulesToLoad) { function invoke(fn, self, locals){ var args = [], - $inject, - length, + $inject = annotate(fn), + length, i, key; - if (typeof fn == 'function') { - $inject = inferInjectionArgs(fn); - length = $inject.length; - } else { - if (isArray(fn)) { - $inject = fn; - length = $inject.length - 1; - fn = $inject[length]; - } - assertArgFn(fn, 'fn'); - } - - for(var i = 0; i < length; i++) { + for(i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; args.push( locals && locals.hasOwnProperty(key) @@ -2688,6 +2775,11 @@ function createInjector(modulesToLoad) { : getService(key, path) ); } + if (!fn.$inject) { + // this means that we must be an array. + fn = fn[length]; + } + // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke switch (self ? -1 : args.length) { @@ -2720,7 +2812,8 @@ function createInjector(modulesToLoad) { return { invoke: invoke, instantiate: instantiate, - get: getService + get: getService, + annotate: annotate }; } } @@ -3342,6 +3435,9 @@ function $TemplateCacheProvider() { */ +var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; + + /** * @ngdoc function * @name angular.module.ng.$compile @@ -3459,7 +3555,7 @@ function $TemplateCacheProvider() { * * * For information on how the compiler works, see the - * {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide. + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. */ @@ -3469,7 +3565,20 @@ function $TemplateCacheProvider() { * @function * * @description + */ + +/** + * @ngdoc function + * @name angular.module.ng.$compileProvider#directive + * @methodOf angular.module.ng.$compileProvider + * @function * + * @description + * Register a new directive with compiler + * + * @param {string} name name of the directive. + * @param {function} directiveFactory An injectable directive factory function. + * @returns {angular.module.ng.$compileProvider} Self for chaining. */ $CompileProvider.$inject = ['$provide']; function $CompileProvider($provide) { @@ -3532,54 +3641,12 @@ function $CompileProvider($provide) { this.$get = [ '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', - '$controller', + '$controller', '$rootScope', function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, - $controller) { - - var LOCAL_MODE = { - attribute: function(localName, mode, parentScope, scope, attr) { - scope[localName] = attr[localName]; - }, - - evaluate: function(localName, mode, parentScope, scope, attr) { - scope[localName] = parentScope.$eval(attr[localName]); - }, - - bind: function(localName, mode, parentScope, scope, attr) { - var getter = $interpolate(attr[localName]); - scope.$watch( - function() { return getter(parentScope); }, - function(v) { scope[localName] = v; } - ); - }, - - accessor: function(localName, mode, parentScope, scope, attr) { - var getter = noop, - setter = noop, - exp = attr[localName]; - - if (exp) { - getter = $parse(exp); - setter = getter.assign || function() { - throw Error("Expression '" + exp + "' not assignable."); - }; - } - - scope[localName] = function(value) { - return arguments.length ? setter(parentScope, value) : getter(parentScope); - }; - }, - - expression: function(localName, mode, parentScope, scope, attr) { - scope[localName] = function(locals) { - $parse(attr[localName])(parentScope, locals); - }; - } - }; + $controller, $rootScope) { var Attributes = function(element, attr) { this.$$element = element; - this.$$observers = {}; this.$attr = attr || {}; }; @@ -3597,7 +3664,8 @@ function $CompileProvider($provide) { * @param {string=} attrName Optional none normalized name. Defaults to key. */ $set: function(key, value, writeAttr, attrName) { - var booleanKey = isBooleanAttr(this.$$element[0], key.toLowerCase()); + var booleanKey = getBooleanAttrName(this.$$element[0], key), + $$observers = this.$$observers; if (booleanKey) { this.$$element.prop(key, value); @@ -3625,7 +3693,7 @@ function $CompileProvider($provide) { } // fire observers - forEach(this.$$observers[key], function(fn) { + $$observers && forEach($$observers[key], function(fn) { try { fn(value); } catch (e) { @@ -3644,10 +3712,17 @@ function $CompileProvider($provide) { * @returns {function(*)} the `fn` Function passed in. */ $observe: function(key, fn) { - // keep only observers for interpolated attrs - if (this.$$observers[key]) { - this.$$observers[key].push(fn); - } + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = {})), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); return fn; } }; @@ -3809,7 +3884,7 @@ function $CompileProvider($provide) { attrs[nName] = value = trim((msie && name == 'href') ? decodeURIComponent(node.getAttribute(name, 2)) : attr.value); - if (isBooleanAttr(node, nName)) { + if (getBooleanAttrName(node, nName)) { attrs[nName] = true; // presence means true } addAttrInterpolateDirective(node, directives, value, nName); @@ -4050,9 +4125,67 @@ function $CompileProvider($provide) { $element = attrs.$$element; if (newScopeDirective && isObject(newScopeDirective.scope)) { - forEach(newScopeDirective.scope, function(mode, name) { - (LOCAL_MODE[mode] || wrongMode)(name, mode, - scope.$parent || scope, scope, attrs); + var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/; + + var parentScope = scope.$parent || scope; + + forEach(newScopeDirective.scope, function(definiton, scopeName) { + var match = definiton.match(LOCAL_REGEXP) || [], + attrName = match[2]|| scopeName, + mode = match[1], // @, =, or & + lastValue, + parentGet, parentSet; + + switch (mode) { + + case '@': { + attrs.$observe(attrName, function(value) { + scope[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = parentScope; + break; + } + + case '=': { + parentGet = $parse(attrs[attrName]); + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = scope[scopeName] = parentGet(parentScope); + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + + ' (directive: ' + newScopeDirective.name + ')'); + }; + lastValue = scope[scopeName] = parentGet(parentScope); + scope.$watch(function() { + var parentValue = parentGet(parentScope); + + if (parentValue !== scope[scopeName]) { + // we are out of sync and need to copy + if (parentValue !== lastValue) { + // parent changed and it has precedence + lastValue = scope[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(parentScope, lastValue = scope[scopeName]); + } + } + return parentValue; + }); + break; + } + + case '&': { + parentGet = $parse(attrs[attrName]); + scope[scopeName] = function(locals) { + return parentGet(parentScope, locals); + } + break; + } + + default: { + throw Error('Invalid isolate scope definition for directive ' + + newScopeDirective.name + ': ' + definiton); + } + } }); } @@ -4065,12 +4198,6 @@ function $CompileProvider($provide) { $transclude: boundTranscludeFn }; - - forEach(directive.inject || {}, function(mode, name) { - (LOCAL_MODE[mode] || wrongMode)(name, mode, - newScopeDirective ? scope.$parent || scope : scope, locals, attrs); - }); - controller = directive.controller; if (controller == '@') { controller = attrs[directive.name]; @@ -4155,6 +4282,7 @@ function $CompileProvider($provide) { var srcAttr = src.$attr, dstAttr = dst.$attr, $element = dst.$$element; + // reapply the old attributes to the new element forEach(dst, function(value, key) { if (key.charAt(0) != '$') { @@ -4164,10 +4292,12 @@ function $CompileProvider($provide) { dst.$set(key, value, true, srcAttr[key]); } }); + // copy the new attributes on the old attrs object forEach(src, function(value, key) { if (key == 'class') { safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; } else if (key == 'style') { $element.attr('style', $element.attr('style') + ';' + value); } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { @@ -4301,19 +4431,20 @@ function $CompileProvider($provide) { directives.push({ priority: 100, compile: valueFn(function(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + if (name === 'class') { // we need to interpolate classes again, in the case the element was replaced // and therefore the two class attrs got merged - we want to interpolate the result interpolateFn = $interpolate(attr[name], true); } - // we define observers array only for interpolated attrs - // and ignore observers for non interpolated attrs to save some memory - attr.$$observers[name] = []; attr[name] = undefined; - scope.$watch(interpolateFn, function(value) { - attr.$set(name, value); - }); + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function(value) { + attr.$set(name, value); + }); }) }); } @@ -4369,6 +4500,43 @@ function directiveNormalize(name) { return camelCase(name.replace(PREFIX_REGEXP, '')); } +/** + * @ngdoc object + * @name angular.module.ng.$compile.directive.Attributes + * @description + * + * A shared object between directive compile / linking functions which contains normalized DOM element + * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed + * since all of these are treated as equivalent in Angular: + * + * + */ + +/** + * @ngdoc property + * @name angular.module.ng.$compile.directive.Attributes#$attr + * @propertyOf angular.module.ng.$compile.directive.Attributes + * @returns {object} A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc function + * @name angular.module.ng.$compile.directive.Attributes#$set + * @methodOf angular.module.ng.$compile.directive.Attributes + * @function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * revers translated using the {@link angular.module.ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. + */ + /** @@ -4906,7 +5074,7 @@ LocationUrl.prototype = { * Return full url representation with all segments encoded according to rules specified in * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. * - * @return {string} + * @return {string} full url */ absUrl: locationGetter('$$absUrl'), @@ -4923,7 +5091,7 @@ LocationUrl.prototype = { * Change path, search and hash, when called with parameter and return `$location`. * * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) - * @return {string} + * @return {string} url */ url: function(url, replace) { if (isUndefined(url)) @@ -4947,7 +5115,7 @@ LocationUrl.prototype = { * * Return protocol of current url. * - * @return {string} + * @return {string} protocol of current url */ protocol: locationGetter('$$protocol'), @@ -4961,7 +5129,7 @@ LocationUrl.prototype = { * * Return host of current url. * - * @return {string} + * @return {string} host of current url. */ host: locationGetter('$$host'), @@ -4975,7 +5143,7 @@ LocationUrl.prototype = { * * Return port of current url. * - * @return {Number} + * @return {Number} port */ port: locationGetter('$$port'), @@ -4995,7 +5163,7 @@ LocationUrl.prototype = { * if it is missing. * * @param {string=} path New path - * @return {string} + * @return {string} path */ path: locationGetterSetter('$$path', function(path) { return path.charAt(0) == '/' ? path : '/' + path; @@ -5017,7 +5185,7 @@ LocationUrl.prototype = { * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a * single search parameter. If the value is `null`, the parameter will be deleted. * - * @return {string} + * @return {string} search */ search: function(search, paramValue) { if (isUndefined(search)) @@ -5050,7 +5218,7 @@ LocationUrl.prototype = { * Change hash fragment when called with parameter and return `$location`. * * @param {string=} hash New hash fragment - * @return {string} + * @return {string} hash */ hash: locationGetterSetter('$$hash', identity), @@ -5097,10 +5265,13 @@ function locationGetterSetter(property, preprocess) { * * @requires $browser * @requires $sniffer - * @requires $document + * @requires $rootElement * * @description - * The $location service parses the URL in the browser address bar (based on the {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to your application. Changes to the URL in the address bar are reflected into $location service and changes to $location are reflected into the browser address bar. + * The $location service parses the URL in the browser address bar (based on the + * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. * * **The $location service:** * @@ -5113,7 +5284,8 @@ function locationGetterSetter(property, preprocess) { * - Clicks on a link. * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). * - * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location} + * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular + * Services: Using $location} */ /** @@ -5160,67 +5332,75 @@ function $LocationProvider(){ } }; - this.$get = ['$rootScope', '$browser', '$sniffer', '$document', - function( $rootScope, $browser, $sniffer, $document) { - var currentUrl, + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', + function( $rootScope, $browser, $sniffer, $rootElement) { + var $location, basePath = $browser.baseHref() || '/', pathPrefix = pathPrefixFromBase(basePath), - initUrl = $browser.url(); + initUrl = $browser.url(), + absUrlPrefix; if (html5Mode) { if ($sniffer.history) { - currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix); + $location = new LocationUrl( + convertToHtml5Url(initUrl, basePath, hashPrefix), + pathPrefix); } else { - currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix), - hashPrefix); + $location = new LocationHashbangUrl( + convertToHashbangUrl(initUrl, basePath, hashPrefix), + hashPrefix); } - - // link rewriting - var u = currentUrl, - absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix; - - $document.bind('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (event.ctrlKey || event.metaKey || event.which == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (elm.length && lowercase(elm[0].nodeName) !== 'a') { - elm = elm.parent(); - } - - var absHref = elm.prop('href'); - - if (!absHref || - elm.attr('target') || - absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path - return; - } - - // 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 - window.angular['ff-684208-preventDefault'] = true; - }); } else { - currentUrl = new LocationHashbangUrl(initUrl, hashPrefix); + $location = new LocationHashbangUrl(initUrl, hashPrefix); } + // link rewriting + absUrlPrefix = composeProtocolHostPort( + $location.protocol(), $location.host(), $location.port()) + pathPrefix; + + $rootElement.bind('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (event.ctrlKey || event.metaKey || event.which == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (elm.length && lowercase(elm[0].nodeName) !== 'a') { + elm = elm.parent(); + } + + var absHref = elm.prop('href'); + + if (!absHref || + elm.attr('target') || + absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path + return; + } + + // update location with href without the prefix + $location.url(absHref.substr(absUrlPrefix.length)); + $rootScope.$apply(); + event.preventDefault(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + window.angular['ff-684208-preventDefault'] = true; + }); + + // rewrite hashbang url <> html5 url - if (currentUrl.absUrl() != initUrl) { - $browser.url(currentUrl.absUrl(), true); + if ($location.absUrl() != initUrl) { + $browser.url($location.absUrl(), true); } // update $location when $browser url changes $browser.onUrlChange(function(newUrl) { - if (currentUrl.absUrl() != newUrl) { + if ($location.absUrl() != newUrl) { $rootScope.$evalAsync(function() { - currentUrl.$$parse(newUrl); + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + afterLocationChange(oldUrl); }); if (!$rootScope.$$phase) $rootScope.$digest(); } @@ -5229,18 +5409,30 @@ function $LocationProvider(){ // update browser var changeCounter = 0; $rootScope.$watch(function $locationWatch() { - if ($browser.url() != currentUrl.absUrl()) { + var oldUrl = $browser.url(); + + if (!changeCounter || oldUrl != $location.absUrl()) { changeCounter++; $rootScope.$evalAsync(function() { - $browser.url(currentUrl.absUrl(), currentUrl.$$replace); - currentUrl.$$replace = false; + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url($location.absUrl(), $location.$$replace); + $location.$$replace = false; + afterLocationChange(oldUrl); + } }); } return changeCounter; }); - return currentUrl; + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } }]; } @@ -6166,6 +6358,39 @@ function getterFn(path, csp) { /////////////////////////////////// +/** + * @ngdoc function + * @name angular.module.ng.$parse + * @function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + *
+ *   var getter = $parse('user.name');
+ *   var setter = getter.assign;
+ *   var context = {user:{name:'angular'}};
+ *   var locals = {user:{name:'local'}};
+ *
+ *   expect(getter(context)).toEqual('angular');
+ *   setter(context, 'newValue');
+ *   expect(context.user.name).toEqual('newValue');
+ *   expect(getter(context, locals)).toEqual('local');
+ * 
+ * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context`: an object against which any expressions embedded in the strings are evaluated + * against (Topically a scope object). + * * `locals`: local variables context object, useful for overriding values in `context`. + * + * The return function also has an `assign` property, if the expression is assignable, which + * allows one to set values to expressions. + * + */ function $ParseProvider() { var cache = {}; this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { @@ -6592,7 +6817,7 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$routeProvider * * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redudant trailing slash or is missing one, the route will still match and the + * contains redundant trailing slash or is missing one, the route will still match and the * `$location.path` will be updated to add or drop the trailing slash to exacly match the * route definition. * @param {Object} route Mapping information to be assigned to `$route.current` on route @@ -6602,16 +6827,30 @@ 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 + * - `template` – `{string=}` – html template as a string that should be used by * {@link angular.module.ng.$compileProvider.directive.ngView ngView} or * {@link angular.module.ng.$compileProvider.directive.ngInclude ngInclude} directives. + * this property takes precedence over `templateUrl`. + * - `templateUrl` – `{string=}` – path to an html template that should be used by + * {@link angular.module.ng.$compileProvider.directive.ngView ngView}. + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, they will be + * resolved and converted to a value before the controller is instantiated and the + * `$aftreRouteChange` event is fired. The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link api/angular.module.AUTO.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is resolved + * before its value is injected into the controller. + * * - `redirectTo` – {(string|function())=} – value to update * {@link angular.module.ng.$location $location} path with and trigger route redirection. * * If `redirectTo` is a function, it will be called with the following parameters: * * - `{Object.}` - route parameters extracted from the current - * `$location.path()` by applying the current route template. + * `$location.path()` by applying the current route templateUrl. * - `{string}` - current `$location.path()` * - `{Object}` - current `$location.search()` * @@ -6662,8 +6901,8 @@ function $RouteProvider(){ }; - this.$get = ['$rootScope', '$location', '$routeParams', - function( $rootScope, $location, $routeParams) { + this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', + function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { /** * @ngdoc object @@ -6672,6 +6911,16 @@ function $RouteProvider(){ * @requires $routeParams * * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as define in route definition. + * - `locals`: A map of locals which is used by {@link angular.module.ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * * @property {Array.} routes Array of all configured routes. * * @description @@ -6704,7 +6953,7 @@ function $RouteProvider(){
$location.path() = {{$location.path()}}
-
$route.current.template = {{$route.current.template}}
+
$route.current.templateUrl = {{$route.current.templateUrl}}
$route.current.params = {{$route.current.params}}
$route.current.scope.name = {{$route.current.scope.name}}
$routeParams = {{$routeParams}}
@@ -6725,11 +6974,19 @@ function $RouteProvider(){ angular.module('ngView', [], function($routeProvider, $locationProvider) { $routeProvider.when('/Book/:bookId', { - template: 'book.html', - controller: BookCntl + templateUrl: 'book.html', + controller: BookCntl, + resolve: { + // I will cause a 1 second delay + delay: function($q, $timeout) { + var delay = $q.defer(); + $timeout(delay.resolve, 1000); + return delay.promise; + } + } }); $routeProvider.when('/Book/:bookId/ch/:chapterId', { - template: 'chapter.html', + templateUrl: 'chapter.html', controller: ChapterCntl }); @@ -6763,6 +7020,7 @@ function $RouteProvider(){ expect(content).toMatch(/Chapter Id\: 1/); element('a:contains("Scarlet")').click(); + sleep(2); // promises are not part of scenario waiting content = element('.doc-example-live [ng-view]').text(); expect(content).toMatch(/controller\: BookCntl/); expect(content).toMatch(/Book Id\: Scarlet/); @@ -6773,11 +7031,15 @@ function $RouteProvider(){ /** * @ngdoc event - * @name angular.module.ng.$route#$beforeRouteChange + * @name angular.module.ng.$route#$routeChangeStart * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted before a route change. + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occurs. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$routeChangeSuccess` is fired. * * @param {Route} next Future route information. * @param {Route} current Current route information. @@ -6785,16 +7047,31 @@ function $RouteProvider(){ /** * @ngdoc event - * @name angular.module.ng.$route#$afterRouteChange + * @name angular.module.ng.$route#$routeChangeSuccess * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted after a route change. + * Broadcasted after a route dependencies are resolved. + * {@link angular.module.ng.$compileProvider.directive.ngView ngView} listens for the directive + * to instantiate the controller and render the view. * * @param {Route} current Current route information. * @param {Route} previous Previous route information. */ + /** + * @ngdoc event + * @name angular.module.ng.$route#$routeChangeError + * @eventOf angular.module.ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted if any of the resolve promises are rejected. + * + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. + */ + /** * @ngdoc event * @name angular.module.ng.$route#$routeUpdate @@ -6807,7 +7084,6 @@ function $RouteProvider(){ */ var matcher = switchRouteMatcher, - dirty = 0, forceReload = false, $route = { routes: routes, @@ -6818,19 +7094,19 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$route * * @description - * Causes `$route` service to reload theR current route even if + * 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.ngView ngView} * creates new scope, reinstantiates the controller. */ reload: function() { - dirty++; forceReload = true; + $rootScope.$evalAsync(updateRoute); } }; - $rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute); + $rootScope.$on('$locationChangeSuccess', updateRoute); return $route; @@ -6871,7 +7147,7 @@ function $RouteProvider(){ $rootScope.$broadcast('$routeUpdate', last); } else if (next || last) { forceReload = false; - $rootScope.$broadcast('$beforeRouteChange', next, last); + $rootScope.$broadcast('$routeChangeStart', next, last); $route.current = next; if (next) { if (next.redirectTo) { @@ -6882,11 +7158,52 @@ function $RouteProvider(){ $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) .replace(); } - } else { - copy(next.params, $routeParams); } } - $rootScope.$broadcast('$afterRouteChange', next, last); + + $q.when(next). + then(function() { + if (next) { + var keys = [], + values = [], + template; + + forEach(next.resolve || {}, function(value, key) { + keys.push(key); + values.push(isFunction(value) ? $injector.invoke(value) : $injector.get(value)); + }); + if (isDefined(template = next.template)) { + } else if (isDefined(template = next.templateUrl)) { + template = $http.get(template, {cache: $templateCache}). + then(function(response) { return response.data; }); + } + if (isDefined(template)) { + keys.push('$template'); + values.push(template); + } + return $q.all(values).then(function(values) { + var locals = {}; + forEach(values, function(value, index) { + locals[keys[index]] = value; + }); + return locals; + }); + } + }). + // after route change + then(function(locals) { + if (next == $route.current) { + if (next) { + next.locals = locals; + copy(next.params, $routeParams); + } + $rootScope.$broadcast('$routeChangeSuccess', next, last); + } + }, function(error) { + if (next == $route.current) { + $rootScope.$broadcast('$routeChangeError', next, last, error); + } + }); } } @@ -7015,7 +7332,7 @@ function $RouteParamsProvider() { * * Every application has a single root {@link angular.module.ng.$rootScope.Scope scope}. * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide - * event processing life-cycle. See {@link guide/dev_guide.scopes developer guide on scopes}. + * event processing life-cycle. See {@link guide/scope developer guide on scopes}. */ function $RootScopeProvider(){ var TTL = 10; @@ -7078,9 +7395,6 @@ function $RootScopeProvider(){ expect(parent.salutation).toEqual('Hello'); * * - * # Dependency Injection - * See {@link guide/dev_guide.di dependency injection}. - * * * @param {Object.=} providers Map of service factory which need to be provided * for the current scope. Defaults to {@link angular.module.ng}. @@ -7127,7 +7441,7 @@ function $RootScopeProvider(){ * the scope and its child scopes to be permanently detached from the parent and thus stop * participating in model change detection and listener notification by invoking. * - * @params {boolean} isolate if true then the scoped does not prototypically inherit from the + * @param {boolean} isolate if true then the scoped does not prototypically inherit from the * parent scope. The scope is isolated, as it can not se parent scope properties. * When creating widgets it is useful for the widget to not accidently read parent * state. @@ -7232,12 +7546,12 @@ function $RootScopeProvider(){ * {@link angular.module.ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a * call to the `listener`. * - * - `string`: Evaluated as {@link guide/dev_guide.expressions expression} + * - `string`: Evaluated as {@link guide/expression expression} * - `function(scope)`: called with current `scope` as a parameter. * @param {(function()|string)=} listener Callback called whenever the return value of * the `watchExpression` changes. * - * - `string`: Evaluated as {@link guide/dev_guide.expressions expression} + * - `string`: Evaluated as {@link guide/expression expression} * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. * * @param {boolean=} objectEquality Compare object for equality rather then for refference. @@ -7284,7 +7598,7 @@ function $RootScopeProvider(){ * Because a {@link angular.module.ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the * `$digest()` keeps calling the {@link angular.module.ng.$rootScope.Scope#$watch watchers} until no more listeners are * firing. This means that it is possible to get into an infinite loop. This function will throw - * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100. + * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. * * Usually you don't call `$digest()` directly in * {@link angular.module.ng.$compileProvider.directive.ngController controllers} or in @@ -7462,9 +7776,8 @@ function $RootScopeProvider(){ * * @param {(string|function())=} expression An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. - * - `function(scope, locals)`: execute the function with the current `scope` parameter. - * @param {Object=} locals Hash object of local variables for the expression. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. * * @returns {*} The result of evaluating the expression. */ @@ -7492,7 +7805,7 @@ function $RootScopeProvider(){ * * @param {(string|function())=} expression An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * */ @@ -7529,7 +7842,7 @@ function $RootScopeProvider(){ * * Scope's `$apply()` method transitions through the following stages: * - * 1. The {@link guide/dev_guide.expressions expression} is executed using the + * 1. The {@link guide/expression expression} is executed using the * {@link angular.module.ng.$rootScope.Scope#$eval $eval()} method. * 2. Any exceptions from the execution of the expression are forwarded to the * {@link angular.module.ng.$exceptionHandler $exceptionHandler} service. @@ -7539,7 +7852,7 @@ function $RootScopeProvider(){ * * @param {(string|function())=} exp An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with current `scope` parameter. * * @returns {*} The result of evaluating the expression. @@ -7933,8 +8246,8 @@ function $HttpProvider() { 'Accept': 'application/json, text/plain, */*', 'X-Requested-With': 'XMLHttpRequest' }, - post: {'Content-Type': 'application/json'}, - put: {'Content-Type': 'application/json'} + post: {'Content-Type': 'application/json;charset=utf-8'}, + put: {'Content-Type': 'application/json;charset=utf-8'} } }; @@ -8852,12 +9165,12 @@ function $TimeoutProvider() { * Cancels a task associated with the `promise`. As a result of this the promise will be * resolved with a rejection. * - * @param {Promise} promise Promise returned by the `$timeout` function. + * @param {Promise=} promise Promise returned by the `$timeout` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */ timeout.cancel = function(promise) { - if (promise.$$timeoutId in deferreds) { + if (promise && promise.$$timeoutId in deferreds) { deferreds[promise.$$timeoutId].reject('canceled'); return $browser.defer.cancel(promise.$$timeoutId); } @@ -9403,7 +9716,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200) * * `format` string can also be one of the following predefined - * {@link guide/dev_guide.i18n localizable formats}: + * {@link guide/i18n localizable formats}: * * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale * (e.g. Sep 3, 2010 12:05:08 pm) @@ -10117,7 +10430,6 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) { priority: 100, compile: function() { return function(scope, element, attr) { - attr.$$observers[attrName] = []; scope.$watch(attr[normalized], function(value) { attr.$set(attrName, !!value); }); @@ -10134,27 +10446,15 @@ forEach(['src', 'href'], function(attrName) { ngAttributeAliasDirectives[normalized] = function() { return { priority: 99, // it needs to run after the attributes are interpolated - compile: function() { - return function(scope, element, attr) { - 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); + link: function(scope, element, attr) { + attr.$observe(normalized, function(value) { + attr.$set(attrName, value); - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect - if (msie) element.prop(attrName, value); - }); - } else { - // value present means that no interpolation, so copy to native attribute. - attr.$set(attrName, value); - element.prop(attrName, value); - } - }; + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect + if (msie) element.prop(attrName, value); + }); } }; }; @@ -11050,6 +11350,7 @@ function checkboxInputType(scope, element, attr, ctrl) { /** * @ngdoc directive * @name angular.module.ng.$compileProvider.directive.textarea + * @restrict E * * @description * HTML textarea element control with angular data-binding. The data-binding and validation @@ -11204,9 +11505,82 @@ var VALID_CLASS = 'ng-valid', * * @description * + * `NgModelController` provides API for the `ng-model` directive. The controller contains + * services for data-binding, validation, CSS update, value formatting and parsing. It + * specifically does not contain any logic which deals with DOM rendering or listening to + * DOM events. The `NgModelController` is meant to be extended by other directives where, the + * directive provides DOM manipulation and the `NgModelController` provides the data-binding. + * + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', []). + directive('contenteditable', function() { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if(!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html(ngModel.$viewValue || ''); + }; + + // Listen for change events to enable binding + element.bind('blur keyup change', function() { + scope.$apply(read); + }); + read(); // initialize + + // Write data to the model + function read() { + ngModel.$setViewValue(element.html()); + } + } + }; + }); + + +
+
Change me!
+ Required! +
+ +
+
+ + it('should data-bind and become invalid', function() { + var contentEditable = element('[contenteditable]'); + + expect(contentEditable.text()).toEqual('Change me!'); + input('userContent').enter(''); + expect(contentEditable.text()).toEqual(''); + expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); + }); + + *
+ * */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$element', - function($scope, $exceptionHandler, $attr, ngModel, $element) { +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', + function($scope, $exceptionHandler, $attr, $element, $parse) { this.$viewValue = Number.NaN; this.$modelValue = Number.NaN; this.$parsers = []; @@ -11216,9 +11590,27 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e this.$dirty = false; this.$valid = true; this.$invalid = false; - this.$render = noop; this.$name = $attr.name; + var ngModelGet = $parse($attr.ngModel), + ngModelSet = ngModelGet.assign; + + if (!ngModelSet) { + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + + ' (' + startingTag($element) + ')'); + } + + /** + * @ngdoc function + * @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$render + * @methodOf angular.module.ng.$compileProvider.directive.ngModel.NgModelController + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + */ + this.$render = noop; + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, invalidCount = 0, // used to easily determine if we are valid $error = this.$error = {}; // keep invalid keys here @@ -11312,7 +11704,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e if (this.$modelValue !== value) { this.$modelValue = value; - ngModel(value); + ngModelSet($scope, value); forEach(this.$viewChangeListeners, function(listener) { try { listener(); @@ -11325,9 +11717,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e // model -> value var ctrl = this; - $scope.$watch(function() { - return ngModel(); - }, function(value) { + $scope.$watch(ngModelGet, function(value) { // ignore change from view if (ctrl.$modelValue === value) return; @@ -11380,11 +11770,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e * - {@link angular.module.ng.$compileProvider.directive.textarea textarea} * */ -var ngModelDirective = [function() { +var ngModelDirective = function() { return { - inject: { - ngModel: 'accessor' - }, require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { @@ -11400,7 +11787,7 @@ var ngModelDirective = [function() { }); } }; -}]; +}; /** @@ -11461,11 +11848,12 @@ var ngChangeDirective = valueFn({ }); -var requiredDirective = [function() { +var requiredDirective = function() { return { require: '?ngModel', link: function(scope, elm, attr, ctrl) { if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element var validator = function(value) { if (attr.required && (isEmpty(value) || value === false)) { @@ -11485,7 +11873,7 @@ var requiredDirective = [function() { }); } }; -}]; +}; /** @@ -11566,7 +11954,7 @@ var ngListDirective = function() { var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; -var ngValueDirective = [function() { +var ngValueDirective = function() { return { priority: 100, compile: function(tpl, tplAttr) { @@ -11576,7 +11964,6 @@ var ngValueDirective = [function() { }; } else { return function(scope, elm, attr) { - attr.$$observers.value = []; scope.$watch(attr.ngValue, function(value) { attr.$set('value', value, false); }); @@ -11584,7 +11971,7 @@ var ngValueDirective = [function() { } } }; -}]; +}; /** * @ngdoc directive @@ -11608,7 +11995,7 @@ var ngValueDirective = [function() { * * * @element ANY - * @param {expression} ngBind {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. * * @example * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. @@ -11713,7 +12100,7 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { * See {@link angular.module.ngSanitize.$sanitize $sanitize} docs for examples. * * @element ANY - * @param {expression} ngBindHtmlUnsafe {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. */ var ngBindHtmlUnsafeDirective = [function() { return function(scope, element, attr) { @@ -11755,7 +12142,7 @@ function classDirective(name, selector) { * new classes are added. * * @element ANY - * @param {expression} ngClass {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClass {@link guide/expression 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. * @@ -11805,7 +12192,7 @@ var ngClassDirective = classDirective('', true); * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassOdd {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -11852,7 +12239,7 @@ var ngClassOddDirective = classDirective('Odd', 0); * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassEven {@link guide/dev_guide.expressions Expression} to eval. The + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The * result of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -11967,7 +12354,7 @@ var ngCloakDirective = ngDirective({ * @element ANY * @scope * @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 + * {@link guide/expression expression} that on the current scope evaluates to a * constructor function. * * @example @@ -12083,7 +12470,7 @@ var ngCspDirective = ['$sniffer', function($sniffer) { * element is clicked. * * @element ANY - * @param {expression} ngClick {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon * click. (Event object is available as `$event`) * * @example @@ -12135,7 +12522,7 @@ forEach( * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. * * @element ANY - * @param {expression} ngDblclick {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon * dblclick. (Event object is available as `$event`) * * @example @@ -12151,7 +12538,7 @@ forEach( * The ngMousedown directive allows you to specify custom behavior on mousedown event. * * @element ANY - * @param {expression} ngMousedown {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon * mousedown. (Event object is available as `$event`) * * @example @@ -12167,7 +12554,7 @@ forEach( * Specify custom behavior on mouseup event. * * @element ANY - * @param {expression} ngMouseup {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon * mouseup. (Event object is available as `$event`) * * @example @@ -12182,7 +12569,7 @@ forEach( * Specify custom behavior on mouseover event. * * @element ANY - * @param {expression} ngMouseover {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon * mouseover. (Event object is available as `$event`) * * @example @@ -12198,7 +12585,7 @@ forEach( * Specify custom behavior on mouseenter event. * * @element ANY - * @param {expression} ngMouseenter {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon * mouseenter. (Event object is available as `$event`) * * @example @@ -12214,7 +12601,7 @@ forEach( * Specify custom behavior on mouseleave event. * * @element ANY - * @param {expression} ngMouseleave {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon * mouseleave. (Event object is available as `$event`) * * @example @@ -12230,7 +12617,7 @@ forEach( * Specify custom behavior on mousemove event. * * @element ANY - * @param {expression} ngMousemove {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon * mousemove. (Event object is available as `$event`) * * @example @@ -12249,7 +12636,7 @@ forEach( * server and reloading the current page). * * @element form - * @param {expression} ngSubmit {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. * * @example @@ -12434,7 +12821,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' * before the template enters execution mode during bootstrap. * * @element ANY - * @param {expression} ngInit {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngInit {@link guide/expression Expression} to eval. * * @example @@ -12502,7 +12889,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * # Overview * `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 ngPluralize directive + * (see {@link 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. @@ -12521,8 +12908,8 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * 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 - * Angular expression}; these are evaluated on the current scope for its binded value. + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. * * The `when` attribute specifies the mappings between plural categories and the actual * string to be displayed. The value of the attribute should be a JSON object so that Angular @@ -12885,7 +13272,7 @@ var ngRepeatDirective = ngDirective({ * conditionally. * * @element ANY - * @param {expression} ngShow If the {@link guide/dev_guide.expressions expression} is truthy + * @param {expression} ngShow If the {@link guide/expression expression} is truthy * then the element is shown or hidden respectively. * * @example @@ -12925,7 +13312,7 @@ var ngShowDirective = ngDirective(function(scope, element, attr){ * of the HTML conditionally. * * @element ANY - * @param {expression} ngHide If the {@link guide/dev_guide.expressions expression} truthy then + * @param {expression} ngHide If the {@link guide/expression expression} truthy then * the element is shown or hidden respectively. * * @example @@ -12963,7 +13350,7 @@ var ngHideDirective = ngDirective(function(scope, element, attr){ * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. * * @element ANY - * @param {expression} ngStyle {@link guide/dev_guide.expressions Expression} which evals to an + * @param {expression} ngStyle {@link guide/expression Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * @@ -13218,11 +13605,11 @@ var ngTranscludeDirective = ngDirective({ angular.module('ngView', [], function($routeProvider, $locationProvider) { $routeProvider.when('/Book/:bookId', { - template: 'book.html', + templateUrl: 'book.html', controller: BookCntl }); $routeProvider.when('/Book/:bookId/ch/:chapterId', { - template: 'chapter.html', + templateUrl: 'chapter.html', controller: ChapterCntl }); @@ -13281,11 +13668,10 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c restrict: 'ECA', terminal: true, link: function(scope, element, attr) { - var changeCounter = 0, - lastScope, + var lastScope, onloadExp = attr.onload || ''; - scope.$on('$afterRouteChange', update); + scope.$on('$routeChangeSuccess', update); update(); @@ -13296,43 +13682,36 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c } } - function update() { - var template = $route.current && $route.current.template, - thisChangeId = ++changeCounter; + function clearContent() { + element.html(''); + destroyLastScope(); + } - function clearContent() { - // ignore callback if another route change occured since - if (thisChangeId === changeCounter) { - element.html(''); - destroyLastScope(); - } - } + function update() { + var locals = $route.current && $route.current.locals, + template = locals && locals.$template; if (template) { - $http.get(template, {cache: $templateCache}).success(function(response) { - // ignore callback if another route change occured since - if (thisChangeId === changeCounter) { - element.html(response); - destroyLastScope(); + element.html(template); + destroyLastScope(); - var link = $compile(element.contents()), - current = $route.current, - controller; + var link = $compile(element.contents()), + current = $route.current, + controller; - lastScope = current.scope = scope.$new(); - if (current.controller) { - controller = $controller(current.controller, {$scope: lastScope}); - element.contents().data('$ngControllerController', controller); - } + lastScope = current.scope = scope.$new(); + if (current.controller) { + locals.$scope = lastScope; + controller = $controller(current.controller, locals); + element.contents().data('$ngControllerController', controller); + } - link(lastScope); - lastScope.$emit('$viewContentLoaded'); - lastScope.$eval(onloadExp); + link(lastScope); + lastScope.$emit('$viewContentLoaded'); + lastScope.$eval(onloadExp); - // $anchorScroll might listen on event... - $anchorScroll(); - } - }).error(clearContent); + // $anchorScroll might listen on event... + $anchorScroll(); } else { clearContent(); } diff --git a/app/lib/angular/angular.min.js b/app/lib/angular/angular.min.js index cf2b9df..1d9a7f6 100644 --- a/app/lib/angular/angular.min.js +++ b/app/lib/angular/angular.min.js @@ -1,155 +1,156 @@ /* - AngularJS v1.0.0rc10 + AngularJS v1.0.0rc11 (c) 2010-2012 Google, Inc. http://angularjs.org License: MIT */ -(function(V,aa,p){'use strict';function l(b,a,c){var d;if(b)if(P(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==l)b.forEach(a,c);else if(L(b)&&sa(b.length))for(d=0;d=0&&b.splice(c,1);return a}function W(b,a){if(la(b)||b&&b.$evalAsync&&b.$watch)throw B("Can't copy Window or Scope");if(a){if(b=== -a)throw B("Can't copy equivalent objects or arrays");if(M(b)){for(;a.length;)a.pop();for(var c=0;c2?ga.call(arguments,2):[];return P(a)&&!(a instanceof RegExp)?c.length? -function(){return arguments.length?a.apply(b,c.concat(ga.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function cc(b,a){var c=a;/^\$+/.test(b)?c=p:la(a)?c="$WINDOW":a&&aa===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function ba(b,a){return JSON.stringify(b,cc,a?" ":null)}function kb(b){return J(b)?JSON.parse(b):b}function Ta(b){b&&b.length!==0?(b=D(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1; -return b}function wa(b){b=t(b).clone();try{b.html("")}catch(a){}return t("
").append(b).html().match(/^(<[^>]+>)/)[1]}function Ua(b){var a={},c,d;l((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=u(c[1])?decodeURIComponent(c[1]):!0)});return a}function lb(b){var a=[];l(b,function(b,d){a.push(Va(d,!0)+(b===!0?"":"="+Va(b,!0)))});return a.length?a.join("&"):""}function Wa(b){return Va(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Va(b, -a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function dc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,i=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;l(i,function(a){i[a]=!0;c(aa.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(l(b.querySelectorAll("."+a),c),l(b.querySelectorAll("."+a+"\\:"),c),l(b.querySelectorAll("["+a+"]"),c))});l(d,function(a){if(!e){var b= -f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):l(a.attributes,function(b){if(!e&&i[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function mb(b,a){b=t(b);a=a||[];a.unshift("ng");var c=nb(a);c.invoke(["$rootScope","$compile","$injector",function(a,c,g){a.$apply(function(){b.data("$injector",g);c(b)(a)})}]);return c}function Xa(b,a){a=a||"_";return b.replace(ec,function(b,d){return(d?a:"")+b.toLowerCase()})}function ma(b,a,c){if(!b)throw new B("Argument '"+(a||"?")+"' is "+(c||"required")); -return b}function na(b,a,c){c&&M(b)&&(b=b[b.length-1]);ma(P(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function fc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw B("No module: "+d);var b=[],c=[],j=a("$injector","invoke"), -k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:j,run:function(a){c.push(a);return this}};g&&j(g);return k})}})}function ob(b){return b.replace(gc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(hc, -"Moz$1")}function Ya(b,a){function c(){var e;for(var b=[this],c=a,i,f,h,j,k,n,m;b.length;){i=b.shift();f=0;for(h=i.length;f 
"+b;a.removeChild(a.firstChild);Za(this,a.childNodes);this.remove()}else Za(this,b)}function $a(b){return b.cloneNode(!0)}function oa(b){pb(b);for(var a=0,b=b.childNodes||[];a-1}function tb(b,a){a&&l(a.split(" "),function(a){b.className=R((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+R(a)+" "," "))})} -function ub(b,a){a&&l(a.split(" "),function(a){if(!za(b,a))b.className=R(b.className+" "+R(a))})}function Za(b,a){if(a)for(var a=!a.nodeName&&u(a.length)&&!la(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),X.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+X.length+" > 20 )")}else{if(h.cookie!==x){x=h.cookie;d=x.split("; ");X={};for(f=0;f0&&(X[unescape(e.substring(0,j))]=unescape(e.substring(j+1)))}return X}};f.defer=function(a,b){var c; -o++;c=n(function(){delete s[c];e(a)},b||0);s[c]=!0;return c};f.defer.cancel=function(a){return s[a]?(delete s[a],m(a),e(r),!0):!1}}function rc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new qc(b,d,a,c)}]}function sc(){this.$get=function(){function b(b,d){function e(a){if(a!=n){if(m){if(m==a)m=a.n}else m=a;g(a.n,a.p);g(a,n);n=a;n.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw B("cacheId "+b+" taken");var i=0,f=C({},d,{id:b}),h={},j=d&&d.capacity|| -Number.MAX_VALUE,k={},n=null,m=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);v(b)||(a in h||i++,h[a]=b,i>j&&this.remove(m.key))},get:function(a){var b=k[a];if(b)return e(b),h[a]},remove:function(a){var b=k[a];if(b==n)n=b.p;if(b==m)m=b.n;g(b.n,b.p);delete k[a];delete h[a];i--},removeAll:function(){h={};i=0;k={};n=m=null},destroy:function(){k=f=h=null;delete a[b]},info:function(){return C({},f,{size:i})}}}var a={};b.info=function(){var b={};l(a,function(a,e){b[e]=a.info()});return b}; -b.get=function(b){return a[b]};return b}}function tc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function yb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ";this.directive=function f(d,e){J(d)?(ma(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];l(a[d],function(a){try{var f=b.invoke(a);if(P(f))f={compile:A(f)}; -else if(!f.compile&&f.link)f.compile=A(f.link);f.priority=f.priority||0;f.name=f.name||d;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(j){c(j)}});return e}])),a[d].push(e)):l(d,jb(f));return this};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller",function(b,h,j,k,n,m,s){function o(a,b,c){a instanceof t||(a=t(a));l(a,function(b,c){b.nodeType==3&&(a[c]=t(b).wrap("").parent()[0])});var d=w(a,b,a,c);return function(b, -c){ma(b,"scope");var e=c?qa.clone.call(a):a;e.data("$scope",b);q(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function O(a,b){throw B("Unsupported '"+b+"' for '"+a+"'.");}function q(a,b){try{a.addClass(b)}catch(c){}}function w(a,b,c,d){function e(a,c,d,j){for(var g,m,h,k,n,S=0,s=0,o=f.length;Sy.priority)break;if(D=y.scope)K("isolated scope",H,y,F),L(D)&&(q(F,"ng-isolate-scope"),H=y),q(F,"ng-scope"), -x=x||y;Y=y.name;if(D=y.controller)A=A||{},K("'"+Y+"' controller",A[Y],y,F),A[Y]=y;if(D=y.transclude)K("transclusion",C,y,F),C=y,k=y.priority,D=="element"?(r=t(b),F=c.$$element=t("<\!-- "+Y+": "+c[Y]+" --\>"),b=F[0],Da(e,t(r[0]),b),u=o(r,d,k)):(r=t($a(b)).contents(),F.html(""),u=o(r,d));if(D=y.template)if(K("template",z,y,F),z=y,r=t("
"+R(D)+"
").contents(),b=r[0],y.replace){if(r.length!=1||b.nodeType!==1)throw new B(g+D);Da(e,F,b);Y={$attr:{}};a=a.concat(G(b,a.splice(I+1,a.length-(I+1)), -Y));T(c,Y);N=a.length}else F.html(D);if(y.templateUrl)K("template",z,y,F),z=y,h=X(a.splice(I,a.length-I),h,F,c,e,y.replace,u),N=a.length;else if(y.compile)try{E=y.compile(F,c,u),P(E)?f(null,E):E&&f(E.pre,E.post)}catch(Q){j(Q,wa(F))}if(y.terminal)h.terminal=!0,k=Math.max(k,y.priority)}h.scope=x&&x.scope;h.transclude=C&&u;return h}function z(d,e,g,h){var m=!1;if(a.hasOwnProperty(e))for(var k,e=b.get(e+c),n=0,s=e.length;nk.priority)&&k.restrict.indexOf(g)!=-1)d.push(k), -m=!0}catch(q){j(q)}return m}function T(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;l(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});l(b,function(b,f){f=="class"?q(e,b):f=="style"?e.attr("style",e.attr("style")+";"+b):f.charAt(0)!="$"&&!a.hasOwnProperty(f)&&(a[f]=b,d[f]=c[f])})}function X(a,b,c,d,e,f,j){var h=[],m,s,q=c[0],o=a.shift(),O=C({},o,{controller:null,templateUrl:null,transclude:null});c.html("");k.get(o.templateUrl,{cache:n}).success(function(k){var n, -o;if(f){o=t("
"+R(k)+"
").contents();n=o[0];if(o.length!=1||n.nodeType!==1)throw new B(g+k);k={$attr:{}};Da(e,c,n);G(n,a,k);T(d,k)}else n=q,c.html(k);a.unshift(O);m=H(a,c,d,j);for(s=w(c.contents(),j);h.length;){var l=h.pop(),k=h.pop();o=h.pop();var x=h.pop(),K=n;o!==q&&(K=$a(n),Da(k,t(o),K));m(function(){b(s,x,K,e,l)},x,K,e,l)}h=null}).error(function(a,b,c,d){throw B("Failed to load template: "+d.url);});return function(a,c,d,e,f){h?(h.push(c),h.push(d),h.push(e),h.push(f)):m(function(){b(s, -c,d,e,f)},c,d,e,f)}}function x(a,b){return b.priority-a.priority}function K(a,b,c,d){if(b)throw B("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+wa(d));}function F(a,b){var c=h(b,!0);c&&a.push({priority:0,compile:A(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function Y(a,b,c,d){var e=h(c,!0);e&&b.push({priority:100,compile:A(function(a,b,c){d==="class"&&(e=h(c[d],!0));c.$$observers[d]= -[];c[d]=p;a.$watch(e,function(a){c.$set(d,a)})})})}function Da(a,b,c){var d=b[0],e=d.parentNode,f,g;if(a){f=0;for(g=a.length;f0){var e=K[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=i(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),K.shift(),b):!1}function h(a){f(a)||e("is unexpected, expecting ["+a+"]",i())}function j(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function n(){for(var a=[];;)if(K.length>0&&!i("}",")",";","]")&&a.push(u()),!f(";"))return a.length==1?a[0]:function(b, -c){for(var d,e=0;e","<=",">="))a=k(a,b.fn,q());return a}function w(){for(var a=G(),b;b=f("*","/","%");)a=k(a,b.fn,G());return a}function G(){var a;return f("+")?H():(a=f("-"))?k(X,a.fn,G()):(a=f("!"))?j(a.fn,G()):H()}function H(){var a;if(f("("))a=u(),h(")");else if(f("["))a=z();else if(f("{"))a=T();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=t(a,c),c=null):b.text==="["?(c=a,a=fa(a)):b.text==="."?(c=a,a=v(a)):e("IMPOSSIBLE"); -return a}function z(){var a=[];if(g().text!="]"){do a.push(F());while(f(","))}h("]");return function(b,c){for(var d=[],e=0;e1;d++){var e=a.shift(),g= -b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function bb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,i=0;i7),hasEvent:function(c){if(c=="input"&&$==9)return!1;if(v(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Rc(){this.$get=A(V)}function Hb(b){var a={},c,d,e;if(!b)return a;l(b.split("\n"),function(b){e=b.indexOf(":");c=D(R(b.substr(0,e)));d=R(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Ib(b){var a=L(b)?b:p;return function(c){a||(a=Hb(b));return c?a[D(c)]||null:a}}function Jb(b,a,c){if(P(c))return c(b,a);l(c, -function(c){b=c(b,a)});return b}function Sc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){J(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=kb(d,!0)));return d}],transformRequest:[function(a){return L(a)&&Pa.apply(a)!=="[object File]"?ba(a):a}],headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json"},put:{"Content-Type":"application/json"}}},e=this.responseInterceptors= -[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,h,j,k){function n(a){function c(a){var b=C({},a,{data:Jb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:j.reject(b)}a.method=ja(a.method);var e=a.transformRequest||d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=C({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[D(a.method)],a.headers),e=Jb(a.data,Ib(g),e),h;v(a.data)&&delete g["Content-Type"];h=m(a,e,g); -h=h.then(c,c);l(O,function(a){h=a(h)});h.success=function(b){h.then(function(c){b(c.data,c.status,c.headers,a)});return h};h.error=function(b){h.then(null,function(c){b(c.data,c.status,c.headers,a)});return h};return h}function m(b,c,d){function e(a,b,c){l&&(200<=a&&a<300?l.put(O,[a,b,Hb(c)]):l.remove(O));f(b,a,c);h.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?k.resolve:k.reject)({data:a,status:c,headers:Ib(d),config:b})}function i(){var a=Ra(n.pendingRequests,b);a!==-1&&n.pendingRequests.splice(a, -1)}var k=j.defer(),m=k.promise,l,p,O=s(b.url,b.params);n.pendingRequests.push(b);m.then(i,i);b.cache&&b.method=="GET"&&(l=L(b.cache)?b.cache:o);if(l)if(p=l.get(O))if(p.then)return p.then(i,i),p;else M(p)?f(p[1],p[0],W(p[2])):f(p,200,{});else l.put(O,m);p||a(b.method,O,c,e,d,b.timeout,b.withCredentials);return m}function s(a,b){if(!b)return a;var c=[];Zb(b,function(a,b){a==null||a==p||(L(a)&&(a=ba(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+ -c.join("&")}var o=c("$http"),O=[];l(e,function(a){O.push(J(a)?k.get(a):k.invoke(a))});n.pendingRequests=[];(function(a){l(arguments,function(a){n[a]=function(b,c){return n(C(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){l(arguments,function(a){n[a]=function(b,c,d){return n(C(d||{},{method:a,url:b,data:c}))}})})("post","put");n.defaults=d;return n}]}function Tc(){this.$get=["$browser","$window","$document",function(b,a,c){return Uc(b,Vc,b.defer,a.angular.callbacks,c[0], -a.location.protocol.replace(":",""))}]}function Uc(b,a,c,d,e,g){function i(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;$?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=d;e.body.appendChild(c)}return function(e,h,j,k,n,m,s){function o(a,c,d,e){c=(h.match(Bb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(r)}b.$$incOutstandingRequestCount();h=h||b.url(); -if(D(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};i(h.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?o(k,200,d[p].data):o(k,-2);delete d[p]})}else{var q=new a;q.open(e,h,!0);l(n,function(a,b){a&&q.setRequestHeader(b,a)});var w;q.onreadystatechange=function(){q.readyState==4&&o(k,w||q.status,q.responseText,q.getAllResponseHeaders())};if(s)q.withCredentials=!0;q.send(j||"");m>0&&c(function(){w=-1;q.abort()},m)}}}function Wc(){this.$get=function(){return{id:"en-us", -NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","), -SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function Xc(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,h){var j=c.defer(),k=j.promise,n=u(h)&&!h,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a), -d(a)}n||b.$apply()},f),h=function(){delete g[k.$$timeoutId]};k.$$timeoutId=f;g[f]=j;k.then(h,h);return k}var g={};e.cancel=function(b){return b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Kb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Lb);a("date",Mb);a("filter",Yc);a("json",Zc);a("limitTo",$c);a("lowercase",ad);a("number", -Nb);a("orderBy",Ob);a("uppercase",bd)}function Yc(){return function(b,a){if(!(b instanceof Array))return b;var c=[];c.check=function(a){for(var b=0;b-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;c=k+n)for(var j=i.length-k,m=0;m0||e>-c)e+=c;e===0&&c==-12&&(e= -12);return fb(e,a,d)}}function Ha(b,a){return function(c,d){var e=c["get"+b](),g=ja(a?"SHORT"+b:b);return d[g][e]}}function Mb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,i=0;b[9]&&(g=E(b[9]+b[10]),i=E(b[9]+b[11]));a.setUTCFullYear(E(b[1]),E(b[2])-1,E(b[3]));a.setUTCHours(E(b[4]||0)-g,E(b[5]||0)-i,E(b[6]||0),E(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;return function(c,e){var g="",i=[],f,h,e=e|| -"mediumDate",e=b.DATETIME_FORMATS[e]||e;J(c)&&(c=cd.test(c)?E(c):a(c));sa(c)&&(c=new Date(c));if(!ka(c))return c;for(;e;)(h=dd.exec(e))?(i=i.concat(ga.call(h,1)),e=i.pop()):(i.push(e),e=null);l(i,function(a){f=ed[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Zc(){return function(b){return ba(b,!0)}}function $c(){return function(b,a){if(!(b instanceof Array))return b;var a=E(a),c=[],d,e;if(!b||!(b instanceof Array))return c;a>b.length?a=b.length:a< --b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dn?(d.$setValidity("maxlength",!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function gb(b,a){b="ngClass"+b;return Q(function(c,d,e){c.$watch(e[b],function(b,e){if(a===!0||c.$index%2===a)e&&b!==e&&(L(e)&&!M(e)&&(e=Qa(e,function(a,b){if(a)return b})),d.removeClass(M(e)?e.join(" "):e)),L(b)&&!M(b)&&(b=Qa(b, -function(a,b){if(a)return b})),b&&d.addClass(M(b)?b.join(" "):b)},!0)})}var D=function(b){return J(b)?b.toLowerCase():b},ja=function(b){return J(b)?b.toUpperCase():b},B=V.Error,$=E((/msie (\d+)/.exec(D(navigator.userAgent))||[])[1]),t,ha,ga=[].slice,Na=[].push,Pa=Object.prototype.toString,Tb=V.angular||(V.angular={}),pa,zb,Z=["0","0","0"];r.$inject=[];ua.$inject=[];zb=$<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?ja(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName? -b.nodeName:b[0].nodeName};var ec=/[A-Z]/g,fd={full:"1.0.0rc10",major:1,minor:0,dot:0,codeName:"tesseract-giftwrapping"},ya=N.cache={},xa=N.expando="ng-"+(new Date).getTime(),ic=1,gd=V.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},rb=V.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},gc=/([\:\-\_]+(.))/g,hc=/^moz([A-Z])/,qa=N.prototype={ready:function(b){function a(){c|| -(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);N(V).bind("load",a)},toString:function(){var b=[];l(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?t(this[b]):t(this[this.length+b])},length:0,push:Na,sort:[].sort,splice:[].splice},Ba={};l("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ba[D(b)]=b});var xb={};l("input,select,option,textarea,button,form".split(","),function(b){xb[ja(b)]=!0});l({data:sb,inheritedData:Aa,scope:function(b){return Aa(b, -"$scope")},controller:vb,injector:function(b){return Aa(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:za,css:function(b,a,c){a=ob(a);if(u(c))b.style[a]=c;else{var d;$<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];$<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=D(a);if(Ba[d])if(u(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||r).specified?d:p;else if(u(c))b.setAttribute(a, -c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(u(c))b[a]=c;else return b[a]},text:C($<9?function(b,a){if(b.nodeType==1){if(v(a))return b.innerText;b.innerText=a}else{if(v(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(v(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(v(a))return b.value;b.value=a},html:function(b,a){if(v(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a, -c,d){return!d(a,c)}},Ic={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},eb={},Vc=V.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw new B("This browser does not support XMLHttpRequest.");};Kb.$inject=["$provide"];Lb.$inject=["$locale"];Nb.$inject=["$locale"];var Qb=".",ed={yyyy:I("FullYear",4),yy:I("FullYear",2,0,!0),y:I("FullYear", -1),MMMM:Ha("Month"),MMM:Ha("Month",!0),MM:I("Month",2,1),M:I("Month",1,1),dd:I("Date",2),d:I("Date",1),HH:I("Hours",2),H:I("Hours",1),hh:I("Hours",2,-12),h:I("Hours",1,-12),mm:I("Minutes",2),m:I("Minutes",1),ss:I("Seconds",2),s:I("Seconds",1),EEEE:Ha("Day"),EEE:Ha("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=a.getTimezoneOffset();return fb(a/60,2)+fb(Math.abs(a%60),2)}},dd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,cd= -/^\d+$/;Mb.$inject=["$locale"];var ad=A(D),bd=A(ja);Ob.$inject=["$parse"];var hd=A({restrict:"E",compile:function(a,c){c.href||c.$set("href","");return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),hb={};l(Ba,function(a,c){var d=da("ng-"+c);hb[d]=function(){return{priority:100,compile:function(){return function(a,g,i){i.$$observers[c]=[];a.$watch(i[d],function(a){i.$set(c,!!a)})}}}}});l(["src","href"],function(a){var c=da("ng-"+a);hb[c]=function(){return{priority:99, -compile:function(){return function(d,e,g){d=g[c];d==p?(g.$$observers[a]=[],g.$observe(c,function(c){g.$set(a,c);$&&e.prop(a,c)})):(g.$set(a,d),e.prop(a,d))}}}}});var Ka={$addControl:r,$removeControl:r,$setValidity:r,$setDirty:r};Rb.$inject=["$element","$attrs","$scope"];var Na={name:"form",restrict:"E",controller:Rb,compile:function(){return{pre:function(a,c,d,e){d.action||c.bind("submit",function(a){a.preventDefault()});var g=c.parent().controller("form"),i=d.name||d.ngForm;i&&(a[i]=e);g&&c.bind("$destroy", -function(){g.$removeControl(e);i&&(a[i]=p);C(e,Ka)})}}}},id=A(Na),jd=A(C(W(Na),{restrict:"EAC"})),kd=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,ld=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,md=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Vb={text:Ma,number:function(a,c,d,e,g,i){Ma(a,c,d,e,g,i);e.$parsers.push(function(a){var c=U(a);return c||md.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),p)});e.$formatters.push(function(a){return U(a)? -"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!U(a)&&ah?(e.$setValidity("max",!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return U(a)||sa(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),p)})},url:function(a,c,d,e,g,i){Ma(a,c,d,e,g,i);a= -function(a){return U(a)||kd.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,i){Ma(a,c,d,e,g,i);a=function(a){return U(a)||ld.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){v(d.name)&&c.attr("name",ta());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked= -d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,i=d.ngFalseValue;J(g)||(g=!0);J(i)||(i=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:i})},hidden:r,button:r,submit:r,reset:r},Wb=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,i){i&& -(Vb[D(g.type)]||Vb.text)(d,e,g,i,c,a)}}}],Ja="ng-valid",Ia="ng-invalid",La="ng-pristine",Sb="ng-dirty",nd=["$scope","$exceptionHandler","$attrs","ngModel","$element",function(a,c,d,e,g){function i(a,c){c=c?"-"+Xa(c,"-"):"";g.removeClass((a?Ia:Ja)+c).addClass((a?Ja:Ia)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$render=r;this.$name=d.name;var f=g.inheritedData("$formController")|| -Ka,h=0,j=this.$error={};g.addClass(La);i(!0);this.$setValidity=function(a,c){if(j[a]!==!c){if(c){if(j[a]&&h--,!h)i(!0),this.$valid=!0,this.$invalid=!1}else i(!1),this.$invalid=!0,this.$valid=!1,h++;j[a]=!c;i(c,a);f.$setValidity(a,c,this)}};this.$setViewValue=function(a){this.$viewValue=a;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,g.removeClass(La).addClass(Sb),f.$setDirty();l(this.$parsers,function(c){a=c(a)});if(this.$modelValue!==a)this.$modelValue=a,e(a),l(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})}; -var k=this;a.$watch(function(){return e()},function(a){if(k.$modelValue!==a){var c=k.$formatters,d=c.length;for(k.$modelValue=a;d--;)a=c[d](a);if(k.$viewValue!==a)k.$viewValue=a,k.$render()}})}],od=[function(){return{inject:{ngModel:"accessor"},require:["ngModel","^?form"],controller:nd,link:function(a,c,d,e){var g=e[0],i=e[1]||Ka;i.$addControl(g);c.bind("$destroy",function(){i.$removeControl(g)})}}}],pd=A({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}), -Xb=[function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){var g=function(a){if(d.required&&(U(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}}],qd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",",i=function(a){var c=[];a&&l(a.split(g),function(a){a&&c.push(R(a))});return c};e.$parsers.push(i); -e.$formatters.push(function(a){return M(a)&&!ea(i(e.$viewValue),a)?a.join(", "):p})}}},rd=/^(true|false|\d+)$/,sd=[function(){return{priority:100,compile:function(a,c){return rd.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){g.$$observers.value=[];a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}}],td=Q(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),ud=["$interpolate",function(a){return function(c, -d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],vd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],wd=gb("",!0),xd=gb("Odd",0),yd=gb("Even",1),zd=Q({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),Ad=[function(){return{scope:!0,controller:"@"}}],Bd=["$sniffer",function(a){return{priority:1E3, -compile:function(){a.csp=!0}}}],Yb={};l("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=da("ng-"+a);Yb[c]=["$parse",function(d){return function(e,g,i){var f=d(i[c]);g.bind(D(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Cd=Q(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Dd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g, -i){var f=i.ngInclude||i.src,h=i.onload||"",j=i.autoscroll;return function(g,i){var m=0,l,o=function(){l&&(l.$destroy(),l=null);i.html("")};g.$watch(f,function(f){var q=++m;f?a.get(f,{cache:c}).success(function(a){q===m&&(l&&l.$destroy(),l=g.$new(),i.html(a),e(i.contents())(l),u(j)&&(!j||g.$eval(j))&&d(),l.$emit("$includeContentLoaded"),g.$eval(h))}).error(function(){q===m&&o()}):o()})}}}}],Ed=Q({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Fd=Q({terminal:!0,priority:1E3}), -Gd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,i){var f=i.count,h=g.attr(i.$attr.when),j=i.offset||0,k=e.$eval(h),n={};l(k,function(a,e){n[e]=c(a.replace(d,"{{"+f+"-"+j+"}}"))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(k[c]||(c=a.pluralCat(c-j)),n[c](e,g,!0))},function(a){g.text(a)})}}}],Hd=Q({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,i){var f=i.ngRepeat,i=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/), -h,j,k;if(!i)throw B("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=i[1];h=i[2];i=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!i)throw B("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");j=i[3]||i[1];k=i[2];var n=new ab;a.$watch(function(a){var e,f,i=a.$eval(h),l=ac(i,!0),p,t=new ab,r,z,v,u,x=c;if(M(i))v=i||[];else{v=[];for(r in i)i.hasOwnProperty(r)&&r.charAt(0)!="$"&&v.push(r);v.sort()}e=0;for(f=v.length;ex;)u.pop().element.remove()}for(;v.length>y;)v.pop()[0].element.remove()}var h;if(!(h=r.match(d)))throw B("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+r+"'.");var j=c(h[2]||h[1]),k=h[4]||h[6],l=h[5],m=c(h[3]||""),n=c(h[2]?h[1]:k),s=c(h[7]),v=[[{element:f,label:""}]];q&&(a(q)(e),q.removeClass("ng-scope"),q.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a, -c=s(e)||[],d={},h,i,j,m,q,r;if(o){i=[];m=0;for(r=v.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}'); +(function(T,$,p){'use strict';function m(b,a,c){var d;if(b)if(M(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==m)b.forEach(a,c);else if(J(b)&&ua(b.length))for(d=0;d=0&&b.splice(c,1);return a}function U(b,a){if(ma(b)||b&&b.$evalAsync&&b.$watch)throw w("Can't copy Window or Scope");if(a){if(b=== +a)throw w("Can't copy equivalent objects or arrays");if(K(b)){for(;a.length;)a.pop();for(var c=0;c2?ga.call(arguments,2):[];return M(a)&&!(a instanceof RegExp)?c.length? +function(){return arguments.length?a.apply(b,c.concat(ga.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function gc(b,a){var c=a;/^\$+/.test(b)?c=p:ma(a)?c="$WINDOW":a&&$===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function aa(b,a){return JSON.stringify(b,gc,a?" ":null)}function mb(b){return G(b)?JSON.parse(b):b}function Va(b){b&&b.length!==0?(b=E(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b} +function na(b){b=u(b).clone();try{b.html("")}catch(a){}return u("
").append(b).html().match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+E(b)})}function Wa(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=s(c[1])?decodeURIComponent(c[1]):!0)});return a}function nb(b){var a=[];m(b,function(b,d){a.push(Xa(d,!0)+(b===!0?"":"="+Xa(b,!0)))});return a.length?a.join("&"):""}function Ya(b){return Xa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi, +"=").replace(/%2B/gi,"+")}function Xa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function hc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(h,function(a){h[a]=!0;c($.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(m(b.querySelectorAll("."+a),c),m(b.querySelectorAll("."+a+"\\:"),c),m(b.querySelectorAll("["+ +a+"]"),c))});m(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):m(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function ob(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=pb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,h){a.$apply(function(){b.data("$injector",h);c(b)(a)})}]);return c}function Za(b,a){a=a||"_";return b.replace(ic, +function(b,d){return(d?a:"")+b.toLowerCase()})}function oa(b,a,c){if(!b)throw new w("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function pa(b,a,c){c&&K(b)&&(b=b[b.length-1]);oa(M(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function jc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c, +d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw w("No module: "+d);var b=[],c=[],j=a("$injector","invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:j,run:function(a){c.push(a); +return this}};g&&j(g);return k})}})}function qb(b){return b.replace(kc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(lc,"Moz$1")}function $a(b,a){function c(){var e;for(var b=[this],c=a,h,f,i,j,k,l,n;b.length;){h=b.shift();f=0;for(i=h.length;f 
"+b;a.removeChild(a.firstChild);ab(this,a.childNodes);this.remove()}else ab(this,b)}function bb(b){return b.cloneNode(!0)}function qa(b){rb(b);for(var a=0,b=b.childNodes||[];a +-1}function vb(b,a){a&&m(a.split(" "),function(a){b.className=Q((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+Q(a)+" "," "))})}function wb(b,a){a&&m(a.split(" "),function(a){if(!Aa(b,a))b.className=Q(b.className+" "+Q(a))})}function ab(b,a){if(a)for(var a=!a.nodeName&&s(a.length)&&!ma(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),V.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+ +V.length+" > 20 )")}else{if(i.cookie!==H){H=i.cookie;d=H.split("; ");V={};for(f=0;f0&&(V[unescape(e.substring(0,j))]=unescape(e.substring(j+1)))}return V}};f.defer=function(a,b){var c;o++;c=l(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};f.defer.cancel=function(a){return r[a]?(delete r[a],n(a),e(A),!0):!1}}function uc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new tc(b,d,a,c)}]}function vc(){this.$get=function(){function b(b, +d){function e(a){if(a!=l){if(n){if(n==a)n=a.n}else n=a;g(a.n,a.p);g(a,l);l=a;l.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw w("cacheId "+b+" taken");var h=0,f=D({},d,{id:b}),i={},j=d&&d.capacity||Number.MAX_VALUE,k={},l=null,n=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);v(b)||(a in i||h++,i[a]=b,h>j&&this.remove(n.key))},get:function(a){var b=k[a];if(b)return e(b),i[a]},remove:function(a){var b=k[a];if(b==l)l=b.p;if(b==n)n=b.n;g(b.n,b.p);delete k[a]; +delete i[a];h--},removeAll:function(){i={};h=0;k={};l=n=null},destroy:function(){k=f=i=null;delete a[b]},info:function(){return D({},f,{size:h})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function wc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Bb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: "; +this.directive=function f(d,e){G(d)?(oa(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d],function(a){try{var f=b.invoke(a);if(M(f))f={compile:B(f)};else if(!f.compile&&f.link)f.compile=B(f.link);f.priority=f.priority||0;f.name=f.name||d;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(j){c(j)}});return e}])),a[d].push(e)):m(d,lb(f));return this};this.$get=["$injector","$interpolate","$exceptionHandler", +"$http","$templateCache","$parse","$controller","$rootScope",function(b,i,j,k,l,n,r,o){function x(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&(a[c]=u(b).wrap("").parent()[0])});var d=t(a,b,a,c);return function(b,c){oa(b,"scope");var e=c?sa.clone.call(a):a;e.data("$scope",b);q(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function q(a,b){try{a.addClass(b)}catch(c){}}function t(a,b,c,d){function e(a,c,d,j){for(var g,i,n,k,o,r=0,l=0,q=f.length;rC.priority)break;if(B=C.scope)N("isolated scope",y,C,F),J(B)&&(q(F,"ng-isolate-scope"),y=C),q(F,"ng-scope"),z=z||C;W=C.name;if(B=C.controller)s=s||{},N("'"+W+"' controller",s[W],C,F),s[W]=C;if(B=C.transclude)N("transclusion",A,C,F),A=C,o=C.priority,B=="element"?(Z=u(b),F=c.$$element=u("<\!-- "+W+": "+c[W]+" --\>"),b=F[0],Ea(e,u(Z[0]),b),v=x(Z,d,o)):(Z=u(bb(b)).contents(),F.html(""),v=x(Z,d));if(B=C.template)if(N("template",H,C,F),H=C,Z=u("
"+Q(B)+"
").contents(),b=Z[0],C.replace){if(Z.length!= +1||b.nodeType!==1)throw new w(g+B);Ea(e,F,b);W={$attr:{}};a=a.concat(fa(b,a.splice(E+1,a.length-(E+1)),W));L(c,W);I=a.length}else F.html(B);if(C.templateUrl)N("template",H,C,F),H=C,k=V(a.splice(E,a.length-E),k,F,c,e,C.replace,v),I=a.length;else if(C.compile)try{D=C.compile(F,c,v),M(D)?f(null,D):D&&f(D.pre,D.post)}catch(O){j(O,na(F))}if(C.terminal)k.terminal=!0,o=Math.max(o,C.priority)}k.scope=z&&z.scope;k.transclude=A&&v;return k}function y(d,e,g,i){var n=!1;if(a.hasOwnProperty(e))for(var k,e=b.get(e+ +c),o=0,l=e.length;ok.priority)&&k.restrict.indexOf(g)!=-1)d.push(k),n=!0}catch(r){j(r)}return n}function L(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b,function(b,f){f=="class"?(q(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):f=="style"?e.attr("style",e.attr("style")+";"+b):f.charAt(0)!="$"&&!a.hasOwnProperty(f)&&(a[f]=b,d[f]=c[f])})}function V(a,b,c,d,e,f,j){var i= +[],n,o,r=c[0],q=a.shift(),x=D({},q,{controller:null,templateUrl:null,transclude:null});c.html("");k.get(q.templateUrl,{cache:l}).success(function(k){var l,q;if(f){q=u("
"+Q(k)+"
").contents();l=q[0];if(q.length!=1||l.nodeType!==1)throw new w(g+k);k={$attr:{}};Ea(e,c,l);fa(l,a,k);L(d,k)}else l=r,c.html(k);a.unshift(x);n=z(a,c,d,j);for(o=t(c.contents(),j);i.length;){var m=i.pop(),k=i.pop();q=i.pop();var y=i.pop(),H=l;q!==r&&(H=bb(l),Ea(k,u(q),H));n(function(){b(o,y,H,e,m)},y,H,e,m)}i=null}).error(function(a, +b,c,d){throw w("Failed to load template: "+d.url);});return function(a,c,d,e,f){i?(i.push(c),i.push(d),i.push(e),i.push(f)):n(function(){b(o,c,d,e,f)},c,d,e,f)}}function H(a,b){return b.priority-a.priority}function N(a,b,c,d){if(b)throw w("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+na(d));}function F(a,b){var c=i(b,!0);c&&a.push({priority:0,compile:B(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue= +a})})})}function W(a,b,c,d){var e=i(c,!0);e&&b.push({priority:100,compile:B(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=i(c[d],!0));c[d]=p;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function Ea(a,b,c){var d=b[0],e=d.parentNode,f,g;if(a){f=0;for(g=a.length;f0){var e=N[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),N.shift(),b):!1}function i(a){f(a)||e("is unexpected, expecting ["+a+"]",h())}function j(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function l(){for(var a=[];;)if(N.length>0&&!h("}",")",";","]")&& +a.push(v()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e","<=",">="))a=k(a,b.fn,q());return a}function t(){for(var a=m(),b;b=f("*","/","%");)a=k(a,b.fn,m());return a}function m(){var a;return f("+")?z():(a=f("-"))?k(V,a.fn,m()):(a=f("!"))?j(a.fn,m()):z()}function z(){var a;if(f("("))a=v(),i(")");else if(f("["))a=y();else if(f("{"))a=L();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=u(a,c),c=null):b.text=== +"["?(c=a,a=ca(a)):b.text==="."?(c=a,a=s(a)):e("IMPOSSIBLE");return a}function y(){var a=[];if(g().text!="]"){do a.push(F());while(f(","))}i("]");return function(b,c){for(var d=[],e=0;e1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function db(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h7),hasEvent:function(c){if(c=="input"&&Y==9)return!1;if(v(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Tc(){this.$get=B(T)}function Lb(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=E(Q(b.substr(0,e)));d=Q(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Mb(b){var a=J(b)?b:p;return function(c){a|| +(a=Lb(b));return c?a[E(c)]||null:a}}function Nb(b,a,c){if(M(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Uc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){G(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=mb(d,!0)));return d}],transformRequest:[function(a){return J(a)&&Ra.apply(a)!=="[object File]"?aa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"}, +put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,i,j,k){function l(a){function c(a){var b=D({},a,{data:Nb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:j.reject(b)}a.method=ka(a.method);var e=a.transformRequest||d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=D({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[E(a.method)], +a.headers),e=Nb(a.data,Mb(g),e),i;v(a.data)&&delete g["Content-Type"];i=n(a,e,g);i=i.then(c,c);m(x,function(a){i=a(i)});i.success=function(b){i.then(function(c){b(c.data,c.status,c.headers,a)});return i};i.error=function(b){i.then(null,function(c){b(c.data,c.status,c.headers,a)});return i};return i}function n(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(x,[a,b,Lb(c)]):m.remove(x));f(b,a,c);i.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?n.resolve:n.reject)({data:a,status:c,headers:Mb(d), +config:b})}function h(){var a=Ta(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var n=j.defer(),k=n.promise,m,p,x=r(b.url,b.params);l.pendingRequests.push(b);k.then(h,h);b.cache&&b.method=="GET"&&(m=J(b.cache)?b.cache:o);if(m)if(p=m.get(x))if(p.then)return p.then(h,h),p;else K(p)?f(p[1],p[0],U(p[2])):f(p,200,{});else m.put(x,k);p||a(b.method,x,c,e,d,b.timeout,b.withCredentials);return k}function r(a,b){if(!b)return a;var c=[];cc(b,function(a,b){a==null||a==p||(J(a)&&(a=aa(a)),c.push(encodeURIComponent(b)+ +"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var o=c("$http"),x=[];m(e,function(a){x.push(G(a)?k.get(a):k.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(D(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]=function(b,c,d){return l(D(d||{},{method:a,url:b,data:c}))}})})("post","put");l.defaults=d;return l}]}function Vc(){this.$get=["$browser","$window","$document", +function(b,a,c){return Wc(b,Xc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Wc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Y?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=d;e.body.appendChild(c)}return function(e,i,j,k,l,n,r){function o(a,c,d,e){c=(i.match(Fb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(A)} +b.$$incOutstandingRequestCount();i=i||b.url();if(E(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};h(i.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?o(k,200,d[p].data):o(k,-2);delete d[p]})}else{var q=new a;q.open(e,i,!0);m(l,function(a,b){a&&q.setRequestHeader(b,a)});var t;q.onreadystatechange=function(){q.readyState==4&&o(k,t||q.status,q.responseText,q.getAllResponseHeaders())};if(r)q.withCredentials=!0;q.send(j||"");n>0&&c(function(){t=-1; +q.abort()},n)}}}function Yc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","), +DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function Zc(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,i){var j=c.defer(), +k=j.promise,l=s(i)&&!i,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}l||b.$apply()},f),i=function(){delete g[k.$$timeoutId]};k.$$timeoutId=f;g[f]=j;k.then(i,i);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Ob(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency", +Pb);a("date",Qb);a("filter",$c);a("json",ad);a("limitTo",bd);a("lowercase",cd);a("number",Rb);a("orderBy",Sb);a("uppercase",dd)}function $c(){return function(b,a){if(!(b instanceof Array))return b;var c=[];c.check=function(a){for(var b=0;b-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&& +d(a[c],b))return!0;return!1;case "array":for(c=0;c=k+l)for(var j=h.length-k,n=0;n0||e>-c)e+=c;e===0&&c==-12&&(e=12);return hb(e,a,d)}}function Ja(b,a){return function(c,d){var e=c["get"+b](),g=ka(a?"SHORT"+b:b);return d[g][e]}}function Qb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=I(b[9]+b[10]),h=I(b[9]+b[11]));a.setUTCFullYear(I(b[1]),I(b[2])-1,I(b[3]));a.setUTCHours(I(b[4]||0)-g,I(b[5]||0)-h,I(b[6]||0),I(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; +return function(c,e){var g="",h=[],f,i,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;G(c)&&(c=ed.test(c)?I(c):a(c));ua(c)&&(c=new Date(c));if(!la(c))return c;for(;e;)(i=fd.exec(e))?(h=h.concat(ga.call(i,1)),e=h.pop()):(h.push(e),e=null);m(h,function(a){f=gd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ad(){return function(b){return aa(b,!0)}}function bd(){return function(b,a){if(!(b instanceof Array))return b;var a=I(a),c=[],d,e;if(!b||!(b instanceof +Array))return c;a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dl?(d.$setValidity("maxlength",!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function ib(b,a){b="ngClass"+b;return R(function(c,d,e){c.$watch(e[b],function(b,e){if(a===!0||c.$index%2===a)e&&b!==e&&(J(e)&&!K(e)&&(e=Sa(e,function(a,b){if(a)return b})),d.removeClass(K(e)? +e.join(" "):e)),J(b)&&!K(b)&&(b=Sa(b,function(a,b){if(a)return b})),b&&d.addClass(K(b)?b.join(" "):b)},!0)})}var E=function(b){return G(b)?b.toLowerCase():b},ka=function(b){return G(b)?b.toUpperCase():b},w=T.Error,Y=I((/msie (\d+)/.exec(E(navigator.userAgent))||[])[1]),u,ha,ga=[].slice,Pa=[].push,Ra=Object.prototype.toString,Xb=T.angular||(T.angular={}),ra,Cb,X=["0","0","0"];A.$inject=[];wa.$inject=[];Cb=Y<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?ka(b.scopeName+":"+ +b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var ic=/[A-Z]/g,hd={full:"1.0.0rc11",major:1,minor:0,dot:0,codeName:"promise-resolution"},za=P.cache={},ya=P.expando="ng-"+(new Date).getTime(),mc=1,id=T.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},tb=T.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},kc=/([\:\-\_]+(.))/g,lc=/^moz([A-Z])/, +sa=P.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);P(T).bind("load",a)},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+b])},length:0,push:Pa,sort:[].sort,splice:[].splice},Ca={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ca[E(b)]=b});var zb={};m("input,select,option,textarea,button,form".split(","),function(b){zb[ka(b)]= +!0});m({data:ub,inheritedData:Ba,scope:function(b){return Ba(b,"$scope")},controller:xb,injector:function(b){return Ba(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Aa,css:function(b,a,c){a=qb(a);if(s(c))b.style[a]=c;else{var d;Y<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Y<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=E(a);if(Ca[d])if(s(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)|| +A).specified?d:p;else if(s(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(s(c))b[a]=c;else return b[a]},text:D(Y<9?function(b,a){if(b.nodeType==1){if(v(a))return b.innerText;b.innerText=a}else{if(v(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(v(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(v(a))return b.value;b.value=a},html:function(b,a){if(v(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a, +c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Kc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},gb={},Xc=T.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw new w("This browser does not support XMLHttpRequest.");};Ob.$inject=["$provide"];Pb.$inject=["$locale"];Rb.$inject=["$locale"];var Ub=".",gd={yyyy:O("FullYear",4), +yy:O("FullYear",2,0,!0),y:O("FullYear",1),MMMM:Ja("Month"),MMM:Ja("Month",!0),MM:O("Month",2,1),M:O("Month",1,1),dd:O("Date",2),d:O("Date",1),HH:O("Hours",2),H:O("Hours",1),hh:O("Hours",2,-12),h:O("Hours",1,-12),mm:O("Minutes",2),m:O("Minutes",1),ss:O("Seconds",2),s:O("Seconds",1),EEEE:Ja("Day"),EEE:Ja("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=a.getTimezoneOffset();return hb(a/60,2)+hb(Math.abs(a%60),2)}},fd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, +ed=/^\d+$/;Qb.$inject=["$locale"];var cd=B(E),dd=B(ka);Sb.$inject=["$parse"];var jd=B({restrict:"E",compile:function(a,c){c.href||c.$set("href","");return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),jb={};m(Ca,function(a,c){var d=da("ng-"+c);jb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});m(["src","href"],function(a){var c=da("ng-"+a);jb[c]=function(){return{priority:99,link:function(d, +e,g){g.$observe(c,function(c){g.$set(a,c);Y&&e.prop(a,c)})}}}});var Ma={$addControl:A,$removeControl:A,$setValidity:A,$setDirty:A};Vb.$inject=["$element","$attrs","$scope"];var Pa={name:"form",restrict:"E",controller:Vb,compile:function(){return{pre:function(a,c,d,e){d.action||c.bind("submit",function(a){a.preventDefault()});var g=c.parent().controller("form"),h=d.name||d.ngForm;h&&(a[h]=e);g&&c.bind("$destroy",function(){g.$removeControl(e);h&&(a[h]=p);D(e,Ma)})}}}},kd=B(Pa),ld=B(D(U(Pa),{restrict:"EAC"})), +md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Zb={text:Oa,number:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);e.$parsers.push(function(a){var c=S(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),p)});e.$formatters.push(function(a){return S(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!S(a)&& +ai?(e.$setValidity("max",!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return S(a)||ua(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),p)})},url:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return S(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url", +!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return S(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){v(d.name)&&c.attr("name",va());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g= +d.ngTrueValue,h=d.ngFalseValue;G(g)||(g=!0);G(h)||(h=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:A,button:A,submit:A,reset:A},$b=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,h){h&&(Zb[E(g.type)]||Zb.text)(d,e,g,h,c,a)}}}],La="ng-valid",Ka="ng-invalid",Na="ng-pristine", +Wb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function h(a,c){c=c?"-"+Za(c,"-"):"";e.removeClass((a?Ka:La)+c).addClass((a?La:Ka)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var g=g(d.ngModel),f=g.assign;if(!f)throw w(Db+d.ngModel+" ("+na(e)+")");this.$render=A;var i=e.inheritedData("$formController")|| +Ma,j=0,k=this.$error={};e.addClass(Na);h(!0);this.$setValidity=function(a,c){if(k[a]!==!c){if(c){if(k[a]&&j--,!j)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,j++;k[a]=!c;h(c,a);i.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Na).addClass(Wb),i.$setDirty();m(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,f(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})}; +var l=this;a.$watch(g,function(a){if(l.$modelValue!==a){var c=l.$formatters,d=c.length;for(l.$modelValue=a;d--;)a=c[d](a);if(l.$viewValue!==a)l.$viewValue=a,l.$render()}})}],qd=function(){return{require:["ngModel","^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],h=e[1]||Ma;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},rd=B({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),ac=function(){return{require:"?ngModel", +link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(S(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},sd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",",h=function(a){var c=[];a&&m(a.split(g),function(a){a&&c.push(Q(a))});return c};e.$parsers.push(h);e.$formatters.push(function(a){return K(a)&& +!ea(h(e.$viewValue),a)?a.join(", "):p})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},vd=R(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),wd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding", +c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],yd=ib("",!0),zd=ib("Odd",0),Ad=ib("Even",1),Bd=R({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],bc={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "), +function(a){var c=da("ng-"+a);bc[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(E(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Ed=R(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Fd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,i=h.onload||"",j=h.autoscroll;return function(g,h){var n=0,m,o=function(){m&&(m.$destroy(),m=null);h.html("")}; +g.$watch(f,function(f){var q=++n;f?a.get(f,{cache:c}).success(function(a){q===n&&(m&&m.$destroy(),m=g.$new(),h.html(a),e(h.contents())(m),s(j)&&(!j||g.$eval(j))&&d(),m.$emit("$includeContentLoaded"),g.$eval(i))}).error(function(){q===n&&o()}):o()})}}}}],Gd=R({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Hd=R({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,i=g.attr(h.$attr.when),j=h.offset|| +0,k=e.$eval(i),l={};m(k,function(a,e){l[e]=c(a.replace(d,"{{"+f+"-"+j+"}}"))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(k[c]||(c=a.pluralCat(c-j)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Jd=R({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),i,j,k;if(!h)throw w("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=h[1];i=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); +if(!h)throw w("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");j=h[3]||h[1];k=h[2];var l=new cb;a.$watch(function(a){var e,f,h=a.$eval(i),m=ec(h,!0),p,u=new cb,z,y,v,s,w=c;if(K(h))v=h||[];else{v=[];for(z in h)h.hasOwnProperty(z)&&z.charAt(0)!="$"&&v.push(z);v.sort()}e=0;for(f=v.length;ex;)u.pop().element.remove()}for(;v.length>w;)v.pop()[0].element.remove()}var h;if(!(h=x.match(d)))throw w("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+ +x+"'.");var j=c(h[2]||h[1]),k=h[4]||h[6],l=h[5],m=c(h[3]||""),n=c(h[2]?h[1]:k),r=c(h[7]),v=[[{element:f,label:""}]];q&&(a(q)(e),q.removeClass("ng-scope"),q.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=r(e)||[],d={},h,i,j,m,q,s;if(o){i=[];m=0;for(s=v.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}'); diff --git a/app/lib/angular/version.txt b/app/lib/angular/version.txt index 7be385a..e1c61a4 100644 --- a/app/lib/angular/version.txt +++ b/app/lib/angular/version.txt @@ -1 +1 @@ -1.0.0rc10 +1.0.0rc11 diff --git a/test/lib/angular/angular-mocks.js b/test/lib/angular/angular-mocks.js index 10facf1..5cbc539 100644 --- a/test/lib/angular/angular-mocks.js +++ b/test/lib/angular/angular-mocks.js @@ -1,6 +1,6 @@ /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT * @@ -578,7 +578,7 @@ angular.mock.$LogProvider = function() { * This method is also available on window, where it can be used to display objects on debug console. * * @param {*} object - any object to turn into string. - * @return a serialized string of the argument + * @return {string} a serialized string of the argument */ angular.mock.dump = function(object) { return serialize(object); @@ -1347,6 +1347,15 @@ function MockXhr() { * Flushes the queue of pending tasks. */ +/** + * + */ +angular.mock.$RootElementProvider = function() { + this.$get = function() { + return angular.element('
'); + } +}; + /** * @ngdoc overview * @name angular.module.ngMock @@ -1359,7 +1368,8 @@ angular.module('ngMock', ['ng']).provider({ $browser: angular.mock.$BrowserProvider, $exceptionHandler: angular.mock.$ExceptionHandlerProvider, $log: angular.mock.$LogProvider, - $httpBackend: angular.mock.$HttpBackendProvider + $httpBackend: angular.mock.$HttpBackendProvider, + $rootElement: angular.mock.$RootElementProvider }).config(function($provide) { $provide.decorator('$timeout', function($delegate, $browser) { $delegate.flush = function() { @@ -1370,7 +1380,6 @@ angular.module('ngMock', ['ng']).provider({ }); - /** * @ngdoc overview * @name angular.module.ngMockE2E diff --git a/test/lib/angular/angular-scenario.js b/test/lib/angular/angular-scenario.js index 2a827df..d307131 100644 --- a/test/lib/angular/angular-scenario.js +++ b/test/lib/angular/angular-scenario.js @@ -9404,7 +9404,7 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) { })( window ); /** - * @license AngularJS v1.0.0rc10 + * @license AngularJS v1.0.0rc11 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ @@ -10166,7 +10166,9 @@ function startingTag(element) { // are not allowed to have children. So we just ignore it. element.html(''); } catch(e) {} - return jqLite('
').append(element).html().match(/^(<[^>]+>)/)[1]; + return jqLite('
').append(element).html(). + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); } @@ -10315,7 +10317,7 @@ function angularInit(element, bootstrap) { * @description * Use this function to manually start up angular application. * - * See: {@link guide/dev_guide.bootstrap.manual_bootstrap Bootstrap} + * See: {@link guide/bootstrap Bootstrap} * * @param {Element} element DOM element which is the root of angular application. * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} @@ -10324,10 +10326,13 @@ function angularInit(element, bootstrap) { function bootstrap(element, modules) { element = jqLite(element); modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); modules.unshift('ng'); var injector = createInjector(modules); injector.invoke( - ['$rootScope', '$compile', '$injector', function(scope, compile, injector){ + ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){ scope.$apply(function() { element.data('$injector', injector); compile(element)(scope); @@ -10648,11 +10653,11 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.0.0rc10', // all of these placeholder strings will be replaced by rake's + full: '1.0.0rc11', // all of these placeholder strings will be replaced by rake's major: 1, // compile task minor: 0, dot: 0, - codeName: 'tesseract-giftwrapping' + codeName: 'promise-resolution' }; @@ -11131,8 +11136,12 @@ forEach('input,select,option,textarea,button,form'.split(','), function(value) { BOOLEAN_ELEMENTS[uppercase(value)] = true; }); -function isBooleanAttr(element, name) { - return BOOLEAN_ELEMENTS[element.nodeName] && BOOLEAN_ATTR[name.toLowerCase()]; +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } forEach({ @@ -11325,11 +11334,7 @@ function createEventHandler(element, events) { }; forEach(events[type || event.type], function(fn) { - try { - fn.call(element, event); - } catch (e) { - // Not much to do here since jQuery ignores these anyway - } + fn.call(element, event); }); // Remove monkey-patched methods (IE), @@ -11622,7 +11627,7 @@ HashQueueMap.prototype = { * * @description * Creates an injector function that can be used for retrieving services as well as for - * dependency injection (see {@link guide/dev_guide.di dependency injection}). + * dependency injection (see {@link guide/di dependency injection}). * * @param {Array.} modules A list of module functions or their aliases. See @@ -11657,19 +11662,32 @@ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(.+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -function inferInjectionArgs(fn) { - assertArgFn(fn); - if (!fn.$inject) { - var args = fn.$inject = []; - var fnText = fn.toString().replace(STRIP_COMMENTS, ''); - var argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ - arg.replace(FN_ARG, function(all, underscore, name){ - args.push(name); +function annotate(fn) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn == 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ + arg.replace(FN_ARG, function(all, underscore, name){ + $inject.push(name); + }); }); - }); + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn') + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); } - return fn.$inject; + return $inject; } /////////////////////////////////////// @@ -11750,7 +11768,7 @@ function inferInjectionArgs(fn) { * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. - * @return the value returned by the invoked `fn` function. + * @returns {*} the value returned by the invoked `fn` function. */ /** @@ -11764,9 +11782,90 @@ function inferInjectionArgs(fn) { * @param {function} Type Annotated constructor function. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. - * @return new instance of `Type`. + * @returns {Object} new instance of `Type`. */ +/** + * @ngdoc method + * @name angular.module.AUTO.$injector#annotate + * @methodOf angular.module.AUTO.$injector + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is used by the injector + * to determine which services need to be injected into the function when the function is invoked. There are three + * ways in which the function can be annotated with the needed dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting + * the function into a string using `toString()` method and extracting the argument names. + *
+ *   // Given
+ *   function MyController($scope, $route) {
+ *     // ...
+ *   }
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies + * are supported. + * + * # The `$injector` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of + * services to be injected into the function. + *
+ *   // Given
+ *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
+ *     // ...
+ *   }
+ *   // Define function dependencies
+ *   MyController.$inject = ['$scope', '$route'];
+ *
+ *   // Then
+ *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ * 
+ * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very + * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives + * minification is a better choice: + * + *
+ *   // We wish to write this (not minification / obfuscation safe)
+ *   injector.invoke(function($compile, $rootScope) {
+ *     // ...
+ *   });
+ *
+ *   // We are forced to write break inlining
+ *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
+ *     // ...
+ *   };
+ *   tmpFn.$inject = ['$compile', '$rootScope'];
+ *   injector.invoke(tempFn);
+ *
+ *   // To better support inline function the inline annotation is supported
+ *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
+ *     // ...
+ *   }]);
+ *
+ *   // Therefore
+ *   expect(injector.annotate(
+ *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
+ *    ).toEqual(['$compile', '$rootScope']);
+ * 
+ * + * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described + * above. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + /** * @ngdoc object @@ -12069,23 +12168,11 @@ function createInjector(modulesToLoad) { function invoke(fn, self, locals){ var args = [], - $inject, - length, + $inject = annotate(fn), + length, i, key; - if (typeof fn == 'function') { - $inject = inferInjectionArgs(fn); - length = $inject.length; - } else { - if (isArray(fn)) { - $inject = fn; - length = $inject.length - 1; - fn = $inject[length]; - } - assertArgFn(fn, 'fn'); - } - - for(var i = 0; i < length; i++) { + for(i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; args.push( locals && locals.hasOwnProperty(key) @@ -12093,6 +12180,11 @@ function createInjector(modulesToLoad) { : getService(key, path) ); } + if (!fn.$inject) { + // this means that we must be an array. + fn = fn[length]; + } + // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke switch (self ? -1 : args.length) { @@ -12125,7 +12217,8 @@ function createInjector(modulesToLoad) { return { invoke: invoke, instantiate: instantiate, - get: getService + get: getService, + annotate: annotate }; } } @@ -12747,6 +12840,9 @@ function $TemplateCacheProvider() { */ +var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; + + /** * @ngdoc function * @name angular.module.ng.$compile @@ -12864,7 +12960,7 @@ function $TemplateCacheProvider() { * * * For information on how the compiler works, see the - * {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide. + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. */ @@ -12874,7 +12970,20 @@ function $TemplateCacheProvider() { * @function * * @description + */ + +/** + * @ngdoc function + * @name angular.module.ng.$compileProvider#directive + * @methodOf angular.module.ng.$compileProvider + * @function * + * @description + * Register a new directive with compiler + * + * @param {string} name name of the directive. + * @param {function} directiveFactory An injectable directive factory function. + * @returns {angular.module.ng.$compileProvider} Self for chaining. */ $CompileProvider.$inject = ['$provide']; function $CompileProvider($provide) { @@ -12937,54 +13046,12 @@ function $CompileProvider($provide) { this.$get = [ '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', - '$controller', + '$controller', '$rootScope', function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, - $controller) { - - var LOCAL_MODE = { - attribute: function(localName, mode, parentScope, scope, attr) { - scope[localName] = attr[localName]; - }, - - evaluate: function(localName, mode, parentScope, scope, attr) { - scope[localName] = parentScope.$eval(attr[localName]); - }, - - bind: function(localName, mode, parentScope, scope, attr) { - var getter = $interpolate(attr[localName]); - scope.$watch( - function() { return getter(parentScope); }, - function(v) { scope[localName] = v; } - ); - }, - - accessor: function(localName, mode, parentScope, scope, attr) { - var getter = noop, - setter = noop, - exp = attr[localName]; - - if (exp) { - getter = $parse(exp); - setter = getter.assign || function() { - throw Error("Expression '" + exp + "' not assignable."); - }; - } - - scope[localName] = function(value) { - return arguments.length ? setter(parentScope, value) : getter(parentScope); - }; - }, - - expression: function(localName, mode, parentScope, scope, attr) { - scope[localName] = function(locals) { - $parse(attr[localName])(parentScope, locals); - }; - } - }; + $controller, $rootScope) { var Attributes = function(element, attr) { this.$$element = element; - this.$$observers = {}; this.$attr = attr || {}; }; @@ -13002,7 +13069,8 @@ function $CompileProvider($provide) { * @param {string=} attrName Optional none normalized name. Defaults to key. */ $set: function(key, value, writeAttr, attrName) { - var booleanKey = isBooleanAttr(this.$$element[0], key.toLowerCase()); + var booleanKey = getBooleanAttrName(this.$$element[0], key), + $$observers = this.$$observers; if (booleanKey) { this.$$element.prop(key, value); @@ -13030,7 +13098,7 @@ function $CompileProvider($provide) { } // fire observers - forEach(this.$$observers[key], function(fn) { + $$observers && forEach($$observers[key], function(fn) { try { fn(value); } catch (e) { @@ -13049,10 +13117,17 @@ function $CompileProvider($provide) { * @returns {function(*)} the `fn` Function passed in. */ $observe: function(key, fn) { - // keep only observers for interpolated attrs - if (this.$$observers[key]) { - this.$$observers[key].push(fn); - } + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = {})), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); return fn; } }; @@ -13214,7 +13289,7 @@ function $CompileProvider($provide) { attrs[nName] = value = trim((msie && name == 'href') ? decodeURIComponent(node.getAttribute(name, 2)) : attr.value); - if (isBooleanAttr(node, nName)) { + if (getBooleanAttrName(node, nName)) { attrs[nName] = true; // presence means true } addAttrInterpolateDirective(node, directives, value, nName); @@ -13455,9 +13530,67 @@ function $CompileProvider($provide) { $element = attrs.$$element; if (newScopeDirective && isObject(newScopeDirective.scope)) { - forEach(newScopeDirective.scope, function(mode, name) { - (LOCAL_MODE[mode] || wrongMode)(name, mode, - scope.$parent || scope, scope, attrs); + var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/; + + var parentScope = scope.$parent || scope; + + forEach(newScopeDirective.scope, function(definiton, scopeName) { + var match = definiton.match(LOCAL_REGEXP) || [], + attrName = match[2]|| scopeName, + mode = match[1], // @, =, or & + lastValue, + parentGet, parentSet; + + switch (mode) { + + case '@': { + attrs.$observe(attrName, function(value) { + scope[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = parentScope; + break; + } + + case '=': { + parentGet = $parse(attrs[attrName]); + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = scope[scopeName] = parentGet(parentScope); + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + + ' (directive: ' + newScopeDirective.name + ')'); + }; + lastValue = scope[scopeName] = parentGet(parentScope); + scope.$watch(function() { + var parentValue = parentGet(parentScope); + + if (parentValue !== scope[scopeName]) { + // we are out of sync and need to copy + if (parentValue !== lastValue) { + // parent changed and it has precedence + lastValue = scope[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(parentScope, lastValue = scope[scopeName]); + } + } + return parentValue; + }); + break; + } + + case '&': { + parentGet = $parse(attrs[attrName]); + scope[scopeName] = function(locals) { + return parentGet(parentScope, locals); + } + break; + } + + default: { + throw Error('Invalid isolate scope definition for directive ' + + newScopeDirective.name + ': ' + definiton); + } + } }); } @@ -13470,12 +13603,6 @@ function $CompileProvider($provide) { $transclude: boundTranscludeFn }; - - forEach(directive.inject || {}, function(mode, name) { - (LOCAL_MODE[mode] || wrongMode)(name, mode, - newScopeDirective ? scope.$parent || scope : scope, locals, attrs); - }); - controller = directive.controller; if (controller == '@') { controller = attrs[directive.name]; @@ -13560,6 +13687,7 @@ function $CompileProvider($provide) { var srcAttr = src.$attr, dstAttr = dst.$attr, $element = dst.$$element; + // reapply the old attributes to the new element forEach(dst, function(value, key) { if (key.charAt(0) != '$') { @@ -13569,10 +13697,12 @@ function $CompileProvider($provide) { dst.$set(key, value, true, srcAttr[key]); } }); + // copy the new attributes on the old attrs object forEach(src, function(value, key) { if (key == 'class') { safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; } else if (key == 'style') { $element.attr('style', $element.attr('style') + ';' + value); } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { @@ -13706,19 +13836,20 @@ function $CompileProvider($provide) { directives.push({ priority: 100, compile: valueFn(function(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + if (name === 'class') { // we need to interpolate classes again, in the case the element was replaced // and therefore the two class attrs got merged - we want to interpolate the result interpolateFn = $interpolate(attr[name], true); } - // we define observers array only for interpolated attrs - // and ignore observers for non interpolated attrs to save some memory - attr.$$observers[name] = []; attr[name] = undefined; - scope.$watch(interpolateFn, function(value) { - attr.$set(name, value); - }); + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function(value) { + attr.$set(name, value); + }); }) }); } @@ -13774,6 +13905,43 @@ function directiveNormalize(name) { return camelCase(name.replace(PREFIX_REGEXP, '')); } +/** + * @ngdoc object + * @name angular.module.ng.$compile.directive.Attributes + * @description + * + * A shared object between directive compile / linking functions which contains normalized DOM element + * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed + * since all of these are treated as equivalent in Angular: + * + * + */ + +/** + * @ngdoc property + * @name angular.module.ng.$compile.directive.Attributes#$attr + * @propertyOf angular.module.ng.$compile.directive.Attributes + * @returns {object} A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc function + * @name angular.module.ng.$compile.directive.Attributes#$set + * @methodOf angular.module.ng.$compile.directive.Attributes + * @function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * revers translated using the {@link angular.module.ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. + */ + /** @@ -14311,7 +14479,7 @@ LocationUrl.prototype = { * Return full url representation with all segments encoded according to rules specified in * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. * - * @return {string} + * @return {string} full url */ absUrl: locationGetter('$$absUrl'), @@ -14328,7 +14496,7 @@ LocationUrl.prototype = { * Change path, search and hash, when called with parameter and return `$location`. * * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) - * @return {string} + * @return {string} url */ url: function(url, replace) { if (isUndefined(url)) @@ -14352,7 +14520,7 @@ LocationUrl.prototype = { * * Return protocol of current url. * - * @return {string} + * @return {string} protocol of current url */ protocol: locationGetter('$$protocol'), @@ -14366,7 +14534,7 @@ LocationUrl.prototype = { * * Return host of current url. * - * @return {string} + * @return {string} host of current url. */ host: locationGetter('$$host'), @@ -14380,7 +14548,7 @@ LocationUrl.prototype = { * * Return port of current url. * - * @return {Number} + * @return {Number} port */ port: locationGetter('$$port'), @@ -14400,7 +14568,7 @@ LocationUrl.prototype = { * if it is missing. * * @param {string=} path New path - * @return {string} + * @return {string} path */ path: locationGetterSetter('$$path', function(path) { return path.charAt(0) == '/' ? path : '/' + path; @@ -14422,7 +14590,7 @@ LocationUrl.prototype = { * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a * single search parameter. If the value is `null`, the parameter will be deleted. * - * @return {string} + * @return {string} search */ search: function(search, paramValue) { if (isUndefined(search)) @@ -14455,7 +14623,7 @@ LocationUrl.prototype = { * Change hash fragment when called with parameter and return `$location`. * * @param {string=} hash New hash fragment - * @return {string} + * @return {string} hash */ hash: locationGetterSetter('$$hash', identity), @@ -14502,10 +14670,13 @@ function locationGetterSetter(property, preprocess) { * * @requires $browser * @requires $sniffer - * @requires $document + * @requires $rootElement * * @description - * The $location service parses the URL in the browser address bar (based on the {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to your application. Changes to the URL in the address bar are reflected into $location service and changes to $location are reflected into the browser address bar. + * The $location service parses the URL in the browser address bar (based on the + * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. * * **The $location service:** * @@ -14518,7 +14689,8 @@ function locationGetterSetter(property, preprocess) { * - Clicks on a link. * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). * - * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location} + * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular + * Services: Using $location} */ /** @@ -14565,67 +14737,75 @@ function $LocationProvider(){ } }; - this.$get = ['$rootScope', '$browser', '$sniffer', '$document', - function( $rootScope, $browser, $sniffer, $document) { - var currentUrl, + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', + function( $rootScope, $browser, $sniffer, $rootElement) { + var $location, basePath = $browser.baseHref() || '/', pathPrefix = pathPrefixFromBase(basePath), - initUrl = $browser.url(); + initUrl = $browser.url(), + absUrlPrefix; if (html5Mode) { if ($sniffer.history) { - currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix); + $location = new LocationUrl( + convertToHtml5Url(initUrl, basePath, hashPrefix), + pathPrefix); } else { - currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix), - hashPrefix); + $location = new LocationHashbangUrl( + convertToHashbangUrl(initUrl, basePath, hashPrefix), + hashPrefix); } - - // link rewriting - var u = currentUrl, - absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix; - - $document.bind('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (event.ctrlKey || event.metaKey || event.which == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (elm.length && lowercase(elm[0].nodeName) !== 'a') { - elm = elm.parent(); - } - - var absHref = elm.prop('href'); - - if (!absHref || - elm.attr('target') || - absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path - return; - } - - // 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 - window.angular['ff-684208-preventDefault'] = true; - }); } else { - currentUrl = new LocationHashbangUrl(initUrl, hashPrefix); + $location = new LocationHashbangUrl(initUrl, hashPrefix); } + // link rewriting + absUrlPrefix = composeProtocolHostPort( + $location.protocol(), $location.host(), $location.port()) + pathPrefix; + + $rootElement.bind('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (event.ctrlKey || event.metaKey || event.which == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (elm.length && lowercase(elm[0].nodeName) !== 'a') { + elm = elm.parent(); + } + + var absHref = elm.prop('href'); + + if (!absHref || + elm.attr('target') || + absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path + return; + } + + // update location with href without the prefix + $location.url(absHref.substr(absUrlPrefix.length)); + $rootScope.$apply(); + event.preventDefault(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + window.angular['ff-684208-preventDefault'] = true; + }); + + // rewrite hashbang url <> html5 url - if (currentUrl.absUrl() != initUrl) { - $browser.url(currentUrl.absUrl(), true); + if ($location.absUrl() != initUrl) { + $browser.url($location.absUrl(), true); } // update $location when $browser url changes $browser.onUrlChange(function(newUrl) { - if (currentUrl.absUrl() != newUrl) { + if ($location.absUrl() != newUrl) { $rootScope.$evalAsync(function() { - currentUrl.$$parse(newUrl); + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + afterLocationChange(oldUrl); }); if (!$rootScope.$$phase) $rootScope.$digest(); } @@ -14634,18 +14814,30 @@ function $LocationProvider(){ // update browser var changeCounter = 0; $rootScope.$watch(function $locationWatch() { - if ($browser.url() != currentUrl.absUrl()) { + var oldUrl = $browser.url(); + + if (!changeCounter || oldUrl != $location.absUrl()) { changeCounter++; $rootScope.$evalAsync(function() { - $browser.url(currentUrl.absUrl(), currentUrl.$$replace); - currentUrl.$$replace = false; + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url($location.absUrl(), $location.$$replace); + $location.$$replace = false; + afterLocationChange(oldUrl); + } }); } return changeCounter; }); - return currentUrl; + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } }]; } @@ -15571,6 +15763,39 @@ function getterFn(path, csp) { /////////////////////////////////// +/** + * @ngdoc function + * @name angular.module.ng.$parse + * @function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + *
+ *   var getter = $parse('user.name');
+ *   var setter = getter.assign;
+ *   var context = {user:{name:'angular'}};
+ *   var locals = {user:{name:'local'}};
+ *
+ *   expect(getter(context)).toEqual('angular');
+ *   setter(context, 'newValue');
+ *   expect(context.user.name).toEqual('newValue');
+ *   expect(getter(context, locals)).toEqual('local');
+ * 
+ * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context`: an object against which any expressions embedded in the strings are evaluated + * against (Topically a scope object). + * * `locals`: local variables context object, useful for overriding values in `context`. + * + * The return function also has an `assign` property, if the expression is assignable, which + * allows one to set values to expressions. + * + */ function $ParseProvider() { var cache = {}; this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { @@ -15997,7 +16222,7 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$routeProvider * * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redudant trailing slash or is missing one, the route will still match and the + * contains redundant trailing slash or is missing one, the route will still match and the * `$location.path` will be updated to add or drop the trailing slash to exacly match the * route definition. * @param {Object} route Mapping information to be assigned to `$route.current` on route @@ -16007,16 +16232,30 @@ 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 + * - `template` – `{string=}` – html template as a string that should be used by * {@link angular.module.ng.$compileProvider.directive.ngView ngView} or * {@link angular.module.ng.$compileProvider.directive.ngInclude ngInclude} directives. + * this property takes precedence over `templateUrl`. + * - `templateUrl` – `{string=}` – path to an html template that should be used by + * {@link angular.module.ng.$compileProvider.directive.ngView ngView}. + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, they will be + * resolved and converted to a value before the controller is instantiated and the + * `$aftreRouteChange` event is fired. The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link api/angular.module.AUTO.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is resolved + * before its value is injected into the controller. + * * - `redirectTo` – {(string|function())=} – value to update * {@link angular.module.ng.$location $location} path with and trigger route redirection. * * If `redirectTo` is a function, it will be called with the following parameters: * * - `{Object.}` - route parameters extracted from the current - * `$location.path()` by applying the current route template. + * `$location.path()` by applying the current route templateUrl. * - `{string}` - current `$location.path()` * - `{Object}` - current `$location.search()` * @@ -16067,8 +16306,8 @@ function $RouteProvider(){ }; - this.$get = ['$rootScope', '$location', '$routeParams', - function( $rootScope, $location, $routeParams) { + this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', + function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { /** * @ngdoc object @@ -16077,6 +16316,16 @@ function $RouteProvider(){ * @requires $routeParams * * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as define in route definition. + * - `locals`: A map of locals which is used by {@link angular.module.ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * * @property {Array.} routes Array of all configured routes. * * @description @@ -16109,7 +16358,7 @@ function $RouteProvider(){
$location.path() = {{$location.path()}}
-
$route.current.template = {{$route.current.template}}
+
$route.current.templateUrl = {{$route.current.templateUrl}}
$route.current.params = {{$route.current.params}}
$route.current.scope.name = {{$route.current.scope.name}}
$routeParams = {{$routeParams}}
@@ -16130,11 +16379,19 @@ function $RouteProvider(){ angular.module('ngView', [], function($routeProvider, $locationProvider) { $routeProvider.when('/Book/:bookId', { - template: 'book.html', - controller: BookCntl + templateUrl: 'book.html', + controller: BookCntl, + resolve: { + // I will cause a 1 second delay + delay: function($q, $timeout) { + var delay = $q.defer(); + $timeout(delay.resolve, 1000); + return delay.promise; + } + } }); $routeProvider.when('/Book/:bookId/ch/:chapterId', { - template: 'chapter.html', + templateUrl: 'chapter.html', controller: ChapterCntl }); @@ -16168,6 +16425,7 @@ function $RouteProvider(){ expect(content).toMatch(/Chapter Id\: 1/); element('a:contains("Scarlet")').click(); + sleep(2); // promises are not part of scenario waiting content = element('.doc-example-live [ng-view]').text(); expect(content).toMatch(/controller\: BookCntl/); expect(content).toMatch(/Book Id\: Scarlet/); @@ -16178,11 +16436,15 @@ function $RouteProvider(){ /** * @ngdoc event - * @name angular.module.ng.$route#$beforeRouteChange + * @name angular.module.ng.$route#$routeChangeStart * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted before a route change. + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occurs. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$routeChangeSuccess` is fired. * * @param {Route} next Future route information. * @param {Route} current Current route information. @@ -16190,16 +16452,31 @@ function $RouteProvider(){ /** * @ngdoc event - * @name angular.module.ng.$route#$afterRouteChange + * @name angular.module.ng.$route#$routeChangeSuccess * @eventOf angular.module.ng.$route * @eventType broadcast on root scope * @description - * Broadcasted after a route change. + * Broadcasted after a route dependencies are resolved. + * {@link angular.module.ng.$compileProvider.directive.ngView ngView} listens for the directive + * to instantiate the controller and render the view. * * @param {Route} current Current route information. * @param {Route} previous Previous route information. */ + /** + * @ngdoc event + * @name angular.module.ng.$route#$routeChangeError + * @eventOf angular.module.ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted if any of the resolve promises are rejected. + * + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. + */ + /** * @ngdoc event * @name angular.module.ng.$route#$routeUpdate @@ -16212,7 +16489,6 @@ function $RouteProvider(){ */ var matcher = switchRouteMatcher, - dirty = 0, forceReload = false, $route = { routes: routes, @@ -16223,19 +16499,19 @@ function $RouteProvider(){ * @methodOf angular.module.ng.$route * * @description - * Causes `$route` service to reload theR current route even if + * 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.ngView ngView} * creates new scope, reinstantiates the controller. */ reload: function() { - dirty++; forceReload = true; + $rootScope.$evalAsync(updateRoute); } }; - $rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute); + $rootScope.$on('$locationChangeSuccess', updateRoute); return $route; @@ -16276,7 +16552,7 @@ function $RouteProvider(){ $rootScope.$broadcast('$routeUpdate', last); } else if (next || last) { forceReload = false; - $rootScope.$broadcast('$beforeRouteChange', next, last); + $rootScope.$broadcast('$routeChangeStart', next, last); $route.current = next; if (next) { if (next.redirectTo) { @@ -16287,11 +16563,52 @@ function $RouteProvider(){ $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) .replace(); } - } else { - copy(next.params, $routeParams); } } - $rootScope.$broadcast('$afterRouteChange', next, last); + + $q.when(next). + then(function() { + if (next) { + var keys = [], + values = [], + template; + + forEach(next.resolve || {}, function(value, key) { + keys.push(key); + values.push(isFunction(value) ? $injector.invoke(value) : $injector.get(value)); + }); + if (isDefined(template = next.template)) { + } else if (isDefined(template = next.templateUrl)) { + template = $http.get(template, {cache: $templateCache}). + then(function(response) { return response.data; }); + } + if (isDefined(template)) { + keys.push('$template'); + values.push(template); + } + return $q.all(values).then(function(values) { + var locals = {}; + forEach(values, function(value, index) { + locals[keys[index]] = value; + }); + return locals; + }); + } + }). + // after route change + then(function(locals) { + if (next == $route.current) { + if (next) { + next.locals = locals; + copy(next.params, $routeParams); + } + $rootScope.$broadcast('$routeChangeSuccess', next, last); + } + }, function(error) { + if (next == $route.current) { + $rootScope.$broadcast('$routeChangeError', next, last, error); + } + }); } } @@ -16420,7 +16737,7 @@ function $RouteParamsProvider() { * * Every application has a single root {@link angular.module.ng.$rootScope.Scope scope}. * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide - * event processing life-cycle. See {@link guide/dev_guide.scopes developer guide on scopes}. + * event processing life-cycle. See {@link guide/scope developer guide on scopes}. */ function $RootScopeProvider(){ var TTL = 10; @@ -16483,9 +16800,6 @@ function $RootScopeProvider(){ expect(parent.salutation).toEqual('Hello'); * * - * # Dependency Injection - * See {@link guide/dev_guide.di dependency injection}. - * * * @param {Object.=} providers Map of service factory which need to be provided * for the current scope. Defaults to {@link angular.module.ng}. @@ -16532,7 +16846,7 @@ function $RootScopeProvider(){ * the scope and its child scopes to be permanently detached from the parent and thus stop * participating in model change detection and listener notification by invoking. * - * @params {boolean} isolate if true then the scoped does not prototypically inherit from the + * @param {boolean} isolate if true then the scoped does not prototypically inherit from the * parent scope. The scope is isolated, as it can not se parent scope properties. * When creating widgets it is useful for the widget to not accidently read parent * state. @@ -16637,12 +16951,12 @@ function $RootScopeProvider(){ * {@link angular.module.ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a * call to the `listener`. * - * - `string`: Evaluated as {@link guide/dev_guide.expressions expression} + * - `string`: Evaluated as {@link guide/expression expression} * - `function(scope)`: called with current `scope` as a parameter. * @param {(function()|string)=} listener Callback called whenever the return value of * the `watchExpression` changes. * - * - `string`: Evaluated as {@link guide/dev_guide.expressions expression} + * - `string`: Evaluated as {@link guide/expression expression} * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. * * @param {boolean=} objectEquality Compare object for equality rather then for refference. @@ -16689,7 +17003,7 @@ function $RootScopeProvider(){ * Because a {@link angular.module.ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the * `$digest()` keeps calling the {@link angular.module.ng.$rootScope.Scope#$watch watchers} until no more listeners are * firing. This means that it is possible to get into an infinite loop. This function will throw - * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100. + * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. * * Usually you don't call `$digest()` directly in * {@link angular.module.ng.$compileProvider.directive.ngController controllers} or in @@ -16867,9 +17181,8 @@ function $RootScopeProvider(){ * * @param {(string|function())=} expression An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. - * - `function(scope, locals)`: execute the function with the current `scope` parameter. - * @param {Object=} locals Hash object of local variables for the expression. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. * * @returns {*} The result of evaluating the expression. */ @@ -16897,7 +17210,7 @@ function $RootScopeProvider(){ * * @param {(string|function())=} expression An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * */ @@ -16934,7 +17247,7 @@ function $RootScopeProvider(){ * * Scope's `$apply()` method transitions through the following stages: * - * 1. The {@link guide/dev_guide.expressions expression} is executed using the + * 1. The {@link guide/expression expression} is executed using the * {@link angular.module.ng.$rootScope.Scope#$eval $eval()} method. * 2. Any exceptions from the execution of the expression are forwarded to the * {@link angular.module.ng.$exceptionHandler $exceptionHandler} service. @@ -16944,7 +17257,7 @@ function $RootScopeProvider(){ * * @param {(string|function())=} exp An angular expression to be executed. * - * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. + * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with current `scope` parameter. * * @returns {*} The result of evaluating the expression. @@ -17338,8 +17651,8 @@ function $HttpProvider() { 'Accept': 'application/json, text/plain, */*', 'X-Requested-With': 'XMLHttpRequest' }, - post: {'Content-Type': 'application/json'}, - put: {'Content-Type': 'application/json'} + post: {'Content-Type': 'application/json;charset=utf-8'}, + put: {'Content-Type': 'application/json;charset=utf-8'} } }; @@ -18257,12 +18570,12 @@ function $TimeoutProvider() { * Cancels a task associated with the `promise`. As a result of this the promise will be * resolved with a rejection. * - * @param {Promise} promise Promise returned by the `$timeout` function. + * @param {Promise=} promise Promise returned by the `$timeout` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */ timeout.cancel = function(promise) { - if (promise.$$timeoutId in deferreds) { + if (promise && promise.$$timeoutId in deferreds) { deferreds[promise.$$timeoutId].reject('canceled'); return $browser.defer.cancel(promise.$$timeoutId); } @@ -18808,7 +19121,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200) * * `format` string can also be one of the following predefined - * {@link guide/dev_guide.i18n localizable formats}: + * {@link guide/i18n localizable formats}: * * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale * (e.g. Sep 3, 2010 12:05:08 pm) @@ -19522,7 +19835,6 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) { priority: 100, compile: function() { return function(scope, element, attr) { - attr.$$observers[attrName] = []; scope.$watch(attr[normalized], function(value) { attr.$set(attrName, !!value); }); @@ -19539,27 +19851,15 @@ forEach(['src', 'href'], function(attrName) { ngAttributeAliasDirectives[normalized] = function() { return { priority: 99, // it needs to run after the attributes are interpolated - compile: function() { - return function(scope, element, attr) { - 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); + link: function(scope, element, attr) { + attr.$observe(normalized, function(value) { + attr.$set(attrName, value); - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect - if (msie) element.prop(attrName, value); - }); - } else { - // value present means that no interpolation, so copy to native attribute. - attr.$set(attrName, value); - element.prop(attrName, value); - } - }; + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect + if (msie) element.prop(attrName, value); + }); } }; }; @@ -20455,6 +20755,7 @@ function checkboxInputType(scope, element, attr, ctrl) { /** * @ngdoc directive * @name angular.module.ng.$compileProvider.directive.textarea + * @restrict E * * @description * HTML textarea element control with angular data-binding. The data-binding and validation @@ -20609,9 +20910,82 @@ var VALID_CLASS = 'ng-valid', * * @description * + * `NgModelController` provides API for the `ng-model` directive. The controller contains + * services for data-binding, validation, CSS update, value formatting and parsing. It + * specifically does not contain any logic which deals with DOM rendering or listening to + * DOM events. The `NgModelController` is meant to be extended by other directives where, the + * directive provides DOM manipulation and the `NgModelController` provides the data-binding. + * + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', []). + directive('contenteditable', function() { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if(!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html(ngModel.$viewValue || ''); + }; + + // Listen for change events to enable binding + element.bind('blur keyup change', function() { + scope.$apply(read); + }); + read(); // initialize + + // Write data to the model + function read() { + ngModel.$setViewValue(element.html()); + } + } + }; + }); + + +
+
Change me!
+ Required! +
+ +
+
+ + it('should data-bind and become invalid', function() { + var contentEditable = element('[contenteditable]'); + + expect(contentEditable.text()).toEqual('Change me!'); + input('userContent').enter(''); + expect(contentEditable.text()).toEqual(''); + expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); + }); + + *
+ * */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$element', - function($scope, $exceptionHandler, $attr, ngModel, $element) { +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', + function($scope, $exceptionHandler, $attr, $element, $parse) { this.$viewValue = Number.NaN; this.$modelValue = Number.NaN; this.$parsers = []; @@ -20621,9 +20995,27 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e this.$dirty = false; this.$valid = true; this.$invalid = false; - this.$render = noop; this.$name = $attr.name; + var ngModelGet = $parse($attr.ngModel), + ngModelSet = ngModelGet.assign; + + if (!ngModelSet) { + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + + ' (' + startingTag($element) + ')'); + } + + /** + * @ngdoc function + * @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$render + * @methodOf angular.module.ng.$compileProvider.directive.ngModel.NgModelController + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + */ + this.$render = noop; + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, invalidCount = 0, // used to easily determine if we are valid $error = this.$error = {}; // keep invalid keys here @@ -20717,7 +21109,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e if (this.$modelValue !== value) { this.$modelValue = value; - ngModel(value); + ngModelSet($scope, value); forEach(this.$viewChangeListeners, function(listener) { try { listener(); @@ -20730,9 +21122,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e // model -> value var ctrl = this; - $scope.$watch(function() { - return ngModel(); - }, function(value) { + $scope.$watch(ngModelGet, function(value) { // ignore change from view if (ctrl.$modelValue === value) return; @@ -20785,11 +21175,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e * - {@link angular.module.ng.$compileProvider.directive.textarea textarea} * */ -var ngModelDirective = [function() { +var ngModelDirective = function() { return { - inject: { - ngModel: 'accessor' - }, require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { @@ -20805,7 +21192,7 @@ var ngModelDirective = [function() { }); } }; -}]; +}; /** @@ -20866,11 +21253,12 @@ var ngChangeDirective = valueFn({ }); -var requiredDirective = [function() { +var requiredDirective = function() { return { require: '?ngModel', link: function(scope, elm, attr, ctrl) { if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element var validator = function(value) { if (attr.required && (isEmpty(value) || value === false)) { @@ -20890,7 +21278,7 @@ var requiredDirective = [function() { }); } }; -}]; +}; /** @@ -20971,7 +21359,7 @@ var ngListDirective = function() { var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; -var ngValueDirective = [function() { +var ngValueDirective = function() { return { priority: 100, compile: function(tpl, tplAttr) { @@ -20981,7 +21369,6 @@ var ngValueDirective = [function() { }; } else { return function(scope, elm, attr) { - attr.$$observers.value = []; scope.$watch(attr.ngValue, function(value) { attr.$set('value', value, false); }); @@ -20989,7 +21376,7 @@ var ngValueDirective = [function() { } } }; -}]; +}; /** * @ngdoc directive @@ -21013,7 +21400,7 @@ var ngValueDirective = [function() { * * * @element ANY - * @param {expression} ngBind {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. * * @example * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. @@ -21118,7 +21505,7 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { * See {@link angular.module.ngSanitize.$sanitize $sanitize} docs for examples. * * @element ANY - * @param {expression} ngBindHtmlUnsafe {@link guide/dev_guide.expressions Expression} to evaluate. + * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. */ var ngBindHtmlUnsafeDirective = [function() { return function(scope, element, attr) { @@ -21160,7 +21547,7 @@ function classDirective(name, selector) { * new classes are added. * * @element ANY - * @param {expression} ngClass {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClass {@link guide/expression 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. * @@ -21210,7 +21597,7 @@ var ngClassDirective = classDirective('', true); * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassOdd {@link guide/dev_guide.expressions Expression} to eval. The result + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -21257,7 +21644,7 @@ var ngClassOddDirective = classDirective('Odd', 0); * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassEven {@link guide/dev_guide.expressions Expression} to eval. The + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The * result of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -21372,7 +21759,7 @@ var ngCloakDirective = ngDirective({ * @element ANY * @scope * @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 + * {@link guide/expression expression} that on the current scope evaluates to a * constructor function. * * @example @@ -21488,7 +21875,7 @@ var ngCspDirective = ['$sniffer', function($sniffer) { * element is clicked. * * @element ANY - * @param {expression} ngClick {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon * click. (Event object is available as `$event`) * * @example @@ -21540,7 +21927,7 @@ forEach( * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. * * @element ANY - * @param {expression} ngDblclick {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon * dblclick. (Event object is available as `$event`) * * @example @@ -21556,7 +21943,7 @@ forEach( * The ngMousedown directive allows you to specify custom behavior on mousedown event. * * @element ANY - * @param {expression} ngMousedown {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon * mousedown. (Event object is available as `$event`) * * @example @@ -21572,7 +21959,7 @@ forEach( * Specify custom behavior on mouseup event. * * @element ANY - * @param {expression} ngMouseup {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon * mouseup. (Event object is available as `$event`) * * @example @@ -21587,7 +21974,7 @@ forEach( * Specify custom behavior on mouseover event. * * @element ANY - * @param {expression} ngMouseover {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon * mouseover. (Event object is available as `$event`) * * @example @@ -21603,7 +21990,7 @@ forEach( * Specify custom behavior on mouseenter event. * * @element ANY - * @param {expression} ngMouseenter {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon * mouseenter. (Event object is available as `$event`) * * @example @@ -21619,7 +22006,7 @@ forEach( * Specify custom behavior on mouseleave event. * * @element ANY - * @param {expression} ngMouseleave {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon * mouseleave. (Event object is available as `$event`) * * @example @@ -21635,7 +22022,7 @@ forEach( * Specify custom behavior on mousemove event. * * @element ANY - * @param {expression} ngMousemove {@link guide/dev_guide.expressions Expression} to evaluate upon + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon * mousemove. (Event object is available as `$event`) * * @example @@ -21654,7 +22041,7 @@ forEach( * server and reloading the current page). * * @element form - * @param {expression} ngSubmit {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. * * @example @@ -21839,7 +22226,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' * before the template enters execution mode during bootstrap. * * @element ANY - * @param {expression} ngInit {@link guide/dev_guide.expressions Expression} to eval. + * @param {expression} ngInit {@link guide/expression Expression} to eval. * * @example @@ -21907,7 +22294,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * # Overview * `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 ngPluralize directive + * (see {@link 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. @@ -21926,8 +22313,8 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); * 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 - * Angular expression}; these are evaluated on the current scope for its binded value. + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. * * The `when` attribute specifies the mappings between plural categories and the actual * string to be displayed. The value of the attribute should be a JSON object so that Angular @@ -22290,7 +22677,7 @@ var ngRepeatDirective = ngDirective({ * conditionally. * * @element ANY - * @param {expression} ngShow If the {@link guide/dev_guide.expressions expression} is truthy + * @param {expression} ngShow If the {@link guide/expression expression} is truthy * then the element is shown or hidden respectively. * * @example @@ -22330,7 +22717,7 @@ var ngShowDirective = ngDirective(function(scope, element, attr){ * of the HTML conditionally. * * @element ANY - * @param {expression} ngHide If the {@link guide/dev_guide.expressions expression} truthy then + * @param {expression} ngHide If the {@link guide/expression expression} truthy then * the element is shown or hidden respectively. * * @example @@ -22368,7 +22755,7 @@ var ngHideDirective = ngDirective(function(scope, element, attr){ * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. * * @element ANY - * @param {expression} ngStyle {@link guide/dev_guide.expressions Expression} which evals to an + * @param {expression} ngStyle {@link guide/expression Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * @@ -22623,11 +23010,11 @@ var ngTranscludeDirective = ngDirective({ angular.module('ngView', [], function($routeProvider, $locationProvider) { $routeProvider.when('/Book/:bookId', { - template: 'book.html', + templateUrl: 'book.html', controller: BookCntl }); $routeProvider.when('/Book/:bookId/ch/:chapterId', { - template: 'chapter.html', + templateUrl: 'chapter.html', controller: ChapterCntl }); @@ -22686,11 +23073,10 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c restrict: 'ECA', terminal: true, link: function(scope, element, attr) { - var changeCounter = 0, - lastScope, + var lastScope, onloadExp = attr.onload || ''; - scope.$on('$afterRouteChange', update); + scope.$on('$routeChangeSuccess', update); update(); @@ -22701,43 +23087,36 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c } } - function update() { - var template = $route.current && $route.current.template, - thisChangeId = ++changeCounter; + function clearContent() { + element.html(''); + destroyLastScope(); + } - function clearContent() { - // ignore callback if another route change occured since - if (thisChangeId === changeCounter) { - element.html(''); - destroyLastScope(); - } - } + function update() { + var locals = $route.current && $route.current.locals, + template = locals && locals.$template; if (template) { - $http.get(template, {cache: $templateCache}).success(function(response) { - // ignore callback if another route change occured since - if (thisChangeId === changeCounter) { - element.html(response); - destroyLastScope(); + element.html(template); + destroyLastScope(); - var link = $compile(element.contents()), - current = $route.current, - controller; + var link = $compile(element.contents()), + current = $route.current, + controller; - lastScope = current.scope = scope.$new(); - if (current.controller) { - controller = $controller(current.controller, {$scope: lastScope}); - element.contents().data('$ngControllerController', controller); - } + lastScope = current.scope = scope.$new(); + if (current.controller) { + locals.$scope = lastScope; + controller = $controller(current.controller, locals); + element.contents().data('$ngControllerController', controller); + } - link(lastScope); - lastScope.$emit('$viewContentLoaded'); - lastScope.$eval(onloadExp); + link(lastScope); + lastScope.$emit('$viewContentLoaded'); + lastScope.$eval(onloadExp); - // $anchorScroll might listen on event... - $anchorScroll(); - } - }).error(clearContent); + // $anchorScroll might listen on event... + $anchorScroll(); } else { clearContent(); } diff --git a/test/lib/angular/version.txt b/test/lib/angular/version.txt index 7be385a..e1c61a4 100644 --- a/test/lib/angular/version.txt +++ b/test/lib/angular/version.txt @@ -1 +1 @@ -1.0.0rc10 +1.0.0rc11