From be6b2ca35e0184de595ae26078859286de84c384 Mon Sep 17 00:00:00 2001 From: Tim Wood Date: Mon, 20 Aug 2012 11:43:05 -0700 Subject: [PATCH] Removing `new Function` for #377 --- moment.js | 194 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 134 insertions(+), 60 deletions(-) diff --git a/moment.js b/moment.js index 95520c0dd..a01f043df 100644 --- a/moment.js +++ b/moment.js @@ -30,9 +30,8 @@ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, // format tokens - formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?)/g, + formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|.)/g, localFormattingTokens = /(LT|LL?L?L?)/g, - formattingRemoveEscapes = /(^\[)|(\\)|\]$/g, // parsing tokens parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi, @@ -77,6 +76,10 @@ // format function strings formatFunctions = {}, + // tokens to ordinalize and pad + ordinalizeTokens = 'DDD w M D d'.split(' '), + paddedTokens = 'M D H h m s w'.split(' '), + /* * moment.fn.format uses new Function() to create an inlined formatting function. * Results are a 3x speed boost @@ -84,7 +87,7 @@ * * These strings are appended into a function using replaceFormatTokens and makeFormatFunction */ - formatFunctionStrings = { + formatTokenFunctions = { // a = placeholder // b = placeholder // t = the current moment being formatted @@ -92,43 +95,119 @@ // o = language.ordinal function // p = leftZeroFill function // m = language.meridiem value or function - M : '(a=t.month()+1)', - MMM : 'v("monthsShort",t.month())', - MMMM : 'v("months",t.month())', - D : '(a=t.date())', - DDD : '(a=new Date(t.year(),t.month(),t.date()),b=new Date(t.year(),0,1),a=~~(((a-b)/864e5)+1.5))', - d : '(a=t.day())', - dd : 'v("weekdaysMin",t.day())', - ddd : 'v("weekdaysShort",t.day())', - dddd : 'v("weekdays",t.day())', - w : '(a=new Date(t.year(),t.month(),t.date()-t.day()+5),b=new Date(a.getFullYear(),0,4),a=~~((a-b)/864e5/7+1.5))', - YY : 'p(t.year()%100,2)', - YYYY : 'p(t.year(),4)', - a : 'm(t.hours(),t.minutes(),!0)', - A : 'm(t.hours(),t.minutes(),!1)', - H : 't.hours()', - h : 't.hours()%12||12', - m : 't.minutes()', - s : 't.seconds()', - S : '~~(t.milliseconds()/100)', - SS : 'p(~~(t.milliseconds()/10),2)', - SSS : 'p(t.milliseconds(),3)', - Z : '((a=-t.zone())<0?((a=-a),"-"):"+")+p(~~(a/60),2)+":"+p(~~a%60,2)', - ZZ : '((a=-t.zone())<0?((a=-a),"-"):"+")+p(~~(10*a/6),4)' - }, + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return getValueFromArray("monthsShort", this.month(), this, format); + }, + MMMM : function (format) { + return getValueFromArray("months", this.month(), this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + var a = new Date(this.year(), this.month(), this.date()), + b = new Date(this.year(), 0, 1); + return ~~(((a - b) / 864e5) + 1.5); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return getValueFromArray("weekdaysMin", this.day(), this, format); + }, + ddd : function (format) { + return getValueFromArray("weekdaysShort", this.day(), this, format); + }, + dddd : function (format) { + return getValueFromArray("weekdays", this.day(), this, format); + }, + w : function () { + var a = new Date(this.year(), this.month(), this.date() - this.day() + 5), + b = new Date(a.getFullYear(), 0, 4); + return ~~((a - b) / 864e5 / 7 + 1.5); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + a : function () { + return this.lang().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.lang().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return ~~(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(~~(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = -this.zone(), + b = "+"; + if (a < 0) { + a = -a; + b = "-"; + } + return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2); + }, + ZZ : function () { + var a = -this.zone(), + b = "+"; + if (a < 0) { + a = -a; + b = "-"; + } + return b + leftZeroFill(~~(10 * a / 6), 4); + } + }; - ordinalizeTokens = 'DDD w M D d'.split(' '), - paddedTokens = 'M D H h m s w'.split(' '); + function getValueFromArray(key, index, m, format) { + var lang = m.lang(); + return lang[key].call ? lang[key](m, format) : lang[key][index]; + } + + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; + } + function ordinalizeToken(func) { + return function (a) { + var b = func.call(this, a); + return b + this.lang().ordinal(b); + }; + } while (ordinalizeTokens.length) { i = ordinalizeTokens.pop(); - formatFunctionStrings[i + 'o'] = formatFunctionStrings[i] + '+o(a)'; + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]); } while (paddedTokens.length) { i = paddedTokens.pop(); - formatFunctionStrings[i + i] = 'p(' + formatFunctionStrings[i] + ',2)'; + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } - formatFunctionStrings.DDDD = 'p(' + formatFunctionStrings.DDD + ',3)'; + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); /************************************ @@ -339,45 +418,40 @@ ************************************/ - // helper for building inline formatting functions - function replaceFormatTokens(token) { - return formatFunctionStrings[token] ? - ("'+(" + formatFunctionStrings[token] + ")+'") : - token.replace(formattingRemoveEscapes, "").replace(/\\?'/g, "\\'"); - } - // helper for recursing long date formatting tokens function replaceLongDateFormatTokens(input) { return getLangDefinition().longDateFormat[input] || input; } - function makeFormatFunction(format) { - var output = "var a,b;return '" + - format.replace(formattingTokens, replaceFormatTokens) + "';", - Fn = Function; // get around jshint - // t = the current moment being formatted - // v = getValueAtKey function - // o = language.ordinal function - // p = leftZeroFill function - // m = language.meridiem value or function - return new Fn('t', 'v', 'o', 'p', 'm', output); + function removeFormattingTokens(input) { + if (input.match(/\[.*\]/)) { + return input.replace(/^\[|\]$/g, ""); + } + return input.replace(/\\/g, ""); } - function makeOrGetFormatFunction(format) { - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } } - return formatFunctions[format]; + + return function (mom) { + var output = ""; + for (i = 0; i < length; i++) { + output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i]; + } + return output; + }; } // format date using native date object function formatMoment(m, format) { - var lang = getLangDefinition(m); - - function getValueFromArray(key, index) { - return lang[key].call ? lang[key](m, format) : lang[key][index]; - } - while (localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); } @@ -386,7 +460,7 @@ formatFunctions[format] = makeFormatFunction(format); } - return formatFunctions[format](m, getValueFromArray, lang.ordinal, leftZeroFill, lang.meridiem); + return formatFunctions[format](m); } -- 2.47.3