vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.2.0 vs vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.2.1

- old
+ new

@@ -9788,11 +9788,11 @@ } })( window ); /** - * @license AngularJS v1.2.0 + * @license AngularJS v1.2.1 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document){ var _jQuery = window.jQuery.noConflict(true); @@ -9830,15 +9830,15 @@ var code = arguments[0], prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, stringify = function (obj) { - if (isFunction(obj)) { + if (typeof obj === 'function') { return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (isUndefined(obj)) { + } else if (typeof obj === 'undefined') { return 'undefined'; - } else if (!isString(obj)) { + } else if (typeof obj !== 'string') { return JSON.stringify(obj); } return obj; }, message, i; @@ -9846,23 +9846,23 @@ message = prefix + template.replace(/\{\d+\}/g, function (match) { var index = +match.slice(1, -1), arg; if (index + 2 < templateArgs.length) { arg = templateArgs[index + 2]; - if (isFunction(arg)) { + if (typeof arg === 'function') { return arg.toString().replace(/ ?\{[\s\S]*$/, ''); - } else if (isUndefined(arg)) { + } else if (typeof arg === 'undefined') { return 'undefined'; - } else if (!isString(arg)) { + } else if (typeof arg !== 'string') { return toJson(arg); } return arg; } return match; }); - message = message + '\nhttp://errors.angularjs.org/' + version.full + '/' + + message = message + '\nhttp://errors.angularjs.org/1.2.1/' + (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + encodeURIComponent(stringify(arguments[i])); } @@ -11219,10 +11219,11 @@ */ function setupModuleLoader(window) { var $injectorMinErr = minErr('$injector'); + var ngMinErr = minErr('ng'); function ensure(obj, name, factory) { return obj[name] || (obj[name] = factory()); } @@ -11279,10 +11280,16 @@ * @param {Function} configFn Optional configuration function for the module. Same as * {@link angular.Module#methods_config Module#config()}. * @returns {module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { + var assertNotHasOwnProperty = function(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } + }; + assertNotHasOwnProperty(name, 'module'); if (requires && modules.hasOwnProperty(name)) { modules[name] = null; } return ensure(modules, name, function() { @@ -11591,15 +11598,15 @@ * - `minor` – `{number}` – Minor version number, such as "9". * - `dot` – `{number}` – Dot version number, such as "18". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.2.0', // all of these placeholder strings will be replaced by grunt's + full: '1.2.1', // all of these placeholder strings will be replaced by grunt's major: 1, // package task - minor: "NG_VERSION_MINOR", - dot: 0, - codeName: 'timely-delivery' + minor: 2, + dot: 1, + codeName: 'underscore-empathy' }; function publishExternalAPI(angular){ extend(angular, { @@ -14486,12 +14493,13 @@ * * #### `priority` * When there are multiple directives defined on a single DOM element, sometimes it * is necessary to specify the order in which the directives are applied. The `priority` is used * to sort the directives before their `compile` functions get called. Priority is defined as a - * number. Directives with greater numerical `priority` are compiled first. The order of directives with - * the same priority is undefined. The default priority is `0`. + * number. Directives with greater numerical `priority` are compiled first. Pre-link functions + * are also run in priority order, but post-link functions are run in reverse order. The order + * of directives with the same priority is undefined. The default priority is `0`. * * #### `terminal` * If set to true then the current `priority` will be the last set of directives * which will execute (any directives at the current priority will still execute * as the order of execution on same `priority` is undefined). @@ -14548,12 +14556,13 @@ * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: * * * `$scope` - Current scope associated with the element * * `$element` - Current element * * `$attrs` - Current attributes object for the element - * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: - * `function(cloneLinkingFn)`. + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope. + * The scope can be overridden by an optional first argument. + * `function([scope], cloneLinkingFn)`. * * * #### `require` * Require another directive and inject its controller as the fourth argument to the linking function. The * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the @@ -14642,19 +14651,25 @@ * safe to do template transformation on the element and child elements only. * * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared * between all directive compile functions. * - * * `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`. + * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` * * <div class="alert alert-warning"> * **Note:** The template instance and the link instance may be different objects if the template has * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration * should be done in a linking function rather than in a compile function. * </div> * + * <div class="alert alert-error"> + * **Note:** The `transclude` function that is passed to the compile function is deperecated, as it + * e.g. does not know about the right outer scope. Please use the transclude function that is passed + * to the link function instead. + * </div> + * A compile function can have a return value which can be either a function or an object. * * * returning a (post-link) function - is equivalent to registering the linking function via the * `link` property of the config object when the compile function is empty. * @@ -14665,11 +14680,11 @@ * * #### `link` * This property is used only if the `compile` property is not defined. * * <pre> - * function link(scope, iElement, iAttrs, controller) { ... } + * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } * </pre> * * The link function is responsible for registering DOM listeners as well as updating the DOM. It is * executed after the template has been cloned. This is where most of the directive logic will be * put. @@ -14686,10 +14701,14 @@ * * * `controller` - a controller instance - A controller instance if at least one directive on the * element defines a controller. The controller is shared among all the directives, which allows * the directives to use the controllers as a communication channel. * + * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. + * The scope can be overridden by an optional first argument. This is the same as the `$transclude` + * parameter of directive controllers. + * `function([scope], cloneLinkingFn)`. * * * #### Pre-linking function * * Executed before the child elements are linked. Not safe to do DOM transformation since the @@ -15191,18 +15210,22 @@ } }); var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority, ignoreDirective, previousCompileContext); - return function publicLinkFn(scope, cloneConnectFn){ + return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){ assertArg(scope, 'scope'); // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. var $linkNode = cloneConnectFn ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! : $compileNodes; + forEach(transcludeControllers, function(instance, name) { + $linkNode.data('$' + name + 'Controller', instance); + }); + // Attach scope only to non-text nodes. for(var i = 0, ii = $linkNode.length; i<ii; i++) { var node = $linkNode[i]; if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) { $linkNode.eq(i).data('$scope', scope); @@ -15297,19 +15320,11 @@ childScope = scope; } childTranscludeFn = nodeLinkFn.transclude; if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) { nodeLinkFn(childLinkFn, childScope, node, $rootElement, - (function(transcludeFn) { - return function(cloneFn) { - var transcludeScope = scope.$new(); - transcludeScope.$$transcluded = true; - - return transcludeFn(transcludeScope, cloneFn). - on('$destroy', bind(transcludeScope, transcludeScope.$destroy)); - }; - })(childTranscludeFn || transcludeFn) + createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn) ); } else { nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn); } } else if (childLinkFn) { @@ -15317,11 +15332,28 @@ } } } } + function createBoundTranscludeFn(scope, transcludeFn) { + return function boundTranscludeFn(transcludedScope, cloneFn, controllers) { + var scopeCreated = false; + if (!transcludedScope) { + transcludedScope = scope.$new(); + transcludedScope.$$transcluded = true; + scopeCreated = true; + } + + var clone = transcludeFn(transcludedScope, cloneFn, controllers); + if (scopeCreated) { + clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy)); + } + return clone; + }; + } + /** * Looks for directives on the given node and adds them to the directive collection which is * sorted. * * @param node Node to search. @@ -15454,13 +15486,13 @@ * @param attrStart * @param attrEnd * @returns {Function} */ function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { - return function(scope, element, attrs, controllers) { + return function(scope, element, attrs, controllers, transcludeFn) { element = groupScan(element[0], attrStart, attrEnd); - return linkFn(scope, element, attrs, controllers); + return linkFn(scope, element, attrs, controllers, transcludeFn); }; } /** * Once the directives have been collected, their compile functions are executed. This method @@ -15493,11 +15525,13 @@ var terminalPriority = -Number.MAX_VALUE, newScopeDirective, controllerDirectives = previousCompileContext.controllerDirectives, newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, templateDirective = previousCompileContext.templateDirective, - transcludeDirective = previousCompileContext.transcludeDirective, + nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, + hasTranscludeDirective = false, + hasElementTranscludeDirective = false, $compileNode = templateAttrs.$$element = jqLite(compileNode), directive, directiveName, $template, replaceDirective = originalReplaceDirective, @@ -15544,19 +15578,22 @@ controllerDirectives[directiveName], directive, $compileNode); controllerDirectives[directiveName] = directive; } if (directiveValue = directive.transclude) { + hasTranscludeDirective = true; + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. // This option should only be used by directives that know how to how to safely handle element transclusion, // where the transcluded nodes are added or replaced after linking. if (!directive.$$tlb) { - assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); - transcludeDirective = directive; + assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); + nonTlbTranscludeDirective = directive; } if (directiveValue == 'element') { + hasElementTranscludeDirective = true; terminalPriority = directive.priority; $template = groupScan(compileNode, attrStart, attrEnd); $compileNode = templateAttrs.$$element = jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); @@ -15568,13 +15605,13 @@ // Don't pass in: // - controllerDirectives - otherwise we'll create duplicates controllers // - newIsolateScopeDirective or templateDirective - combining templates with // element transclusion doesn't make sense. // - // We need only transcludeDirective so that we prevent putting transclusion + // We need only nonTlbTranscludeDirective so that we prevent putting transclusion // on the same element more than once. - transcludeDirective: transcludeDirective + nonTlbTranscludeDirective: nonTlbTranscludeDirective }); } else { $template = jqLite(jqLiteClone(compileNode)).contents(); $compileNode.html(''); // clear contents childTranscludeFn = compile($template, transcludeFn); @@ -15639,11 +15676,11 @@ nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, templateAttrs, jqCollection, childTranscludeFn, preLinkFns, postLinkFns, { controllerDirectives: controllerDirectives, newIsolateScopeDirective: newIsolateScopeDirective, templateDirective: templateDirective, - transcludeDirective: transcludeDirective + nonTlbTranscludeDirective: nonTlbTranscludeDirective }); ii = directives.length; } else if (directive.compile) { try { linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); @@ -15663,11 +15700,11 @@ } } nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; - nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; + nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn; // might be normal or delayed nodeLinkFn depending on if templateUrl is present return nodeLinkFn; //////////////////// @@ -15690,46 +15727,45 @@ postLinkFns.push(post); } } - function getControllers(require, $element) { + function getControllers(require, $element, elementControllers) { var value, retrievalMethod = 'data', optional = false; if (isString(require)) { while((value = require.charAt(0)) == '^' || value == '?') { require = require.substr(1); if (value == '^') { retrievalMethod = 'inheritedData'; } optional = optional || value == '?'; } + value = null; - value = $element[retrievalMethod]('$' + require + 'Controller'); - - if ($element[0].nodeType == 8 && $element[0].$$controller) { // Transclusion comment node - value = value || $element[0].$$controller; - $element[0].$$controller = null; + if (elementControllers && retrievalMethod === 'data') { + value = elementControllers[require]; } + value = value || $element[retrievalMethod]('$' + require + 'Controller'); if (!value && !optional) { throw $compileMinErr('ctreq', "Controller '{0}', required by directive '{1}', can't be found!", require, directiveName); } return value; } else if (isArray(require)) { value = []; forEach(require, function(require) { - value.push(getControllers(require, $element)); + value.push(getControllers(require, $element, elementControllers)); }); } return value; } function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { - var attrs, $element, i, ii, linkFn, controller, isolateScope; + var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn; if (compileNode === linkNode) { attrs = templateAttrs; } else { attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); @@ -15819,36 +15855,36 @@ " Definition: {... {1}: '{2}' ...}", newIsolateScopeDirective.name, scopeName, definition); } }); } - + transcludeFn = boundTranscludeFn && controllersBoundTransclude; if (controllerDirectives) { forEach(controllerDirectives, function(directive) { var locals = { $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, $element: $element, $attrs: attrs, - $transclude: boundTranscludeFn + $transclude: transcludeFn }, controllerInstance; controller = directive.controller; if (controller == '@') { controller = attrs[directive.name]; } controllerInstance = $controller(controller, locals); - - // Directives with element transclusion and a controller need to attach controller - // to the comment node created by the compiler, but jQuery .data doesn't support - // attaching data to comment nodes so instead we set it directly on the element and - // remove it after we read it later. - if ($element[0].nodeType == 8) { // Transclusion comment node - $element[0].$$controller = controllerInstance; - } else { + // For directives with element transclusion the element is a comment, + // but jQuery .data doesn't support attaching data to comment nodes as it's hard to + // clean up (http://bugs.jquery.com/ticket/8335). + // Instead, we save the controllers for the element in a local hash and attach to .data + // later, once we have the actual element. + elementControllers[directive.name] = controllerInstance; + if (!hasElementTranscludeDirective) { $element.data('$' + directive.name + 'Controller', controllerInstance); } + if (directive.controllerAs) { locals.$scope[directive.controllerAs] = controllerInstance; } }); } @@ -15856,11 +15892,11 @@ // PRELINKING for(i = 0, ii = preLinkFns.length; i < ii; i++) { try { linkFn = preLinkFns[i]; linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); + linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn); } catch (e) { $exceptionHandler(e, startingTag($element)); } } @@ -15876,15 +15912,32 @@ // POSTLINKING for(i = postLinkFns.length - 1; i >= 0; i--) { try { linkFn = postLinkFns[i]; linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); + linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn); } catch (e) { $exceptionHandler(e, startingTag($element)); } } + + // This is the function that is injected as `$transclude`. + function controllersBoundTransclude(scope, cloneAttachFn) { + var transcludeControllers; + + // no scope passed + if (arguments.length < 2) { + cloneAttachFn = scope; + scope = undefined; + } + + if (hasElementTranscludeDirective) { + transcludeControllers = elementControllers; + } + + return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers); + } } } function markDirectivesAsIsolate(directives) { // mark all directives as needing isolate scope. @@ -15959,10 +16012,11 @@ if (key == 'class') { safeAddClass($element, value); dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; } else if (key == 'style') { $element.attr('style', $element.attr('style') + ';' + value); + dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; // `dst` will never contain hasOwnProperty as DOM parser won't let it. // You will get an "InvalidCharacterError: DOM Exception 5" error if you // have an attribute like "has-own-property" or "data-has-own-property", etc. } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { dst[key] = value; @@ -15989,11 +16043,11 @@ $compileNode.html(''); $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}). success(function(content) { - var compileNode, tempTemplateAttrs, $template; + var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; content = denormalizeTemplate(content); if (origAsyncDirective.replace) { $template = jqLite('<div>' + trim(content) + '</div>').contents(); @@ -16034,36 +16088,40 @@ while(linkQueue.length) { var scope = linkQueue.shift(), beforeTemplateLinkNode = linkQueue.shift(), linkRootElement = linkQueue.shift(), - controller = linkQueue.shift(), + boundTranscludeFn = linkQueue.shift(), linkNode = $compileNode[0]; if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { // it was cloned therefore we have to clone as well. linkNode = jqLiteClone(compileNode); replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); } - + if (afterTemplateNodeLinkFn.transclude) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude); + } else { + childBoundTranscludeFn = boundTranscludeFn; + } afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, - controller); + childBoundTranscludeFn); } linkQueue = null; }). error(function(response, code, headers, config) { throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url); }); - return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { if (linkQueue) { linkQueue.push(scope); linkQueue.push(node); linkQueue.push(rootElement); - linkQueue.push(controller); + linkQueue.push(boundTranscludeFn); } else { - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller); + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn); } }; } @@ -16764,24 +16822,30 @@ * into `$http`. * * * # Caching * - * To enable caching, set the configuration property `cache` to `true`. When the cache is - * enabled, `$http` stores the response from the server in local cache. Next time the - * response is served from the cache without sending a request to the server. + * To enable caching, set the request configuration `cache` property to `true` (to use default + * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). + * When the cache is enabled, `$http` stores the response from the server in the specified + * cache. The next time the same request is made, the response is served from the cache without + * sending a request to the server. * * Note that even if the response is served from cache, delivery of the data is asynchronous in * the same way that real requests are. * * If there are multiple GET requests for the same URL that should be cached using the same * cache, but the cache is not populated yet, only one request to the server will be made and * the remaining requests will be fulfilled using the response from the first request. * - * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache. - * To skip it, set configuration property `cache` to `false`. + * You can change the default cache to a new object (built with + * {@link ng.$cacheFactory `$cacheFactory`}) by updating the + * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set + * their `cache` property to `true` will now use this cache object. * + * If you set the default cache to `false` then only requests that specify their own custom + * cache object will be cached. * * # Interceptors * * Before you start creating interceptors, be sure to understand the * {@link ng.$q $q and deferred/promise APIs}. @@ -18041,25 +18105,25 @@ } return segments.join('/'); } -function parseAbsoluteUrl(absoluteUrl, locationObj) { - var parsedUrl = urlResolve(absoluteUrl); +function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { + var parsedUrl = urlResolve(absoluteUrl, appBase); locationObj.$$protocol = parsedUrl.protocol; locationObj.$$host = parsedUrl.hostname; locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; } -function parseAppUrl(relativeUrl, locationObj) { +function parseAppUrl(relativeUrl, locationObj, appBase) { var prefixed = (relativeUrl.charAt(0) !== '/'); if (prefixed) { relativeUrl = '/' + relativeUrl; } - var match = urlResolve(relativeUrl); + var match = urlResolve(relativeUrl, appBase); locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname); locationObj.$$search = parseKeyValue(match.search); locationObj.$$hash = decodeURIComponent(match.hash); @@ -18110,11 +18174,11 @@ */ function LocationHtml5Url(appBase, basePrefix) { this.$$html5 = true; basePrefix = basePrefix || ''; var appBaseNoFile = stripFile(appBase); - parseAbsoluteUrl(appBase, this); + parseAbsoluteUrl(appBase, this, appBase); /** * Parse given html5 (regular) url string into properties * @param {string} newAbsoluteUrl HTML5 url @@ -18125,11 +18189,11 @@ if (!isString(pathUrl)) { throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, appBaseNoFile); } - parseAppUrl(pathUrl, this); + parseAppUrl(pathUrl, this, appBase); if (!this.$$path) { this.$$path = '/'; } @@ -18177,11 +18241,11 @@ * @param {string} hashPrefix hashbang prefix */ function LocationHashbangUrl(appBase, hashPrefix) { var appBaseNoFile = stripFile(appBase); - parseAbsoluteUrl(appBase, this); + parseAbsoluteUrl(appBase, this, appBase); /** * Parse given hashbang url into properties * @param {string} url Hashbang url @@ -18197,11 +18261,11 @@ if (!isString(withoutHashUrl)) { throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, hashPrefix); } - parseAppUrl(withoutHashUrl, this); + parseAppUrl(withoutHashUrl, this, appBase); this.$$compose(); }; /** * Compose hashbang url and update `absUrl` property @@ -18845,27 +18909,22 @@ // Sandboxing Angular Expressions // ------------------------------ // Angular expressions are generally considered safe because these expressions only have direct // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by -// obtaining a reference to native JS functions such as the Function constructor, the global Window -// or Document object. In addition, many powerful functions for use by JavaScript code are -// published on scope that shouldn't be available from within an Angular expression. +// obtaining a reference to native JS functions such as the Function constructor. // // As an example, consider the following Angular expression: // // {}.toString.constructor(alert("evil JS code")) // // We want to prevent this type of access. For the sake of performance, during the lexing phase we -// disallow any "dotted" access to any member named "constructor" or to any member whose name begins -// or ends with an underscore. The latter allows one to exclude the private / JavaScript only API -// available on the scope and controllers from the context of an Angular expression. +// disallow any "dotted" access to any member named "constructor". // -// For reflective calls (a[b]), we check that the value of the lookup is not the Function -// constructor, Window or DOM node while evaluating the expression, which is a stronger but more -// expensive test. Since reflective calls are expensive anyway, this is not such a big deal compared -// to static dereferencing. +// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor +// while evaluating the expression, which is a stronger but more expensive test. Since reflective +// calls are expensive anyway, this is not such a big deal compared to static dereferencing. // // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits // against the expression language, but not to prevent exploits that were enabled by exposing // sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good // practice and therefore we are not even trying to protect against interaction with an object @@ -18875,24 +18934,16 @@ // name on the scope. // // In general, it is not possible to access a Window object from an angular expression unless a // window or some DOM object that has a reference to window is published onto a Scope. -function ensureSafeMemberName(name, fullExpression, allowConstructor) { - if (typeof name !== 'string' && toString.apply(name) !== "[object String]") { - return name; - } - if (name === "constructor" && !allowConstructor) { +function ensureSafeMemberName(name, fullExpression) { + if (name === "constructor") { throw $parseMinErr('isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}', fullExpression); } - if (name.charAt(0) === '_' || name.charAt(name.length-1) === '_') { - throw $parseMinErr('isecprv', - 'Referencing private fields in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } return name; } function ensureSafeObject(obj, fullExpression) { // nifty check if obj is Function that is fast and works across iframes and other contexts @@ -19572,14 +19623,11 @@ var indexFn = this.expression(); this.consume(']'); return extend(function(self, locals) { var o = obj(self, locals), - // In the getter, we will not block looking up "constructor" by name in order to support user defined - // constructors. However, if value looked up is the Function constructor, we will still block it in the - // ensureSafeObject call right after we look up o[i] (a few lines below.) - i = ensureSafeMemberName(indexFn(self, locals), parser.text, true /* allowConstructor */), + i = indexFn(self, locals), v, p; if (!o) return undefined; v = ensureSafeObject(o[i], parser.text); if (v && v.then && parser.options.unwrapPromises) { @@ -19591,11 +19639,11 @@ v = v.$$v; } return v; }, { assign: function(self, value, locals) { - var key = ensureSafeMemberName(indexFn(self, locals), parser.text); + var key = indexFn(self, locals); // prevent overwriting of Function.constructor which would break ensureSafeObject check var safe = ensureSafeObject(obj(self, locals), parser.text); return safe[key] = value; } }); @@ -20251,11 +20299,11 @@ * expect(resolvedValue).toBeUndefined(); * * // Propagate promise resolution to 'then' functions using $apply(). * $rootScope.$apply(); * expect(resolvedValue).toEqual(123); - * }); + * })); * </pre> */ function $QProvider() { this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { @@ -22068,14 +22116,14 @@ * * Here's an example of a binding in a privileged context: * * <pre class="prettyprint"> * <input ng-model="userHtml"> - * <div ng-bind-html="{{userHtml}}"> + * <div ng-bind-html="userHtml"> * </pre> * - * Notice that `ng-bind-html` is bound to `{{userHtml}}` controlled by the user. With SCE + * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE * disabled, this application allows the user to render arbitrary HTML into the DIV. * In a more realistic example, one may be rendering user comments, blog articles, etc. via * bindings. (HTML is just one example of a context where rendering user controlled input creates * security vulnerabilities.) * @@ -22992,12 +23040,18 @@ // cause us to break tests. In addition, when the browser resolves a URL for XHR, it // doesn't know about mocked locations and resolves URLs to the real document - which is // exactly the behavior needed here. There is little value is mocking these out for this // service. var urlParsingNode = document.createElement("a"); +/* +Matches paths for file protocol on windows, +such as /C:/foo/bar, and captures only /foo/bar. +*/ +var windowsFilePathExp = /^\/?.*?:(\/.*)/; var originUrl = urlResolve(window.location.href, true); + /** * * Implementation Notes for non-IE browsers * ---------------------------------------- * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, @@ -23012,11 +23066,11 @@ * --------------------------- * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other * browsers. However, the parsed components will not be set if the URL assigned did not specify * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We * work around that by performing the parsing in a 2nd step by taking a previously normalized - * URL (e.g. by assining to a.href) and assigning it a.href again. This correctly populates the + * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the * properties such as protocol, hostname, port, etc. * * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one * uses the inner HTML approach to assign the URL as part of an HTML snippet - * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL. @@ -23046,32 +23100,48 @@ * | hostname | The hostname * | port | The port, without ":" * | pathname | The pathname, beginning with "/" * */ -function urlResolve(url) { - var href = url; +function urlResolve(url, base) { + var href = url, + pathname; + if (msie) { // Normalize before parse. Refer Implementation Notes on why this is // done in two steps on IE. urlParsingNode.setAttribute("href", href); href = urlParsingNode.href; } urlParsingNode.setAttribute('href', href); - // $$urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + /* + * In Windows, on an anchor node on documents loaded from + * the filesystem, the browser will return a pathname + * prefixed with the drive name ('/C:/path') when a + * pathname without a drive is set: + * * a.setAttribute('href', '/foo') + * * a.pathname === '/C:/foo' //true + * + * Inside of Angular, we're always using pathnames that + * do not include drive names for routing. + */ + + pathname = removeWindowsDriveName(urlParsingNode.pathname, url, base); + pathname = (pathname.charAt(0) === '/') ? pathname : '/' + pathname; + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils return { href: urlParsingNode.href, protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', host: urlParsingNode.host, search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', hostname: urlParsingNode.hostname, port: urlParsingNode.port, - pathname: urlParsingNode.pathname && urlParsingNode.pathname.charAt(0) === '/' ? - urlParsingNode.pathname : '/' + urlParsingNode.pathname + pathname: pathname }; } /** @@ -23085,10 +23155,30 @@ var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; return (parsed.protocol === originUrl.protocol && parsed.host === originUrl.host); } +function removeWindowsDriveName (path, url, base) { + var firstPathSegmentMatch; + + //Get the relative path from the input URL. + if (url.indexOf(base) === 0) { + url = url.replace(base, ''); + } + + /* + * The input URL intentionally contains a + * first path segment that ends with a colon. + */ + if (windowsFilePathExp.exec(url)) { + return path; + } + + firstPathSegmentMatch = windowsFilePathExp.exec(path); + return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; +} + /** * @ngdoc object * @name ng.$window * * @description @@ -24601,11 +24691,11 @@ * @property {boolean} $invalid True if at least one containing control or form is invalid. * * @property {Object} $error Is an object hash, containing references to all invalid controls or * forms, where: * - * - keys are validation tokens (error names) — such as `required`, `url` or `email`), + * - keys are validation tokens (error names) — such as `required`, `url` or `email`, * - values are arrays of controls or forms that are invalid with given error. * * @description * `FormController` keeps track of all its controls and nested forms as well as state of them, * such as being valid/invalid or dirty/pristine. @@ -25786,10 +25876,15 @@ * return value.toUpperCase(); * } * } * ngModel.$formatters.push(formatter); * </pre> + * + * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the + * view value has changed. It is called with no arguments, and its return value is ignored. + * This can be used in place of additional $watches against the model value. + * * @property {Object} $error An object hash with all errors as keys. * * @property {boolean} $pristine True if user has not interacted with the control yet. * @property {boolean} $dirty True if user has already interacted with the control. * @property {boolean} $valid True if there is no error. @@ -26049,19 +26144,24 @@ * @ngdoc function * @name ng.directive:ngModel.NgModelController#$setViewValue * @methodOf ng.directive:ngModel.NgModelController * * @description - * Read a value from view. + * Update the view value. * - * This method should be called from within a DOM event handler. - * For example {@link ng.directive:input input} or + * This method should be called when the view value changes, typically from within a DOM event handler. + * For example {@link ng.directive:input input} and * {@link ng.directive:select select} directives call it. * - * It internally calls all `$parsers` (including validators) and updates the `$modelValue` and the actual model path. - * Lastly it calls all registered change listeners. + * It will update the $viewValue, then pass this value through each of the functions in `$parsers`, + * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to + * `$modelValue` and the **expression** specified in the `ng-model` attribute. + * + * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called. * + * Note that calling this function does not trigger a `$digest`. + * * @param {string} value Value from the view. */ this.$setViewValue = function(value) { this.$viewValue = value; @@ -26558,31 +26658,37 @@ * * @element ANY * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. * * @example - * Try it here: enter text in text box and watch the greeting change. - <doc:example module="ngBindHtmlExample" deps="angular-sanitize.js" > - <doc:source> - <script> - angular.module('ngBindHtmlExample', ['ngSanitize']) - - .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) { - $scope.myHTML = 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'; - }]); - </script> + Try it here: enter text in text box and watch the greeting change. + + <example module="ngBindHtmlExample" deps="angular-sanitize.js"> + <file name="index.html"> <div ng-controller="ngBindHtmlCtrl"> <p ng-bind-html="myHTML"></p> </div> - </doc:source> - <doc:scenario> + </file> + + <file name="script.js"> + angular.module('ngBindHtmlExample', ['ngSanitize']) + + .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) { + $scope.myHTML = + 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'; + }]); + </file> + + <file name="scenario.js"> it('should check ng-bind-html', function() { expect(using('.doc-example-live').binding('myHTML')). - toBe('I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'); + toBe( + 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>' + ); }); - </doc:scenario> - </doc:example> + </file> + </example> */ var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { return function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBindHtml); @@ -26691,22 +26797,22 @@ * element. * * @example Example that demonstrates basic bindings via ngClass directive. <example> <file name="index.html"> - <p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p> - <input type="checkbox" ng-model="bold"> bold - <input type="checkbox" ng-model="strike"> strike - <input type="checkbox" ng-model="red"> red + <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p> + <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br> + <input type="checkbox" ng-model="important"> important (apply "bold" class)<br> + <input type="checkbox" ng-model="error"> error (apply "red" class) <hr> <p ng-class="style">Using String Syntax</p> <input type="text" ng-model="style" placeholder="Type: bold strike red"> <hr> <p ng-class="[style1, style2, style3]">Using Array Syntax</p> - <input ng-model="style1" placeholder="Type: bold"><br> - <input ng-model="style2" placeholder="Type: strike"><br> - <input ng-model="style3" placeholder="Type: red"><br> + <input ng-model="style1" placeholder="Type: bold, strike or red"><br> + <input ng-model="style2" placeholder="Type: bold, strike or red"><br> + <input ng-model="style3" placeholder="Type: bold, strike or red"><br> </file> <file name="style.css"> .strike { text-decoration: line-through; } @@ -26721,14 +26827,14 @@ it('should let you toggle the class', function() { expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/); expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/); - input('bold').check(); + input('important').check(); expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/); - input('red').check(); + input('error').check(); expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/); }); it('should let you toggle string example', function() { expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe(''); @@ -27570,11 +27676,11 @@ border:1px solid black; padding:10px; } /&#42; - The transition styles can also be placed on the CSS base class above + The transition styles can also be placed on the CSS base class above &#42;/ .animate-if.ng-enter, .animate-if.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } @@ -27596,26 +27702,25 @@ transclude: 'element', priority: 600, terminal: true, restrict: 'A', $$tlb: true, - compile: function (element, attr, transclude) { - return function ($scope, $element, $attr) { + link: function ($scope, $element, $attr, ctrl, $transclude) { var block, childScope; $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { if (toBoolean(value)) { - - childScope = $scope.$new(); - transclude(childScope, function (clone) { - block = { - startNode: clone[0], - endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ') - }; - $animate.enter(clone, $element.parent(), $element); - }); - + if (!childScope) { + childScope = $scope.$new(); + $transclude(childScope, function (clone) { + block = { + startNode: clone[0], + endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ') + }; + $animate.enter(clone, $element.parent(), $element); + }); + } } else { if (childScope) { childScope.$destroy(); childScope = null; @@ -27625,11 +27730,10 @@ $animate.leave(getBlockElements(block)); block = null; } } }); - }; } }; }]; /** @@ -27784,16 +27888,16 @@ return { restrict: 'ECA', priority: 400, terminal: true, transclude: 'element', - compile: function(element, attr, transclusion) { + compile: function(element, attr) { var srcExp = attr.ngInclude || attr.src, onloadExp = attr.onload || '', autoScrollExp = attr.autoscroll; - return function(scope, $element) { + return function(scope, $element, $attr, ctrl, $transclude) { var changeCounter = 0, currentScope, currentElement; var cleanupLastIncludeContent = function() { @@ -27818,11 +27922,11 @@ if (src) { $http.get(src, {cache: $templateCache}).success(function(response) { if (thisChangeId !== changeCounter) return; var newScope = scope.$new(); - transclusion(newScope, function(clone) { + $transclude(newScope, function(clone) { cleanupLastIncludeContent(); currentScope = newScope; currentElement = clone; @@ -28347,12 +28451,11 @@ return { transclude: 'element', priority: 1000, terminal: true, $$tlb: true, - compile: function(element, attr, linker) { - return function($scope, $element, $attr){ + link: function($scope, $element, $attr, ctrl, $transclude){ var expression = $attr.ngRepeat; var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, hashFnLocals = {$id: hashKey}; @@ -28510,11 +28613,11 @@ // jshint bitwise: false childScope.$odd = !(childScope.$even = (index&1) === 0); // jshint bitwise: true if (!block.startNode) { - linker(childScope, function(clone) { + $transclude(childScope, function(clone) { clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); $animate.enter(clone, null, jqLite(previousNode)); previousNode = clone; block.scope = childScope; block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0]; @@ -28523,11 +28626,10 @@ }); } } lastBlockMap = nextBlockMap; }); - }; } }; }]; /** @@ -29032,27 +29134,25 @@ var ngSwitchWhenDirective = ngDirective({ transclude: 'element', priority: 800, require: '^ngSwitch', - compile: function(element, attrs, transclude) { - return function(scope, element, attr, ctrl) { + compile: function(element, attrs) { + return function(scope, element, attr, ctrl, $transclude) { ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); - ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: transclude, element: element }); + ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); }; } }); var ngSwitchDefaultDirective = ngDirective({ transclude: 'element', priority: 800, require: '^ngSwitch', - compile: function(element, attrs, transclude) { - return function(scope, element, attr, ctrl) { - ctrl.cases['?'] = (ctrl.cases['?'] || []); - ctrl.cases['?'].push({ transclude: transclude, element: element }); - }; - } + link: function(scope, element, attr, ctrl, $transclude) { + ctrl.cases['?'] = (ctrl.cases['?'] || []); + ctrl.cases['?'].push({ transclude: $transclude, element: element }); + } }); /** * @ngdoc directive * @name ng.directive:ngTransclude \ No newline at end of file