diff options
| author | Guillaume Horel <guillaume.horel@gmail.com> | 2012-05-01 23:10:36 -0400 |
|---|---|---|
| committer | Guillaume Horel <guillaume.horel@gmail.com> | 2012-05-01 23:10:36 -0400 |
| commit | 40c7d904c328179782745e8452be2a596d29c521 (patch) | |
| tree | 7e39fb13f37d55ed0b2f2f18220dc65f898252e9 /alias-angular/app/lib/angular/angular.js | |
| parent | 3fdb17a79de4507e8a4f3fbe47f6c078bdedda46 (diff) | |
| download | alias-40c7d904c328179782745e8452be2a596d29c521.tar.gz | |
bump angular to rc7
Diffstat (limited to 'alias-angular/app/lib/angular/angular.js')
| -rw-r--r-- | alias-angular/app/lib/angular/angular.js | 427 |
1 files changed, 321 insertions, 106 deletions
diff --git a/alias-angular/app/lib/angular/angular.js b/alias-angular/app/lib/angular/angular.js index b621c96..dca7169 100644 --- a/alias-angular/app/lib/angular/angular.js +++ b/alias-angular/app/lib/angular/angular.js @@ -1,6 +1,6 @@ /** - * @license AngularJS v1.0.0rc5 - * (c) 2010-2012 AngularJS http://angularjs.org + * @license AngularJS v1.0.0rc7 + * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document, undefined) { @@ -8,9 +8,6 @@ //////////////////////////////////// -if (typeof document.getAttribute == 'undefined') - document.getAttribute = function() {}; - /** * @ngdoc function * @name angular.lowercase @@ -1246,11 +1243,11 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.0.0rc5', // all of these placeholder strings will be replaced by rake's + full: '1.0.0rc7', // all of these placeholder strings will be replaced by rake's major: 1, // compile task minor: 0, dot: 0, - codeName: 'reality-distortion' + codeName: 'rc-generation' }; @@ -1308,6 +1305,7 @@ function publishExternalAPI(angular){ ngClass: ngClassDirective, ngClassEven: ngClassEvenDirective, ngClassOdd: ngClassOddDirective, + ngCsp: ngCspDirective, ngCloak: ngCloakDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, @@ -1876,8 +1874,8 @@ function createEventHandler(element) { // Remove monkey-patched methods (IE), // as they would cause memory leaks in IE8. - if (msie < 8) { - // IE7 does not allow to delete property on native object + if (msie <= 8) { + // IE7/8 does not allow to delete property on native object event.preventDefault = null; event.stopPropagation = null; event.isDefaultPrevented = null; @@ -1921,14 +1919,12 @@ forEach({ bindFn(element, 'mouseover', function(event) { counter++; if (counter == 1) { - event.type = 'mouseenter'; mouseenter(event); } }); bindFn(element, 'mouseout', function(event) { counter --; if (counter == 0) { - event.type = 'mouseleave'; mouseleave(event); } }); @@ -3365,7 +3361,7 @@ function $TemplateCacheProvider() { * Calling the linking function returns the element of the template. It is either the original element * passed in, or the clone of the element if the `cloneAttachFn` is provided. * - * After linking the view is not updateh until after a call to $digest which typically is done by + * After linking the view is not updated until after a call to $digest which typically is done by * Angular automatically. * * If you need access to the bound view, there are two ways to do it: @@ -3742,12 +3738,17 @@ function $CompileProvider($provide) { addTextInterpolateDirective(directives, node.nodeValue); break; case 8: /* Comment */ - match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); - if (match) { - nName = directiveNormalize(match[1]); - if (addDirective(directives, nName, 'M', maxPriority)) { - attrs[nName] = trim(match[2]); + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority)) { + attrs[nName] = trim(match[2]); + } } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) } break; } @@ -5275,7 +5276,7 @@ var OPERATORS = { }; var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -function lex(text){ +function lex(text, csp){ var tokens = [], token, index = 0, @@ -5435,7 +5436,7 @@ function lex(text){ if (OPERATORS.hasOwnProperty(ident)) { token.fn = token.json = OPERATORS[ident]; } else { - var getter = getterFn(ident); + var getter = getterFn(ident, csp); token.fn = extend(function(self, locals) { return (getter(self, locals)); }, { @@ -5509,10 +5510,10 @@ function lex(text){ ///////////////////////////////////////// -function parser(text, json, $filter){ +function parser(text, json, $filter, csp){ var ZERO = valueFn(0), value, - tokens = lex(text), + tokens = lex(text, csp), assignment = _assignment, functionCall = _functionCall, fieldAccess = _fieldAccess, @@ -5780,7 +5781,7 @@ function parser(text, json, $filter){ function _fieldAccess(object) { var field = expect().text; - var getter = getterFn(field); + var getter = getterFn(field, csp); return extend( function(self, locals) { return getter(object(self, locals), locals); @@ -5933,32 +5934,119 @@ function getter(obj, path, bindFnToScope) { var getterFnCache = {}; -function getterFn(path) { +/** + * Implementation of the "Black Hole" variant from: + * - http://jsperf.com/angularjs-parse-getter/4 + * - http://jsperf.com/path-evaluation-simplified/7 + */ +function cspSafeGetterFn(key0, key1, key2, key3, key4) { + return function(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, + promise; + + if (!pathVal) return pathVal; + + pathVal = pathVal[key0]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key1 || !pathVal) return pathVal; + + pathVal = pathVal[key1]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key2 || !pathVal) return pathVal; + + pathVal = pathVal[key2]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key3 || !pathVal) return pathVal; + + pathVal = pathVal[key3]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key4 || !pathVal) return pathVal; + + pathVal = pathVal[key4]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + return pathVal; + }; +}; + +function getterFn(path, csp) { if (getterFnCache.hasOwnProperty(path)) { return getterFnCache[path]; } - var fn, code = 'var l, fn, p;\n'; - forEach(path.split('.'), function(key, index) { - code += 'if(!s) return s;\n' + - 'l=s;\n' + - 's='+ (index - // we simply direference 's' on any .dot notation - ? 's' - // but if we are first then we check locals firs, and if so read it first - : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + - 'if (s && s.then) {\n' + - ' if (!("$$v" in s)) {\n' + - ' p=s;\n' + - ' p.$$v = undefined;\n' + - ' p.then(function(v) {p.$$v=v;});\n' + - '}\n' + - ' s=s.$$v\n' + - '}\n'; - }); - code += 'return s;'; - fn = Function('s', 'k', code); - fn.toString = function() { return code; }; + var pathKeys = path.split('.'), + pathKeysLength = pathKeys.length, + fn; + + if (csp) { + fn = (pathKeysLength < 6) + ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) + : function(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn( + pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] + )(scope, locals); + locals = undefined; // clear after first iteration + } while (i < pathKeysLength); + }; + } else { + var code = 'var l, fn, p;\n'; + forEach(pathKeys, function(key, index) { + code += 'if(!s) return s;\n' + + 'l=s;\n' + + 's='+ (index + // we simply dereference 's' on any .dot notation + ? 's' + // but if we are first then we check locals first, and if so read it first + : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + + 'if (s && s.then) {\n' + + ' if (!("$$v" in s)) {\n' + + ' p=s;\n' + + ' p.$$v = undefined;\n' + + ' p.then(function(v) {p.$$v=v;});\n' + + '}\n' + + ' s=s.$$v\n' + + '}\n'; + }); + code += 'return s;'; + fn = Function('s', 'k', code); // s=scope, k=locals + fn.toString = function() { return code; }; + } return getterFnCache[path] = fn; } @@ -5967,13 +6055,13 @@ function getterFn(path) { function $ParseProvider() { var cache = {}; - this.$get = ['$filter', function($filter) { + this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { return function(exp) { switch(typeof exp) { case 'string': return cache.hasOwnProperty(exp) ? cache[exp] - : cache[exp] = parser(exp, false, $filter); + : cache[exp] = parser(exp, false, $filter, $sniffer.csp); case 'function': return exp; default: @@ -7549,7 +7637,9 @@ function $SnifferProvider() { } return eventSupport[event]; - } + }, + // TODO(i): currently there is no way to feature detect CSP without triggering alerts + csp: false }; }]; } @@ -11264,7 +11354,7 @@ var ngValueDirective = [function() { * * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when * it's desirable to put bindings into template that is momentarily displayed by the browser in its - * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes make the + * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the * bindings invisible to the user while the page is loading. * * An alternative solution to this problem would be using the @@ -11371,10 +11461,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { * @description * Creates a binding that will innerHTML the result of evaluating the `expression` into the current * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if - * {@link angular.module.ng.$compileProvider.directive.ngBindHtml ngBindHtml} directive is too + * {@link angular.module.ngSanitize.directive.ngBindHtml ngBindHtml} directive is too * restrictive and when you absolutely trust the source of the content you are binding to. * - * See {@link angular.module.ng.$sanitize $sanitize} docs for examples. + * See {@link angular.module.ngSanitize.$sanitize $sanitize} docs for examples. * * @element ANY * @param {expression} ngBindHtmlUnsafe {@link guide/dev_guide.expressions Expression} to evaluate. @@ -11694,6 +11784,32 @@ var ngControllerDirective = [function() { /** * @ngdoc directive + * @name angular.module.ng.$compileProvider.directive.ngCsp + * @priority 1000 + * + * @description + * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * This directive should be used on the root element of the application (typically the `<html>` + * element or other element with the {@link angular.module.ng.$compileProvider.directive.ngApp ngApp} + * directive). + * + * If enabled the performance of template expression evaluator will suffer slightly, so don't enable + * this mode unless you need it. + * + * @element html + */ + +var ngCspDirective = ['$sniffer', function($sniffer) { + return { + priority: 1000, + compile: function() { + $sniffer.csp = true; + } + }; +}]; + +/** + * @ngdoc directive * @name angular.module.ng.$compileProvider.directive.ngClick * * @description @@ -12982,8 +13098,11 @@ var scriptDirective = ['$templateCache', function($templateCache) { terminal: true, compile: function(element, attr) { if (attr.type == 'text/ng-template') { - var templateUrl = attr.id; - $templateCache.put(templateUrl, element.text()); + var templateUrl = attr.id, + // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent + text = element[0].text; + + $templateCache.put(templateUrl, text); } } }; @@ -13011,16 +13130,10 @@ var scriptDirective = ['$templateCache', function($templateCache) { * be nested into the `<select>` element. This element will then represent `null` or "not selected" * option. See example below for demonstration. * - * Note: `ngOptions` provides iterator facility for `<option>` element which must be used instead - * of {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}. `ngRepeat` is not - * suitable for use with `<option>` element because of the following reasons: - * - * * value attribute of the option element that we need to bind to requires a string, but the - * source of data for the iteration might be in a form of array containing objects instead of - * strings - * * {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} unrolls after the - * select binds causing incorect rendering on most browsers. - * * binding to a value not in list confuses most browsers. + * Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead + * of {@link angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} when you want the + * `select` model to be bound to a non-string value. This is because an option element can currently + * be bound to string values only. * * @param {string} name assignable expression to data-bind to. * @param {string=} required The control is considered valid only if value is entered. @@ -13081,11 +13194,11 @@ var scriptDirective = ['$templateCache', function($templateCache) { <select ng-model="color" ng-options="c.name for c in colors"></select><br> Color (null allowed): - <div class="nullable"> + <span class="nullable"> <select ng-model="color" ng-options="c.name for c in colors"> <option value="">-- chose color --</option> </select> - </div><br/> + </span><br/> Color grouped by shade: <select ng-model="color" ng-options="c.name group by c.shade for c in colors"> @@ -13115,49 +13228,136 @@ var scriptDirective = ['$templateCache', function($templateCache) { var ngOptionsDirective = valueFn({ terminal: true }); var selectDirective = ['$compile', '$parse', function($compile, $parse) { //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777 - var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/; + var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/, + nullModelCtrl = {$setViewValue: noop}; return { restrict: 'E', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (!ctrl) return; + require: ['select', '?ngModel'], + controller: ['$element', '$scope', function($element, $scope) { + var self = this, + optionsMap = {}, + ngModelCtrl = nullModelCtrl, + nullOption, + unknownOption; - var multiple = attr.multiple, - optionsExp = attr.ngOptions; + self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { + ngModelCtrl = ngModelCtrl_; + nullOption = nullOption_; + unknownOption = unknownOption_; + } + + + self.addOption = function(value) { + optionsMap[value] = true; + + if (ngModelCtrl.$viewValue == value) { + $element.val(value); + if (unknownOption.parent()) unknownOption.remove(); + } + }; + + + self.removeOption = function(value) { + if (this.hasOption(value)) { + delete optionsMap[value]; + if (ngModelCtrl.$viewValue == value) { + this.renderUnknownOption(value); + } + } + }; + + + self.renderUnknownOption = function(val) { + var unknownVal = '? ' + hashKey(val) + ' ?'; + unknownOption.val(unknownVal); + $element.prepend(unknownOption); + $element.val(unknownVal); + unknownOption.prop('selected', true); // needed for IE + } + + + self.hasOption = function(value) { + return optionsMap.hasOwnProperty(value); + } + + $scope.$on('$destroy', function() { + // disable unknown option so that we don't do work when the whole select is being destroyed + self.renderUnknownOption = noop; + }); + }], + + link: function(scope, element, attr, ctrls) { + // if ngModel is not defined, we don't need to do anything + if (!ctrls[1]) return; + + var selectCtrl = ctrls[0], + ngModelCtrl = ctrls[1], + multiple = attr.multiple, + optionsExp = attr.ngOptions, + nullOption = false, // if false, user will not be able to select it (used by ngOptions) + emptyOption, + // we can't just jqLite('<option>') since jqLite is not smart enough + // to create it in <select> and IE barfs otherwise. + optionTemplate = jqLite(document.createElement('option')), + optGroupTemplate =jqLite(document.createElement('optgroup')), + unknownOption = optionTemplate.clone(); + + // find "null" option + for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) { + if (children[i].value == '') { + emptyOption = nullOption = children.eq(i); + break; + } + } + + selectCtrl.init(ngModelCtrl, nullOption, unknownOption); // required validator if (multiple && (attr.required || attr.ngRequired)) { var requiredValidator = function(value) { - ctrl.$setValidity('required', !attr.required || (value && value.length)); + ngModelCtrl.$setValidity('required', !attr.required || (value && value.length)); return value; }; - ctrl.$parsers.push(requiredValidator); - ctrl.$formatters.unshift(requiredValidator); + ngModelCtrl.$parsers.push(requiredValidator); + ngModelCtrl.$formatters.unshift(requiredValidator); attr.$observe('required', function() { - requiredValidator(ctrl.$viewValue); + requiredValidator(ngModelCtrl.$viewValue); }); } - if (optionsExp) Options(scope, element, ctrl); - else if (multiple) Multiple(scope, element, ctrl); - else Single(scope, element, ctrl); + if (optionsExp) Options(scope, element, ngModelCtrl); + else if (multiple) Multiple(scope, element, ngModelCtrl); + else Single(scope, element, ngModelCtrl, selectCtrl); //////////////////////////// - function Single(scope, selectElement, ctrl) { - ctrl.$render = function() { - selectElement.val(ctrl.$viewValue); + function Single(scope, selectElement, ngModelCtrl, selectCtrl) { + ngModelCtrl.$render = function() { + var viewValue = ngModelCtrl.$viewValue; + + if (selectCtrl.hasOption(viewValue)) { + if (unknownOption.parent()) unknownOption.remove(); + selectElement.val(viewValue); + if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy + } else { + if (isUndefined(viewValue) && emptyOption) { + selectElement.val(''); + } else { + selectCtrl.renderUnknownOption(viewValue); + } + } }; selectElement.bind('change', function() { scope.$apply(function() { - ctrl.$setViewValue(selectElement.val()); + if (unknownOption.parent()) unknownOption.remove(); + ngModelCtrl.$setViewValue(selectElement.val()); }); }); } @@ -13208,26 +13408,26 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { groupByFn = $parse(match[3] || ''), valueFn = $parse(match[2] ? match[1] : valueName), valuesFn = $parse(match[7]), - // we can't just jqLite('<option>') since jqLite is not smart enough - // to create it in <select> and IE barfs otherwise. - optionTemplate = jqLite(document.createElement('option')), - optGroupTemplate = jqLite(document.createElement('optgroup')), - nullOption = false, // if false then user will not be able to select it // This is an array of array of existing option groups in DOM. We try to reuse these if possible // optionGroupsCache[0] is the options with no option group // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element optionGroupsCache = [[{element: selectElement, label:''}]]; - // find existing special options - forEach(selectElement.children(), function(option) { - if (option.value == '') { - // developer declared null option, so user should be able to select it - nullOption = jqLite(option).remove(); - // compile the element since there might be bindings in it - $compile(nullOption)(scope); - } - }); - selectElement.html(''); // clear contents + if (nullOption) { + // compile the element since there might be bindings in it + $compile(nullOption)(scope); + + // remove the class, which is added automatically because we recompile the element and it + // becomes the compilation root + nullOption.removeClass('ng-scope'); + + // we need to remove it before calling selectElement.html('') because otherwise IE will + // remove the label from the element. wtf? + nullOption.remove(); + } + + // clear contents, we'll add what's needed based on the model + selectElement.html(''); selectElement.bind('change', function() { scope.$apply(function() { @@ -13239,8 +13439,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { if (multiple) { value = []; for (groupIndex = 0, groupLength = optionGroupsCache.length; - groupIndex < groupLength; - groupIndex++) { + groupIndex < groupLength; + groupIndex++) { // list of options for that group. (first item has the parent) optionGroup = optionGroupsCache[groupIndex]; @@ -13354,7 +13554,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { } } - lastElement = null; // start at the begining + lastElement = null; // start at the beginning for(index = 0, length = optionGroup.length; index < length; index++) { option = optionGroup[index]; if ((existingOption = existingOptions[index+1])) { @@ -13420,19 +13620,34 @@ var optionDirective = ['$interpolate', function($interpolate) { return { restrict: 'E', priority: 100, + require: '^select', compile: function(element, attr) { if (isUndefined(attr.value)) { var interpolateFn = $interpolate(element.text(), true); - if (interpolateFn) { - return function (scope, element, attr) { - scope.$watch(interpolateFn, function(value) { - attr.$set('value', value); - }); - } - } else { + if (!interpolateFn) { attr.$set('value', element.text()); } } + + // For some reason Opera defaults to true and if not overridden this messes up the repeater. + // We don't want the view to drive the initialization of the model anyway. + element.prop('selected', false); + + return function (scope, element, attr, selectCtrl) { + if (interpolateFn) { + scope.$watch(interpolateFn, function(newVal, oldVal) { + attr.$set('value', newVal); + if (newVal !== oldVal) selectCtrl.removeOption(oldVal); + selectCtrl.addOption(newVal); + }); + } else { + selectCtrl.addOption(attr.value); + } + + element.bind('$destroy', function() { + selectCtrl.removeOption(attr.value); + }); + }; } } }]; |
