@pqina/flip
Version:
A Beautifully Animated Flip Clock
1,995 lines (1,581 loc) • 130 kB
JavaScript
/* 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 = "position:absolute;padding:0;visibility:hidden;margin-top:" + value;
root.appendChild(pipette);
requestAnimationFrame(function () {
pipette.parentNode.removeChild(pipette);
});
return parseInt(window.getComputedStyle(pipette).marginTop, 10);
};
/**
* @param string { string } - any valid CSS color value
* @returns { string }
*/
var toColor = typeof document === "undefined" ? function (string) {
return string;
} : function (string) {
if (string === "transparent") {
return "rgba(0,0,0,0)";
}
var pipette = document.createElement("span");
pipette.style.cssText = "position:absolute;visibility:hidden;color:" + string;
document.body.appendChild(pipette);
requestAnimationFrame(function () {
pipette.parentNode.removeChild(pipette);
});
return window.getComputedStyle(pipette).getPropertyValue("color");
};
var toShadow = function toShadow(style) {
if (typeof style !== "string") {
return style;
}
return style.match(/([-.\d]+(?:%|ms|s|deg|cm|em|ch|ex|q|in|mm|pc|pt|px|vh|vw|vmin|vmax)?)|[%#A-Za-z0-9,.()]+/g);
};
var toURL = function toURL(style) {
var urls = style.match(/url\((.*?)\)/g).map(function (url) {
return url.substring(4, url.length - 1);
});
return urls.length === 1 ? urls[0] : urls;
};
var toStyleProperty = function toStyleProperty(key) {
return key.trim().split("-").map(function (key, index) {
return index > 0 ? capitalizeFirstLetter$1(key) : key;
}).join("");
};
var toStyleValue = function toStyleValue(value, property) {
if (isBoolean$1.test(value)) {
return toBoolean$1(value);
}
if (isInt.test(value)) {
return parseInt(value, 10);
}
if (isFloat.test(value)) {
return parseFloat(value);
}
if (isURL.test(value)) {
return toURL(value);
}
if (isColor.test(property)) {
if (isGradient.test(value)) {
return cache(value, toGradient);
}
return cache(value, toColor);
}
if (isShadow.test(property)) {
return cache(value, toShadow);
}
if (isTransition.test(property)) {
if (value === "none") {
return value;
}
return cache(value, toTransition);
}
return value;
};
var toStyle = function toStyle(string) {
var parts = string.split(":").map(trim$1);
var property = toStyleProperty(parts[0]);
var value = toStyleValue(parts[1], parts[0]);
if (!property || value === null || typeof value === "undefined") {
return null;
}
return {
property: property,
value: value
};
};
var toStyles = function toStyles(string) {
return string.split(";")
// remove empty values
.filter(function (style) {
return style.trim().length;
})
// turn into objects
.map(toStyle)
// remove invalid styles
.filter(function (style) {
return style !== null;
})
// create styles object
.reduce(function (styles, style) {
styles[style.property] = style.value;
return styles;
}, {});
};
// https://gist.github.com/gre/1650294
// http://easings.net/
// https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
// http://gizma.com/easing/
var easeLinear = function easeLinear(t) {
return t;
};
var easeInSine = function easeInSine(t) {
return -1 * Math.cos(t * (Math.PI / 2)) + 1;
};
var easeOutSine = function easeOutSine(t) {
return Math.sin(t * (Math.PI / 2));
};
var easeInOutSine = function easeInOutSine(t) {
return -0.5 * (Math.cos(Math.PI * t) - 1);
};
var easeInQuad = function easeInQuad(t) {
return t * t;
};
var easeOutQuad = function easeOutQuad(t) {
return t * (2 - t);
};
var easeInOutQuad = function easeInOutQuad(t) {
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
};
var easeInCubic = function easeInCubic(t) {
return t * t * t;
};
var easeOutCubic = function easeOutCubic(t) {
var t1 = t - 1;
return t1 * t1 * t1 + 1;
};
var easeInOutCubic = function easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
};
var easeInQuart = function easeInQuart(t) {
return t * t * t * t;
};
var easeOutQuart = function easeOutQuart(t) {
return 1 - --t * t * t * t;
};
var easeInOutQuart = function easeInOutQuart(t) {
return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
};
var easeInExpo = function easeInExpo(t) {
if (t === 0) {
return 0;
}
return Math.pow(2, 10 * (t - 1));
};
var easeOutExpo = function easeOutExpo(t) {
if (t === 1) {
return 1;
}
return -Math.pow(2, -10 * t) + 1;
};
var easeInOutExpo = function easeInOutExpo(t) {
if (t === 0 || t === 1) {
return t;
}
var scaledTime = t * 2;
var scaledTime1 = scaledTime - 1;
if (scaledTime < 1) {
return 0.5 * Math.pow(2, 10 * scaledTime1);
}
return 0.5 * (-Math.pow(2, -10 * scaledTime1) + 2);
};
var easeInCirc = function easeInCirc(t) {
var scaledTime = t / 1;
return -1 * (Math.sqrt(1 - scaledTime * t) - 1);
};
var easeOutCirc = function easeOutCirc(t) {
var t1 = t - 1;
return Math.sqrt(1 - t1 * t1);
};
var easeInOutCirc = function easeInOutCirc(t) {
var scaledTime = t * 2;
var scaledTime1 = scaledTime - 2;
if (scaledTime < 1) {
return -0.5 * (Math.sqrt(1 - scaledTime * scaledTime) - 1);
}
return 0.5 * (Math.sqrt(1 - scaledTime1 * scaledTime1) + 1);
};
var easeInBack = function easeInBack(t) {
var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;
var scaledTime = t / 1;
return scaledTime * scaledTime * ((magnitude + 1) * scaledTime - magnitude);
};
var easeOutBack = function easeOutBack(t) {
var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;
var scaledTime = t / 1 - 1;
return scaledTime * scaledTime * ((magnitude + 1) * scaledTime + magnitude) + 1;
};
var easeInOutBack = function easeInOutBack(t) {
var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;
var scaledTime = t * 2;
var scaledTime2 = scaledTime - 2;
var s = magnitude * 1.525;
if (scaledTime < 1) {
return 0.5 * scaledTime * scaledTime * ((s + 1) * scaledTime - s);
}
return 0.5 * (scaledTime2 * scaledTime2 * ((s + 1) * scaledTime2 + s) + 2);
};
var easeOutElastic = function easeOutElastic(t) {
var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.7;
var p = 1 - magnitude;
var scaledTime = t * 2;
if (t === 0 || t === 1) {
return t;
}
var s = p / (2 * Math.PI) * Math.asin(1);
return Math.pow(2, -10 * scaledTime) * Math.sin((scaledTime - s) * (2 * Math.PI) / p) + 1;
};
var easeOutBounce = function easeOutBounce(t) {
var scaledTime = t / 1;
if (scaledTime < 1 / 2.75) {
return 7.5625 * scaledTime * scaledTime;
} else if (scaledTime < 2 / 2.75) {
var scaledTime2 = scaledTime - 1.5 / 2.75;
return 7.5625 * scaledTime2 * scaledTime2 + 0.75;
} else if (scaledTime < 2.5 / 2.75) {
var _scaledTime = scaledTime - 2.25 / 2.75;
return 7.5625 * _scaledTime * _scaledTime + 0.9375;
} else {
var _scaledTime2 = scaledTime - 2.625 / 2.75;
return 7.5625 * _scaledTime2 * _scaledTime2 + 0.984375;
}
};
var EasingFunctions = {
'ease-linear': easeLinear,
'ease-in-sine': easeInSine,
'ease-out-sine': easeOutSine,
'ease-in-out-sine': easeInOutSine,
'ease-in-cubic': easeInCubic,
'ease-out-cubic': easeOutCubic,
'ease-in-out-cubic': easeInOutCubic,
'ease-in-circ': easeInCirc,
'ease-out-circ': easeOutCirc,
'ease-in-out-circ': easeInOutCirc,
'ease-in-quad': easeInQuad,
'ease-out-quad': easeOutQuad,
'ease-in-out-quad': easeInOutQuad,
'ease-in-quart': easeInQuart,
'ease-out-quart': easeOutQuart,
'ease-in-out-quart': easeInOutQuart,
'ease-in-expo': easeInExpo,
'ease-out-expo': easeOutExpo,
'ease-in-out-expo': easeInOutExpo,
'ease-in-back': easeInBack,
'ease-out-back': easeOutBack,
'ease-in-out-back': easeInOutBack,
'ease-out-elastic': easeOutElastic,
'ease-out-bounce': easeOutBounce
};
addExtensions(ExtensionType.EASING_FUNCTION, EasingFunctions);
/**
* animate a certain amount of time (between 0 and 1)
* @param cb - update function
* @param complete
* @param duration - duration in milliseconds
* @param ease - easing function
* @param delay
*/
var animate = function animate(cb, complete) {
var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeLinear;
var delay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
return interpolate(function (t) {
cb(ease(t));
}, complete, duration, delay);
};
/**
* interpolate between 0 and 1 over x amount of frames
* @param update - update function
* @param complete
* @param duration - duration in milliseconds
* @param delay - milliseconds to wait before starting
*/
var interpolate = function interpolate(update) {
var complete = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
var delay = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
// no update function supplied -> exit
if (!update) {
return null;
}
// set start time
var start = null;
var t = void 0;
var frame = null;
// animation loop
var tick = function tick(ts) {
if (start === null) {
start = ts;
}
t = ts - start - delay;
if (t < duration) {
update(t >= 0 ? t / duration : 0);
frame = requestAnimationFrame(tick);
return null;
}
update(1);
if (complete) {
complete();
}
};
tick(now());
// return cancel function
return function () {
cancelAnimationFrame(frame);
};
};
/**
* Translates movements values
*/
var translator = function translator() {
var fps = 24;
var interval = 1000 / fps;
var frame = null;
var state = {
velocity: 0,
origin: 0,
position: 0,
destination: 1
};
var cancel = function cancel() {
cancelAnimationFrame(frame);
};
var translate = function translate(cb, from, to, update) {
// cancel previous animations if are running
cancel();
// 'to' not supplied, so 'from' is destination
if (to === null) {
state.destination = from;
} else {
// both supplied, also reset velocity
state.position = from;
state.destination = to;
state.velocity = 0;
}
// always set origin to current position
state.origin = state.position;
var last = null;
var tick = function tick(ts) {
// queue next tick
frame = requestAnimationFrame(tick);
// limit fps
if (!last) {
last = ts;
}
var delta = ts - last;
if (delta <= interval) {
// skip frame
return;
}
// align next frame
last = ts - delta % interval;
update(state, cancel);
cb(state.position);
};
tick(now());
};
return {
getPosition: function getPosition() {
return state.position;
},
cancel: cancel,
translate: translate
};
};
/**
* Translator builder
* @param type
* @param options
* @returns {*}
*/
var createTranslator = function createTranslator(type) {
for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
options[_key - 1] = arguments[_key];
}
var t = translator();
var updater = {
update: null,
cancel: t.cancel,
getPosition: t.getPosition
};
if (type === 'arrive') {
updater.update = arrive.apply(undefined, [t.translate].concat(options));
} else if (type === 'spring') {
updater.update = spring.apply(undefined, [t.translate].concat(options));
} else if (type === 'step') {
updater.update = step.apply(undefined, [t.translate].concat(options));
}
return updater;
};
/**
* Arrive at destination
* @param update
* @param maxVelocity
* @param friction
*/
var arrive = function arrive(update) {
var maxVelocity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var friction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.01;
return function (cb) {
var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
update(cb, from, to, function (state, cancel) {
// distance to target
var distance = state.destination - state.position;
var halfway = state.origin + (state.destination - state.origin) * 0.5;
// update velocity based on distance
state.velocity += (-(halfway - state.origin) + distance) * 2 * friction;
// update position by adding velocity
state.position += state.velocity < 0 ? Math.max(state.velocity, -maxVelocity) : Math.min(state.velocity, maxVelocity);
// we've arrived if we're near target and our velocity is near zero
if (state.origin < state.destination && state.position >= state.destination || state.origin >= state.destination && state.position <= state.destination) {
cancel();
state.velocity = 0;
state.position = state.destination;
}
});
};
};
var step = function step(update) {
var velocity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.01;
return function (cb) {
var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
update(cb, from, to, function (state, cancel) {
// update velocity based on distance
state.velocity = velocity;
// update position by adding velocity
state.position += state.velocity;
// we've arrived if we're near target and our velocity is near zero
if (state.origin < state.destination && state.position >= state.destination || state.origin >= state.destination && state.position <= state.destination) {
cancel();
state.velocity = 0;
state.position = state.destination;
}
});
};
};
/**
* Animate movement based no spring physics
* @param update
* @param stiffness - the higher the more intense the vibration
* @param damping - a factor that slows down the calculated velocity by a percentage, needs to be less than 1
* @param mass - the higher the slower the spring springs in action
* @returns {function(*=, *=)}
*/
var spring = function spring(update) {
var stiffness = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.5;
var damping = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.75;
var mass = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 10;
return function (cb) {
var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
update(cb, from, to, function (state, cancel) {
// calculate spring force
var f = -(state.position - state.destination) * stiffness;
// update velocity by adding force based on mass
state.velocity += f / mass;
// update position by adding velocity
state.position += state.velocity;
// slow down based on amount of damping
state.velocity *= damping;
// we've arrived if we're near target and our velocity is near zero
if (thereYet(state.position, state.destination, state.velocity)) {
cancel();
state.position = state.destination;
state.velocity = 0;
}
});
};
};
var thereYet = function thereYet(position, destination, velocity) {
var errorMargin = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.001;
return Math.abs(position - destination) < errorMargin && Math.abs(velocity) < errorMargin;
};
/**
* Returns a function that applies the transitions to the given element
* @param transitions
* @returns {function(*)}
*/
var createTransitioner = function createTransitioner(transitions) {
var transitioners = transitions.map(function (t) {
return createDurationTransitioner(createTransition(t.name, t.parameters, t.ease), t.origin, t.duration, t.delay);
});
return function (element, direction, complete) {
// don't run animations when no element is supplied
if (!isHTMLElement(element)) {
return false;
}
var count = transitioners.length;
transitioners.forEach(function (transitioner) {
transitioner(element, direction, function () {
count--;
if (!count && complete) {
complete(element);
}
});
});
};
};
var createTransition = function createTransition(name, parameters, ease) {
var easing = ease ? getExtension(ExtensionType.EASING_FUNCTION, ease) : ease;
var transition = getExtension(ExtensionType.TRANSITION, name);
return function (element, direction, p) {
transition.apply(undefined, [element, p, direction, easing].concat(toConsumableArray(parameters)));
};
};
var createDurationTransitioner = function createDurationTransitioner(transition) {
var origin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '50% 50% 0';
var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
var delay = arguments[3];
return function (element) {
var direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var complete = arguments[2];
// set transform origin
setTransformOrigin(element, origin);
// run animation
interpolate(function (p) {
transition(element, direction, p);
}, complete, duration, delay);
};
};
var getComposedTransitionActs = function getComposedTransitionActs(transition) {
return getExtension(ExtensionType.TRANSITION, transition.name).apply(undefined, toConsumableArray(transition.parameters || []));
};
/**
* Styler
* @param state
* @param base
*/
var styler = (function (state) {
var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// styles that were last applied to the element
state.lastAppliedStyles = n