Last active
August 29, 2015 14:12
-
-
Save rage-shadowman/550f7686b98c9ff6fcd4 to your computer and use it in GitHub Desktop.
Revisions
-
Shadow Man revised this gist
Dec 31, 2014 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,7 @@ public class MomentJsDateFormatHelper { // RAJ - This file must be in the root classpath (I stick it under src/main/resources and maven puts it where it needs to go) // RAJ - I am using v1.7.2, as the latest version doesn't set a global "moment" constructor correctly under Rhino // RAJ - You can find v1.7.2 here: https://raw.githubusercontent.com/moment/moment/1.7.2/moment.js @NotNull private static final String MOMENT_JS_FILE = "moment.js"; @NotNull private final ScriptEngine m_momentJsScriptEngine = initializeScriptEngine( MOMENT_JS_FILE ); -
Shadow Man revised this gist
Dec 31, 2014 . 1 changed file with 4 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,7 +15,7 @@ public class MomentJsDateFormatHelperTest { @Test public void testMomentJsDateFormatting () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); @@ -27,7 +27,7 @@ public void testTemplateWithMomentJsDateFormatting () @Test // RAJ - internally we evaluate the javascript inline using single quotes to surround the format public void testFormatIncludingQuotes1 () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); @@ -42,7 +42,7 @@ public void testTemplateWithFormatIncludingQuotes1 () @Test // RAJ - internally we evaluate the javascript inline using single quotes to surround the format public void testFormatIncludingQuotes2 () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); @@ -56,7 +56,7 @@ public void testTemplateWithFormatIncludingQuotes2 () } @Test public void testFormatAndEmptyFormatString () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,7 +65,7 @@ public void testTemplateWithFormatAndEmptyFormatString () Context context = createContext( Collections.singletonMap( "date", date ) ); // RAJ - I have no idea how this should behave under non-US locales, so I'm not sure how useful this assertion is // RAJ - We should probably just check to make sure that something was generated here, rather than the specific format // RAJ - Then again, all of the MMMM stuff above is also locale (language) specific... Assert.assertEquals( "date is 10/01/2014", template.apply( context ) ); } -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,6 +65,7 @@ public void testTemplateWithFormatAndEmptyFormatString () Context context = createContext( Collections.singletonMap( "date", date ) ); // RAJ - I have no idea how this should behave under non-US locales, so I'm not sure how useful this assertion is // RAJ - We should probably just check to make sure that something was generated here, rather than the specific format // RAJ - Then again, all of the MMMM stuff above is also locale specific... Assert.assertEquals( "date is 10/01/2014", template.apply( context ) ); } -
Shadow Man revised this gist
Dec 30, 2014 . 2 changed files with 4 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -89,6 +89,8 @@ private static ScriptEngine initializeScriptEngine ( @NotNull String momentJsFil try { engine.eval( reader ); // RAJ - Since I'm only concerned about my own locale, I don't worry about it but you may want to load // any locale data you care about here... return engine; } catch( ScriptException e ) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -63,6 +63,8 @@ public void testTemplateWithFormatAndEmptyFormatString () Date date = sdf.parse( "2014-10-01 12:32:23 PM" ); Template template = compileTemplate( "date is {{dateFormat date}}" ); Context context = createContext( Collections.singletonMap( "date", date ) ); // RAJ - I have no idea how this should behave under non-US locales, so I'm not sure how useful this assertion is // RAJ - We should probably just check to make sure that something was generated here, rather than the specific format Assert.assertEquals( "date is 10/01/2014", template.apply( context ) ); } -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 0 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -56,7 +56,6 @@ public void testTemplateWithFormatIncludingQuotes2 () } @Test public void testTemplateWithFormatAndEmptyFormatString () throws IOException, ParseException { -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -97,6 +97,7 @@ private static ScriptEngine initializeScriptEngine ( @NotNull String momentJsFil } } // RAJ - I have no idea how thread safe the Rhino code is, so I'm synchronizing this method just to be safe @NotNull private synchronized String evalJavascript ( @NotNull String script ) { -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 84 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,84 @@ import com.github.jknack.handlebars.Context; import com.github.jknack.handlebars.Handlebars; import com.github.jknack.handlebars.Template; import com.github.jknack.handlebars.context.MapValueResolver; import junit.framework.Assert; import org.apache.commons.lang3.StringEscapeUtils; import org.junit.Test; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; public class MomentJsDateFormatHelperTest { @Test public void testTemplateWithMomentJsDateFormatting () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); Date date = sdf.parse( "2014-10-01 12:32:23 PM" ); Template template = compileTemplate( "date is {{dateFormat date format='MMMM Do, YYYY [at] hh:mm a [and] ss [seconds]'}}" ); Context context = createContext( Collections.singletonMap( "date", date ) ); Assert.assertEquals( "date is October 1st, 2014 at 12:32 pm and 23 seconds", template.apply( context ) ); } @Test // RAJ - internally we evaluate the javascript inline using single quotes to surround the format public void testTemplateWithFormatIncludingQuotes1 () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); Date date = sdf.parse( "2014-10-01 12:32:23 PM" ); Template template = compileTemplate( "date is {{dateFormat date format='MMMM Do, [\\']YY [\"yay\"]'}}" ); Context context = createContext( Collections.singletonMap( "date", date ) ); // RAJ - By default, Handlebars escapes text for HTML and we have quotes in our generated output String htmlEscapedText = template.apply( context ); String unescapedText = StringEscapeUtils.unescapeHtml4( htmlEscapedText ); Assert.assertEquals( "date is October 1st, '14 \"yay\"", unescapedText ); } @Test // RAJ - internally we evaluate the javascript inline using single quotes to surround the format public void testTemplateWithFormatIncludingQuotes2 () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); Date date = sdf.parse( "2014-10-01 12:32:23 PM" ); Template template = compileTemplate( "date is {{dateFormat date format=\"MMMM Do, [']YY [\\\"yay\\\"]\"}}" ); Context context = createContext( Collections.singletonMap( "date", date ) ); // RAJ - By default, Handlebars escapes text for HTML and we have quotes in our generated output String htmlEscapedText = template.apply( context ); String unescapedText = StringEscapeUtils.unescapeHtml4( htmlEscapedText ); Assert.assertEquals( "date is October 1st, '14 \"yay\"", unescapedText ); } @Test // RAJ - internally we evaluate the javascript inline using single quotes to surround the format public void testTemplateWithFormatAndEmptyFormatString () throws IOException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss a" ); Date date = sdf.parse( "2014-10-01 12:32:23 PM" ); Template template = compileTemplate( "date is {{dateFormat date}}" ); Context context = createContext( Collections.singletonMap( "date", date ) ); Assert.assertEquals( "date is 10/01/2014", template.apply( context ) ); } private Context createContext ( Object data ) { return Context.newBuilder( data ) .resolver( MapValueResolver.INSTANCE ) .build(); } private Template compileTemplate ( String templateText ) throws IOException { Handlebars handlebars = new Handlebars(); handlebars.registerHelper( "dateFormat", new MomentJsDateFormatHelper() ); return handlebars.compileInline( templateText ); } } -
Shadow Man revised this gist
Dec 30, 2014 . 1 changed file with 0 additions and 1213 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,1213 +0,0 @@ -
Shadow Man created this gist
Dec 30, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,112 @@ import com.github.jknack.handlebars.Helper; import com.github.jknack.handlebars.Options; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.Date; public class MomentJsDateFormatHelper implements Helper<Date> { // RAJ - This file must be in the root classpath (I stick it under src/main/resources and maven puts it where it needs to go) // RAJ - I am using v1.7.2, as the latest version doesn't set a global "moment" constructor correctly under Rhino @NotNull private static final String MOMENT_JS_FILE = "moment.js"; @NotNull private final ScriptEngine m_momentJsScriptEngine = initializeScriptEngine( MOMENT_JS_FILE ); @Override @NotNull public CharSequence apply ( @NotNull Date date, @NotNull Options options ) throws IOException { String format = options.param(0, options.<String>hash("format", null)); return formatDate( date, format ); } /* @NotNull private synchronized String formatDate ( @NotNull Date date, @Nullable String format ) { try { Object moment = ( (Invocable)m_momentJsScriptEngine ).invokeFunction( "moment", date.getTime() ); if( format == null ) return ( (Invocable)m_momentJsScriptEngine ).invokeMethod( moment, "calendar" ).toString(); String[] formatArgs = new String[] { format }; // RAJ - It is probably cleaner to use Invocable instead of eval but this line always throws NPE under Java6 return ( (Invocable)m_momentJsScriptEngine ).invokeMethod( moment, "format", formatArgs ).toString(); } catch( ScriptException e ) { throw new RuntimeException( "Failed to format date", e ); } catch( NoSuchMethodException e ) { throw new RuntimeException( "Failed to format date", e ); } } */ @NotNull private CharSequence formatDate ( @NotNull Date date, @Nullable String format ) { String javascript = getMomentJsFormattingJavascript( date, format ); return evalJavascript( javascript ); } @NotNull private String getMomentJsFormattingJavascript ( @NotNull Date date, @Nullable String format ) { if( format == null ) return "new moment(" + date.getTime() + ").calendar();"; format = format.replace( "'", "\\'" ); return "new moment(" + date.getTime() + ").format('" + format + "');"; } @NotNull private static ScriptEngine initializeScriptEngine ( @NotNull String momentJsFile) { InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( momentJsFile ); Reader reader = new InputStreamReader( is ); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName( "JavaScript" ); try { engine.eval( reader ); return engine; } catch( ScriptException e ) { throw new RuntimeException( e ); } } @NotNull private synchronized String evalJavascript ( @NotNull String script ) { try { return m_momentJsScriptEngine.eval( script ).toString(); } catch( ScriptException e ) { throw new RuntimeException( e ); } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,1213 @@ // moment.js // version : 1.7.2 // author : Tim Wood // license : MIT // momentjs.com (function (undefined) { /************************************ Constants ************************************/ var moment, VERSION = "1.7.2", round = Math.round, i, // internal storage for language config files languages = {}, currentLanguage = 'en', // check for nodeJS hasModule = (typeof module !== 'undefined' && module.exports), // Parameters to check for on the lang config. This list of properties // will be inherited from English if not provided in a language // definition. monthsParse is also a lang config property, but it // cannot be inherited and as such cannot be enumerated here. langConfigProperties = 'months|monthsShort|weekdays|weekdaysShort|weekdaysMin|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'), // ASP.NET json date format regex 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, localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?)/g, // parsing tokens parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi, // parsing token regexes parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 parseTokenThreeDigits = /\d{3}/, // 000 - 999 parseTokenFourDigits = /\d{1,4}/, // 0 - 9999 parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z parseTokenT = /T/i, // T (ISO seperator) // preliminary iso regex // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/, isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', // iso time formats and regexes isoTimes = [ ['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/], ['HH:mm:ss', /T\d\d:\d\d:\d\d/], ['HH:mm', /T\d\d:\d\d/], ['HH', /T\d\d/] ], // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] parseTimezoneChunker = /([\+\-]|\d\d)/gi, // getter and setter names proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), unitMillisecondFactors = { 'Milliseconds' : 1, 'Seconds' : 1e3, 'Minutes' : 6e4, 'Hours' : 36e5, 'Days' : 864e5, 'Months' : 2592e6, 'Years' : 31536e6 }, // 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 * http://jsperf.com/momentjs-cached-format-functions * * These strings are appended into a function using replaceFormatTokens and makeFormatFunction */ formatTokenFunctions = { // a = placeholder // b = placeholder // t = the current moment being formatted // v = getValueAtKey function // o = language.ordinal function // p = leftZeroFill function // m = language.meridiem value or function 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); } }; 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(); formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]); } while (paddedTokens.length) { i = paddedTokens.pop(); formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); /************************************ Constructors ************************************/ // Moment prototype object function Moment(date, isUTC, lang) { this._d = date; this._isUTC = !!isUTC; this._a = date._a || null; this._lang = lang || false; } // Duration Constructor function Duration(duration) { var data = this._data = {}, years = duration.years || duration.y || 0, months = duration.months || duration.M || 0, weeks = duration.weeks || duration.w || 0, days = duration.days || duration.d || 0, hours = duration.hours || duration.h || 0, minutes = duration.minutes || duration.m || 0, seconds = duration.seconds || duration.s || 0, milliseconds = duration.milliseconds || duration.ms || 0; // representation for dateAddRemove this._milliseconds = milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 36e5; // 1000 * 60 * 60 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = months + years * 12; // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds += absRound(milliseconds / 1000); data.seconds = seconds % 60; minutes += absRound(seconds / 60); data.minutes = minutes % 60; hours += absRound(minutes / 60); data.hours = hours % 24; days += absRound(hours / 24); days += weeks * 7; data.days = days % 30; months += absRound(days / 30); data.months = months % 12; years += absRound(months / 12); data.years = years; this._lang = false; } /************************************ Helpers ************************************/ function absRound(number) { if (number < 0) { return Math.ceil(number); } else { return Math.floor(number); } } // left zero fill a number // see http://jsperf.com/left-zero-filling for performance comparison function leftZeroFill(number, targetLength) { var output = number + ''; while (output.length < targetLength) { output = '0' + output; } return output; } // helper function for _.addTime and _.subtractTime function addOrSubtractDurationFromMoment(mom, duration, isAdding) { var ms = duration._milliseconds, d = duration._days, M = duration._months, currentDate; if (ms) { mom._d.setTime(+mom + ms * isAdding); } if (d) { mom.date(mom.date() + d * isAdding); } if (M) { currentDate = mom.date(); mom.date(1) .month(mom.month() + M * isAdding) .date(Math.min(currentDate, mom.daysInMonth())); } } // check if is an array function isArray(input) { return Object.prototype.toString.call(input) === '[object Array]'; } // compare two arrays, return the number of differences function compareArrays(array1, array2) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if (~~array1[i] !== ~~array2[i]) { diffs++; } } return diffs + lengthDiff; } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function dateFromArray(input, asUTC, hoursOffset, minutesOffset) { var i, date, forValid = []; for (i = 0; i < 7; i++) { forValid[i] = input[i] = (input[i] == null) ? (i === 2 ? 1 : 0) : input[i]; } // we store whether we used utc or not in the input array input[7] = forValid[7] = asUTC; // if the parser flagged the input as invalid, we pass the value along if (input[8] != null) { forValid[8] = input[8]; } // add the offsets to the time to be parsed so that we can have a clean array // for checking isValid input[3] += hoursOffset || 0; input[4] += minutesOffset || 0; date = new Date(0); if (asUTC) { date.setUTCFullYear(input[0], input[1], input[2]); date.setUTCHours(input[3], input[4], input[5], input[6]); } else { date.setFullYear(input[0], input[1], input[2]); date.setHours(input[3], input[4], input[5], input[6]); } date._a = forValid; return date; } // Loads a language definition into the `languages` cache. The function // takes a key and optionally values. If not in the browser and no values // are provided, it will load the language file module. As a convenience, // this function also returns the language values. function loadLang(key, values) { var i, m, parse = []; if (!values && hasModule) { values = require('./lang/' + key); } for (i = 0; i < langConfigProperties.length; i++) { // If a language definition does not provide a value, inherit // from English values[langConfigProperties[i]] = values[langConfigProperties[i]] || languages.en[langConfigProperties[i]]; } for (i = 0; i < 12; i++) { m = moment([2000, i]); parse[i] = new RegExp('^' + (values.months[i] || values.months(m, '')) + '|^' + (values.monthsShort[i] || values.monthsShort(m, '')).replace('.', ''), 'i'); } values.monthsParse = values.monthsParse || parse; languages[key] = values; return values; } // Determines which language definition to use and returns it. // // With no parameters, it will return the global language. If you // pass in a language key, such as 'en', it will return the // definition for 'en', so long as 'en' has already been loaded using // moment.lang. If you pass in a moment or duration instance, it // will decide the language based on that, or default to the global // language. function getLangDefinition(m) { var langKey = (typeof m === 'string') && m || m && m._lang || null; return langKey ? (languages[langKey] || loadLang(langKey)) : moment; } /************************************ Formatting ************************************/ function removeFormattingTokens(input) { if (input.match(/\[.*\]/)) { return input.replace(/^\[|\]$/g, ""); } return input.replace(/\\/g, ""); } 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 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 i = 5; function replaceLongDateFormatTokens(input) { return m.lang().longDateFormat[input] || input; } while (i-- && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); } if (!formatFunctions[format]) { formatFunctions[format] = makeFormatFunction(format); } return formatFunctions[format](m); } /************************************ Parsing ************************************/ // get the regex to find the next token function getParseRegexForToken(token) { switch (token) { case 'DDDD': return parseTokenThreeDigits; case 'YYYY': return parseTokenFourDigits; case 'S': case 'SS': case 'SSS': case 'DDD': return parseTokenOneToThreeDigits; case 'MMM': case 'MMMM': case 'dd': case 'ddd': case 'dddd': case 'a': case 'A': return parseTokenWord; case 'Z': case 'ZZ': return parseTokenTimezone; case 'T': return parseTokenT; case 'MM': case 'DD': case 'YY': case 'HH': case 'hh': case 'mm': case 'ss': case 'M': case 'D': case 'd': case 'H': case 'h': case 'm': case 's': return parseTokenOneOrTwoDigits; default : return new RegExp(token.replace('\\', '')); } } // function to convert string input to date function addTimeToArrayFromToken(token, input, datePartArray, config) { var a, b; switch (token) { // MONTH case 'M' : // fall through to MM case 'MM' : datePartArray[1] = (input == null) ? 0 : ~~input - 1; break; case 'MMM' : // fall through to MMMM case 'MMMM' : for (a = 0; a < 12; a++) { if (getLangDefinition().monthsParse[a].test(input)) { datePartArray[1] = a; b = true; break; } } // if we didn't find a month name, mark the date as invalid. if (!b) { datePartArray[8] = false; } break; // DAY OF MONTH case 'D' : // fall through to DDDD case 'DD' : // fall through to DDDD case 'DDD' : // fall through to DDDD case 'DDDD' : if (input != null) { datePartArray[2] = ~~input; } break; // YEAR case 'YY' : datePartArray[0] = ~~input + (~~input > 70 ? 1900 : 2000); break; case 'YYYY' : datePartArray[0] = ~~Math.abs(input); break; // AM / PM case 'a' : // fall through to A case 'A' : config.isPm = ((input + '').toLowerCase() === 'pm'); break; // 24 HOUR case 'H' : // fall through to hh case 'HH' : // fall through to hh case 'h' : // fall through to hh case 'hh' : datePartArray[3] = ~~input; break; // MINUTE case 'm' : // fall through to mm case 'mm' : datePartArray[4] = ~~input; break; // SECOND case 's' : // fall through to ss case 'ss' : datePartArray[5] = ~~input; break; // MILLISECOND case 'S' : case 'SS' : case 'SSS' : datePartArray[6] = ~~ (('0.' + input) * 1000); break; // TIMEZONE case 'Z' : // fall through to ZZ case 'ZZ' : config.isUTC = true; a = (input + '').match(parseTimezoneChunker); if (a && a[1]) { config.tzh = ~~a[1]; } if (a && a[2]) { config.tzm = ~~a[2]; } // reverse offsets if (a && a[0] === '+') { config.tzh = -config.tzh; config.tzm = -config.tzm; } break; } // if the input is null, the date is not valid if (input == null) { datePartArray[8] = false; } } // date from string and format string function makeDateFromStringAndFormat(string, format) { // This array is used to make a Date, either with `new Date` or `Date.UTC` // We store some additional data on the array for validation // datePartArray[7] is true if the Date was created with `Date.UTC` and false if created with `new Date` // datePartArray[8] is false if the Date is invalid, and undefined if the validity is unknown. var datePartArray = [0, 0, 1, 0, 0, 0, 0], config = { tzh : 0, // timezone hour offset tzm : 0 // timezone minute offset }, tokens = format.match(formattingTokens), i, parsedInput; for (i = 0; i < tokens.length; i++) { parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0]; if (parsedInput) { string = string.slice(string.indexOf(parsedInput) + parsedInput.length); } // don't parse if its not a known token if (formatTokenFunctions[tokens[i]]) { addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config); } } // handle am pm if (config.isPm && datePartArray[3] < 12) { datePartArray[3] += 12; } // if is 12 am, change hours to 0 if (config.isPm === false && datePartArray[3] === 12) { datePartArray[3] = 0; } // return return dateFromArray(datePartArray, config.isUTC, config.tzh, config.tzm); } // date from string and array of format strings function makeDateFromStringAndArray(string, formats) { var output, inputParts = string.match(parseMultipleFormatChunker) || [], formattedInputParts, scoreToBeat = 99, i, currentDate, currentScore; for (i = 0; i < formats.length; i++) { currentDate = makeDateFromStringAndFormat(string, formats[i]); formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || []; currentScore = compareArrays(inputParts, formattedInputParts); if (currentScore < scoreToBeat) { scoreToBeat = currentScore; output = currentDate; } } return output; } // date from iso format function makeDateFromString(string) { var format = 'YYYY-MM-DDT', i; if (isoRegex.exec(string)) { for (i = 0; i < 4; i++) { if (isoTimes[i][1].exec(string)) { format += isoTimes[i][0]; break; } } return parseTokenTimezone.exec(string) ? makeDateFromStringAndFormat(string, format + ' Z') : makeDateFromStringAndFormat(string, format); } return new Date(string); } /************************************ Relative Time ************************************/ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { var rt = lang.relativeTime[string]; return (typeof rt === 'function') ? rt(number || 1, !!withoutSuffix, string, isFuture) : rt.replace(/%d/i, number || 1); } function relativeTime(milliseconds, withoutSuffix, lang) { var seconds = round(Math.abs(milliseconds) / 1000), minutes = round(seconds / 60), hours = round(minutes / 60), days = round(hours / 24), years = round(days / 365), args = seconds < 45 && ['s', seconds] || minutes === 1 && ['m'] || minutes < 45 && ['mm', minutes] || hours === 1 && ['h'] || hours < 22 && ['hh', hours] || days === 1 && ['d'] || days <= 25 && ['dd', days] || days <= 45 && ['M'] || days < 345 && ['MM', round(days / 30)] || years === 1 && ['y'] || ['yy', years]; args[2] = withoutSuffix; args[3] = milliseconds > 0; args[4] = lang; return substituteTimeAgo.apply({}, args); } /************************************ Top Level Functions ************************************/ moment = function (input, format) { if (input === null || input === '') { return null; } var date, matched; // parse Moment object if (moment.isMoment(input)) { return new Moment(new Date(+input._d), input._isUTC, input._lang); // parse string and format } else if (format) { if (isArray(format)) { date = makeDateFromStringAndArray(input, format); } else { date = makeDateFromStringAndFormat(input, format); } // evaluate it as a JSON-encoded date } else { matched = aspNetJsonRegex.exec(input); date = input === undefined ? new Date() : matched ? new Date(+matched[1]) : input instanceof Date ? input : isArray(input) ? dateFromArray(input) : typeof input === 'string' ? makeDateFromString(input) : new Date(input); } return new Moment(date); }; // creating with utc moment.utc = function (input, format) { if (isArray(input)) { return new Moment(dateFromArray(input, true), true); } // if we don't have a timezone, we need to add one to trigger parsing into utc if (typeof input === 'string' && !parseTokenTimezone.exec(input)) { input += ' +0000'; if (format) { format += ' Z'; } } return moment(input, format).utc(); }; // creating with unix timestamp (in seconds) moment.unix = function (input) { return moment(input * 1000); }; // duration moment.duration = function (input, key) { var isDuration = moment.isDuration(input), isNumber = (typeof input === 'number'), duration = (isDuration ? input._data : (isNumber ? {} : input)), ret; if (isNumber) { if (key) { duration[key] = input; } else { duration.milliseconds = input; } } ret = new Duration(duration); if (isDuration) { ret._lang = input._lang; } return ret; }; // humanizeDuration // This method is deprecated in favor of the new Duration object. Please // see the moment.duration method. moment.humanizeDuration = function (num, type, withSuffix) { return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix); }; // version number moment.version = VERSION; // default format moment.defaultFormat = isoFormat; // This function will load languages and then set the global language. If // no arguments are passed in, it will simply return the current global // language key. moment.lang = function (key, values) { var i; if (!key) { return currentLanguage; } if (values || !languages[key]) { loadLang(key, values); } if (languages[key]) { // deprecated, to get the language definition variables, use the // moment.fn.lang method or the getLangDefinition function. for (i = 0; i < langConfigProperties.length; i++) { moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]]; } moment.monthsParse = languages[key].monthsParse; currentLanguage = key; } }; // returns language data moment.langData = getLangDefinition; // compare moment object moment.isMoment = function (obj) { return obj instanceof Moment; }; // for typechecking Duration objects moment.isDuration = function (obj) { return obj instanceof Duration; }; // Set default language, other languages will inherit from English. moment.lang('en', { months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), longDateFormat : { LT : "h:mm A", L : "MM/DD/YYYY", LL : "MMMM D YYYY", LLL : "MMMM D YYYY LT", LLLL : "dddd, MMMM D YYYY LT" }, meridiem : function (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } }, calendar : { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[last] dddd [at] LT', sameElse : 'L' }, relativeTime : { future : "in %s", past : "%s ago", s : "a few seconds", m : "a minute", mm : "%d minutes", h : "an hour", hh : "%d hours", d : "a day", dd : "%d days", M : "a month", MM : "%d months", y : "a year", yy : "%d years" }, ordinal : function (number) { var b = number % 10; return (~~ (number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; } }); /************************************ Moment Prototype ************************************/ moment.fn = Moment.prototype = { clone : function () { return moment(this); }, valueOf : function () { return +this._d; }, unix : function () { return Math.floor(+this._d / 1000); }, toString : function () { return this._d.toString(); }, toDate : function () { return this._d; }, toArray : function () { var m = this; return [ m.year(), m.month(), m.date(), m.hours(), m.minutes(), m.seconds(), m.milliseconds(), !!this._isUTC ]; }, isValid : function () { if (this._a) { // if the parser finds that the input is invalid, it sets // the eighth item in the input array to false. if (this._a[8] != null) { return !!this._a[8]; } return !compareArrays(this._a, (this._a[7] ? moment.utc(this._a) : moment(this._a)).toArray()); } return !isNaN(this._d.getTime()); }, utc : function () { this._isUTC = true; return this; }, local : function () { this._isUTC = false; return this; }, format : function (inputString) { return formatMoment(this, inputString ? inputString : moment.defaultFormat); }, add : function (input, val) { var dur = val ? moment.duration(+val, input) : moment.duration(input); addOrSubtractDurationFromMoment(this, dur, 1); return this; }, subtract : function (input, val) { var dur = val ? moment.duration(+val, input) : moment.duration(input); addOrSubtractDurationFromMoment(this, dur, -1); return this; }, diff : function (input, val, asFloat) { var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(), zoneDiff = (this.zone() - inputMoment.zone()) * 6e4, diff = this._d - inputMoment._d - zoneDiff, year = this.year() - inputMoment.year(), month = this.month() - inputMoment.month(), date = this.date() - inputMoment.date(), output; if (val === 'months') { output = year * 12 + month + date / 30; } else if (val === 'years') { output = year + (month + date / 30) / 12; } else { output = val === 'seconds' ? diff / 1e3 : // 1000 val === 'minutes' ? diff / 6e4 : // 1000 * 60 val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60 val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24 val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7 diff; } return asFloat ? output : round(output); }, from : function (time, withoutSuffix) { return moment.duration(this.diff(time)).lang(this._lang).humanize(!withoutSuffix); }, fromNow : function (withoutSuffix) { return this.from(moment(), withoutSuffix); }, calendar : function () { var diff = this.diff(moment().sod(), 'days', true), calendar = this.lang().calendar, allElse = calendar.sameElse, format = diff < -6 ? allElse : diff < -1 ? calendar.lastWeek : diff < 0 ? calendar.lastDay : diff < 1 ? calendar.sameDay : diff < 2 ? calendar.nextDay : diff < 7 ? calendar.nextWeek : allElse; return this.format(typeof format === 'function' ? format.apply(this) : format); }, isLeapYear : function () { var year = this.year(); return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; }, isDST : function () { return (this.zone() < moment([this.year()]).zone() || this.zone() < moment([this.year(), 5]).zone()); }, day : function (input) { var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); return input == null ? day : this.add({ d : input - day }); }, startOf: function (val) { // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (val.replace(/s$/, '')) { case 'year': this.month(0); /* falls through */ case 'month': this.date(1); /* falls through */ case 'day': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); /* falls through */ } return this; }, endOf: function (val) { return this.startOf(val).add(val.replace(/s?$/, 's'), 1).subtract('ms', 1); }, sod: function () { return this.clone().startOf('day'); }, eod: function () { // end of day = start of day plus 1 day, minus 1 millisecond return this.clone().endOf('day'); }, zone : function () { return this._isUTC ? 0 : this._d.getTimezoneOffset(); }, daysInMonth : function () { return moment.utc([this.year(), this.month() + 1, 0]).date(); }, // If passed a language key, it will set the language for this // instance. Otherwise, it will return the language configuration // variables for this instance. lang : function (lang) { if (lang === undefined) { return getLangDefinition(this); } else { this._lang = lang; return this; } } }; // helper for adding shortcuts function makeGetterAndSetter(name, key) { moment.fn[name] = function (input) { var utc = this._isUTC ? 'UTC' : ''; if (input != null) { this._d['set' + utc + key](input); return this; } else { return this._d['get' + utc + key](); } }; } // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) for (i = 0; i < proxyGettersAndSetters.length; i ++) { makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]); } // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') makeGetterAndSetter('year', 'FullYear'); /************************************ Duration Prototype ************************************/ moment.duration.fn = Duration.prototype = { weeks : function () { return absRound(this.days() / 7); }, valueOf : function () { return this._milliseconds + this._days * 864e5 + this._months * 2592e6; }, humanize : function (withSuffix) { var difference = +this, rel = this.lang().relativeTime, output = relativeTime(difference, !withSuffix, this.lang()), fromNow = difference <= 0 ? rel.past : rel.future; if (withSuffix) { if (typeof fromNow === 'function') { output = fromNow(output); } else { output = fromNow.replace(/%s/i, output); } } return output; }, lang : moment.fn.lang }; function makeDurationGetter(name) { moment.duration.fn[name] = function () { return this._data[name]; }; } function makeDurationAsGetter(name, factor) { moment.duration.fn['as' + name] = function () { return +this / factor; }; } for (i in unitMillisecondFactors) { if (unitMillisecondFactors.hasOwnProperty(i)) { makeDurationAsGetter(i, unitMillisecondFactors[i]); makeDurationGetter(i.toLowerCase()); } } makeDurationAsGetter('Weeks', 6048e5); /************************************ Exposing Moment ************************************/ // CommonJS module is defined if (hasModule) { module.exports = moment; } /*global ender:false */ if (typeof ender === 'undefined') { // here, `this` means `window` in the browser, or `global` on the server // add `moment` as a global object via a string identifier, // for Closure Compiler "advanced" mode this['moment'] = moment; } /*global define:false */ if (typeof define === "function" && define.amd) { define("moment", [], function () { return moment; }); } }).call(this);