UNPKG

@pqina/flip

Version:

A Beautifully Animated Flip Clock

1,995 lines (1,581 loc) 130 kB
/* 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