(doc=>{ Array.prototype.unique = function() { return this.filter((v, i, a) => a.indexOf(v) === i); }; Array.prototype.validUnique = function(fn) { return this.filter((v, i, a) => (fn ? fn(v) : v) && a.indexOf(v) === i); }; Date.defineRelativeTimeFormat = config => relativeTime = config; let formats = { LT : {hour: 'numeric', minute: 'numeric'}, L : {}, LL : {year: 'numeric', month: 'short', day: 'numeric'}, LLL : {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'}, LLLL : {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'}, 'd M': {day: '2-digit', month: 'short'} }, phpFormats = { // Day d: {day: '2-digit'}, D: {weekday: 'short'}, j: {day: 'numeric'}, l: {weekday: 'long'}, z: {}, // Month F: {month: 'long'}, m: {month: '2-digit'}, M: {month: 'short'}, n: {month: 'numeric'}, Y: {year: 'numeric'}, y: {year: '2-digit'}, // Time A: {hour12: true}, G: {hour: 'numeric'}, H: {hour: '2-digit'}, i: {minute: '2-digit'}, s: {second: '2-digit'}, u: {fractionalSecondDigits: 3}, Z: {timeZone: 'UTC'} }, relativeTime = { // see /snappymail/v/0.0.0/app/localization/relativetimeformat/ }, pad2 = v => 10 > v ? '0' + v : v; // Format momentjs/PHP date formats to Intl.DateTimeFormat // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat Date.prototype.format = function (options, UTC) { if (typeof options == 'string') { if ('Y-m-d\\TH:i:s' == options) { return this.getFullYear() + '-' + pad2(1 + this.getMonth()) + '-' + pad2(this.getDate()) + 'T' + pad2(this.getHours()) + ':' + pad2(this.getMinutes()) + ':' + pad2(this.getSeconds()); } if (formats[options]) { options = formats[options]; } else { let o, s = options + (UTC?'Z':''), i = s.length; console.log('Date.format('+s+')'); options = {}; while (i--) { o = phpFormats[s[i]] || phpFormats[s[i].toUpperCase()]; o && Object.entries(o).forEach(([k,v])=>options[k]=v); } formats[s] = options; } } return new Intl.DateTimeFormat(doc.documentElement.lang, options).format(this); }; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat Date.prototype.fromNow = function() { let unit = 'second', value = Math.round((this.getTime() - Date.now()) / 1000), t = [[60,'minute'],[3600,'hour'],[86400,'day'],[2628000,'month'],[31536000,'year']], i = 5, abs = Math.abs(value); while (i--) { if (t[i][0] <= abs) { value = Math.round(value / t[i][0]); unit = t[i][1]; break; } } if (Intl.RelativeTimeFormat) { let rtf = new Intl.RelativeTimeFormat(doc.documentElement.lang); return rtf.format(value, unit); } abs = Math.abs(value); let rtf = relativeTime.long[unit][0 > value ? 'past' : 'future'], plural = relativeTime.plural(abs); return (rtf[plural] || rtf).replace('{0}', abs); }; Element.prototype.closestWithin = function(selector, parent) { const el = this.closest(selector); return (el && el !== parent && parent.contains(el)) ? el : null; }; Element.fromHTML = string => { const template = doc.createElement('template'); template.innerHTML = string.trim(); return template.content.firstChild; }; /** * Every time the function is executed, * it will delay the execution with the given amount of milliseconds. */ if (!Function.prototype.debounce) { Function.prototype.debounce = function(ms) { let func = this, timer; return function(...args) { timer && clearTimeout(timer); timer = setTimeout(()=>{ func.apply(this, args) timer = 0; }, ms); }; }; } /** * No matter how many times the event is executed, * the function will be executed only once, after the given amount of milliseconds. */ if (!Function.prototype.throttle) { Function.prototype.throttle = function(ms) { let func = this, timer; return function(...args) { if (!timer) { timer = setTimeout(()=>{ func.apply(this, args) timer = 0; }, ms); } }; }; } })(document);