filepond
Version:
FilePond, Where files go to stretch their bits.
1,481 lines (1,250 loc) • 439 kB
JavaScript
/*!
* FilePond 4.32.7
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
/* eslint-disable */
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
? factory(exports)
: typeof define === 'function' && define.amd
? define(['exports'], factory)
: ((global = global || self), factory((global.FilePond = {})));
})(this, function(exports) {
'use strict';
var isNode = function isNode(value) {
return value instanceof HTMLElement;
};
var createStore = function createStore(initialState) {
var queries = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var actions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
// internal state
var state = Object.assign({}, initialState);
// contains all actions for next frame, is clear when actions are requested
var actionQueue = [];
var dispatchQueue = [];
// returns a duplicate of the current state
var getState = function getState() {
return Object.assign({}, state);
};
// returns a duplicate of the actions array and clears the actions array
var processActionQueue = function processActionQueue() {
// create copy of actions queue
var queue = [].concat(actionQueue);
// clear actions queue (we don't want no double actions)
actionQueue.length = 0;
return queue;
};
// processes actions that might block the main UI thread
var processDispatchQueue = function processDispatchQueue() {
// create copy of actions queue
var queue = [].concat(dispatchQueue);
// clear actions queue (we don't want no double actions)
dispatchQueue.length = 0;
// now dispatch these actions
queue.forEach(function(_ref) {
var type = _ref.type,
data = _ref.data;
dispatch(type, data);
});
};
// adds a new action, calls its handler and
var dispatch = function dispatch(type, data, isBlocking) {
// is blocking action (should never block if document is hidden)
if (isBlocking && !document.hidden) {
dispatchQueue.push({ type: type, data: data });
return;
}
// if this action has a handler, handle the action
if (actionHandlers[type]) {
actionHandlers[type](data);
}
// now add action
actionQueue.push({
type: type,
data: data,
});
};
var query = function query(str) {
var _queryHandles;
for (
var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
return queryHandles[str]
? (_queryHandles = queryHandles)[str].apply(_queryHandles, args)
: null;
};
var api = {
getState: getState,
processActionQueue: processActionQueue,
processDispatchQueue: processDispatchQueue,
dispatch: dispatch,
query: query,
};
var queryHandles = {};
queries.forEach(function(query) {
queryHandles = Object.assign({}, query(state), {}, queryHandles);
});
var actionHandlers = {};
actions.forEach(function(action) {
actionHandlers = Object.assign({}, action(dispatch, query, state), {}, actionHandlers);
});
return api;
};
var defineProperty = function defineProperty(obj, property, definition) {
if (typeof definition === 'function') {
obj[property] = definition;
return;
}
Object.defineProperty(obj, property, Object.assign({}, definition));
};
var forin = function forin(obj, cb) {
for (var key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
cb(key, obj[key]);
}
};
var createObject = function createObject(definition) {
var obj = {};
forin(definition, function(property) {
defineProperty(obj, property, definition[property]);
});
return obj;
};
var attr = function attr(node, name) {
var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
if (value === null) {
return node.getAttribute(name) || node.hasAttribute(name);
}
node.setAttribute(name, value);
};
var ns = 'http://www.w3.org/2000/svg';
var svgElements = ['svg', 'path']; // only svg elements used
var isSVGElement = function isSVGElement(tag) {
return svgElements.includes(tag);
};
var createElement = function createElement(tag, className) {
var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (typeof className === 'object') {
attributes = className;
className = null;
}
var element = isSVGElement(tag)
? document.createElementNS(ns, tag)
: document.createElement(tag);
if (className) {
if (isSVGElement(tag)) {
attr(element, 'class', className);
} else {
element.className = className;
}
}
forin(attributes, function(name, value) {
attr(element, name, value);
});
return element;
};
var appendChild = function appendChild(parent) {
return function(child, index) {
if (typeof index !== 'undefined' && parent.children[index]) {
parent.insertBefore(child, parent.children[index]);
} else {
parent.appendChild(child);
}
};
};
var appendChildView = function appendChildView(parent, childViews) {
return function(view, index) {
if (typeof index !== 'undefined') {
childViews.splice(index, 0, view);
} else {
childViews.push(view);
}
return view;
};
};
var removeChildView = function removeChildView(parent, childViews) {
return function(view) {
// remove from child views
childViews.splice(childViews.indexOf(view), 1);
// remove the element
if (view.element.parentNode) {
parent.removeChild(view.element);
}
return view;
};
};
var IS_BROWSER = (function() {
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
})();
var isBrowser = function isBrowser() {
return IS_BROWSER;
};
var testElement = isBrowser() ? createElement('svg') : {};
var getChildCount =
'children' in testElement
? function(el) {
return el.children.length;
}
: function(el) {
return el.childNodes.length;
};
var getViewRect = function getViewRect(elementRect, childViews, offset, scale) {
var left = offset[0] || elementRect.left;
var top = offset[1] || elementRect.top;
var right = left + elementRect.width;
var bottom = top + elementRect.height * (scale[1] || 1);
var rect = {
// the rectangle of the element itself
element: Object.assign({}, elementRect),
// the rectangle of the element expanded to contain its children, does not include any margins
inner: {
left: elementRect.left,
top: elementRect.top,
right: elementRect.right,
bottom: elementRect.bottom,
},
// the rectangle of the element expanded to contain its children including own margin and child margins
// margins will be added after we've recalculated the size
outer: {
left: left,
top: top,
right: right,
bottom: bottom,
},
};
// expand rect to fit all child rectangles
childViews
.filter(function(childView) {
return !childView.isRectIgnored();
})
.map(function(childView) {
return childView.rect;
})
.forEach(function(childViewRect) {
expandRect(rect.inner, Object.assign({}, childViewRect.inner));
expandRect(rect.outer, Object.assign({}, childViewRect.outer));
});
// calculate inner width and height
calculateRectSize(rect.inner);
// append additional margin (top and left margins are included in top and left automatically)
rect.outer.bottom += rect.element.marginBottom;
rect.outer.right += rect.element.marginRight;
// calculate outer width and height
calculateRectSize(rect.outer);
return rect;
};
var expandRect = function expandRect(parent, child) {
// adjust for parent offset
child.top += parent.top;
child.right += parent.left;
child.bottom += parent.top;
child.left += parent.left;
if (child.bottom > parent.bottom) {
parent.bottom = child.bottom;
}
if (child.right > parent.right) {
parent.right = child.right;
}
};
var calculateRectSize = function calculateRectSize(rect) {
rect.width = rect.right - rect.left;
rect.height = rect.bottom - rect.top;
};
var isNumber = function isNumber(value) {
return typeof value === 'number';
};
/**
* Determines if position is at destination
* @param position
* @param destination
* @param velocity
* @param errorMargin
* @returns {boolean}
*/
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;
};
/**
* Spring animation
*/
var spring =
// default options
function spring() // method definition
{
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$stiffness = _ref.stiffness,
stiffness = _ref$stiffness === void 0 ? 0.5 : _ref$stiffness,
_ref$damping = _ref.damping,
damping = _ref$damping === void 0 ? 0.75 : _ref$damping,
_ref$mass = _ref.mass,
mass = _ref$mass === void 0 ? 10 : _ref$mass;
var target = null;
var position = null;
var velocity = 0;
var resting = false;
// updates spring state
var interpolate = function interpolate(ts, skipToEndState) {
// in rest, don't animate
if (resting) return;
// need at least a target or position to do springy things
if (!(isNumber(target) && isNumber(position))) {
resting = true;
velocity = 0;
return;
}
// calculate spring force
var f = -(position - target) * stiffness;
// update velocity by adding force based on mass
velocity += f / mass;
// update position by adding velocity
position += velocity;
// slow down based on amount of damping
velocity *= damping;
// we've arrived if we're near target and our velocity is near zero
if (thereYet(position, target, velocity) || skipToEndState) {
position = target;
velocity = 0;
resting = true;
// we done
api.onupdate(position);
api.oncomplete(position);
} else {
// progress update
api.onupdate(position);
}
};
/**
* Set new target value
* @param value
*/
var setTarget = function setTarget(value) {
// if currently has no position, set target and position to this value
if (isNumber(value) && !isNumber(position)) {
position = value;
}
// next target value will not be animated to
if (target === null) {
target = value;
position = value;
}
// let start moving to target
target = value;
// already at target
if (position === target || typeof target === 'undefined') {
// now resting as target is current position, stop moving
resting = true;
velocity = 0;
// done!
api.onupdate(position);
api.oncomplete(position);
return;
}
resting = false;
};
// need 'api' to call onupdate callback
var api = createObject({
interpolate: interpolate,
target: {
set: setTarget,
get: function get() {
return target;
},
},
resting: {
get: function get() {
return resting;
},
},
onupdate: function onupdate(value) {},
oncomplete: function oncomplete(value) {},
});
return api;
};
var easeLinear = function easeLinear(t) {
return t;
};
var easeInOutQuad = function easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
};
var tween =
// default values
function tween() // method definition
{
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$duration = _ref.duration,
duration = _ref$duration === void 0 ? 500 : _ref$duration,
_ref$easing = _ref.easing,
easing = _ref$easing === void 0 ? easeInOutQuad : _ref$easing,
_ref$delay = _ref.delay,
delay = _ref$delay === void 0 ? 0 : _ref$delay;
var start = null;
var t;
var p;
var resting = true;
var reverse = false;
var target = null;
var interpolate = function interpolate(ts, skipToEndState) {
if (resting || target === null) return;
if (start === null) {
start = ts;
}
if (ts - start < delay) return;
t = ts - start - delay;
if (t >= duration || skipToEndState) {
t = 1;
p = reverse ? 0 : 1;
api.onupdate(p * target);
api.oncomplete(p * target);
resting = true;
} else {
p = t / duration;
api.onupdate((t >= 0 ? easing(reverse ? 1 - p : p) : 0) * target);
}
};
// need 'api' to call onupdate callback
var api = createObject({
interpolate: interpolate,
target: {
get: function get() {
return reverse ? 0 : target;
},
set: function set(value) {
// is initial value
if (target === null) {
target = value;
api.onupdate(value);
api.oncomplete(value);
return;
}
// want to tween to a smaller value and have a current value
if (value < target) {
target = 1;
reverse = true;
} else {
// not tweening to a smaller value
reverse = false;
target = value;
}
// let's go!
resting = false;
start = null;
},
},
resting: {
get: function get() {
return resting;
},
},
onupdate: function onupdate(value) {},
oncomplete: function oncomplete(value) {},
});
return api;
};
var animator = {
spring: spring,
tween: tween,
};
/*
{ type: 'spring', stiffness: .5, damping: .75, mass: 10 };
{ translation: { type: 'spring', ... }, ... }
{ translation: { x: { type: 'spring', ... } } }
*/
var createAnimator = function createAnimator(definition, category, property) {
// default is single definition
// we check if transform is set, if so, we check if property is set
var def =
definition[category] && typeof definition[category][property] === 'object'
? definition[category][property]
: definition[category] || definition;
var type = typeof def === 'string' ? def : def.type;
var props = typeof def === 'object' ? Object.assign({}, def) : {};
return animator[type] ? animator[type](props) : null;
};
var addGetSet = function addGetSet(keys, obj, props) {
var overwrite = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
obj = Array.isArray(obj) ? obj : [obj];
obj.forEach(function(o) {
keys.forEach(function(key) {
var name = key;
var getter = function getter() {
return props[key];
};
var setter = function setter(value) {
return (props[key] = value);
};
if (typeof key === 'object') {
name = key.key;
getter = key.getter || getter;
setter = key.setter || setter;
}
if (o[name] && !overwrite) {
return;
}
o[name] = {
get: getter,
set: setter,
};
});
});
};
// add to state,
// add getters and setters to internal and external api (if not set)
// setup animators
var animations = function animations(_ref) {
var mixinConfig = _ref.mixinConfig,
viewProps = _ref.viewProps,
viewInternalAPI = _ref.viewInternalAPI,
viewExternalAPI = _ref.viewExternalAPI;
// initial properties
var initialProps = Object.assign({}, viewProps);
// list of all active animations
var animations = [];
// setup animators
forin(mixinConfig, function(property, animation) {
var animator = createAnimator(animation);
if (!animator) {
return;
}
// when the animator updates, update the view state value
animator.onupdate = function(value) {
viewProps[property] = value;
};
// set animator target
animator.target = initialProps[property];
// when value is set, set the animator target value
var prop = {
key: property,
setter: function setter(value) {
// if already at target, we done!
if (animator.target === value) {
return;
}
animator.target = value;
},
getter: function getter() {
return viewProps[property];
},
};
// add getters and setters
addGetSet([prop], [viewInternalAPI, viewExternalAPI], viewProps, true);
// add it to the list for easy updating from the _write method
animations.push(animator);
});
// expose internal write api
return {
write: function write(ts) {
var skipToEndState = document.hidden;
var resting = true;
animations.forEach(function(animation) {
if (!animation.resting) resting = false;
animation.interpolate(ts, skipToEndState);
});
return resting;
},
destroy: function destroy() {},
};
};
var addEvent = function addEvent(element) {
return function(type, fn) {
element.addEventListener(type, fn);
};
};
var removeEvent = function removeEvent(element) {
return function(type, fn) {
element.removeEventListener(type, fn);
};
};
// mixin
var listeners = function listeners(_ref) {
var mixinConfig = _ref.mixinConfig,
viewProps = _ref.viewProps,
viewInternalAPI = _ref.viewInternalAPI,
viewExternalAPI = _ref.viewExternalAPI,
viewState = _ref.viewState,
view = _ref.view;
var events = [];
var add = addEvent(view.element);
var remove = removeEvent(view.element);
viewExternalAPI.on = function(type, fn) {
events.push({
type: type,
fn: fn,
});
add(type, fn);
};
viewExternalAPI.off = function(type, fn) {
events.splice(
events.findIndex(function(event) {
return event.type === type && event.fn === fn;
}),
1
);
remove(type, fn);
};
return {
write: function write() {
// not busy
return true;
},
destroy: function destroy() {
events.forEach(function(event) {
remove(event.type, event.fn);
});
},
};
};
// add to external api and link to props
var apis = function apis(_ref) {
var mixinConfig = _ref.mixinConfig,
viewProps = _ref.viewProps,
viewExternalAPI = _ref.viewExternalAPI;
addGetSet(mixinConfig, viewExternalAPI, viewProps);
};
var isDefined = function isDefined(value) {
return value != null;
};
// add to state,
// add getters and setters to internal and external api (if not set)
// set initial state based on props in viewProps
// apply as transforms each frame
var defaults = {
opacity: 1,
scaleX: 1,
scaleY: 1,
translateX: 0,
translateY: 0,
rotateX: 0,
rotateY: 0,
rotateZ: 0,
originX: 0,
originY: 0,
};
var styles = function styles(_ref) {
var mixinConfig = _ref.mixinConfig,
viewProps = _ref.viewProps,
viewInternalAPI = _ref.viewInternalAPI,
viewExternalAPI = _ref.viewExternalAPI,
view = _ref.view;
// initial props
var initialProps = Object.assign({}, viewProps);
// current props
var currentProps = {};
// we will add those properties to the external API and link them to the viewState
addGetSet(mixinConfig, [viewInternalAPI, viewExternalAPI], viewProps);
// override rect on internal and external rect getter so it takes in account transforms
var getOffset = function getOffset() {
return [viewProps['translateX'] || 0, viewProps['translateY'] || 0];
};
var getScale = function getScale() {
return [viewProps['scaleX'] || 0, viewProps['scaleY'] || 0];
};
var getRect = function getRect() {
return view.rect
? getViewRect(view.rect, view.childViews, getOffset(), getScale())
: null;
};
viewInternalAPI.rect = { get: getRect };
viewExternalAPI.rect = { get: getRect };
// apply view props
mixinConfig.forEach(function(key) {
viewProps[key] =
typeof initialProps[key] === 'undefined' ? defaults[key] : initialProps[key];
});
// expose api
return {
write: function write() {
// see if props have changed
if (!propsHaveChanged(currentProps, viewProps)) {
return;
}
// moves element to correct position on screen
applyStyles(view.element, viewProps);
// store new transforms
Object.assign(currentProps, Object.assign({}, viewProps));
// no longer busy
return true;
},
destroy: function destroy() {},
};
};
var propsHaveChanged = function propsHaveChanged(currentProps, newProps) {
// different amount of keys
if (Object.keys(currentProps).length !== Object.keys(newProps).length) {
return true;
}
// lets analyze the individual props
for (var prop in newProps) {
if (newProps[prop] !== currentProps[prop]) {
return true;
}
}
return false;
};
var applyStyles = function applyStyles(element, _ref2) {
var opacity = _ref2.opacity,
perspective = _ref2.perspective,
translateX = _ref2.translateX,
translateY = _ref2.translateY,
scaleX = _ref2.scaleX,
scaleY = _ref2.scaleY,
rotateX = _ref2.rotateX,
rotateY = _ref2.rotateY,
rotateZ = _ref2.rotateZ,
originX = _ref2.originX,
originY = _ref2.originY,
width = _ref2.width,
height = _ref2.height;
var transforms = '';
var styles = '';
// handle transform origin
if (isDefined(originX) || isDefined(originY)) {
styles += 'transform-origin: ' + (originX || 0) + 'px ' + (originY || 0) + 'px;';
}
// transform order is relevant
// 0. perspective
if (isDefined(perspective)) {
transforms += 'perspective(' + perspective + 'px) ';
}
// 1. translate
if (isDefined(translateX) || isDefined(translateY)) {
transforms +=
'translate3d(' + (translateX || 0) + 'px, ' + (translateY || 0) + 'px, 0) ';
}
// 2. scale
if (isDefined(scaleX) || isDefined(scaleY)) {
transforms +=
'scale3d(' +
(isDefined(scaleX) ? scaleX : 1) +
', ' +
(isDefined(scaleY) ? scaleY : 1) +
', 1) ';
}
// 3. rotate
if (isDefined(rotateZ)) {
transforms += 'rotateZ(' + rotateZ + 'rad) ';
}
if (isDefined(rotateX)) {
transforms += 'rotateX(' + rotateX + 'rad) ';
}
if (isDefined(rotateY)) {
transforms += 'rotateY(' + rotateY + 'rad) ';
}
// add transforms
if (transforms.length) {
styles += 'transform:' + transforms + ';';
}
// add opacity
if (isDefined(opacity)) {
styles += 'opacity:' + opacity + ';';
// if we reach zero, we make the element inaccessible
if (opacity === 0) {
styles += 'visibility:hidden;';
}
// if we're below 100% opacity this element can't be clicked
if (opacity < 1) {
styles += 'pointer-events:none;';
}
}
// add height
if (isDefined(height)) {
styles += 'height:' + height + 'px;';
}
// add width
if (isDefined(width)) {
styles += 'width:' + width + 'px;';
}
// apply styles
var elementCurrentStyle = element.elementCurrentStyle || '';
// if new styles does not match current styles, lets update!
if (styles.length !== elementCurrentStyle.length || styles !== elementCurrentStyle) {
element.style.cssText = styles;
// store current styles so we can compare them to new styles later on
// _not_ getting the style value is faster
element.elementCurrentStyle = styles;
}
};
var Mixins = {
styles: styles,
listeners: listeners,
animations: animations,
apis: apis,
};
var updateRect = function updateRect() {
var rect = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var element = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var style = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!element.layoutCalculated) {
rect.paddingTop = parseInt(style.paddingTop, 10) || 0;
rect.marginTop = parseInt(style.marginTop, 10) || 0;
rect.marginRight = parseInt(style.marginRight, 10) || 0;
rect.marginBottom = parseInt(style.marginBottom, 10) || 0;
rect.marginLeft = parseInt(style.marginLeft, 10) || 0;
element.layoutCalculated = true;
}
rect.left = element.offsetLeft || 0;
rect.top = element.offsetTop || 0;
rect.width = element.offsetWidth || 0;
rect.height = element.offsetHeight || 0;
rect.right = rect.left + rect.width;
rect.bottom = rect.top + rect.height;
rect.scrollTop = element.scrollTop;
rect.hidden = element.offsetParent === null;
return rect;
};
var createView =
// default view definition
function createView() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$tag = _ref.tag,
tag = _ref$tag === void 0 ? 'div' : _ref$tag,
_ref$name = _ref.name,
name = _ref$name === void 0 ? null : _ref$name,
_ref$attributes = _ref.attributes,
attributes = _ref$attributes === void 0 ? {} : _ref$attributes,
_ref$read = _ref.read,
read = _ref$read === void 0 ? function() {} : _ref$read,
_ref$write = _ref.write,
write = _ref$write === void 0 ? function() {} : _ref$write,
_ref$create = _ref.create,
create = _ref$create === void 0 ? function() {} : _ref$create,
_ref$destroy = _ref.destroy,
destroy = _ref$destroy === void 0 ? function() {} : _ref$destroy,
_ref$filterFrameActio = _ref.filterFrameActionsForChild,
filterFrameActionsForChild =
_ref$filterFrameActio === void 0
? function(child, actions) {
return actions;
}
: _ref$filterFrameActio,
_ref$didCreateView = _ref.didCreateView,
didCreateView = _ref$didCreateView === void 0 ? function() {} : _ref$didCreateView,
_ref$didWriteView = _ref.didWriteView,
didWriteView = _ref$didWriteView === void 0 ? function() {} : _ref$didWriteView,
_ref$ignoreRect = _ref.ignoreRect,
ignoreRect = _ref$ignoreRect === void 0 ? false : _ref$ignoreRect,
_ref$ignoreRectUpdate = _ref.ignoreRectUpdate,
ignoreRectUpdate = _ref$ignoreRectUpdate === void 0 ? false : _ref$ignoreRectUpdate,
_ref$mixins = _ref.mixins,
mixins = _ref$mixins === void 0 ? [] : _ref$mixins;
return function(
// each view requires reference to store
store
) {
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// root element should not be changed
var element = createElement(tag, 'filepond--' + name, attributes);
// style reference should also not be changed
var style = window.getComputedStyle(element, null);
// element rectangle
var rect = updateRect();
var frameRect = null;
// rest state
var isResting = false;
// pretty self explanatory
var childViews = [];
// loaded mixins
var activeMixins = [];
// references to created children
var ref = {};
// state used for each instance
var state = {};
// list of writers that will be called to update this view
var writers = [
write, // default writer
];
var readers = [
read, // default reader
];
var destroyers = [
destroy, // default destroy
];
// core view methods
var getElement = function getElement() {
return element;
};
var getChildViews = function getChildViews() {
return childViews.concat();
};
var getReference = function getReference() {
return ref;
};
var createChildView = function createChildView(store) {
return function(view, props) {
return view(store, props);
};
};
var getRect = function getRect() {
if (frameRect) {
return frameRect;
}
frameRect = getViewRect(rect, childViews, [0, 0], [1, 1]);
return frameRect;
};
var getStyle = function getStyle() {
return style;
};
/**
* Read data from DOM
* @private
*/
var _read = function _read() {
frameRect = null;
// read child views
childViews.forEach(function(child) {
return child._read();
});
var shouldUpdate = !(ignoreRectUpdate && rect.width && rect.height);
if (shouldUpdate) {
updateRect(rect, element, style);
}
// readers
var api = { root: internalAPI, props: props, rect: rect };
readers.forEach(function(reader) {
return reader(api);
});
};
/**
* Write data to DOM
* @private
*/
var _write = function _write(ts, frameActions, shouldOptimize) {
// if no actions, we assume that the view is resting
var resting = frameActions.length === 0;
// writers
writers.forEach(function(writer) {
var writerResting = writer({
props: props,
root: internalAPI,
actions: frameActions,
timestamp: ts,
shouldOptimize: shouldOptimize,
});
if (writerResting === false) {
resting = false;
}
});
// run mixins
activeMixins.forEach(function(mixin) {
// if one of the mixins is still busy after write operation, we are not resting
var mixinResting = mixin.write(ts);
if (mixinResting === false) {
resting = false;
}
});
// updates child views that are currently attached to the DOM
childViews
.filter(function(child) {
return !!child.element.parentNode;
})
.forEach(function(child) {
// if a child view is not resting, we are not resting
var childResting = child._write(
ts,
filterFrameActionsForChild(child, frameActions),
shouldOptimize
);
if (!childResting) {
resting = false;
}
});
// append new elements to DOM and update those
childViews
//.filter(child => !child.element.parentNode)
.forEach(function(child, index) {
// skip
if (child.element.parentNode) {
return;
}
// append to DOM
internalAPI.appendChild(child.element, index);
// call read (need to know the size of these elements)
child._read();
// re-call write
child._write(
ts,
filterFrameActionsForChild(child, frameActions),
shouldOptimize
);
// we just added somthing to the dom, no rest
resting = false;
});
// update resting state
isResting = resting;
didWriteView({
props: props,
root: internalAPI,
actions: frameActions,
timestamp: ts,
});
// let parent know if we are resting
return resting;
};
var _destroy = function _destroy() {
activeMixins.forEach(function(mixin) {
return mixin.destroy();
});
destroyers.forEach(function(destroyer) {
destroyer({ root: internalAPI, props: props });
});
childViews.forEach(function(child) {
return child._destroy();
});
};
// sharedAPI
var sharedAPIDefinition = {
element: {
get: getElement,
},
style: {
get: getStyle,
},
childViews: {
get: getChildViews,
},
};
// private API definition
var internalAPIDefinition = Object.assign({}, sharedAPIDefinition, {
rect: {
get: getRect,
},
// access to custom children references
ref: {
get: getReference,
},
// dom modifiers
is: function is(needle) {
return name === needle;
},
appendChild: appendChild(element),
createChildView: createChildView(store),
linkView: function linkView(view) {
childViews.push(view);
return view;
},
unlinkView: function unlinkView(view) {
childViews.splice(childViews.indexOf(view), 1);
},
appendChildView: appendChildView(element, childViews),
removeChildView: removeChildView(element, childViews),
registerWriter: function registerWriter(writer) {
return writers.push(writer);
},
registerReader: function registerReader(reader) {
return readers.push(reader);
},
registerDestroyer: function registerDestroyer(destroyer) {
return destroyers.push(destroyer);
},
invalidateLayout: function invalidateLayout() {
return (element.layoutCalculated = false);
},
// access to data store
dispatch: store.dispatch,
query: store.query,
});
// public view API methods
var externalAPIDefinition = {
element: {
get: getElement,
},
childViews: {
get: getChildViews,
},
rect: {
get: getRect,
},
resting: {
get: function get() {
return isResting;
},
},
isRectIgnored: function isRectIgnored() {
return ignoreRect;
},
_read: _read,
_write: _write,
_destroy: _destroy,
};
// mixin API methods
var mixinAPIDefinition = Object.assign({}, sharedAPIDefinition, {
rect: {
get: function get() {
return rect;
},
},
});
// add mixin functionality
Object.keys(mixins)
.sort(function(a, b) {
// move styles to the back of the mixin list (so adjustments of other mixins are applied to the props correctly)
if (a === 'styles') {
return 1;
} else if (b === 'styles') {
return -1;
}
return 0;
})
.forEach(function(key) {
var mixinAPI = Mixins[key]({
mixinConfig: mixins[key],
viewProps: props,
viewState: state,
viewInternalAPI: internalAPIDefinition,
viewExternalAPI: externalAPIDefinition,
view: createObject(mixinAPIDefinition),
});
if (mixinAPI) {
activeMixins.push(mixinAPI);
}
});
// construct private api
var internalAPI = createObject(internalAPIDefinition);
// create the view
create({
root: internalAPI,
props: props,
});
// append created child views to root node
var childCount = getChildCount(element); // need to know the current child count so appending happens in correct order
childViews.forEach(function(child, index) {
internalAPI.appendChild(child.element, childCount + index);
});
// call did create
didCreateView(internalAPI);
// expose public api
return createObject(externalAPIDefinition);
};
};
var createPainter = function createPainter(read, write) {
var fps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 60;
var name = '__framePainter';
// set global painter
if (window[name]) {
window[name].readers.push(read);
window[name].writers.push(write);
return;
}
window[name] = {
readers: [read],
writers: [write],
};
var painter = window[name];
var interval = 1000 / fps;
var last = null;
var id = null;
var requestTick = null;
var cancelTick = null;
var setTimerType = function setTimerType() {
if (document.hidden) {
requestTick = function requestTick() {
return window.setTimeout(function() {
return tick(performance.now());
}, interval);
};
cancelTick = function cancelTick() {
return window.clearTimeout(id);
};
} else {
requestTick = function requestTick() {
return window.requestAnimationFrame(tick);
};
cancelTick = function cancelTick() {
return window.cancelAnimationFrame(id);
};
}
};
document.addEventListener('visibilitychange', function() {
if (cancelTick) cancelTick();
setTimerType();
tick(performance.now());
});
var tick = function tick(ts) {
// queue next tick
id = requestTick(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 view
painter.readers.forEach(function(read) {
return read();
});
painter.writers.forEach(function(write) {
return write(ts);
});
};
setTimerType();
tick(performance.now());
return {
pause: function pause() {
cancelTick(id);
},
};
};
var createRoute = function createRoute(routes, fn) {
return function(_ref) {
var root = _ref.root,
props = _ref.props,
_ref$actions = _ref.actions,
actions = _ref$actions === void 0 ? [] : _ref$actions,
timestamp = _ref.timestamp,
shouldOptimize = _ref.shouldOptimize;
actions
.filter(function(action) {
return routes[action.type];
})
.forEach(function(action) {
return routes[action.type]({
root: root,
props: props,
action: action.data,
timestamp: timestamp,
shouldOptimize: shouldOptimize,
});
});
if (fn) {
fn({
root: root,
props: props,
actions: actions,
timestamp: timestamp,
shouldOptimize: shouldOptimize,
});
}
};
};
var insertBefore = function insertBefore(newNode, referenceNode) {
return referenceNode.parentNode.insertBefore(newNode, referenceNode);
};
var insertAfter = function insertAfter(newNode, referenceNode) {
return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
};
var isArray = function isArray(value) {
return Array.isArray(value);
};
var isEmpty = function isEmpty(value) {
return value == null;
};
var trim = function trim(str) {
return str.trim();
};
var toString = function toString(value) {
return '' + value;
};
var toArray = function toArray(value) {
v