@pqina/flip
Version:
A Beautifully Animated Flip Clock
2,060 lines (1,649 loc) • 149 kB
JavaScript
(function() {
'use strict';
if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', {
value: function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
}
});
}
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function (predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
for (var i = 0; i !== length; i++) {
if (predicate.call(thisArg, this[i], i, list)) {
return this[i];
}
}
return undefined;
}
});
}
// Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// The length property of the from method is 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;
// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError("Array.from requires an array-like object - not null or undefined");
}
// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);
// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method
// of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
}());
}
Array.prototype.includes = Array.prototype.includes||function(searchElement , fromIndex) {
if (!this) {
throw new TypeError('Array.prototype.includes called on null or undefined');
}
if (fromIndex===undefined){
var i = this.length;
while(i--){
if (this[i]===searchElement){return true}
}
} else {
var i = fromIndex, len=this.length;
while(i++!==len){ // Addittion on hardware will perform as fast as, if not faster than subtraction
if (this[i]===searchElement){return true}
}
}
return false;
};
// These polyfills are required for using Tick on IE 11
if (typeof Object.assign != 'function') {
Object.assign = function (target, varArgs) { // .length of function is 2
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function() {
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
}());
/* eslint-disable */
/*
* @pqina/flip v1.8.3 - A Beautifully Animated Flip Clock
* Copyright (c) 2023 PQINA - https://pqina.nl/flip/
*/
(function(root, undefined) {
'use strict';
// only create tick extensions queue if not already available
if (!root.Tick) {
root.Tick = [];
}
// add this extension
root.Tick.push(['view', 'flip', (function() {
if (!module) {
var module = {};
}
'use strict';
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var index = (function (_ref) {
var DOM = _ref.DOM,
animate = _ref.Animation.animate,
Extension = _ref.Extension,
performance = _ref.Date.performance,
_ref$View = _ref.View,
rooter = _ref$View.rooter,
destroyer = _ref$View.destroyer,
drawer = _ref$View.drawer,
updater = _ref$View.updater,
styler = _ref$View.styler;
var easeOutCubic = Extension.getExtension(Extension.Type.EASING_FUNCTION, 'ease-out-cubic');
var easeOutSine = Extension.getExtension(Extension.Type.EASING_FUNCTION, 'ease-out-sine');
var draw = function draw(state) {
// create cards if not already created
if (state.isInitialValue()) {
// clear current content
state.root.textContent = '';
// value spacer
state.spacer = DOM.create('span', 'tick-flip-spacer');
state.root.appendChild(state.spacer);
// shaders
var shadowTop = DOM.create('span', 'tick-flip-shadow-top tick-flip-shadow tick-flip-front');
var shadowBottom = DOM.create('span', 'tick-flip-shadow-bottom tick-flip-shadow tick-flip-back');
state.root.appendChild(shadowTop);
state.root.appendChild(shadowBottom);
// create shadow element
state.shadowCard = DOM.create('span', 'tick-flip-card-shadow');
state.root.appendChild(state.shadowCard);
}
// set spacer value
state.spacer.textContent = state.value;
// don't animate when invisible to the user
if (!state.isInitialValue() && !DOM.visible(state.root)) {
state.cards.forEach(function (card) {
card.back = state.value;
card.front = state.value;
});
return;
}
// get previous card
var turningCard = state.cards[state.cards.length - 1];
if (turningCard) {
turningCard.waiting = false;
turningCard.offset = performance();
turningCard.back = state.value;
}
// create a quick flipped initial card and then exit
if (state.isInitialValue()) {
// create flipped state (bottom)
var initialBottomCard = new FlipCard();
initialBottomCard.back = state.value;
initialBottomCard.offset = null;
initialBottomCard.progress = 1;
state.root.insertBefore(initialBottomCard.root, state.root.firstChild);
state.cards.push(initialBottomCard);
}
// create a new card
var topCard = new FlipCard();
topCard.offset = null;
topCard.progress = 0;
topCard.visual_progress = 0;
topCard.waiting = true;
topCard.front = state.value;
topCard.rotate(0);
// topCard.rotate(-1); // prevents slight anti-aliasing issues on Safari / Firefox
state.root.insertBefore(topCard.root, state.root.firstChild);
state.cards.push(topCard);
if (!state.animating) {
state.animating = true;
var ease = Extension.getExtension(Extension.Type.EASING_FUNCTION, state.style.flipEasing);
var tick = function tick() {
// find cards that require animation
var cardsToAnimate = state.cards.filter(function (card) {
return !card.done && !card.waiting;
});
if (cardsToAnimate.length === 0) {
state.animating = false;
return;
}
// calculate card progress
cardsToAnimate.forEach(function (card) {
if (card.offset !== null) {
card.progress = (performance() - card.offset) / state.style.flipDuration;
}
if (card.progress >= 1) {
card.progress = 1;
card.done = true;
}
card.visual_progress = ease(card.progress);
});
// sort
var cardDistance = 0.01;
cardsToAnimate.reverse().forEach(function (card, index) {
var previousCard = cardsToAnimate[index - 1];
if (previousCard && card.visual_progress <= previousCard.visual_progress) {
card.visual_progress = previousCard.visual_progress + cardDistance;
}
});
cardsToAnimate.reverse();
// update shadows
state.cards.forEach(function (card, index) {
// set default shadow and highlight levels based on visual animation progress
var shadowFrontProgress = 1 - Math.abs(card.visual_progress - .5) * 2;
var highlightBackProgress = 1 - (card.visual_progress - .5) / .5;
card.shadowFront = shadowFrontProgress;
card.highlightBack = highlightBackProgress;
// recalculate levels based on other card positions
var cardAbove = state.cards[index + 1];
// if there's a card above me, my back is visible, and the above card is falling
if (cardAbove && card.visual_progress > .5 && card.visual_progress > 0) {
card.shadowBack = easeOutCubic(cardAbove.visual_progress);
}
});
// update and animate cards
cardsToAnimate.forEach(function (card, index) {
var p = card.visual_progress;
if (p > .5 && !card.done) {
card.root.style.zIndex = 10 + index;
} else {
card.root.style.removeProperty('z-index');
}
card.rotate(p * -180);
});
// handle card stack shadow
var shadowProgress = 0;
var dist = 1;
cardsToAnimate.forEach(function (card) {
var d = Math.abs(card.visual_progress - .5);
if (d < dist) {
dist = d;
shadowProgress = card.visual_progress;
}
});
var s = shadowProgress < .5 ? easeOutSine(shadowProgress / .5) : easeOutSine((1 - shadowProgress) / .5);
state.shadowCard.style.opacity = s;
DOM.transform(state.shadowCard, 'scaleY', s);
// clean up cards that finished animating
state.cards.filter(function (card) {
return card.done;
}) // gather all done cards
.slice(0, -1) // don't delete the last one
.forEach(function (card) {
// let's delete them
// remove predecessor from cards array
state.cards = state.cards.filter(function (c) {
return c !== card;
});
// remove predecessor from the DOM
if (card.root.parentNode) {
state.root.removeChild(card.root);
}
});
requestAnimationFrame(tick);
};
tick();
}
};
var FlipCard = function () {
function FlipCard() {
classCallCheck(this, FlipCard);
this._root = DOM.create('span', 'tick-flip-card');
// card front
var front = DOM.create('span', 'tick-flip-panel-front tick-flip-front tick-flip-panel');
var textFront = DOM.create('span', 'tick-flip-panel-front-text');
var textFrontWrapper = DOM.create('span', 'tick-flip-panel-text-wrapper');
textFront.appendChild(textFrontWrapper);
var shadowFront = DOM.create('span', 'tick-flip-panel-front-shadow');
front.appendChild(textFront);
front.appendChild(shadowFront);
var back = DOM.create('span', 'tick-flip-panel-back tick-flip-back tick-flip-panel');
var textBack = DOM.create('span', 'tick-flip-panel-back-text');
var textBackWrapper = DOM.create('span', 'tick-flip-panel-text-wrapper');
textBack.appendChild(textBackWrapper);
var highlightBack = DOM.create('span', 'tick-flip-panel-back-highlight');
var shadowBack = DOM.create('span', 'tick-flip-panel-back-shadow');
back.appendChild(textBack);
back.appendChild(highlightBack);
back.appendChild(shadowBack);
// create card
this._root.appendChild(front);
this._root.appendChild(back);
// references for animation
this._front = front;
this._back = back;
this._shadowFront = shadowFront;
this._shadowBack = shadowBack;
this._highlightBack = highlightBack;
// back
this._textBack = textBackWrapper;
this._textFront = textFrontWrapper;
// front and back values
this._frontValue = null;
this._backValue = null;
}
createClass(FlipCard, [{
key: 'rotate',
value: function rotate(degrees) {
this._front.style.transform = 'rotateX(' + degrees + 'deg)';
this._back.style.transform = 'rotateX(' + (-180 + degrees) + 'deg)';
}
}, {
key: 'root',
get: function get$$1() {
return this._root;
}
}, {
key: 'front',
set: function set$$1(value) {
this._frontValue = value;
this._textFront.textContent = value;
},
get: function get$$1() {
return this._frontValue;
}
}, {
key: 'back',
set: function set$$1(value) {
this._backValue = value;
this._textBack.textContent = value;
},
get: function get$$1() {
return this._backValue;
}
}, {
key: 'highlightBack',
set: function set$$1(value) {
this._highlightBack.style.opacity = value;
}
}, {
key: 'shadowBack',
set: function set$$1(value) {
this._shadowBack.style.opacity = value;
}
}, {
key: 'shadowFront',
set: function set$$1(value) {
this._shadowFront.style.opacity = value;
}
}]);
return FlipCard;
}();
/**
* Expose
*/
return function (root) {
var state = {
cards: [],
lastCard: null,
initialCard: null,
shadowAbove: null,
shadowBelow: null,
shadowCard: null,
currentValue: null,
lastValue: null,
front: null,
back: null
};
return Object.assign({}, rooter(state, root, 'flip'), updater(state), styler(state, {
flipDuration: 800,
flipEasing: 'ease-out-bounce'
}), drawer(state, draw), destroyer(state));
};
});
module.exports = index;
module.exports.identifier = {
name:'flip',
type:'view'
};
return module.exports;
}())]);
}(window));
/* eslint-disable */
/*
* @pqina/tick v1.8.2 - Counters Made Easy
* Copyright (c) 2023 PQINA - https://github.com/pqina/tick/
*/
(function(root, plugins, undefined) {
'use strict';
// Cut the mustard for really old browsers
if (!root || !('MutationObserver' in root) || !('requestAnimationFrame' in root)) {
return;
}
// private library reference
var Tick = (function() {
if (!module) {
var module = {};
}
'use strict';
// Available extension types
var ExtensionType = {
FONT: 'font',
VIEW: 'view',
TRANSFORM: 'transform',
EASING_FUNCTION: 'easing-function',
TRANSITION: 'transition'
};
// Registered extension collection
var Extensions = {};
Extensions[ExtensionType.FONT] = {};
Extensions[ExtensionType.VIEW] = {};
Extensions[ExtensionType.TRANSFORM] = {};
Extensions[ExtensionType.EASING_FUNCTION] = {};
Extensions[ExtensionType.TRANSITION] = {};
/**
* Adds multiple extensions in one go
* @param type
* @param extensions
* @returns {null}
*/
var addExtensions = function addExtensions(type, extensions) {
// type does not exist
if (!Extensions[type]) {
return null;
}
for (var name in extensions) {
if (!extensions.hasOwnProperty(name)) {
continue;
}
// name already exists
if (Extensions[type][name]) {
return null;
}
// register
Extensions[type][name] = extensions[name];
}
};
/**
* Adds an extension function by type
* @param type
* @param name
* @param fn
* @returns {null}
*/
var addExtension = function addExtension(type, name, fn) {
// type does not exist
if (!Extensions[type]) {
throw 'Can\'t add extension with type of "' + type + '", "' + type + '" is not a valid extension type. The following types are valid: ' + keysToList(Extensions);
}
// if is invalid name
if (!/^[-a-z]+$/.test(name)) {
throw 'Can\'t add extension with name "' + name + '", "' + name + '" is contains invalid characters. Only lowercase alphabetical characters and dashes are allowed.';
}
// name in type already exists
if (Extensions[type][name]) {
throw 'Can\'t add extension with name "' + name + '", "' + name + '" is already added.';
}
// add
Extensions[type][name] = fn;
};
/**
* Returns an extension function by name and type
* @param type
* @param name
* @returns {*}
*/
var getExtension = function getExtension(type, name) {
// type does not exist
if (!Extensions[type]) {
throw 'Can\'t get extension with type of "' + type + '", "' + type + '" is not a valid extension type. The following types are available: ' + keysToList(Extensions);
}
// name in type does not exist
if (!Extensions[type][name]) {
throw 'Can\'t get extension with name "' + name + '", "' + name + '" is not available. The following extensions are available: ' + keysToList(Extensions[type]);
}
return Extensions[type][name];
};
var MILLISECOND = 1;
var SECOND = 1000;
var MINUTE = 60000;
var HOUR = 3600000;
var DAY = 86400000;
var WEEK = 604800000;
var MONTH = 2628000000;
var YEAR = 31536000000;
var TimeUnit = {
'Week': WEEK,
'Day': DAY,
'Hour': HOUR,
'Minute': MINUTE,
'Second': SECOND,
'Millisecond': MILLISECOND,
'Month': MONTH,
'Year': YEAR
};
var Months = ['Januari', 'Februari', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
for (var key in TimeUnit) {
if (!TimeUnit.hasOwnProperty(key)) {
continue;
}
var val = TimeUnit[key];
if (val === MILLISECOND) {
TimeUnit['mi'] = val;
TimeUnit['ms'] = val;
} else if (val === MONTH) {
TimeUnit['M'] = val;
} else {
TimeUnit[key.charAt(0).toLowerCase()] = val;
}
TimeUnit[key.toLowerCase()] = val;
TimeUnit[key.toLowerCase() + 's'] = val;
}
var Days = {
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6,
Sunday: 0
};
var MonthFactor = {
'M': 1,
'y': 12
};
var serverDate = function serverDate(cb) {
var xhr = new XMLHttpRequest();
var now = Date.now();
xhr.open('HEAD', window.location + '?noCache=' + now);
xhr.setRequestHeader('Content-Type', 'text/html');
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.onload = function () {
var correction = (now - Date.now()) * .5;
var responseDate = new Date(xhr.getResponseHeader('Date'));
cb(new Date(responseDate.getTime() + correction));
};
xhr.send();
};
var isDate = function isDate(date) {
return date instanceof Date;
};
var setTime = function setTime(date, time) {
date.setHours(time[0] || 0, time[1] || 0, time[2] || 0, time[3] || 0);
return date;
};
var setDay = function setDay(date, day) {
var current = date.getDay();
var dist = day - current;
date.setDate(date.getDate() + dist);
return date;
};
var setDayOfMonth = function setDayOfMonth(date, day) {
var totalDays = daysInMonth(date.getMonth() + 1, date.getFullYear());
day = day === 'last' ? totalDays : Math.max(1, Math.min(totalDays, day));
date.setDate(day);
return date;
};
var setMonth = function setMonth(date, month) {
date.setMonth(Months.map(function (m) {
return m.toLowerCase();
}).indexOf(month));
return date;
};
/*
Z
±hh:mm
±hhmm
±hh
*/
var toTimezoneOffset = function toTimezoneOffset(ISO8601Timezone) {
var current = new Date().getTimezoneOffset() * 60000;
if (ISO8601Timezone === 'Z') {
return current;
}
var parts = ISO8601Timezone.match(/\+|-|[\d]{2}|[\d]{2}/g);
var multiplier = parts.shift() === '-' ? -1 : 1;
var hours = parseInt(parts[0], 10);
var minutes = parseInt(parts[1], 10);
// calculate zone offset plus our current zone offset, all in milliseconds
return multiplier * (hours * 3600000 + minutes * 60000) + current;
};
var offsetDate = function offsetDate(offset) {
return new Date(Date.now() + offset);
};
var timezoneDate = function timezoneDate(date, offset) {
return new Date(date.getTime() + offset);
};
// same date (day)
var sameDate = function sameDate(a, b) {
return a.toDateString() === b.toDateString();
};
// exact same date and time
var sameTime = function sameTime(a, b) {
return a.getTime() === b.getTime();
};
var daysInMonth = function daysInMonth(month, year) {
return new Date(year, month, 0).getDate();
};
var dateFromISO = function dateFromISO(iso) {
// use existing timezone
if (iso.match(/(Z)|([+\-][0-9]{2}:?[0-9]*$)/g)) {
return new Date(iso);
}
// add local timezone
iso += iso.indexOf('T') !== -1 ? 'Z' : '';
return dateToLocal(new Date(iso));
};
var dateToLocal = function dateToLocal(date) {
return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
};
var timeDuration = function timeDuration(milliseconds, components) {
return components.map(function (key) {
var requiredMilliseconds = TimeUnit[key];
var count = Math.max(0, Math.floor(milliseconds / requiredMilliseconds));
milliseconds = milliseconds % requiredMilliseconds;
return count;
});
};
// makes use of time duration for everything expect years and months
var dateDiff = function dateDiff(a, b, components) {
// do calculations
var diff = b - a;
var swapped = false;
if (diff < 0) {
diff = a - b;
var _ref = [b, a];
a = _ref[0];
b = _ref[1];
swapped = true;
}
// set default components
if (!components) {
components = ['d', 'h', 'm'];
}
// correct month uppercase M if set to lower case
var mIndex = components.indexOf('m');
if (mIndex >= 0 && (components[mIndex - 1] === 'y' || components[mIndex + 1] === 'd')) {
components[mIndex].key = 'M';
}
var anchor = void 0;
var monthsRemaining = void 0;
var months = void 0;
var presentsYears = components.includes('y');
var presentsMonths = components.includes('M');
if (presentsMonths || presentsYears) {
anchor = new Date(a.valueOf() + diff);
monthsRemaining = diffInMonths(anchor, a);
months = presentsMonths ? Math.floor(monthsRemaining) : Math.floor(monthsRemaining / 12) * 12;
diff = anchor.valueOf() - addMonths(clone$1(a), months).valueOf();
}
var output = components.map(function (key) {
// if is month or year
if (key === 'y' || key === 'M') {
var _count = Math.max(0, Math.floor(monthsRemaining / MonthFactor[key]));
monthsRemaining -= _count * MonthFactor[key];
return _count;
}
var requiredMilliseconds = TimeUnit[key];
var count = Math.max(0, Math.floor(diff / requiredMilliseconds));
diff = diff % requiredMilliseconds;
return count;
});
return swapped ? output.map(function (v) {
return v > 0 ? -v : v;
}) : output;
};
/**
* Tick.helper.duration(10, 'seconds') -> milliseconds
* Tick.helper.duration(a, b, format, cascade) -> [0, 10, 20, 4, 0];
* @param args
* @returns {*}
*/
var duration = function duration() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
// if is countdown x amount of milliseconds
if (typeof args[0] === 'number' && typeof args[1] === 'string') {
if (!TimeUnit[args[1]]) {
throw '"' + args[1] + '" is not a valid amount.';
}
return args[0] * TimeUnit[args[1]];
}
// is date diff
if (isDate(args[0])) {
return dateDiff.apply(undefined, args);
}
// is duration in milliseconds
if (typeof args[0] === 'number' && Array.isArray(args[1])) {
return timeDuration.apply(undefined, args);
}
return null;
};
/**
* Returns current date
*/
var now$1 = function now() {
return new Date();
};
/**
* Clones the given date object
* @param date
* @returns {Date}
*/
var clone$1 = function clone(date) {
return new Date(date.valueOf());
};
/**
* Adds x amount of months to date
* @param date
* @param months
* @returns {*}
*/
var addMonths = function addMonths(date, months) {
date.setMonth(date.getMonth() + months);
return date;
};
/**
* Difference in months between date `a` and date `b`
* @param a
* @param b
* @returns {number}
*/
var diffInMonths = function diffInMonths(a, b) {
var wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
var anchor = addMonths(clone$1(a), wholeMonthDiff);
var anchor2 = void 0;
var adjust = void 0;
if (b - anchor < 0) {
anchor2 = addMonths(clone$1(a), wholeMonthDiff - 1);
adjust = (b - anchor) / (anchor - anchor2);
} else {
anchor2 = addMonths(clone$1(a), wholeMonthDiff + 1);
adjust = (b - anchor) / (anchor2 - anchor);
}
return -(wholeMonthDiff + adjust);
};
/**
* Destroyer
* @param state
*/
var destroyer = (function (state) {
return {
destroy: function destroy() {
state.destroyed = true;
if (state.frame) {
cancelAnimationFrame(state.frame);
}
if (state.styleObserver) {
state.styleObserver.disconnect();
}
if (state.didResizeWindow) {
window.removeEventListener('resize', state.didResizeWindow);
}
if (state.root && state.root.parentNode) {
state.root.parentNode.removeChild(state.root);
}
}
};
});
/**
* Rooter
* @param state
* @param root
* @param name
*/
var rooter = (function (state) {
var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.createElement('span');
var name = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
state.root = root;
state.aligned = null;
state.destroyed = false;
if (root && name) {
state.root.classList.add('tick-' + name);
state.root.setAttribute('data-view', name);
}
if (root && root.dataset.layout) {
state.align = (root.dataset.layout.match(/left|right|center/) || [])[0] || 'left';
}
return {
appendTo: function appendTo(element) {
var location = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'last';
// if no root or already attached -> exit
if (!state.root || state.root && state.root.parentNode) {
return;
}
if (location === 'last') {
// place before last text node if found
if (element.childNodes.length && element.childNodes[element.childNodes.length - 1].nodeType === Node.TEXT_NODE) {
element.insertBefore(state.root, element.childNodes[element.childNodes.length - 1]);
} else {
// else just append
element.appendChild(state.root);
}
return;
}
if (location === 'first') {
// no elements and no text
if (element.childNodes.length === 0) {
element.appendChild(state.root);
}
// no elements but does contain text
else if (element.children.length === 0 && element.childNodes.length) {
element.insertBefore(state.root, element.childNodes[element.childNodes.length - 1]);
}
// elements!
else {
element.insertBefore(state.root, element.children[0]);
}
}
if (typeof location !== 'string') {
element.insertBefore(state.root, location);
}
}
};
});
/**
* Grouper
* @param state
* @param definition
*/
var grouper = (function (state, definition) {
state.definition = definition;
return {
setDefinition: function setDefinition(definition) {
state.definition = definition;
}
};
});
/**
* Drawer
* @param state
* @param draw
* @param present
* @param drawViews
*/
var drawer = (function (state, _draw, drawViews, present) {
return {
draw: function draw() {
// not dirty, might need to draw subviews
if (!state.dirty) {
if (drawViews) {
// draw sub views
var redrawn = drawViews(state);
if (redrawn) {
// let's fit it! (if necessary)
fit(state);
}
}
return false;
}
// draw everything
_draw(state, present);
// let's fit this view (if necessary)
fit(state);
// no longer dirty
state.dirty = false;
return true;
}
};
});
var fit = function fit(state) {
if (!state.fit) {
// nope
if (!state.root || !(state.root.getAttribute('data-layout') || '').match(/fit/)) {
state.fit = false;
return;
}
// create fit info object
var style = window.getComputedStyle(state.root, null);
state.fit = true;
state.fitInfo = {
currentFontSize: parseInt(style.getPropertyValue('font-size'), 10)
};
}
// get available width from parent node
state.fitInfo.availableWidth = state.root.parentNode.clientWidth;
// the space our target element uses
state.fitInfo.currentWidth = state.root.scrollWidth;
// let's calculate the new font size
var newFontSize = Math.min(Math.max(4, state.fitInfo.availableWidth / state.fitInfo.currentWidth * state.fitInfo.currentFontSize), 1024);
// size has not changed enough?
var dist = Math.abs(newFontSize - state.fitInfo.currentFontSize);
// prevents flickering on firefox / safari / ie by not redrawing tiny font size changes
if (dist <= 1) return;
state.fitInfo.currentFontSize = newFontSize;
state.root.style.fontSize = state.fitInfo.currentFontSize + 'px';
// redraw once more to quickly create better fit
if (state.fitInfo.currentWidth / state.fitInfo.availableWidth < 0.5) {
requestAnimationFrame(function () {
return fit(state);
});
}
};
var updater = (function (state) {
state.dirty = true;
state.value = null;
state.valueUpdateCount = 0;
state.isInitialValue = function () {
return state.valueUpdateCount <= 1;
};
return {
reset: function reset() {
state.dirty = true;
state.value = null;
state.valueUpdateCount = 0;
},
update: function update(value) {
// don't update on same value
if (equal(state.value, value)) {
return;
}
state.value = value;
state.valueUpdateCount++;
state.dirty = true;
}
};
});
/**
* Resizer
* @param state
*/
var resizer = (function (state) {
state.didResizeWindow = function () {
state.dirty = true;
};
window.addEventListener('resize', state.didResizeWindow);
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var toConsumableArray = function (arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
} else {
return Array.from(arr);
}
};
var draw = function draw(state, present) {
var views = (state.definition || []).concat();
if (state.align === 'right') {
views.reverse();
}
var value = Array.isArray(state.value) ? state.value.concat() : _typeof(state.value) === 'object' ? clone(state.value) : state.value;
views.forEach(function (view) {
if (!view.presenter) {
state.update = present(view);
if (!view.presenter) {
return;
}
view.presenter.appendTo(state.root);
}
});
views.filter(function (view) {
return view.presenter !== undefined;
}).forEach(function (view) {
if (Array.isArray(value) && state.valueMapping) {
// if set to indexes divide values over views, else (must be "none") just pass array
state.update(view, state.valueMapping === 'indexes' ? state.align === 'right' ? value.pop() : value.shift() : value);
} else if (view.key && value[view.key] !== undefined) {
// view expects a key so value should be object
state.update(view, value[view.key]);
} else {
// just pass on value to all sub views
state.update(view, value);
}
});
state.views = views;
// also draw subviews
drawViews(state);
};
var drawViews = function drawViews(state) {
var redrawn = false;
state.views.filter(function (view) {
return view.presenter !== undefined;
}).forEach(function (view) {
if (view.presenter.draw()) {
redrawn = true;
}
});
return redrawn;
};
var createRoot = (function (root, definition, present) {
var state = {
valueMapping: null // "none" or "indexes"
};
if (root && root.dataset.valueMapping) {
var allowed = ['none', 'indexes'];
var mapping = root.dataset.valueMapping;
state.valueMapping = allowed.indexOf(mapping) !== -1 ? mapping : null;
}
return Object.assign({}, rooter(state, root), resizer(state), updater(state), grouper(state, definition), drawer(state, draw, drawViews, present), destroyer(state));
});
var draw$1 = function draw(state, present, ready) {
// if value is not in form of array force to array
var value = copyArray(Array.isArray(state.value) ? state.value : (state.value + '').split(''));
// if we're aligned to the right we will append items differently so view updating is less jumpy
if (state.align === 'right') {
value.reverse();
}
// clean up presenters if too much presenters
if (state.definitions.length > value.length) {
while (state.definitions.length > value.length) {
var def = state.definitions.pop();
def.presenter.destroy();
}
}
// setup presenters
value.forEach(function (value, index) {
var def = state.definitions[index];
if (!def) {
def = state.definitions[index] = cloneDefinition(state.definition);
state.update = present(def);
def.presenter.appendTo(state.root, state.align === 'right' ? 'first' : 'last');
}
});
// let's update all subs (possibly sets dirty flag)
value.forEach(function (value, index) {
return state.update(state.definitions[index], value);
});
state.views = value;
// also draw subviews
drawViews$1(state);
};
var drawViews$1 = function drawViews(state) {
var redrawn = false;
state.views.forEach(function (view, index) {
if (state.definitions[index].presenter.draw()) {
redrawn = true;
}
});
return redrawn;
};
var createRepeater = (function (root, definition, present) {
var state = {
definitions: []
};
return Object.assign({}, rooter(state, root), updater(state), grouper(state, definition), drawer(state, draw$1, drawViews$1, present), destroyer(state));
});
var VENDOR_PREFIX = typeof document === 'undefined' ? null : function () {
var VENDORS = ['webkit', 'Moz', 'ms', 'O'];
var i = 0;
var l = VENDORS.length;
var transform = void 0;
var elementStyle = document.createElement('div').style;
for (; i < l; i++) {
transform = VENDORS[i] + 'Transform';
if (transform in elementStyle) {
return VENDORS[i];
}
}
return null;
}();
var text = function text(node, value) {
var textNode = node.childNodes[0];
if (!textNode) {
textNode = document.createTextNode(value);
node.appendChild(textNode);
} else if (value !== textNode.nodeValue) {
textNode.nodeValue = value;
}
};
var create$1 = function create(name, className) {
var el = document.createElement(name);
if (className) {
el.className = className;
}
return el;
};
var observeAttributes = function observeAttributes(element, attributes, cb) {
var observer = new MutationObserver(function (mutations) {
attributes.forEach(function (attr) {
if (mutations.filter(function (mutation) {
return attributes.includes(mutation.attributeName);
}).length) {
cb(element.getAttribute(attr));
}
});
});
observer.observe(element, { attributes: true });
return observer;
};
var isHTMLElement = function isHTMLElement(value) {
return value instanceof HTMLElement;
};
/**
* Element Transform Origin
* @param element
* @param value
*/
var setTransformOrigin = function setTransformOrigin(element, value) {
element.style.transformOrigin = value;
};
/**
* Element Transforms
* @param element
* @param name
* @param value
* @param unit
*/
var setTransform = function setTransform(element, name, value) {
var unit = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
if (!element.transforms) {
element.transforms = [];
}
var t = element.transforms.find(function (t) {
return t.name === name;
});
if (t) {
t.value = value;
} else {
element.transforms.push({ name: name, value: value, unit: unit });
}
setTransformStyle(element, element.transforms);
};
var setTransformStyle = function setTransformStyle(element, transforms) {
element.style.transform = transforms.map(function (t) {
return t.name + '(' + t.value + t.unit + ')';
}).join(' ');
};
var isVisible = function isVisible(element) {
var elementRect = element.getBoundingClientRect();
// is above top of the page
if (elementRect.bottom < 0) {
return false;
}
// is below bottom of page
if (elementRect.top > window.scrollY + window.innerHeight) {
return false;
}
return true;
};
/**
* @param value { * }
*/
var toBoolean$1 = function toBoolean(value) {
return typeof value === 'string' ? value === 'true' : value;
};
/**
* @param string { string }
*/
var capitalizeFirstLetter$1 = function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
/**
* @param string { string }
*/
var trim$1 = function trim(string) {
return string.trim();
};
var CACHE = {};
var cache = (function (value, fn) {
var fns = fn.toString();
if (!CACHE[fns]) {
CACHE[fns] = {};
}
if (!CACHE[fns][value]) {
CACHE[fns][value] = fn(value);
}
return CACHE[fns][value];
});
var isInt = new RegExp("^[0-9]+$");
var isBoolean$1 = new RegExp("^(true|false)$");
var isFloat = new RegExp("^[0-9.]+$");
var isColor = new RegExp("color");
var isShadow = new RegExp("shadow");
var isGradient = new RegExp("^(follow-gradient|horizontal-gradient|vertical-gradient)");
var isDuration = new RegExp("^[.0-9]+(?:ms|s){1}$");
var isTransition = new RegExp("^transition-?(?:in|out)?$");
var isURL = new RegExp("^url\\(");
var toDuration = function toDuration(string) {
return string ? parseFloat(string) * (/ms$/.test(string) ? 1 : 1000) : 0;
};
var toTransition = function toTransition(string) {
return string.match(/[a-z]+(?:\(.*?\))?\s?(?:origin\(.*?\))?\s?(?:[a-z]+\(.*?\))?[ .a-z-0-9]*/g).map(toTransitionPartial);
};
var toTransitionPartial = function toTransitionPartial(string) {
var parts = string.match(/([a-z]+(?:\(.*?\))?)\s?(?:origin\((.*?)\))?\s?([a-z]+(?:\(.*?\))?)?\s?(?:([.0-9ms]+)?\s?(?:(ease-[a-z-]+))?\s?([.0-9ms]+)?)?/);
// get transition function definition
var fn = toFunctionOutline(parts[1]);
// get duration and easing
var origin = undefined;
var duration = undefined;
var ease = undefined;
var delay = undefined;
var resolver = undefined;
// skip function and figure out what other parts are
parts.slice(2).filter(function (part) {
return typeof part !== "undefined";
}).forEach(function (part) {
// is either duration or delay
if (isDuration.test(part)) {
if (typeof duration === "undefined") {
duration = toDuration(part);
} else {
delay = toDuration(part);
}
}
// is origin if contains a space
else if (/ /.test(part)) {
origin = part;
}
// should be ease
else if (/^ease-[a-z-]+$/.test(part)) {
ease = part;
}
// should be transform
else if (/^[a-z]+/.test(part)) {
resolver = toFunctionOutline(part);
}
});
// reset easing and duration when transform is defined, these settings don't work together
if (resolver) {
duration = undefined;
ease = undefined;
}
// return transition object
return {
name: fn.name,
parameters: fn.parameters,
duration: duration,
ease: ease,
delay: delay,
origin: origin,
resolver: resolver
};
};
/**
* toGradient
* @param string { string } - string should be in format <type>(color, color)
* @returns { {type: *, colors: *} }
*/
var toGradient = function toGradient(string) {
var type = string.match(/follow-gradient|horizontal-gradient|vertical-gradient/)[0];
var colors = string.substring(type.length).match(/(?:transparent|rgb\(.*?\)|hsl\(.*?\)|hsla\(.*?\)|rgba\(.*?\)|[a-z]+|#[abcdefABCDEF\d]+)\s?(?:[\d]{1,3}%?)?/g).map(toGradientColor);
return {
type: type,
colors: colors
};
};
var gradientOffsetRegex = /\s([\d]{1,3})%?$/;
var toGradientColor = function toGradientColor(string) {
var offset = string.match(gradientOffsetRegex);
return {
offset: offset ? parseFloat(offset[1]) / 100 : null,
value: toColor(string.replace(gradientOffsetRegex, ""))
};
};
/**
* Returns the pixels amount for the given value
*/
var pipetteCache = [];
var getPipette = function getPipette(id, root) {
if (!pipetteCache[id]) {
return null;
}
return pipetteCache[id].find(function (p) {
return p.node.parentNode === root;
});
};
var setPipette = function setPipette(id, pipette) {
if (!pipetteCache[id]) {
pipetteCache[id] = [];
}
pipetteCache[id].push(pipette);
};
var toPixels = typeof document === "undefined" ? function (value) {
return 0;
} : function (value) {
var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.body;
var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
if (value == 0) {
return 0;
}
if (id) {
var _pipette = getPipette(id, root) || {};
if (!_pipette.node) {
_pipette.node = document.createElement("span");
_pipette.node.style.cssText = "position:absolute;padding:0;visibility:hidden;";
root.appendChild(_pipette.node);
}
// update value
_pipette.node.style.marginTop = value;
// compute style for first time
if (!_pipette.style) {
_pipette.style = window.getComputedStyle(_pipette.node);
}
setPipette(id, _pipette);
return parseInt(_pipette.style.marginTop, 10);
}
// old method
var pipette = document.createElement("span");
pipette.style.cssText = "pos