radi
Version:
**Radi** is a tiny javascript framework.
1,878 lines (1,604 loc) • 57.8 kB
JavaScript
var GLOBALS = {
HEADLESS_COMPONENTS: {},
FROZEN_STATE: false,
VERSION: '0.4.2',
// TODO: Collect active components
ACTIVE_COMPONENTS: {},
CUSTOM_ATTRIBUTES: {},
CUSTOM_TAGS: {},
};
/**
* @param {*[]} list
* @returns {*[]}
*/
var flatten = function flatten(list) {
return list.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
};
/**
* UUID v4 generator
* https://gist.github.com/jcxplorer/823878
* @returns {string}
*/
var generateId = () => {
var uuid = '';
for (var i = 0; i < 32; i++) {
var random = (Math.random() * 16) | 0; // eslint-disable-line
if (i === 8 || i === 12 || i === 16 || i === 20) {
uuid += '-';
}
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16); // eslint-disable-line
}
return uuid;
};
var PrivateStore = function PrivateStore() {
this.store = {};
};
/**
* @param {string} key
* @param {Listener} listener
* @param {number} depth
*/
PrivateStore.prototype.addListener = function addListener (key, listener, depth) {
if (typeof this.store[key] === 'undefined') {
this.createItemWrapper(key);
}
this.store[key].listeners[depth] = (this.store[key].listeners[depth] || []).filter(item => (
item.attached
));
this.store[key].listeners[depth].push(listener);
return listener;
};
/**
* Removes all listeners for all keys
*/
PrivateStore.prototype.removeListeners = function removeListeners () {
var o = Object.keys(this.store);
for (var i = 0; i < o.length; i++) {
this.store[o[i]].listeners = {};
this.store[o[i]].value = null;
}
};
/**
* setState
* @param {*} newState
* @returns {*}
*/
PrivateStore.prototype.setState = function setState (newState) {
// Find and trigger changes for listeners
for (var key of Object.keys(newState)) {
if (typeof this.store[key] === 'undefined') {
this.createItemWrapper(key);
}
this.store[key].value = newState[key];
this.triggerListeners(key);
}
return newState;
};
/**
* createItemWrapper
* @private
* @param {string} key
* @returns {object}
*/
PrivateStore.prototype.createItemWrapper = function createItemWrapper (key) {
return this.store[key] = {
listeners: {},
value: null,
};
};
/**
* triggerListeners
* @private
* @param {string} key
*/
PrivateStore.prototype.triggerListeners = function triggerListeners (key) {
var item = this.store[key];
if (item) {
var clone = Object.keys(item.listeners)
.sort()
.map(key => (
item.listeners[key].map(listener => listener)
));
for (var i = 0; i < clone.length; i++) {
for (var n = clone[i].length - 1; n >= 0; n--) {
if (clone[i][n].attached) { clone[i][n].handleUpdate(item.value); }
}
}
}
};
/**
* @param {*} obj
* @returns {*}
*/
var clone = obj => {
if (typeof obj !== 'object') { return obj; }
if (obj === null) { return obj; }
if (Array.isArray(obj)) { return obj.map(clone); }
/*eslint-disable*/
// Reverted as currently throws some errors
var cloned = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = clone(obj[key]);
}
}
/* eslint-enable */
return cloned;
};
var skipInProductionAndTest = fn => {
if (typeof process === 'undefined'
|| (process.env.NODE_ENV === 'production'
|| process.env.NODE_ENV === 'test')) {
return false;
}
return fn && fn();
};
/* eslint-disable no-param-reassign */
/* eslint-disable no-shadow */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
// import fuseDom from '../r/utils/fuseDom';
var Listener = function Listener(component, ...path) {
var assign;
this.component = component;
(assign = path, this.key = assign[0]);
this.path = path.slice(1, path.length);
this.depth = 0;
this.attached = true;
this.processValue = value => value;
this.changeListener = () => {};
this.addedListeners = [];
};
/**
* Applies values and events to listener
*/
Listener.prototype.init = function init () {
this.value = this.getValue(this.component.state[this.key]);
this.component.addListener(this.key, this, this.depth);
this.handleUpdate(this.component.state[this.key]);
return this;
};
/**
* Removes last active value with destroying listeners and
* @param {*} value
*/
Listener.prototype.unlink = function unlink () {
if (this.value instanceof Node) {
// Destroy this Node
// fuseDom.destroy(this.value);
} else
if (this.value instanceof Listener) {
// Deattach this Listener
this.value.deattach();
}
};
Listener.prototype.clone = function clone (target, source) {
var out = {};
for (var i in target) {
out[i] = target[i];
}
for (var i$1 in source) {
out[i$1] = source[i$1];
}
return out;
};
Listener.prototype.setPartialState = function setPartialState (path, value, source) {
var target = {};
if (path.length) {
target[path[0]] =
path.length > 1
? this.setPartialState(path.slice(1), value, source[path[0]])
: value;
return this.clone(source, target);
}
return value;
};
/**
* Updates state value
* @param {*} value
*/
Listener.prototype.updateValue = function updateValue (value) {
var source = this.component.state[this.key];
return this.component.setState({
[this.key]: this.setPartialState(this.path, value, source),
});
};
Listener.prototype.extractListeners = function extractListeners (value) {
// if (this.value instanceof Listener && value instanceof Listener) {
// console.log('middle')
// } else
if (value instanceof Listener) {
// if (this.value instanceof Listener) {
// this.value.processValue = value.processValue;
// // this.value = value;
// this.handleUpdate(value.getValue(value.component.state[value.key]));
// console.log(value, value.getValue(value.component.state[value.key]));
// value.deattach();
// }
// value.component.addListener(value.key, value, value.depth);
// value.handleUpdate = () => {
// console.log('inner handler')
// }
var tempListener = {
depth: value.depth,
attached: true,
processValue: value => value,
handleUpdate: () => {
if (this.component) {
this.handleUpdate(this.getValue(this.component.state[this.key]));
}
tempListener.attached = false;
},
changeListener: () => {},
};
this.addedListeners.push(tempListener);
value.component.addListener(value.key, tempListener, value.depth);
// value.init()
// value.handleUpdate = () => {
// console.log('inner handler')
// }
// value.onValueChange((v) => {
// this.handleUpdate(this.getValue(this.component.state[this.key]));
// console.log('me got changed', v)
// });
var newValue = value.processValue(
value.getValue(value.component.state[value.key])
);
value.deattach();
return this.extractListeners(newValue);
}
return value;
// return this.processValue(this.getValue(value));
};
/**
* @param {*} value
*/
Listener.prototype.handleUpdate = function handleUpdate (value) {
var newValue = this.processValue(this.getValue(value));
// if (this.value instanceof Listener && newValue instanceof Listener) {
// this.value.processValue = newValue.processValue;
// // this.value = newValue;
// this.value.handleUpdate(newValue.component.state[newValue.key]);
// console.log(newValue, newValue.getValue(newValue.component.state[newValue.key]));
// newValue.deattach();
// } else
if (newValue instanceof Listener) {
// if (this.value instanceof Listener) {
// this.value.processValue = newValue.processValue;
// // this.value = newValue;
// this.value.handleUpdate(newValue.component.state[newValue.key]);
// console.log(newValue, newValue.getValue(newValue.component.state[newValue.key]));
// newValue.deattach();
// } else {
for (var i = 0; i < this.addedListeners.length; i++) {
this.addedListeners[i].attached = false;
}
this.addedListeners = [];
this.value = this.extractListeners(newValue);
this.changeListener(this.value);
// }
// // console.log(this.value.processValue('P'), newValue.processValue('A'));
// // console.log(this.extractListeners(newValue));
// // newValue.handleUpdate(newValue.component.state[newValue.key]);
// // this.value = newValue;
// // this.value.processValue = newValue.processValue;
// this.value = this.extractListeners(newValue);
// this.changeListener(this.value);
// // this.value.processValue = newValue.processValue;
// // // this.value = newValue;
// // this.value.handleUpdate(newValue.component.state[newValue.key]);
// // console.log(newValue, newValue.getValue(newValue.component.state[newValue.key]));
// // newValue.deattach();
} else {
this.unlink();
this.value = newValue;
this.changeListener(this.value);
}
};
/**
* @param {*} source
* @returns {*}
*/
Listener.prototype.getValue = function getValue (source) {
var i = 0;
while (i < this.path.length) {
if (source === null
|| (!source[this.path[i]]
&& typeof source[this.path[i]] !== 'number')) {
source = null;
} else {
source = source[this.path[i]];
}
i += 1;
}
return source;
};
/**
* @param {number} depth
* @returns {Listener}
*/
Listener.prototype.applyDepth = function applyDepth (depth) {
this.depth = depth;
return this;
};
/**
* @param {function(*)} changeListener
*/
Listener.prototype.onValueChange = function onValueChange (changeListener) {
this.changeListener = changeListener;
this.changeListener(this.value);
};
/**
* @param {function(*): *} processValue
* @returns {function(*): *}
*/
Listener.prototype.process = function process (processValue) {
this.processValue = processValue;
return this;
};
Listener.prototype.deattach = function deattach () {
this.component = null;
this.attached = false;
this.key = null;
this.childPath = null;
this.path = null;
this.unlink();
this.value = null;
this.changeListener = () => {};
this.processValue = () => {};
};
var onMountEvent = document.createEvent('Event');
onMountEvent.initEvent('mount', true, true);
var onLoadEvent = document.createEvent('Event');
onLoadEvent.initEvent('load', true, true);
/**
* Append dom node to dom tree (after - (true) should append after 'to' element
* or (false) inside it)
* @param {HTMLElement} node
* @param {HTMLElement} to
* @param {Boolean} after
* @returns {HTMLElement}
*/
var append = (node, to, after) => {
if (typeof node.dispatchEvent === 'function') {
node.dispatchEvent(onLoadEvent);
}
if (after && to) {
if (to.parentNode) {
to.parentNode.insertBefore(node, to);
if (typeof node.dispatchEvent === 'function') {
node.dispatchEvent(onMountEvent);
}
// if (!to.nextSibling) {
// to.parentNode.appendChild(node);
// } else {
// to.parentNode.insertBefore(node, to.nextSibling || to);
// }
}
return node;
}
to.appendChild(node);
if (typeof node.dispatchEvent === 'function') {
node.dispatchEvent(onMountEvent);
}
return node;
};
var getLast = (child) => {
if (child.$redirect && child.$redirect[child.$redirect.length - 1]) {
return getLast(child.$redirect[child.$redirect.length - 1]);
}
// if (child.children && child.children.length > 0) {
// return child.children;
// }
return child;
};
/**
* @param {Structure} child
*/
var mountChildren = (child, isSvg, depth) => {
if ( depth === void 0 ) depth = 0;
if (!child) { return; }
if (child.$redirect && child.$redirect.length > 0) {
mountChildren(getLast(child), isSvg, depth + 1);
} else if (child.children && child.children.length > 0) {
if (child.html && child.html.length === 1) {
mount(child.children,
child.html[0],
child.html[0].nodeType !== 1,
child.$isSvg,
child.$depth);
} else {
mount(child.children,
child.$pointer,
true,
child.$isSvg,
child.$depth);
}
}
};
/**
* @param {string} value
* @returns {HTMLElement}
*/
var textNode = value => (
document.createTextNode(
(typeof value === 'object'
? JSON.stringify(value)
: value)
)
);
// import Component from './component/Component';
/**
* Appends structure[] to dom node
* @param {*} component
* @param {string} id
* @param {boolean} isSvg
* @param {number} depth
* @returns {HTMLElement|Node}
*/
var mount = (raw, parent, after, isSvg, depth) => {
if ( after === void 0 ) after = false;
if ( isSvg === void 0 ) isSvg = false;
if ( depth === void 0 ) depth = 0;
parent = typeof parent === 'string' ? document.getElementById(parent) : parent;
var nodes = flatten([raw]).map(filterNode);
// console.log(1, 'MOUNT')
var loop = function ( i ) {
var nn = nodes[i];
// console.log(2, nodes[i])
if (nn instanceof Node) {
append(nn, parent, after);
} else
if (nn && typeof nn.render === 'function') {
// nn.$pointer = text('[pointer]');
nn.$pointer = textNode('');
append(nn.$pointer, parent, after);
nodes[i].render(rendered => {
// console.log(3, rendered)
// Abort! Pointer was destroyed
if (nn.$pointer === false) { return false; }
for (var n = 0; n < rendered.length; n++) {
if (nn.$pointer) {
append(rendered[n], nn.$pointer, true);
} else {
append(rendered[n], parent, after);
}
}
mountChildren(nn, nn.$isSvg, depth + 1);
}, nn, depth, isSvg);
}
// if (!nn.html) {
// nn.$pointer = text('[pointer]');
// append(nn.$pointer, parent, after);
// }
};
for (var i = 0; i < nodes.length; i++) loop( i );
return nodes;
};
/**
* @param {*} query
* @returns {Node}
*/
var getElementFromQuery = (query, isSvg) => {
if (typeof query === 'string' || typeof query === 'number')
{ return query !== 'template'
? isSvg || query === 'svg'
? document.createElementNS(
"http://www.w3.org/2000/svg",
query
)
: document.createElement(query)
: document.createDocumentFragment(); }
console.warn(
'[Radi.js] Warn: Creating a JSX element whose query is not of type string, automatically converting query to string.'
);
return document.createElement(query.toString());
};
/**
* @param {*[]} raw
* @param {HTMLElement} parent
* @param {string} raw
* @returns {HTMLElement}
*/
var explode = (raw, parent, next, depth, isSvg) => {
if ( depth === void 0 ) depth = 0;
var nodes = flatten([raw]).map(filterNode);
// console.log('EXPLODE', nodes)
// console.log('explode', {parent, nodes})
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i] instanceof Structure || nodes[i].isStructure) && !nodes[i].html) {
// let pp = depth === 0 ? parent : nodes[i];
// let pp = parent;
// console.log('EXPLODE 1', parent.$depth, depth, parent.$redirect, nodes[i].$redirect)
if (parent.children.length <= 0) {
if (!parent.$redirect) {
parent.$redirect = [nodes[i]];
} else {
parent.$redirect.push(nodes[i]);
}
}
if (!parent.$redirect && nodes[i].children) {
parent.children = parent.children.concat(nodes[i].children);
}
if (typeof nodes[i].render === 'function') {
nodes[i].render(v => {
// if (parent.children.length <= 0) {
// if (!parent.$redirect) {
// parent.$redirect = [nodes[n]];
// } else {
// parent.$redirect.push(nodes[n]);
// }
// }
// console.log('EXPLODE 2', nodes[n], v, parent.$depth, nodes[n].$depth)
next(v);
// nodes[n].mount();
}, nodes[i], depth + 1, isSvg);
}
}
}
return;
};
/**
* @param {*} value
* @return {*}
*/
var parseValue = value =>
typeof value === 'number' && !Number.isNaN(value) ? `${value}px` : value;
/* eslint-disable no-continue */
/**
* @param {Structure} structure
* @param {object} styles
* @param {object} oldStyles
* @returns {object}
*/
var setStyles = (structure, styles, oldStyles) => {
if ( styles === void 0 ) styles = {};
if ( oldStyles === void 0 ) oldStyles = {};
if (!structure.html || !structure.html[0]) { return styles; }
var element = structure.html[0];
// Handle Listeners
if (styles instanceof Listener) {
if (typeof structure.$styleListeners.general !== 'undefined') {
return element.style;
}
structure.$styleListeners.general = styles;
structure.$styleListeners.general.applyDepth(structure.depth).init();
structure.$styleListeners.general.onValueChange(value => {
setStyles(structure, value, {});
});
return element.style;
}
if (typeof styles === 'string') {
element.style = styles;
return element.style;
}
var toRemove = Object.keys(oldStyles)
.filter(key => typeof styles[key] === 'undefined');
var loop = function ( style ) {
if (styles.hasOwnProperty(style)) {
// Skip if styles are the same
if (typeof oldStyles !== 'undefined' && oldStyles[style] === styles[style]) { return; }
// Need to remove falsy style
if (!styles[style] && typeof styles[style] !== 'number') {
element.style[style] = null;
return;
}
// Handle Listeners
if (styles[style] instanceof Listener) {
if (typeof structure.$styleListeners[style] !== 'undefined') { return; }
structure.$styleListeners[style] = styles[style];
structure.$styleListeners[style].applyDepth(structure.depth).init();
structure.$styleListeners[style].onValueChange(value => {
setStyles(structure, {
[style]: value,
}, {});
});
styles[style] = structure.$styleListeners[style].value;
return;
}
element.style[style] = parseValue(styles[style]);
}
};
for (var style in styles) loop( style );
for (var i = 0; i < toRemove.length; i++) {
element.style[toRemove[i]] = null;
}
return element.style;
};
/**
* @param {*} value
* @return {*}
*/
var parseClass = value => {
if (Array.isArray(value)) {
return value.filter(item => item).join(' ')
}
return value;
};
/* eslint-disable no-continue */
// import AttributeListener from './utils/AttributeListener';
/**
* @param {Structure} structure
* @param {object} propsSource
* @param {object} oldPropsSource
*/
var setAttributes = (structure, propsSource, oldPropsSource) => {
if ( propsSource === void 0 ) propsSource = {};
if ( oldPropsSource === void 0 ) oldPropsSource = {};
var props = propsSource || {};
var oldProps = oldPropsSource || {};
if (!structure.html || !structure.html[0]) { return structure; }
var element = structure.html[0];
if (!(element instanceof Node && element.nodeType !== 3)) { return structure; }
var toRemove = Object.keys(oldProps)
.filter(key => typeof props[key] === 'undefined');
var loop = function ( prop ) {
if (props.hasOwnProperty(prop)) {
// Skip if proprs are the same
if (typeof oldProps !== 'undefined' && oldProps[prop] === props[prop]) { return; }
if (prop === 'checked') {
element.checked = props[prop];
}
// Need to remove falsy attribute
if (!props[prop] && typeof props[prop] !== 'number' && typeof props[prop] !== 'string') {
element.removeAttribute(prop);
return;
}
// Handle Listeners
if (props[prop] instanceof Listener) {
if (typeof structure.$attrListeners[prop] !== 'undefined') { return; }
structure.$attrListeners[prop] = props[prop];
props[prop].applyDepth(structure.depth).init();
if (prop.toLowerCase() === 'model' || prop.toLowerCase() === 'checked') {
if (element.getAttribute('type') === 'radio') {
element.addEventListener('input', (e) => {
structure.$attrListeners[prop].updateValue(
(e.target.checked && e.target.value)
|| e.target.checked
);
}, false);
structure.$attrListeners[prop].onValueChange(value => {
setAttributes(structure, {
checked: element.value === value && Boolean(value),
}, {});
});
} else
if (element.getAttribute('type') === 'checkbox') {
element.addEventListener('input', (e) => {
structure.$attrListeners[prop].updateValue(
Boolean(e.target.checked)
);
}, false);
structure.$attrListeners[prop].onValueChange(value => {
setAttributes(structure, {
checked: Boolean(value),
}, {});
});
} else {
element.addEventListener('input', (e) => {
structure.$attrListeners[prop].updateValue(e.target.value);
}, false);
}
}
if (!/(checkbox|radio)/.test(element.getAttribute('type'))) {
structure.$attrListeners[prop].onValueChange(value => {
setAttributes(structure, {
[prop]: value,
}, {});
});
}
// structure.setProps(Object.assign(structure.data.props, {
// [prop]: props[prop].value,
// }));
props[prop] = structure.$attrListeners[prop].value;
return;
}
if (prop === 'value' || prop === 'model') {
element.value = props[prop];
}
if (typeof GLOBALS.CUSTOM_ATTRIBUTES[prop] !== 'undefined') {
var ref = GLOBALS.CUSTOM_ATTRIBUTES[prop];
var allowedTags = ref.allowedTags;
if (!allowedTags || (
allowedTags
&& allowedTags.length > 0
&& allowedTags.indexOf(element.localName) >= 0
)) {
if (typeof GLOBALS.CUSTOM_ATTRIBUTES[prop].caller === 'function') {
GLOBALS.CUSTOM_ATTRIBUTES[prop].caller(element, props[prop]);
}
if (!GLOBALS.CUSTOM_ATTRIBUTES[prop].addToElement) { return; }
}
}
if (prop.toLowerCase() === 'style') {
if (typeof props[prop] === 'object') {
setStyles(structure, props[prop], (oldProps && oldProps.style) || {});
// props[prop] = structure.setStyles(props[prop], (oldProps && oldProps.style) || {});
} else {
element.style = props[prop];
}
return;
}
if (prop.toLowerCase() === 'class' || prop.toLowerCase() === 'classname') {
element.setAttribute('class', parseClass(props[prop]));
return;
}
if (prop.toLowerCase() === 'loadfocus') {
element.addEventListener('mount', () => {
element.focus();
}, false);
return;
}
if (prop.toLowerCase() === 'html') {
element.innerHTML = props[prop];
return;
}
// Handles events 'on<event>'
if (prop.substring(0, 2).toLowerCase() === 'on' && typeof props[prop] === 'function') {
var fn = props[prop];
if (prop.substring(0, 8).toLowerCase() === 'onsubmit') {
element[prop] = (e) => {
if (props.prevent) {
e.preventDefault();
}
var data = [];
var inputs = e.target.elements || [];
for (var input of inputs) {
if ((input.name !== ''
&& (input.type !== 'radio' && input.type !== 'checkbox'))
|| input.checked) {
var item = {
name: input.name,
el: input,
type: input.type,
default: input.defaultValue,
value: input.value,
set(val) {
if (structure && structure.el && structure.el.value) {
structure.el.value = val;
}
},
reset(val) {
if (structure && structure.el && structure.el.value) {
structure.el.value = val;
structure.el.defaultValue = val;
}
},
};
data.push(item);
if (!data[item.name]) {
Object.defineProperty(data, item.name, {
value: item,
});
}
}
}
return fn(e, data);
};
} else {
element[prop] = (e, ...args) => fn(e, ...args);
}
return;
}
element.setAttribute(prop, props[prop]);
}
};
for (var prop in props) loop( prop );
for (var i = 0; i < toRemove.length; i++) {
element.removeAttribute(toRemove[i]);
}
structure.props = props;
return structure;
};
/* eslint-disable no-restricted-syntax */
/**
* @param {*} query
* @param {object} props
* @param {...*} children
* @param {number} depth
*/
var Structure = function Structure(query, props, children, depth) {
if ( props === void 0 ) props = {};
if ( depth === void 0 ) depth = 0;
this.query = query;
this.props = Boolean !== props ? props : {};
if (isComponent(query) || query instanceof Component) {
this.$compChildren = flatten(children || []).map(filterNode);
this.children = [];
} else {
this.children = flatten(children || []).map(filterNode);
this.$compChildren = [];
}
this.html = null;
this.$attrListeners = [];
this.$styleListeners = [];
this.$pointer = null;
this.$component = null;
this.$listener = null;
this.$redirect = null;
this.$destroyed = false;
this.$isSvg = query === 'svg';
this.$depth = depth;
};
Structure.prototype.mount = function mount () {
this.$destroyed = false;
if (this.$component instanceof Component) {
this.$component.mount();
}
if (typeof this.onMount === 'function') {
this.onMount();
}
};
Structure.prototype.destroy = function destroy (childrenToo) {
if ( childrenToo === void 0 ) childrenToo = true;
if (this.$destroyed) { return false; }
for (var l in this.$styleListeners) {
if (this.$styleListeners[l]
&& typeof this.$styleListeners[l].deattach === 'function') {
this.$styleListeners[l].deattach();
}
}
for (var l$1 in this.$attrListeners) {
if (this.$attrListeners[l$1]
&& typeof this.$attrListeners[l$1].deattach === 'function') {
this.$attrListeners[l$1].deattach();
}
}
if (this.$redirect) {
for (var i = 0; i < this.$redirect.length; i++) {
if (typeof this.$redirect[i].destroy === 'function') {
this.$redirect[i].destroy();
}
}
}
if (childrenToo && this.children) {
for (var i$1 = 0; i$1 < this.children.length; i$1++) {
if (typeof this.children[i$1].destroy === 'function') {
this.children[i$1].destroy();
}
}
}
if (this.html) {
var items = this.html;
var loop = function ( i ) {
if (items[i].parentNode) {
var destroyHTML = () => items[i].parentNode.removeChild(items[i]);
if (typeof items[i].beforedestroy === 'function') {
items[i].beforedestroy(destroyHTML);
} else {
destroyHTML();
}
}
};
for (var i$2 = 0; i$2 < this.html.length; i$2++) loop( i$2 );
}
if (this.$component instanceof Component) {
this.$component.destroy();
}
if (this.$listener instanceof Listener) {
this.$listener.deattach();
}
if (this.$pointer && this.$pointer.parentNode) {
this.$pointer.parentNode.removeChild(this.$pointer);
}
if (typeof this.onDestroy === 'function') {
this.onDestroy();
}
this.$pointer = null;
this.$redirect = null;
this.$component = null;
this.render = () => {};
this.html = null;
this.$destroyed = true;
return true;
};
Structure.prototype.render = function render (next, parent, depth, isSvg) {
if ( depth === void 0 ) depth = 0;
if ( isSvg === void 0 ) isSvg = false;
// console.log('RENDER', isSvg, parent, parent && parent.$isSvg)
this.$depth = Math.max(this.$depth, depth);
this.$isSvg = isSvg || (parent && parent.$isSvg) || this.query === 'svg';
if (this.query === '#text') {
this.html = [textNode(this.props)];
return next(this.html);
}
if (typeof this.query === 'string' || typeof this.query === 'number') {
this.html = [getElementFromQuery(this.query, this.$isSvg)];
setAttributes(this, this.props, {});
return next(this.html);
}
if (this.query instanceof Listener) {
if (!this.$listener) {
this.$listener = this.query.applyDepth(this.$depth).init();
this.mount();
}
return this.query.onValueChange(v => {
if (this.html) {
var tempParent = this.html[0];
if (this.$pointer) {
this.$redirect = patch(this.$redirect, v, this.$pointer,
true, this.$isSvg, this.$depth + 1);
} else {
this.$redirect = patch(this.$redirect, v, tempParent,
true, this.$isSvg, this.$depth + 1);
}
// let a = {
// $redirect: [],
// children: [],
// };
//
// explode(v, a, output => {
// // this.html = output;
// if (this.$pointer) {
// this.$redirect = patch(this.$redirect, a.$redirect,
// this.$pointer, true, this.$isSvg, this.$depth + 1);
// } else {
// this.$redirect = patch(this.$redirect, a.$redirect,
// tempParent, true, this.$isSvg, this.$depth + 1);
// }
// // next(output);
// }, this.$depth + 1, this.$isSvg);
} else {
explode(v, parent || this, output => {
// console.warn('change HTML', this.html)
this.html = output;
next(output);
}, this.$depth + 1, this.$isSvg);
}
});
}
if (this.query instanceof Promise
|| this.query.constructor.name === 'LazyPromise') {
return this.query.then(v => {
var normalisedValue = v.default || v;
explode(normalisedValue, parent || this, output => {
this.html = output;
next(output);
}, this.$depth, this.$isSvg);
});
}
if (this.query instanceof Component
&& typeof this.query.render === 'function') {
this.$component = this.query;
return explode(this.$component.render(), parent || this, v => {
this.html = v;
next(v);
this.mount();
}, this.$depth, this.$isSvg);
}
if (isComponent(this.query)) {
if (!this.$component) {
this.$component =
new this.query(this.$compChildren).setProps(this.props); // eslint-disable-line
}
if (typeof this.$component.render === 'function') {
explode(this.$component.render(), parent || this, v => {
this.html = v;
next(v);
}, this.$depth, this.$isSvg);
this.mount();
}
return null;
}
if (typeof this.query === 'function') {
return explode(this.query(this.props), parent || this, v => {
this.html = v;
next(v);
}, this.$depth, this.$isSvg);
}
return next(textNode(this.query));
};
Structure.prototype.isStructure = function isStructure () {
return true;
};
/* eslint-disable no-restricted-syntax */
// const hasRedirect = item => (
// item && item.$redirect
// );
var patch = (rawfirst, rawsecond, parent,
after, isSvg, depth) => {
if ( after === void 0 ) after = false;
if ( isSvg === void 0 ) isSvg = false;
if ( depth === void 0 ) depth = 0;
var first = flatten([rawfirst]);
var second = flatten([rawsecond]).map(filterNode);
var length = Math.max(first.length, second.length);
var loop = function ( i ) {
// debugger
// const nn = i;
// first[i] = first[i].$redirect || first[i];
if (typeof first[i] === 'undefined') {
// mount
mount(second[i], parent, after, isSvg, depth);
return;
}
if (typeof second[i] === 'undefined') {
// remove
if (typeof first[i].destroy === 'function') {
first[i].destroy();
}
return;
}
second[i].$depth = depth;
if ((first[i] instanceof Structure || first[i].isStructure)
&& (second[i] instanceof Structure || second[i].isStructure)
&& first[i] !== second[i]) {
// if (second[i].$redirect2) {
// second[i] = patch(
// // first[i].$redirect || first[i],
// hasRedirect(first[i]) || first[i],
// second[i].$redirect[second[i].$redirect.length - 1] || second[i],
// parent,
// after,
// isSvg,
// depth
// );
// continue;
// }
if (first[i].html
&& first[i].query === '#text'
&& second[i].query === '#text') {
for (var n = 0; n < first[i].html.length; n++) {
if (first[i].props !== second[i].props) {
first[i].html[n].textContent = first[i].props = second[i].props;
}
}
second[i].html = first[i].html;
first[i].html = null;
if (first[i].$pointer) {
if (second[i].$pointer && second[i].$pointer.parentNode) {
second[i].$pointer.parentNode.removeChild(second[i].$pointer);
}
second[i].$pointer = first[i].$pointer;
first[i].$pointer = null;
}
first[i].destroy();
return;
}
if (first[i].html
&& typeof first[i].query === 'string'
&& typeof second[i].query === 'string'
&& first[i].query === second[i].query) {
// for (var n = 0; n < first[i].html.length; n++) {
// if (first[i].props !== second[i].props) {
// // first[i].html[n].textContent = second[i].props;
// }
// }
second[i].html = first[i].html;
first[i].html = null;
if (first[i].$pointer) {
if (second[i].$pointer && second[i].$pointer.parentNode) {
second[i].$pointer.parentNode.removeChild(second[i].$pointer);
}
second[i].$pointer = first[i].$pointer;
first[i].$pointer = null;
}
setAttributes(second[i], second[i].props, first[i].props);
// mountChildren(second[i], second[i].$isSvg, second[i].$depth + 1);
if (second[i].html[0]
&& second[i].children
&& second[i].children.length > 0) {
second[i].children = patch(first[i].children,
second[i].children,
second[i].html[0],
false,
second[i].$isSvg,
second[i].$depth + 1);
}
first[i].destroy();
return;
}
// maybe merge
var n1 = first[i];
var n2 = second[i];
// n2.$pointer = textNode('[pointer2]');
n2.$pointer = textNode('');
append(n2.$pointer, parent, after);
n2.render(rendered => {
if (n1.$pointer) {
if (n2.$pointer && n2.$pointer.parentNode) {
n2.$pointer.parentNode.removeChild(n2.$pointer);
}
n2.$pointer = n1.$pointer;
n1.$pointer = null;
}
for (var n = 0; n < rendered.length; n++) {
if ((n1.html && !n1.html[i]) || !n1.html) {
append(rendered[n], n2.$pointer, true);
} else {
append(rendered[n], n1.html[i], true);
}
}
mountChildren(n2, isSvg, depth + 1);
n1.destroy(false);
}, n2, depth, isSvg);
}
};
for (var i = 0; i < length; i++) loop( i );
return second;
};
/* eslint-disable guard-for-in */
var capitalise = lower => lower.charAt(0).toUpperCase() + lower.substr(1);
var Component = function Component(children, props) {
this.addNonEnumerableProperties({
$id: generateId(),
$name: this.constructor.name,
$config: (typeof this.config === 'function') ? this.config() : {
listen: true,
},
__$events: {},
__$privateStore: new PrivateStore(),
});
// TODO: Remove this! Deprecated!
if (typeof this.on !== 'function'
|| (typeof this.on === 'function' && typeof this.on() === 'object')) {
throw new Error('[Radi.js] Using `on.eventName()` is deprecated. Please use `onEventName()`.');
}
this.children = [];
// Links headless components
for (var key in GLOBALS.HEADLESS_COMPONENTS) {
if (this[key] && typeof this[key].on === 'function') {
this[key].on('update', () => this.setState());
}
}
this.state = typeof this.state === 'function'
? this.state()
: (this.state || {});
skipInProductionAndTest(() => Object.freeze(this.state));
if (children) { this.setChildren(children); }
if (props) { this.setProps(props); }
};
/**
* @returns {HTMLElement}
*/
Component.prototype.render = function render () {
if (typeof this.view !== 'function') { return null; }
return this.html = this.view();
};
/**
* @param {object} props
* @returns {Component}
*/
Component.prototype.setProps = function setProps (props) {
var newState = {};
// Self is needed cause of compilation
var self = this;
var loop = function ( key ) {
if (typeof props[key] === 'function' && key.substr(0, 2) === 'on') {
self.on(key.substring(2, key.length), props[key]);
} else
if (props[key] instanceof Listener) {
newState[key] = props[key].init().value;
props[key].changeListener = (value => {
self.setState({
[key]: value,
});
});
} else {
newState[key] = props[key];
}
};
for (var key in props) loop( key );
this.setState(newState);
return this;
};
/**
* @param {Node[]|*[]} children
*/
Component.prototype.setChildren = function setChildren (children) {
this.children = children;
this.setState();
for (var i = 0; i < this.children.length; i++) {
if (typeof this.children[i].on === 'function') {
this.children[i].on('update', () => this.setState());
}
}
return this;
};
/**
* @private
* @param {object} obj
*/
Component.prototype.addNonEnumerableProperties = function addNonEnumerableProperties (obj) {
for (var key in obj) {
if (typeof this[key] !== 'undefined') { continue; }
Object.defineProperty(this, key, {
value: obj[key],
});
}
};
/**
* @param {string} key
* @param {Listener} listener
* @param {number} depth
*/
Component.prototype.addListener = function addListener (key, listener, depth) {
this.__$privateStore.addListener(key, listener, depth);
};
Component.prototype.mount = function mount () {
this.trigger('mount');
};
Component.prototype.destroy = function destroy () {
// if (this.html) {
// for (var i = 0; i < this.html.length; i++) {
// if (this.html[i].parentNode) {
// this.html[i].parentNode.removeChild(this.html[i]);
// }
// }
// }
this.html = null;
this.trigger('destroy');
this.__$privateStore.removeListeners();
};
// TODO: Remove this! Deprecated!
Component.prototype.when = function when () {
throw new Error('[Radi.js] Using `.when(\'Event\')` is deprecated. Use `.on(\'Event\')` instead.');
};
/**
* @param {string} key
* @param {function} fn
* @returns {function}
*/
Component.prototype.on = function on (key, fn) {
if (typeof this.__$events[key] === 'undefined') { this.__$events[key] = []; }
this.__$events[key].push(fn);
return fn;
};
/**
* @param {string} key
* @param {*} value
*/
Component.prototype.trigger = function trigger (key, ...args) {
var event = this[`on${capitalise(key)}`];
if (typeof event === 'function') {
event.call(this, ...args);
}
if (typeof this.__$events[key] !== 'undefined') {
for (var i in this.__$events[key]) {
this.__$events[key][i].call(this, ...args);
}
}
};
/**
* @param {object} newState
* @param {string} actionName
*/
Component.prototype.setState = function setState (newState, actionName) {
if (typeof newState === 'object') {
var oldstate = this.state;
skipInProductionAndTest(() => oldstate = clone(this.state));
this.state = Object.assign(oldstate, newState);
skipInProductionAndTest(() => Object.freeze(this.state));
if (this.$config.listen) {
this.__$privateStore.setState(newState);
}
}
if (!this.$config.listen && typeof this.view === 'function' && this.html) {
this.html = patch(this.html, this.view());
}
if (typeof actionName === 'string' && typeof this[actionName] === 'function') {
this.trigger(`after${capitalise(actionName)}`, newState);
}
// if (typeof newState === 'object') {
// let oldstate = this.state;
//
// skipInProductionAndTest(() => oldstate = clone(this.state));
//
// this.state = Object.assign(oldstate, newState);
//
// skipInProductionAndTest(() => Object.freeze(this.state));
//
// if (this.$config.listen) {
// this.__$privateStore.setState(newState);
// }
// }
//
// if (!this.$config.listen && typeof this.view === 'function' && this.html) {
// fuseDom.fuse(this.html, this.view());
// }
this.trigger('update');
return newState;
};
/**
* @returns {boolean}
*/
Component.isComponent = function isComponent () {
return true;
};
/**
* @param {*} value
* @returns {Boolean}
*/
var isComponent = value => {
if (value) {
if (value.prototype instanceof Component) {
return true;
}
if (value.isComponent) {
return true;
}
}
return false;
};
/**
* @param {function} value
* @returns {object}
*/
var filterNode = value => {
if (Array.isArray(value)) {
return value.map(filterNode);
}
if (typeof value === 'string' || typeof value === 'number') {
return r('#text', value);
}
if (!value || typeof value === 'boolean') {
return r('#text', '');
}
if (value instanceof Listener) {
return r(value);
}
if (isComponent(value) || value instanceof Component) {
return r(value);
}
if (typeof value === 'function') {
return r(value);
}
if (value instanceof Promise || value.constructor.name === 'LazyPromise') {
return r(value);
}
return value;
};
// import Component from '../component/Component';
/**
* @param {*} query
* @param {object} props
* @param {...*} children
* @returns {object}
*/
var r = (query, props, ...children) => {
if (typeof GLOBALS.CUSTOM_TAGS[query] !== 'undefined') {
return GLOBALS.CUSTOM_TAGS[query].onmount(
props || {},
(children && flatten([children]).map(filterNode)) || [],
filterNode,
v => (GLOBALS.CUSTOM_TAGS[query].saved = v)
) || null;
}
if (query === 'await') {
var output = null;
if (props.src && props.src instanceof Promise) {
props.src.then(v => {
var nomalizedData = filterNode(
typeof props.transform === 'function'
? props.transform(v)
: v
);
if (output) {
output = patch(output, nomalizedData, output.html[0].parentNode);
} else {
output = nomalizedData;
}
}).catch(error => {
var placerror = filterNode(
typeof props.error === 'function'
? props.error(error)
: props.error
);
if (output) {
output = patch(output, placerror, output.html[0].parentNode);
} else {
output = placerror;
}
});
}
if (!output) {
output = filterNode(props.placeholder);
}
return output;
}
if (query === 'template') {
// return flatten([children]).map(filterNode);
return new Structure('section', props, flatten([children]).map(filterNode));
}
return new Structure(query, props, flatten([children]).map(filterNode));
};
/**
* The listen function is used for dynamically binding a component property
* to the DOM. Also commonly imported as 'l'.
* @param {Component} component
* @param {...string} path
* @returns {Listener}
*/
var listen = (component, ...path) =>
new Listener(component, ...path);
var headless = (key, Comp) => {
// TODO: Validate component and key
var name = '$'.concat(key);
var mountedComponent = new Comp();
mountedComponent.mount();
Component.prototype[name] = mountedComponent;
return GLOBALS.HEADLESS_COMPONENTS[name] = mountedComponent;
};
// Decorator for actions
var action = (target, key, descriptor) => {
var fn = descriptor.value;
return {
configurable: true,
value(...args) {
return this.setState.call(this, fn.call(this, ...args), key);
},
};
};
/* eslint-disable func-names */
var createWorker = fn => {
var fire = () => {};
var blob = new window.Blob([`self.onmessage = function(e) {
self.postMessage((${fn.toString()})(e.data));
}`], { type: 'text/javascript' });
var url = window.URL.createObjectURL(blob);
var myWorker = new window.Worker(url);
myWorker.onmessage = e => { fire(e.data, null); };
myWorker.onerror = e => { fire(null, e.data); };
return arg => new Promise((resolve, reject) => {
fire = (data, err) => !err ? resolve(data) : reject(data);
myWorker.postMessage(arg);
});
};
// Descriptor for worker
var worker = (target, key, descriptor) => {
var act = descriptor.value;
var promisedWorker = createWorker(act);
descriptor.value = function (...args) {
promisedWorker(...args).then(newState => {
this.setState.call(this, newState);
});
};
return descriptor;
};
// Descriptor for subscriptions
var subscribe = (container, eventName/* , triggerMount */) =>
(target, key, descriptor) => {
var fn = descriptor.value;
var boundFn = () => {};
if (typeof fn !== 'function') {
throw new Error(`@subscribe decorator can only be applied to methods not: ${typeof fn}`);
}
// In IE11 calling Object.defineProperty has a side-effect of evaluating the
// getter for the property which is being replaced. This causes infinite
// recursion and an "Out of stack space" error.
var definingProperty = false;
container[eventName] = (...args) => boundFn(...args);
return {
configurable: true,
get() {
if (definingProperty || this === target.prototype || this.hasOwnProperty(key)
|| typeof fn !== 'function') {
return fn;
}
boundFn = fn.bind(this);
definingProperty = true;
Object.defineProperty(this, key, {
configurable: true,
get() {
return boundFn;
},
set(value) {
fn = value;
delete this[key];
},
});
definingProperty = false;
return boundFn;
},
set(value) {
fn = value;
},
};
};
/**
* @param {string} tagName
* @param {function} onmount
* @param {function} ondestroy
* @returns {object}
*/
var customTag = (tagName, onmount, ondestroy) => GLOBALS.CUSTOM_TAGS[tagName] = {
name: tagName,
onmount: onmount || (() => {}),
ondestroy: ondestroy || (() => {}),
saved: null,
};
/**
* @param {string} attributeName
* @param {function} caller
* @param {object} object
* @returns {object}
*/
var customAttribute = (attributeName, caller, ref) => {
if ( ref === void 0 ) ref = {};
var allowedTags = ref.allowedTags;
var addToElement = ref.addToElement;
return GLOBALS.CUSTOM_ATTRIBUTES[attributeName] = {
name: attributeName,
caller,
allowedTags: allowedTags || null,
addToElement,
};
};
var remountActiveComponents = () => {
Object.values(GLOBALS.ACTIVE_COMPONENTS).forEach(component => {
if (typeof component.onMount === 'function') {
component.onMount(component);
}
});
};
var animate = (target, type, opts, done) => {
var direct = opts[type];
if (typeof direct !== 'function') {
console.warn(`[Radi.js] Animation \`${type}\` for node \`${target.nodeName.toLowerCase}\` should be function`);
return;
}
return direct(target, done);
};
customAttribute('animation', (el, props) => {
animate(el, 'in', props, () => {});
el.beforedestroy = done => animate(el, 'out', props, done);
});
/* eslint-disable consistent-return */
var Modal = (function (Component$$1) {
function Modal () {
Component$$1.apply(this, arguments);
}
if ( Component$$1 ) Modal.__proto__ = Component$$1;
Modal.prototype = Object.create( Component$$1 && Component$$1.prototype );
Modal.prototype.constructor = Modal;
Modal.prototype.state = function state () {
return {
registry: {},
};
};
Modal.prototype.register = function register (name, element) {
if (typeof this.state.registry[name] !== 'undefined') {
console.warn(`[Radi.js] Warn: Modal with name "${name}" is already registerd!`);
return;
}
this.setState({
registry: Object.assign({}, this.state.registry, {
[name]: {
status: false,
element,
},
}),
}, 'register');
};
Modal.prototype.exists = function exists (name) {
if (typeof this.state.registry[name] === 'undefined') {
console.warn(`[Radi.js] Warn: Modal with name "${name}" is not registerd!`);
return false;
}
return true;
};
Modal.prototype.open = function open (name) {
if (!this.exists(name) || this.state.registry[name].status) { return; }
return this.setState({
registry: Object.assign({}, this.state.registry, {
[name]: {
status: true,
element: this.state.registry[name].element,
},
}),
}, 'open');
};
Modal.prototype.close = function close (name) {
if (!this.exists(name) || !this.state.registry[name].status) { return; }
return this.setState({
registry: Object.assign({}, this.state.registry, {
[name]: {
status: false,
element: this.state.registry[name].element,
},
}),
}, 'close');
};
Modal.prototype.closeAll = function closeAll () {
var keys = Object.keys(this.state.registry);
var registry = keys.reduce((acc,