diff options
author | Bernhard Posselt <dev@bernhard-posselt.com> | 2014-11-18 17:13:56 +0100 |
---|---|---|
committer | Bernhard Posselt <dev@bernhard-posselt.com> | 2014-11-18 17:13:56 +0100 |
commit | c4d9c7a30adf34aa52bf5e7736b25264203b838c (patch) | |
tree | 81ecdb761071719e2bf16d077f42121c53d98b46 /js/vendor/angular/angular.js | |
parent | a2e3490f8ee635c5edf9b951b55c3795aab3d075 (diff) |
fix #365
Diffstat (limited to 'js/vendor/angular/angular.js')
-rw-r--r-- | js/vendor/angular/angular.js | 555 |
1 files changed, 292 insertions, 263 deletions
diff --git a/js/vendor/angular/angular.js b/js/vendor/angular/angular.js index 36c17d2ff..57da51b3d 100644 --- a/js/vendor/angular/angular.js +++ b/js/vendor/angular/angular.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.3.2 + * @license AngularJS v1.3.3 * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ @@ -42,40 +42,23 @@ function minErr(module, ErrorConstructor) { prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, - stringify = function(obj) { - if (typeof obj === 'function') { - return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (typeof obj === 'undefined') { - return 'undefined'; - } else if (typeof obj !== 'string') { - return JSON.stringify(obj); - } - return obj; - }, + message, i; 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 (typeof arg === 'function') { - return arg.toString().replace(/ ?\{[\s\S]*$/, ''); - } else if (typeof arg === 'undefined') { - return 'undefined'; - } else if (typeof arg !== 'string') { - return toJson(arg); - } - return arg; + return toDebugString(templateArgs[index + 2]); } return match; }); - message = message + '\nhttp://errors.angularjs.org/1.3.2/' + + message = message + '\nhttp://errors.angularjs.org/1.3.3/' + (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { - message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + - encodeURIComponent(stringify(arguments[i])); + message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + + encodeURIComponent(toDebugString(arguments[i])); } return new ErrorConstructor(message); }; @@ -715,7 +698,7 @@ function includes(array, obj) { function arrayRemove(array, value) { var index = array.indexOf(value); - if (index >=0) + if (index >= 0) array.splice(index, 1); return value; } @@ -916,7 +899,7 @@ function equals(o1, o2) { if (isArray(o1)) { if (!isArray(o2)) return false; if ((length = o1.length) == o2.length) { - for (key=0; key<length; key++) { + for (key = 0; key < length; key++) { if (!equals(o1[key], o2[key])) return false; } return true; @@ -1002,7 +985,7 @@ function bind(self, fn) { return curryArgs.length ? function() { return arguments.length - ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) + ? fn.apply(self, concat(curryArgs, arguments, 0)) : fn.apply(self, curryArgs); } : function() { @@ -1202,7 +1185,7 @@ var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; function getNgAttribute(element, ngAttr) { var attr, i, ii = ngAttrPrefixes.length; element = jqLite(element); - for (i=0; i<ii; ++i) { + for (i = 0; i < ii; ++i) { attr = ngAttrPrefixes[i] + ngAttr; if (isString(attr = element.attr(attr))) { return attr; @@ -1987,6 +1970,34 @@ function setupModuleLoader(window) { } +/* global: toDebugString: true */ + +function serializeObject(obj) { + var seen = []; + + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '<<already seen>>'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (typeof obj === 'undefined') { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj); + } + return obj; +} + /* global angularModule: true, version: true, @@ -2089,11 +2100,11 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.3.2', // all of these placeholder strings will be replaced by grunt's + full: '1.3.3', // all of these placeholder strings will be replaced by grunt's major: 1, // package task minor: 3, - dot: 2, - codeName: 'cardiovasculatory-magnification' + dot: 3, + codeName: 'undersea-arithmetic' }; @@ -2826,7 +2837,7 @@ forEach({ } } else { return (element[name] || - (element.attributes.getNamedItem(name)|| noop).specified) + (element.attributes.getNamedItem(name) || noop).specified) ? lowercasedName : undefined; } @@ -4162,7 +4173,7 @@ function $AnchorScrollProvider() { * @name $anchorScrollProvider#disableAutoScrolling * * @description - * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically will detect changes to + * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> * Use this method to disable automatic scrolling. * @@ -4811,8 +4822,7 @@ function $$AsyncCallbackProvider() { /** * @param {object} window The global window object. * @param {object} document jQuery wrapped document. - * @param {function()} XHR XMLHttpRequest constructor. - * @param {object} $log console.log or an object with the same interface. + * @param {object} $log window.console or an object with the same interface. * @param {object} $sniffer $sniffer service */ function Browser(window, document, $log, $sniffer) { @@ -5162,8 +5172,8 @@ function Browser(window, document, $log, $sniffer) { // - 20 cookies per unique domain // - 4096 bytes per cookie if (cookieLength > 4096) { - $log.warn("Cookie '"+ name + - "' possibly not set or overflowed because it was too large ("+ + $log.warn("Cookie '" + name + + "' possibly not set or overflowed because it was too large (" + cookieLength + " > 4096 bytes)!"); } } @@ -5828,7 +5838,7 @@ function $TemplateCacheProvider() { * * * #### `bindToController` - * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will + * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will * allow a component to have its properties bound to the controller, rather than to scope. When the controller * is instantiated, the initial values of the isolate scope bindings are already available. * @@ -6677,16 +6687,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { // for each tuples var nbrUrisWith2parts = Math.floor(rawUris.length / 2); - for (var i=0; i<nbrUrisWith2parts; i++) { - var innerIdx = i*2; + for (var i = 0; i < nbrUrisWith2parts; i++) { + var innerIdx = i * 2; // sanitize the uri result += $$sanitizeUri(trim(rawUris[innerIdx]), true); // add the descriptor - result += (" " + trim(rawUris[innerIdx+1])); + result += (" " + trim(rawUris[innerIdx + 1])); } // split the last item into uri and descriptor - var lastTuple = trim(rawUris[i*2]).split(/\s/); + var lastTuple = trim(rawUris[i * 2]).split(/\s/); // sanitize the last uri result += $$sanitizeUri(trim(lastTuple[0]), true); @@ -6880,7 +6890,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (!node) { return 'html'; } else { - return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg': 'html'; + return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html'; } } @@ -7693,7 +7703,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var match = null; if (hasDirectives.hasOwnProperty(name)) { for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i<ii; i++) { + i = 0, ii = directives.length; i < ii; i++) { try { directive = directives[i]; if ((maxPriority === undefined || maxPriority > directive.priority) && @@ -7722,7 +7732,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { function directiveIsMultiElement(name) { if (hasDirectives.hasOwnProperty(name)) { for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i<ii; i++) { + i = 0, ii = directives.length; i < ii; i++) { directive = directives[i]; if (directive.multiElement) { return true; @@ -7941,7 +7951,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { case 'svg': case 'math': var wrapper = document.createElement('div'); - wrapper.innerHTML = '<'+type+'>'+template+'</'+type+'>'; + wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>'; return wrapper.childNodes[0].childNodes; default: return template; @@ -8298,6 +8308,10 @@ function $ControllerProvider() { * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global * `window` object (not recommended) * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * * @param {Object} locals Injection locals for Controller. * @return {Object} Instance of given controller. * @@ -8472,7 +8486,7 @@ function defaultHttpResponseTransform(data, headers) { // strip json vulnerability protection prefix data = data.replace(JSON_PROTECTION_PREFIX, ''); var contentType = headers('Content-Type'); - if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) || + if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) || (JSON_START.test(data) && JSON_END.test(data))) { data = fromJson(data); } @@ -10043,7 +10057,8 @@ function $InterpolateProvider() { function parseStringifyInterceptor(value) { try { - return stringify(getValue(value)); + value = getValue(value); + return allOrNothing && !isDefined(value) ? value : stringify(value); } catch (err) { var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); @@ -10367,8 +10382,8 @@ function encodePath(path) { return segments.join('/'); } -function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { - var parsedUrl = urlResolve(absoluteUrl, appBase); +function parseAbsoluteUrl(absoluteUrl, locationObj) { + var parsedUrl = urlResolve(absoluteUrl); locationObj.$$protocol = parsedUrl.protocol; locationObj.$$host = parsedUrl.hostname; @@ -10376,12 +10391,12 @@ function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { } -function parseAppUrl(relativeUrl, locationObj, appBase) { +function parseAppUrl(relativeUrl, locationObj) { var prefixed = (relativeUrl.charAt(0) !== '/'); if (prefixed) { relativeUrl = '/' + relativeUrl; } - var match = urlResolve(relativeUrl, appBase); + var match = urlResolve(relativeUrl); locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname); locationObj.$$search = parseKeyValue(match.search); @@ -10436,7 +10451,7 @@ function LocationHtml5Url(appBase, basePrefix) { this.$$html5 = true; basePrefix = basePrefix || ''; var appBaseNoFile = stripFile(appBase); - parseAbsoluteUrl(appBase, this, appBase); + parseAbsoluteUrl(appBase, this); /** @@ -10451,7 +10466,7 @@ function LocationHtml5Url(appBase, basePrefix) { appBaseNoFile); } - parseAppUrl(pathUrl, this, appBase); + parseAppUrl(pathUrl, this); if (!this.$$path) { this.$$path = '/'; @@ -10514,7 +10529,7 @@ function LocationHtml5Url(appBase, basePrefix) { function LocationHashbangUrl(appBase, hashPrefix) { var appBaseNoFile = stripFile(appBase); - parseAbsoluteUrl(appBase, this, appBase); + parseAbsoluteUrl(appBase, this); /** @@ -10534,7 +10549,7 @@ function LocationHashbangUrl(appBase, hashPrefix) { throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, hashPrefix); } - parseAppUrl(withoutHashUrl, this, appBase); + parseAppUrl(withoutHashUrl, this); this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); @@ -11174,11 +11189,19 @@ function $LocationProvider() { $rootScope.$evalAsync(function() { var oldUrl = $location.absUrl(); var oldState = $location.$$state; + var defaultPrevented; $location.$$parse(newUrl); $location.$$state = newState; - if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - newState, oldState).defaultPrevented) { + + defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + newState, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { $location.$$parse(oldUrl); $location.$$state = oldState; setBrowserUrlWithFallback(oldUrl, false, oldState); @@ -11202,13 +11225,20 @@ function $LocationProvider() { initializing = false; $rootScope.$evalAsync(function() { - if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl, - $location.$$state, oldState).defaultPrevented) { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { $location.$$parse(oldUrl); $location.$$state = oldState; } else { if (urlOrStateChanged) { - setBrowserUrlWithFallback($location.absUrl(), currentReplace, + setBrowserUrlWithFallback(newUrl, currentReplace, oldState === $location.$$state ? null : $location.$$state); } afterLocationChange(oldUrl, oldState); @@ -11398,7 +11428,7 @@ var $parseMinErr = minErr('$parse'); // 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 +// 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. // // As an example, consider the following Angular expression: @@ -11407,7 +11437,7 @@ var $parseMinErr = minErr('$parse'); // // 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 +// 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 // explicitly exposed in this way. // @@ -11415,6 +11445,8 @@ var $parseMinErr = minErr('$parse'); // window or some DOM object that has a reference to window is published onto a Scope. // Similarly we prevent invocations of function known to be dangerous, as well as assignments to // native objects. +// +// See https://docs.angularjs.org/guide/security function ensureSafeMemberName(name, fullExpression) { @@ -11423,7 +11455,7 @@ function ensureSafeMemberName(name, fullExpression) { || name === "__proto__") { throw $parseMinErr('isecfld', 'Attempting to access a disallowed field in Angular expressions! ' - +'Expression: {0}', fullExpression); + + 'Expression: {0}', fullExpression); } return name; } @@ -11500,24 +11532,24 @@ var OPERATORS = extend(createMap(), { } return a; } - return isDefined(b)?b:undefined;}, + return isDefined(b) ? b : undefined;}, '-':function(self, locals, a, b) { a=a(self, locals); b=b(self, locals); - return (isDefined(a)?a:0)-(isDefined(b)?b:0); + return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0); }, - '*':function(self, locals, a, b) {return a(self, locals)*b(self, locals);}, - '/':function(self, locals, a, b) {return a(self, locals)/b(self, locals);}, - '%':function(self, locals, a, b) {return a(self, locals)%b(self, locals);}, - '===':function(self, locals, a, b) {return a(self, locals)===b(self, locals);}, - '!==':function(self, locals, a, b) {return a(self, locals)!==b(self, locals);}, - '==':function(self, locals, a, b) {return a(self, locals)==b(self, locals);}, - '!=':function(self, locals, a, b) {return a(self, locals)!=b(self, locals);}, - '<':function(self, locals, a, b) {return a(self, locals)<b(self, locals);}, - '>':function(self, locals, a, b) {return a(self, locals)>b(self, locals);}, - '<=':function(self, locals, a, b) {return a(self, locals)<=b(self, locals);}, - '>=':function(self, locals, a, b) {return a(self, locals)>=b(self, locals);}, - '&&':function(self, locals, a, b) {return a(self, locals)&&b(self, locals);}, - '||':function(self, locals, a, b) {return a(self, locals)||b(self, locals);}, + '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);}, + '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);}, + '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);}, + '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);}, + '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);}, + '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);}, + '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);}, + '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);}, + '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);}, + '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);}, + '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);}, + '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);}, + '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);}, '!':function(self, locals, a) {return !a(self, locals);}, //Tokenized as operators but parsed as assignment/filters @@ -11543,44 +11575,31 @@ Lexer.prototype = { lex: function(text) { this.text = text; this.index = 0; - this.ch = undefined; this.tokens = []; while (this.index < this.text.length) { - this.ch = this.text.charAt(this.index); - if (this.is('"\'')) { - this.readString(this.ch); - } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { + var ch = this.text.charAt(this.index); + if (ch === '"' || ch === "'") { + this.readString(ch); + } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { this.readNumber(); - } else if (this.isIdent(this.ch)) { + } else if (this.isIdent(ch)) { this.readIdent(); - } else if (this.is('(){}[].,;:?')) { - this.tokens.push({ - index: this.index, - text: this.ch - }); + } else if (this.is(ch, '(){}[].,;:?')) { + this.tokens.push({index: this.index, text: ch}); this.index++; - } else if (this.isWhitespace(this.ch)) { + } else if (this.isWhitespace(ch)) { this.index++; } else { - var ch2 = this.ch + this.peek(); + var ch2 = ch + this.peek(); var ch3 = ch2 + this.peek(2); - var fn = OPERATORS[this.ch]; - var fn2 = OPERATORS[ch2]; - var fn3 = OPERATORS[ch3]; - if (fn3) { - this.tokens.push({index: this.index, text: ch3, fn: fn3}); - this.index += 3; - } else if (fn2) { - this.tokens.push({index: this.index, text: ch2, fn: fn2}); - this.index += 2; - } else if (fn) { - this.tokens.push({ - index: this.index, - text: this.ch, - fn: fn - }); - this.index += 1; + var op1 = OPERATORS[ch]; + var op2 = OPERATORS[ch2]; + var op3 = OPERATORS[ch3]; + if (op1 || op2 || op3) { + var token = op3 ? ch3 : (op2 ? ch2 : ch); + this.tokens.push({index: this.index, text: token, operator: true}); + this.index += token.length; } else { this.throwError('Unexpected next character ', this.index, this.index + 1); } @@ -11589,8 +11608,8 @@ Lexer.prototype = { return this.tokens; }, - is: function(chars) { - return chars.indexOf(this.ch) !== -1; + is: function(ch, chars) { + return chars.indexOf(ch) !== -1; }, peek: function(i) { @@ -11599,7 +11618,7 @@ Lexer.prototype = { }, isNumber: function(ch) { - return ('0' <= ch && ch <= '9'); + return ('0' <= ch && ch <= '9') && typeof ch === "string"; }, isWhitespace: function(ch) { @@ -11652,79 +11671,28 @@ Lexer.prototype = { } this.index++; } - number = 1 * number; this.tokens.push({ index: start, text: number, constant: true, - fn: function() { return number; } + value: Number(number) }); }, readIdent: function() { - var expression = this.text; - - var ident = ''; var start = this.index; - - var lastDot, peekIndex, methodName, ch; - while (this.index < this.text.length) { - ch = this.text.charAt(this.index); - if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { - if (ch === '.') lastDot = this.index; - ident += ch; - } else { + var ch = this.text.charAt(this.index); + if (!(this.isIdent(ch) || this.isNumber(ch))) { break; } this.index++; } - - //check if the identifier ends with . and if so move back one char - if (lastDot && ident[ident.length - 1] === '.') { - this.index--; - ident = ident.slice(0, -1); - lastDot = ident.lastIndexOf('.'); - if (lastDot === -1) { - lastDot = undefined; - } - } - - //check if this is not a method invocation and if it is back out to last dot - if (lastDot) { - peekIndex = this.index; - while (peekIndex < this.text.length) { - ch = this.text.charAt(peekIndex); - if (ch === '(') { - methodName = ident.substr(lastDot - start + 1); - ident = ident.substr(0, lastDot - start); - this.index = peekIndex; - break; - } - if (this.isWhitespace(ch)) { - peekIndex++; - } else { - break; - } - } - } - this.tokens.push({ index: start, - text: ident, - fn: CONSTANTS[ident] || getterFn(ident, this.options, expression) + text: this.text.slice(start, this.index), + identifier: true }); - - if (methodName) { - this.tokens.push({ - index: lastDot, - text: '.' - }); - this.tokens.push({ - index: lastDot + 1, - text: methodName - }); - } }, readString: function(quote) { @@ -11755,9 +11723,8 @@ Lexer.prototype = { this.tokens.push({ index: start, text: rawString, - string: string, constant: true, - fn: function() { return string; } + value: string }); return; } else { @@ -11818,16 +11785,12 @@ Parser.prototype = { primary = this.arrayDeclaration(); } else if (this.expect('{')) { primary = this.object(); + } else if (this.peek().identifier) { + primary = this.identifier(); + } else if (this.peek().constant) { + primary = this.constant(); } else { - var token = this.expect(); - primary = token.fn; - if (!primary) { - this.throwError('not a primary expression', token); - } - if (token.constant) { - primary.constant = true; - primary.literal = true; - } + this.throwError('not a primary expression', this.peek()); } var next, context; @@ -11861,8 +11824,11 @@ Parser.prototype = { }, peek: function(e1, e2, e3, e4) { - if (this.tokens.length > 0) { - var token = this.tokens[0]; + return this.peekAhead(0, e1, e2, e3, e4); + }, + peekAhead: function(i, e1, e2, e3, e4) { + if (this.tokens.length > i) { + var token = this.tokens[i]; var t = token.text; if (t === e1 || t === e2 || t === e3 || t === e4 || (!e1 && !e2 && !e3 && !e4)) { @@ -11882,12 +11848,19 @@ Parser.prototype = { }, consume: function(e1) { - if (!this.expect(e1)) { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + + var token = this.expect(e1); + if (!token) { this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); } + return token; }, - unaryFn: function(fn, right) { + unaryFn: function(op, right) { + var fn = OPERATORS[op]; return extend(function $parseUnaryFn(self, locals) { return fn(self, locals, right); }, { @@ -11896,7 +11869,8 @@ Parser.prototype = { }); }, - binaryFn: function(left, fn, right, isBranching) { + binaryFn: function(left, op, right, isBranching) { + var fn = OPERATORS[op]; return extend(function $parseBinaryFn(self, locals) { return fn(self, locals, left, right); }, { @@ -11905,6 +11879,28 @@ Parser.prototype = { }); }, + identifier: function() { + var id = this.consume().text; + + //Continue reading each `.identifier` unless it is a method invocation + while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) { + id += this.consume().text + this.consume().text; + } + + return CONSTANTS[id] || getterFn(id, this.options, this.text); + }, + + constant: function() { + var value = this.consume().value; + + return extend(function $parseConstant() { + return value; + }, { + constant: true, + literal: true + }); + }, + statements: function() { var statements = []; while (true) { @@ -11936,8 +11932,7 @@ Parser.prototype = { }, filter: function(inputFn) { - var token = this.expect(); - var fn = this.$filter(token.text); + var fn = this.$filter(this.consume().text); var argsFn; var args; @@ -12000,7 +11995,7 @@ Parser.prototype = { var token; if ((token = this.expect('?'))) { middle = this.assignment(); - if ((token = this.expect(':'))) { + if (this.consume(':')) { var right = this.assignment(); return extend(function $parseTernary(self, locals) { @@ -12008,9 +12003,6 @@ Parser.prototype = { }, { constant: left.constant && middle.constant && right.constant }); - - } else { - this.throwError('expected :', token); } } @@ -12021,7 +12013,7 @@ Parser.prototype = { var left = this.logicalAND(); var token; while ((token = this.expect('||'))) { - left = this.binaryFn(left, token.fn, this.logicalAND(), true); + left = this.binaryFn(left, token.text, this.logicalAND(), true); } return left; }, @@ -12030,7 +12022,7 @@ Parser.prototype = { var left = this.equality(); var token; if ((token = this.expect('&&'))) { - left = this.binaryFn(left, token.fn, this.logicalAND(), true); + left = this.binaryFn(left, token.text, this.logicalAND(), true); } return left; }, @@ -12039,7 +12031,7 @@ Parser.prototype = { var left = this.relational(); var token; if ((token = this.expect('==','!=','===','!=='))) { - left = this.binaryFn(left, token.fn, this.equality()); + left = this.binaryFn(left, token.text, this.equality()); } return left; }, @@ -12048,7 +12040,7 @@ Parser.prototype = { var left = this.additive(); var token; if ((token = this.expect('<', '>', '<=', '>='))) { - left = this.binaryFn(left, token.fn, this.relational()); + left = this.binaryFn(left, token.text, this.relational()); } return left; }, @@ -12057,7 +12049,7 @@ Parser.prototype = { var left = this.multiplicative(); var token; while ((token = this.expect('+','-'))) { - left = this.binaryFn(left, token.fn, this.multiplicative()); + left = this.binaryFn(left, token.text, this.multiplicative()); } return left; }, @@ -12066,7 +12058,7 @@ Parser.prototype = { var left = this.unary(); var token; while ((token = this.expect('*','/','%'))) { - left = this.binaryFn(left, token.fn, this.unary()); + left = this.binaryFn(left, token.text, this.unary()); } return left; }, @@ -12076,9 +12068,9 @@ Parser.prototype = { if (this.expect('+')) { return this.primary(); } else if ((token = this.expect('-'))) { - return this.binaryFn(Parser.ZERO, token.fn, this.unary()); + return this.binaryFn(Parser.ZERO, token.text, this.unary()); } else if ((token = this.expect('!'))) { - return this.unaryFn(token.fn, this.unary()); + return this.unaryFn(token.text, this.unary()); } else { return this.primary(); } @@ -12086,7 +12078,7 @@ Parser.prototype = { fieldAccess: function(object) { var expression = this.text; - var field = this.expect().text; + var field = this.consume().text; var getter = getterFn(field, this.options, expression); return extend(function $parseFieldAccess(scope, locals, self) { @@ -12171,8 +12163,7 @@ Parser.prototype = { // Support trailing commas per ES5.1. break; } - var elementFn = this.expression(); - elementFns.push(elementFn); + elementFns.push(this.expression()); } while (this.expect(',')); } this.consume(']'); @@ -12198,11 +12189,16 @@ Parser.prototype = { // Support trailing commas per ES5.1. break; } - var token = this.expect(); - keys.push(token.string || token.text); + var token = this.consume(); + if (token.constant) { + keys.push(token.value); + } else if (token.identifier) { + keys.push(token.text); + } else { + this.throwError("invalid key", token); |