diff --git a/README.md b/README.md index 57058915e..99f86ac88 100644 --- a/README.md +++ b/README.md @@ -68,26 +68,27 @@ Things might work in Edge 15-18, Firefox 47-62 and Chrome 54-68 due to one polyf * Removed jquery-scrollstop * Removed jquery-mousewheel * Removed matchmedia-polyfill +* Removed momentjs (localization still used) |js/* |1.14.0 |native |gzip 1.14 |gzip | |----------- |--------: |--------: |--------: |--------: | -|admin.js |2.130.942 |1.216.846 | 485.481 | 298.402 | -|app.js |4.184.455 |2.962.814 | 932.725 | 691.920 | -|boot.js | 671.522 | 94.230 | 169.502 | 28.385 | -|libs.js | 647.614 | 505.981 | 194.728 | 153.718 | +|admin.js |2.130.942 |1.216.173 | 485.481 | 298.376 | +|app.js |4.184.455 |2.961.284 | 932.725 | 691.792 | +|boot.js | 671.522 | 94.230 | 169.502 | 28.382 | +|libs.js | 647.614 | 458.564 | 194.728 | 139.408 | |polyfills.js | 325.834 | 0 | 71.825 | 0 | -|TOTAL js |7.960.367 |4.779.871 |1.854.261 |1.172.425 | +|TOTAL js |7.960.367 |4.730.251 |1.854.261 |1.157.958 | |js/min/* |1.14.0 |native |gzip 1.14 |gzip | |--------------- |--------: |--------: |--------: |--------: | -|admin.min.js | 252.147 | 156.875 | 73.657 | 44.814 | -|app.min.js | 511.202 | 384.939 |140.462 |101.744 | -|boot.min.js | 66.007 | 11.575 | 22.567 | 4.463 | -|libs.min.js | 572.545 | 463.408 |176.720 |143.754 | +|admin.min.js | 252.147 | 156.868 | 73.657 | 44.788 | +|app.min.js | 511.202 | 384.861 |140.462 |101.677 | +|boot.min.js | 66.007 | 11.575 | 22.567 | 4.460 | +|libs.min.js | 572.545 | 413.893 |176.720 |129.076 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | -|TOTAL js/min |1.434.353 |1.016.797 |424.718 |294.775 | +|TOTAL js/min |1.434.353 | 967.197 |424.718 |280.001 | -417.556 bytes (129.943 gzip) is not much, but it feels faster. +467.156 bytes (144.717 gzip) is not much, but it feels faster. |css/* |1.14.0 |native | diff --git a/dev/Common/Momentor.js b/dev/Common/Momentor.js index ebeb99070..025b7a1fe 100644 --- a/dev/Common/Momentor.js +++ b/dev/Common/Momentor.js @@ -1,6 +1,5 @@ import window from 'window'; import $ from '$'; -import moment from 'moment'; import { i18n } from 'Common/Translator'; let _moment = null; @@ -11,7 +10,7 @@ const updateMomentNow = ()=>{ // leading debounce if (!d) { d = setTimeout(()=>d=0, 500); - _moment = moment(); + _moment = new Date(); } }; @@ -19,7 +18,7 @@ const updateMomentNowUnix = ()=>{ // leading debounce if (!du) { du = setTimeout(()=>du=0, 500); - _momentNow = moment().unix(); + _momentNow = new Date().getTime(); } }; @@ -28,7 +27,7 @@ const updateMomentNowUnix = ()=>{ */ export function momentNow() { updateMomentNow(); - return _moment || moment(); + return _moment || new Date(); } /** @@ -47,21 +46,19 @@ function formatCustomShortDate(m) { const now = momentNow(); if (m && now) { switch (true) { - case 4 >= now.diff(m, 'hours'): + case 4 >= (now.getTime() - m.getTime()) / 3600: return m.fromNow(); case now.format('L') === m.format('L'): return i18n('MESSAGE_LIST/TODAY_AT', { TIME: m.format('LT') }); - case now - .clone() - .subtract(1, 'days') + case new Date(now.getTime() - 86400000) // subtract 1 day .format('L') === m.format('L'): return i18n('MESSAGE_LIST/YESTERDAY_AT', { TIME: m.format('LT') }); - case now.year() === m.year(): - return m.format('D MMM.'); + case now.getFullYear() === m.getFullYear(): + return m.format('d M.'); // no default } } @@ -83,9 +80,9 @@ export function format(timeStampInUTC, formatStr) { timeStampInUTC = 0 < timeStampInUTC ? timeStampInUTC : 0 === timeStampInUTC ? now : 0; timeStampInUTC = now < timeStampInUTC ? now : timeStampInUTC; - m = 0 < timeStampInUTC ? moment.unix(timeStampInUTC) : null; + m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null; - if (m && 1970 === m.year()) { + if (m && 1970 === m.getFullYear()) { m = null; } diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 2f97c3b3d..a02d3d759 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -1,6 +1,5 @@ import $ from '$'; import ko from 'ko'; -import moment from 'moment'; import classnames from 'classnames'; import { MessagePriority, SignedVerifyStatus } from 'Common/Enums'; @@ -625,7 +624,7 @@ class MessageModel extends AbstractModel { viewPopupMessage(print = false) { const timeStampInUTC = this.dateTimeStampInUTC() || 0, ccLine = this.ccToLine(false), - m = 0 < timeStampInUTC ? moment.unix(timeStampInUTC) : null; + m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null; previewMessage( { diff --git a/dev/View/Popup/AdvancedSearch.js b/dev/View/Popup/AdvancedSearch.js index a3f308b02..e8956bf01 100644 --- a/dev/View/Popup/AdvancedSearch.js +++ b/dev/View/Popup/AdvancedSearch.js @@ -118,9 +118,9 @@ class AdvancedSearchPopupView extends AbstractViewNext { } if (-1 < this.selectedDateValue()) { - let d = new Date(), pad2 = v => 10 > v ? '0' + v : v; + let d = new Date(); d.setDate(d.getDate() - this.selectedDateValue()); - result.push('date:' + d.getFullYear()+'.'+pad2(d.getMonth()+1)+'.'+pad2(d.getDate()) + '/'); + result.push('date:' + d.format('Y.m.d') + '/'); } if (text) { diff --git a/dev/prototype-date.js b/dev/prototype-date.js new file mode 100644 index 000000000..fa4fe6f1c --- /dev/null +++ b/dev/prototype-date.js @@ -0,0 +1,193 @@ + +(w=>{ + + // Import momentjs locales function + w.moment = { + defineLocale: (name, config)=>{ + locale = config; + for (let i = 0; i < 12; ++i) Date.shortMonths[i] = config.monthsShort(i+1, 'MMM'); + Date.longMonths = config.months, + Date.longDays = config.weekdays; + Date.shortDays = config.weekdaysMin; // config.weekdaysShort + } + }; + + let locale = { + longDateFormat: { + LT : 'h:mm A', // 'g:i A', + L : 'MM/DD/YYYY', // 'Y-m-d', + LL : 'MMMM D, YYYY', // 'F j, Y' + LLL : 'MMMM D, YYYY h:mm A', // 'F j, Y g:i A' + LLLL : 'dddd, MMMM D, YYYY h:mm A' // 'l, F j, Y g:i A' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + ss : '%d 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' + } + }, + pad2 = v => 10 > v ? '0' + v : v, + pad3 = v => 10 > v ? '00' + v : (100 > v ? '0' + v : v), + getISODay = x => x.getDay() || 7, + getDayOfYear = x => Math.floor((Date.UTC(x.getFullYear(),x.getMonth(),x.getDate()) + - Date.UTC(x.getFullYear(),0,1)) / 86400000), + getWeek = x => { + let d = new Date(x.getFullYear(),0,1), + wd = getISODay(d), + w = Math.ceil((getDayOfYear(x)+wd) / 7); + /* ISO 8601 states that week 1 is the week with january 4th in it */ + if (4 < wd) --w; + return (1 > w + ? getWeek(new Date(x.getFullYear()-1,11,31)) /* previous year, last week */ + : (52 < w && 4 > getISODay(x) ? 1 /* next year, first week */ : w) ); + }, + isDST = x => { + let y=x.getFullYear(); + return x.getTimezoneOffset() != Math.max( + new Date(y, 0, 1).getTimezoneOffset(), + new Date(y, 6, 1).getTimezoneOffset() + ); + }; + + // Defining locale + Date.shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + Date.longMonths = ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December']; + Date.shortDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + Date.longDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + + // Simulate PHP's date function + Date.prototype.format = function (str, UTC) { + if (locale.longDateFormat[str]) { + str = locale.longDateFormat[str] + .replace('YYYY', 'Y') + .replace(/(^|[^M])M([^M]|$)/, '$1n$2') + .replace('MMMM', 'F') + .replace('MMM', 'M') + .replace('MM', 'm') + .replace(/(^|[^D])D([^D]|$)/, '$1j$2') + .replace('DD', 'd') + .replace('dddd', 'l') + .replace('ddd', 'D') + .replace('dd', 'D') + .replace(/(^|[^H])H([^H]|$)/, '$1G$2') + .replace('HH', 'H') + .replace('h', 'g') + .replace('hh', 'h') + .replace('mm', 'i'); + } + UTC = UTC || str.match(/\\Z$/); + let x = this, + d = UTC ? { + D: x.getUTCDay(), + Y: x.getUTCFullYear(), + m: x.getUTCMonth(), + d: x.getUTCDate(), + H: x.getUTCHours(), + Z: 0 + } : { + D: x.getDay(), + Y: x.getFullYear(), + m: x.getMonth(), + d: x.getDate(), + H: x.getHours(), + Z: -x.getTimezoneOffset() + }; + return str + ? str.replace(/\\?[a-zA-Z]/g, m => { + if (m[0] === '\\') { return m[1]; } + switch (m) { + // Day + case 'd': return pad2(d.d); + case 'D': return Date.shortDays[d.D]; + case 'j': return d.d; + case 'l': return Date.longDays[d.D]; + case 'N': return getISODay(x); + case 'w': return d.D; + case 'z': return getDayOfYear(x); + // Week + case 'W': return pad2(getWeek(x)); + // Month + case 'F': return Date.longMonths[d.m]; + case 'm': return pad2(d.m + 1); + case 'M': return Date.shortMonths[d.m]; + case 'n': return d.m + 1; + case 't': return 32 - new Date(x.getFullYear(), x.getMonth(), 32).getDate(); + // Year + case 'L': return (((d.Y%4===0)&&(d.Y%100 !== 0)) || (d.Y%400===0)) ? '1' : '0'; + case 'o': return new Date( + x.getFullYear(), + x.getMonth(), + x.getDate() - ((x.getDay() + 6) % 7) + 3 + ).getFullYear(); + case 'Y': return d.Y; + case 'y': return ('' + d.Y).substr(2); + // Time + case 'a': return d.H < 12 ? "am" : "pm"; + case 'A': return d.H < 12 ? "AM" : "PM"; + case 'g': return d.H % 12 || 12; + case 'G': return d.H; + case 'h': return pad2(d.H % 12 || 12); + case 'H': return pad2(d.H); + case 'i': return pad2(UTC?x.getUTCMinutes():x.getMinutes()); + case 's': return pad2(UTC?x.getUTCSeconds():x.getSeconds()); + case 'u': return pad3(UTC?x.getUTCMilliseconds():x.getMilliseconds()); + // Timezone + case 'I': return UTC ? 0 : isDST(x) ? 1 : 0; + case 'O': return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + '00'; + case 'P': return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + ':' + pad2(Math.abs(d.Z % 60)); + case 'T': return UTC ? 'UTC' : new Date(d.Y, 0, 1).toTimeString().replace(/^.+ \(?([^)]+)\)?$/, '$1'); + case 'Z': return d.Z * 60; + // Full Date/Time + case 'c': return x.format("Y-m-d\\TH:i:sO"); + case 'r': return x.format("D, d M Y H:i:s O"); + case 'U': return x.getTime() / 1000; + } + return m; + }) + : x.toString(); + }; + + // Simulate momentjs fromNow function + Date.prototype.fromNow = function() { + let format, + seconds = ((new Date()).getTime() - this.getTime()) / 1000, + str = locale.relativeTime[0 < seconds ? 'future' : 'past']; + seconds = Math.abs(seconds); + if (60 > seconds) { + format = 's'; + } else if (3600 > seconds) { + seconds = seconds / 60; + format = 'm'; + } else if (86400 > seconds) { + seconds = seconds / 3600; + format = 'h'; + } else if (2628000 > seconds) { + seconds = seconds / 86400; + format = 'd'; + } else if (31536000 > seconds) { + seconds = seconds / 2628000; + format = 'M'; + } else { + seconds = seconds / 31536000; + format = 'y'; + } + seconds = Math.round(seconds); + if (1 < seconds) { + format += format; + } + return str.replace('%s', locale.relativeTime[format].replace('%d', seconds)); + } + +})(this); diff --git a/tasks/config.js b/tasks/config.js index 85b2fb167..768090880 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -81,7 +81,7 @@ config.paths.js = { 'vendors/keymaster/keymaster.js', // custom (modified) 'vendors/qr.js/qr.min.js', // fixed (license) 'vendors/bootstrap/js/bootstrap.min.js', // fixed - 'node_modules/moment/min/moment.min.js', + 'dev/prototype-date.js', 'node_modules/knockout/build/output/knockout-latest.js', 'node_modules/knockout-sortable/build/knockout-sortable.min.js ', 'node_modules/simplestatemanager/dist/ssm.min.js',