oxe
Version:
A mighty tiny web components framework/library
1,719 lines (1,470 loc) • 111 kB
JavaScript
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
(function (global, factory) {
(typeof exports === "undefined" ? "undefined" : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.Oxe = factory());
})(this, function () {
'use strict';
var Utility = {
PIPE: /\s?\|\s?/,
PIPES: /\s?,\s?|\s+/,
value: function value(element, model) {
if (!model) throw new Error('Utility.value - requires model argument');
if (!element) throw new Error('Utility.value - requires element argument');
var type = this.type(element);
if (type === 'radio' || type === 'checkbox') {
var name = this.name(element);
var query = 'input[type="' + type + '"][name="' + name + '"]';
var form = this.form(element);
var elements = form ? this.form(element).querySelectorAll(query) : [element];
var multiple = elements.length > 1;
var result = multiple ? [] : undefined;
for (var i = 0, l = elements.length; i < l; i++) {
var child = elements[i];
var checked = this.checked(child);
if (!checked) continue;
var value = this.value(child, model);
if (multiple) {
result.push(value);
} else {
result = value;
break;
}
}
return result;
} else if (type === 'select-one' || type === 'select-multiple') {
var _multiple = this.multiple(element);
var options = element.options;
var _result = _multiple ? [] : undefined;
for (var _i = 0, _l = options.length; _i < _l; _i++) {
var option = options[_i];
var selected = option.selected;
var _value = this.value(option, model);
var match = this[_multiple ? 'includes' : 'compare'](this.data, _value);
if (selected && !match) {
if (this.multiple) {
_result.push(_value);
} else {
_result = _value;
}
} else if (!selected && match) {
option.selected = true;
}
}
return _result;
} else {
var attribute = element.attributes['o-value'];
if (attribute) {
var values = this.binderValues(attribute.value);
var _value2 = this.getByPath(model, values);
return _value2 || element.value;
} else {
return element.value;
}
}
},
form: function form(element) {
if (element.form) {
return element.form;
} else {
while (element = element.parentElement) {
if (element.nodeName === 'FORM' || element.nodeName.indexOf('-FORM') !== -1) {
return element;
}
}
}
},
type: function type(element) {
if (typeof element.type === 'string') {
return element.type;
} else {
return element.getAttribute('type');
}
},
name: function name(element) {
if (typeof element.name === 'string') {
return element.name;
} else {
return element.getAttribute('name');
}
},
checked: function checked(element) {
if (typeof element.checked === 'boolean') {
return element.checked;
} else {
switch (element.getAttribute('checked')) {
case undefined:
return false;
case 'true':
return true;
case null:
return false;
case '':
return true;
default:
return false;
}
}
},
multiple: function multiple(element) {
if (typeof element.multiple === 'boolean') {
return element.multiple;
} else {
switch (element.getAttribute('multiple')) {
case undefined:
return false;
case 'true':
return true;
case null:
return false;
case '':
return true;
default:
return false;
}
}
},
disabled: function disabled(element) {
if (typeof element.disabled === 'boolean') {
return element.disabled;
} else {
switch (element.getAttribute('disabled')) {
case undefined:
return false;
case 'true':
return true;
case null:
return false;
case '':
return true;
default:
return false;
}
}
},
index: function index(items, item) {
for (var i = 0, l = items.length; i < l; i++) {
if (this.match(items[i], item)) {
return i;
}
}
return -1;
},
includes: function includes(items, item) {
for (var i = 0, l = items.length; i < l; i++) {
if (this.match(items[i], item)) {
return true;
}
}
return false;
},
match: function match(source, target) {
if (source === target) {
return true;
}
if (_typeof(source) !== _typeof(target)) {
return false;
}
if (source.constructor !== target.constructor) {
return false;
}
if (_typeof(source) !== 'object' || _typeof(target) !== 'object') {
return source === target;
}
var sourceKeys = Object.keys(source);
var targetKeys = Object.keys(target);
if (sourceKeys.length !== targetKeys.length) {
return false;
}
for (var i = 0, l = sourceKeys.length; i < l; i++) {
var name = sourceKeys[i];
if (!this.match(source[name], target[name])) {
return false;
}
}
return true;
},
binderNames: function binderNames(data) {
data = data.split('o-')[1];
return data ? data.split('-') : [];
},
binderValues: function binderValues(data) {
data = data.split(this.PIPE)[0];
return data ? data.split('.') : [];
},
binderPipes: function binderPipes(data) {
data = data.split(this.PIPE)[1];
return data ? data.split(this.PIPES) : [];
},
ensureElement: function ensureElement(data) {
data.query = data.query || '';
data.scope = data.scope || document.body;
var element = data.scope.querySelector("".concat(data.name).concat(data.query));
if (!element) {
element = document.createElement(data.name);
if (data.position === 'afterbegin') {
data.scope.insertBefore(element, data.scope.firstChild);
} else if (data.position === 'beforeend') {
data.scope.appendChild(element);
} else {
data.scope.appendChild(element);
}
}
for (var i = 0, l = data.attributes.length; i < l; i++) {
var attribute = data.attributes[i];
element.setAttribute(attribute.name, attribute.value);
}
return element;
},
setByPath: function setByPath(data, path, value) {
var keys = typeof path === 'string' ? path.split('.') : path;
var last = keys.length - 1;
for (var i = 0; i < last; i++) {
var key = keys[i];
if (!(key in data)) {
if (isNaN(keys[i + 1])) {
data[key] = {};
} else {
data[key] = [];
}
}
data = data[key];
}
return data[keys[last]] = value;
},
getByPath: function getByPath(data, path) {
var keys = typeof path === 'string' ? path.split('.') : path;
var last = keys.length - 1;
if (keys[last] === '$key' || keys[last] === '$index') {
return keys[last - 1];
}
for (var i = 0; i < last; i++) {
var key = keys[i];
if (key in data === false) {
return undefined;
} else {
data = data[key];
}
}
return data[keys[last]];
},
clone: function clone(source) {
if (source === null || source === undefined || source.constructor !== Array && source.constructor !== Object) {
return source;
}
var target = source.constructor();
for (var name in source) {
var descriptor = Object.getOwnPropertyDescriptor(source, name);
if (descriptor) {
if ('value' in descriptor) {
descriptor.value = this.clone(descriptor.value);
}
Object.defineProperty(target, name, descriptor);
}
}
return target;
}
};
var Batcher = {
reads: [],
writes: [],
time: 1000 / 30,
pending: false,
setup: function setup(options) {
options = options || {};
this.time = options.time || this.time;
},
tick: function tick(callback) {
window.requestAnimationFrame(callback.bind(this, null));
},
schedule: function schedule() {
if (this.pending) return;
this.pending = true;
this.tick(this.flush);
},
flush: function flush(time) {
time = time || performance.now();
var task;
if (this.writes.length === 0) {
while (task = this.reads.shift()) {
if (task) {
task();
}
if (performance.now() - time > this.time) {
return this.tick(this.flush);
}
}
}
while (task = this.writes.shift()) {
if (task) {
task();
}
if (performance.now() - time > this.time) {
return this.tick(this.flush);
}
}
if (this.reads.length === 0 && this.writes.length === 0) {
this.pending = false;
} else if (performance.now() - time > this.time) {
this.tick(this.flush);
} else {
this.flush(time);
}
},
remove: function remove(tasks, task) {
var index = tasks.indexOf(task);
return !!~index && !!tasks.splice(index, 1);
},
clear: function clear(task) {
return this.remove(this.reads, task) || this.remove(this.writes, task);
},
batch: function batch(data) {
var self = this;
if (!data) return;
if (!data.read && !data.write) return;
data.context = data.context || {};
var read = function read() {
var result;
if (data.read) {
result = data.read.call(data.context, data.context);
}
if (data.write && result !== false) {
var write = data.write.bind(data.context, data.context);
self.writes.push(write);
}
};
self.reads.push(read);
self.schedule();
}
};
function Piper(binder, data) {
if (binder.type === 'on') {
return data;
}
if (!binder.pipes.length) {
return data;
}
var methods = binder.container.methods;
if (!methods) {
return data;
}
for (var i = 0, l = binder.pipes.length; i < l; i++) {
var name = binder.pipes[i];
if (name in methods) {
var method = methods[name];
if (method && method.constructor === Function) {
data = methods[name].call(binder.container, data);
} else {
console.warn("Oxe.piper - pipe ".concat(name, " invalid type"));
}
} else {
console.warn("Oxe.piper - pipe ".concat(name, " not found"));
}
}
return data;
}
function Class(binder) {
return {
read: function read() {
this.data = binder.data;
if (binder.names.length > 1) {
this.name = binder.names.slice(1).join('-');
}
},
write: function write() {
if (this.name) {
if (this.data === undefined || this.data === null) {
binder.target.classList.remove(this.name);
} else {
binder.target.classList.toggle(this.name, this.data);
}
} else {
if (this.data === undefined || this.data === null) {
binder.target.setAttribute('class', '');
} else {
binder.target.setAttribute('class', this.data);
}
}
}
};
}
function Default(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === undefined || this.data === null) {
this.data = '';
} else if (_typeof(this.data) === 'object') {
this.data = JSON.stringify(this.data);
} else if (typeof this.data !== 'string') {
this.data = this.data.toString();
}
if (this.data === binder.target[binder.type]) {
return false;
}
},
write: function write() {
binder.target[binder.type] = this.data;
}
};
}
function Disable(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === binder.target.disabled) {
return false;
}
},
write: function write() {
binder.target.disabled = this.data;
}
};
}
function Each(binder) {
var self = this;
var render = {
read: function read() {
this.data = binder.data || [];
if (binder.meta.keys === undefined) {
binder.meta.keys = [];
binder.meta.pending = false;
binder.meta.targetLength = 0;
binder.meta.currentLength = 0;
if (binder.target.firstElementChild) {
binder.meta.template = binder.target.removeChild(binder.target.firstElementChild);
} else {
var element = document.createElement('div');
var text = document.createTextNode("{{$".concat(binder.names[1], "}}"));
element.appendChild(text);
binder.meta.template = element;
}
}
binder.meta.keys = Object.keys(this.data);
binder.meta.targetLength = binder.meta.keys.length;
if (binder.meta.currentLength === binder.meta.targetLength) {
return false;
}
},
write: function write() {
if (binder.meta.currentLength === binder.meta.targetLength) {
binder.meta.pending = false;
return;
}
if (binder.meta.currentLength > binder.meta.targetLength) {
var element = binder.target.lastElementChild;
binder.target.removeChild(element);
self.remove(element);
binder.meta.currentLength--;
} else if (binder.meta.currentLength < binder.meta.targetLength) {
var _element = binder.meta.template.cloneNode(true);
var _index = binder.meta.currentLength++;
self.add(_element, {
index: _index,
path: binder.path,
variable: binder.names[1],
container: binder.container,
scope: binder.container.scope,
key: binder.meta.keys[_index]
});
binder.target.appendChild(_element);
}
if (binder.meta.pending && render.read) {
return;
} else {
binder.meta.pending = true;
}
delete render.read;
Batcher.batch(render);
}
};
return render;
}
function Enable(binder) {
return {
read: function read() {
this.data = !binder.data;
if (this.data === binder.target.disabled) {
return false;
}
},
write: function write() {
binder.target.disabled = this.data;
}
};
}
function Hide(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === binder.target.hidden) {
return false;
}
},
write: function write() {
binder.target.hidden = this.data;
}
};
}
function Href(binder) {
return {
read: function read() {
this.data = binder.data || '';
if (this.data === binder.target.href) {
return false;
}
},
write: function write() {
binder.target.href = this.data;
}
};
}
function Html(binder) {
var self = this;
return {
read: function read() {
this.data = binder.data;
if (this.data === undefined || this.data === null) {
this.data = '';
} else if (_typeof(this.data) === 'object') {
this.data = JSON.stringify(this.data);
} else if (typeof this.data !== 'string') {
this.data = String(this.data);
}
},
write: function write() {
while (binder.target.firstChild) {
var node = binder.target.removeNode(binder.target.firstChild);
self.remove(node);
}
var fragment = document.createDocumentFragment();
var parser = document.createElement('div');
parser.innerHTML = this.data;
while (parser.firstElementChild) {
self.add(parser.firstElementChild, {
container: binder.container,
scope: binder.container.scope
});
fragment.appendChild(parser.firstElementChild);
}
binder.target.appendChild(fragment);
}
};
}
function Label(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === undefined || this.data === null) {
this.data = '';
} else if (_typeof(this.data) === 'object') {
this.data = JSON.stringify(this.data);
} else if (typeof this.data !== 'string') {
this.data = this.data.toString();
}
if (this.data === binder.target.getAttribute('label')) {
return false;
}
},
write: function write() {
binder.target.setAttribute('label', this.data);
}
};
}
function On(binder) {
return {
read: function read(context) {
context.data = binder.data;
if (typeof context.data !== 'function') {
console.warn("Oxe - binder o-on=\"".concat(binder.keys.join('.'), "\" invalid type function required"));
return;
}
if (binder.meta.method) {
binder.target.removeEventListener(binder.names[1], binder.meta.method);
} else {
binder.meta.method = function (events) {
var parameters = [];
for (var i = 0, l = binder.pipes.length; i < l; i++) {
var keys = binder.pipes[i].split('.');
var parameter = Utility.getByPath(binder.container.model, keys);
parameters.push(parameter);
}
parameters.push(events);
parameters.push(this);
Promise.resolve(context.data.bind(binder.container).apply(null, parameters));
};
}
binder.target.addEventListener(binder.names[1], binder.meta.method);
}
};
}
function Read(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === binder.target.readOnly) {
return false;
}
},
write: function write() {
binder.target.readOnly = this.data;
}
};
}
function Require(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === binder.target.required) {
return false;
}
},
write: function write() {
binder.target.required = this.data;
}
};
}
function Show(binder) {
return {
read: function read() {
this.data = binder.data;
if (!this.data === binder.target.hidden) {
return false;
}
},
write: function write() {
binder.target.hidden = !this.data;
}
};
}
function Style(binder) {
return {
read: function read() {
this.data = binder.data;
if (binder.names.length > 1) {
this.name = '';
this.names = binder.names.slice(1);
for (var i = 0, l = this.names.length; i < l; i++) {
if (i === 0) {
this.name = this.names[i].toLowerCase();
} else {
this.name += this.names[i].charAt(0).toUpperCase() + this.names[i].slice(1).toLowerCase();
}
}
}
},
write: function write() {
if (binder.names.length > 1) {
if (this.data) {
binder.target.style[this.name] = this.data;
} else {
binder.target.style[this.name] = '';
}
} else {
if (this.data) {
binder.target.style.cssText = this.data;
} else {
binder.target.style.cssText = '';
}
}
}
};
}
function Text(binder) {
return {
read: function read() {
this.data = binder.data;
if (this.data === undefined || this.data === null) {
this.data = '';
} else if (_typeof(this.data) === 'object') {
this.data = JSON.stringify(this.data);
} else if (typeof this.data !== 'string') {
this.data = this.data.toString();
}
if (this.data === binder.target.textContent) {
return false;
}
},
write: function write() {
binder.target.textContent = this.data;
}
};
}
function Value(binder, caller) {
var self = this;
var type = binder.target.type;
if (binder.meta.busy) return;else binder.meta.busy = true;
if (type === 'select-one' || type === 'select-multiple') {
return {
read: function read() {
this.data = binder.data;
this.model = binder.model;
this.options = binder.target.options;
this.multiple = Utility.multiple(binder.target);
if (this.multiple && (!this.data || this.data.constructor !== Array)) {
binder.meta.busy = false;
throw new Error("Oxe - invalid o-value ".concat(binder.keys.join('.'), " multiple select requires array"));
}
},
write: function write() {
var fallback = false;
var fallbackValue = this.multiple ? [] : null;
var fallbackOption = this.multiple ? [] : null;
for (var i = 0, l = this.options.length; i < l; i++) {
var option = this.options[i];
var selected = option.selected;
var optionBinder = self.get('attribute', option, 'o-value');
var optionValue = optionBinder ? optionBinder.data : option.value;
var selectedAtrribute = option.hasAttribute('selected');
if (this.multiple) {
if (selectedAtrribute) {
fallback = true;
fallbackOption.push(option);
fallbackValue.push(optionValue);
}
} else {
if (i === 0 || selectedAtrribute) {
fallback = true;
fallbackOption = option;
fallbackValue = optionValue;
}
}
if (caller === 'view') {
if (selected) {
if (this.multiple) {
var includes = Utility.includes(this.data, optionValue);
if (!includes) {
this.selected = true;
binder.data.push(optionValue);
}
} else if (!this.selected) {
this.selected = true;
binder.data = optionValue;
}
} else {
if (this.multiple) {
var _index2 = Utility.index(this.data, optionValue);
if (_index2 !== -1) {
binder.data.splice(_index2, 1);
}
} else if (!this.selected && i === l - 1) {
binder.data = null;
}
}
} else {
if (this.multiple) {
var _includes = Utility.includes(this.data, optionValue);
if (_includes) {
this.selected = true;
option.selected = true;
} else {
option.selected = false;
}
} else {
if (!this.selected) {
var match = Utility.match(this.data, optionValue);
if (match) {
this.selected = true;
option.selected = true;
} else {
option.selected = false;
}
} else {
option.selected = false;
}
}
}
}
if (!this.selected && fallback) {
if (this.multiple) {
for (var _i2 = 0, _l2 = fallbackOption.length; _i2 < _l2; _i2++) {
fallbackOption[_i2].selected = true;
binder.data.push(fallbackValue[_i2]);
}
} else {
binder.data = fallbackValue;
fallbackOption.selected = true;
}
}
binder.meta.busy = false;
}
};
} else if (type === 'radio') {
return {
read: function read() {
this.form = binder.target.form || binder.container;
this.query = "input[type=\"radio\"][o-value=\"".concat(binder.value, "\"]");
this.nodes = this.form.querySelectorAll(this.query);
this.radios = Array.prototype.slice.call(this.nodes);
if (caller === 'view') {
binder.data = this.radios.indexOf(binder.target);
binder.meta.busy = false;
return false;
}
this.data = binder.data;
if (typeof this.data !== 'number') {
binder.meta.busy = false;
return false;
}
},
write: function write() {
for (var i = 0, l = this.radios.length; i < l; i++) {
var radio = this.radios[i];
if (i === this.data) {
radio.checked = true;
} else {
radio.checked = false;
}
}
binder.meta.busy = false;
}
};
} else if (type === 'checkbox') {
return {
read: function read() {
if (caller === 'view') {
binder.data = binder.target.checked;
binder.meta.busy = false;
return false;
}
this.data = binder.data;
if (typeof this.data !== 'boolean') {
binder.meta.busy = false;
return false;
}
},
write: function write() {
binder.target.checked = this.data;
binder.meta.busy = false;
}
};
} else {
return {
read: function read() {
if (caller === 'view') {
binder.data = binder.target.value;
binder.meta.busy = false;
return false;
}
this.data = binder.data;
if (this.data === binder.target.value) {
binder.meta.busy = false;
return false;
}
},
write: function write() {
binder.target.value = this.data === undefined || this.data === null ? '' : this.data;
binder.meta.busy = false;
}
};
}
}
function Write(binder) {
return {
read: function read() {
this.data = binder.data;
if (!this.data === binder.target.readOnly) {
return false;
}
},
write: function write() {
binder.target.readOnly = !this.data;
}
};
}
var DATA = new Map();
var BINDERS = {
class: Class,
css: Style,
default: Default,
disable: Disable,
disabled: Disable,
each: Each,
enable: Enable,
enabled: Enable,
hide: Hide,
hidden: Hide,
href: Href,
html: Html,
label: Label,
on: On,
read: Read,
require: Require,
required: Require,
show: Show,
showed: Show,
style: Style,
text: Text,
value: Value,
write: Write
};
var Binder = {
get data() {
return DATA;
},
get binders() {
return BINDERS;
},
setup: function setup(options) {
return new Promise(function ($return, $error) {
options = options || {};
this.data.set('location', new Map());
this.data.set('attribute', new Map());
for (var name in this.binders) {
this.binders[name] = this.binders[name].bind(this);
}
if (options.binders) {
for (var _name in options.binders) {
if (_name in this.binders === false) {
this.binders[_name] = options.binders[_name].bind(this);
}
}
}
return $return();
}.bind(this));
},
get: function get(type) {
if (!type) throw new Error('Oxe.binder.get - type argument required');
var result = this.data.get(type);
if (!result) return result;
for (var i = 1, l = arguments.length; i < l; i++) {
var argument = arguments[i];
result = result.get(argument);
if (!result) {
return result;
}
}
return result;
},
create: function create(data) {
if (data.name === undefined) throw new Error('Oxe.binder.create - missing name');
if (data.value === undefined) throw new Error('Oxe.binder.create - missing value');
if (data.target === undefined) throw new Error('Oxe.binder.create - missing target');
if (data.container === undefined) throw new Error('Oxe.binder.create - missing container');
var originalValue = data.value;
if (data.value.slice(0, 2) === '{{' && data.value.slice(-2) === '}}') {
data.value = data.value.slice(2, -2);
}
if (data.value.indexOf('$') !== -1 && data.context && data.context.variable && data.context.path && data.context.key) {
var pattern = new RegExp("\\$".concat(data.context.variable, "(,|\\s+|\\.|\\|)?(.*)?$"), 'ig');
data.value = data.value.replace(pattern, "".concat(data.context.path, ".").concat(data.context.key, "$1$2"));
}
var scope = data.container.scope;
var names = data.names || Utility.binderNames(data.name);
var pipes = data.pipes || Utility.binderPipes(data.value);
var values = data.values || Utility.binderValues(data.value);
var type = names[0];
var path = values.join('.');
var keys = [scope].concat(values);
var location = keys.join('.');
var meta = data.meta || {};
var context = data.context || {};
var source = type === 'on' || type === 'submit' ? data.container.methods : data.container.model;
return {
get location() {
return location;
},
get type() {
return type;
},
get path() {
return path;
},
get scope() {
return scope;
},
get name() {
return data.name;
},
get value() {
return data.value;
},
get target() {
return data.target;
},
get container() {
return data.container;
},
get model() {
return data.container.model;
},
get methods() {
return data.container.methods;
},
get keys() {
return keys;
},
get names() {
return names;
},
get pipes() {
return pipes;
},
get values() {
return values;
},
get meta() {
return meta;
},
get context() {
return context;
},
get originalValue() {
return originalValue;
},
get data() {
var data = Utility.getByPath(source, values);
return Piper(this, data);
},
set data(value) {
return Utility.setByPath(source, values, value);
}
};
},
render: function render(binder, caller) {
if (binder.type === 'submit') return;
var type = binder.type in this.binders ? binder.type : 'default';
var render = this.binders[type](binder, caller);
Batcher.batch(render);
},
unbind: function unbind(node) {
this.data.get('location').forEach(function (scopes) {
scopes.forEach(function (binders) {
binders.forEach(function (binder, index) {
if (binder.target === node) {
binders.splice(index, 1);
}
});
});
});
this.data.get('attribute').delete(node);
},
bind: function bind(node, name, value, context) {
if (value === "$".concat(context.variable, ".$key") || value === "{{$".concat(context.variable, ".$key}}")) {
return Batcher.batch({
write: function write() {
node.textContent = context.key;
}
});
}
if (value === "$".concat(context.variable, ".$index") || value === "{{$".concat(context.variable, ".$index}}")) {
return Batcher.batch({
write: function write() {
node.textContent = context.index;
}
});
}
var binder = this.create({
name: name,
value: value,
target: node,
context: context,
container: context.container,
scope: context.container.scope
});
if (!this.data.get('attribute').has(binder.target)) {
this.data.get('attribute').set(binder.target, new Map());
}
if (!this.data.get('location').has(binder.scope)) {
this.data.get('location').set(binder.scope, new Map());
}
if (!this.data.get('location').get(binder.scope).has(binder.path)) {
this.data.get('location').get(binder.scope).set(binder.path, []);
}
this.data.get('attribute').get(binder.target).set(binder.name, binder);
this.data.get('location').get(binder.scope).get(binder.path).push(binder);
this.render(binder);
},
remove: function remove(node) {
this.unbind(node);
for (var i = 0; i < node.childNodes.length; i++) {
this.remove(node.childNodes[i]);
}
},
add: function add(node, context) {
if (node.nodeType === Node.TEXT_NODE) {
if (node.textContent.indexOf('{{') === -1 || node.textContent.indexOf('}}') === -1) {
return;
}
var start = node.textContent.indexOf('{{');
if (start !== -1 && start !== 0) {
node = node.splitText(start);
}
var end = node.textContent.indexOf('}}');
var length = node.textContent.length;
if (end !== -1 && end !== length - 2) {
var split = node.splitText(end + 2);
this.add(split, context);
}
this.bind(node, 'o-text', node.textContent, context);
} else if (node.nodeType === Node.ELEMENT_NODE) {
var skipChildren = false;
var attributes = node.attributes;
for (var i = 0, l = attributes.length; i < l; i++) {
var attribute = attributes[i];
if (attribute.name === 'o-html' || attribute.name === 'o-scope' || attribute.name.indexOf('o-each') === 0) {
skipChildren = true;
}
if (attribute.name === 'o-value' || attribute.name === 'o-scope' || attribute.name === 'o-reset' || attribute.name === 'o-action' || attribute.name === 'o-method' || attribute.name === 'o-enctype' || attribute.name.indexOf('o-') !== 0) {
continue;
}
this.bind(node, attribute.name, attribute.value, context);
}
if ('o-value' in attributes) {
this.bind(node, 'o-value', attributes['o-value'].value, context);
}
if (skipChildren) return;
for (var _i3 = 0; _i3 < node.childNodes.length; _i3++) {
this.add(node.childNodes[_i3], context);
}
}
}
};
function Change(event) {
return new Promise(function ($return, $error) {
if ('attributes' in event.target && 'o-value' in event.target.attributes) {
var binder = Binder.get('attribute', event.target, 'o-value');
Binder.render(binder, 'view');
}
return $return();
});
}
var Fetcher = {
header: null,
method: 'get',
mime: {
xml: 'text/xml; charset=utf-8',
html: 'text/html; charset=utf-8',
text: 'text/plain; charset=utf-8',
json: 'application/json; charset=utf-8',
js: 'application/javascript; charset=utf-8'
},
setup: function setup(options) {
return new Promise(function ($return, $error) {
options = options || {};
this.path = options.path;
this.origin = options.origin;
this.request = options.request;
this.response = options.response;
this.acceptType = options.acceptType;
this.credentials = options.credentials;
this.contentType = options.contentType;
this.responseType = options.responseType;
this.method = options.method || this.method;
this.headers = options.headers || this.headers;
return $return();
}.bind(this));
},
serialize: function serialize(data) {
return new Promise(function ($return, $error) {
var query = '';
for (var name in data) {
query = query.length > 0 ? query + '&' : query;
query = query + encodeURIComponent(name) + '=' + encodeURIComponent(data[name]);
}
return $return(query);
});
},
fetch: function fetch(options) {
return new Promise(function ($return, $error) {
var data, copy, result, fetched, _copy, _result2;
data = Object.assign({}, options);
data.path = data.path || this.path;
data.origin = data.origin || this.origin;
if (data.path && typeof data.path === 'string' && data.path.charAt(0) === '/') data.path = data.path.slice(1);
if (data.origin && typeof data.origin === 'string' && data.origin.charAt(data.origin.length - 1) === '/') data.origin = data.origin.slice(0, -1);
if (data.path && data.origin && !data.url) data.url = data.origin + '/' + data.path;
if (!data.method) return $error(new Error('Oxe.fetcher - requires method option'));
if (!data.url) return $error(new Error('Oxe.fetcher - requires url or origin and path option'));
if (!data.headers && this.headers) data.headers = this.headers;
if (typeof data.method === 'string') data.method = data.method.toUpperCase() || this.method;
if (!data.acceptType && this.acceptType) data.acceptType = this.acceptType;
if (!data.contentType && this.contentType) data.contentType = this.contentType;
if (!data.responseType && this.responseType) data.responseType = this.responseType;
if (!data.credentials && this.credentials) data.credentials = this.credentials;
if (!data.mode && this.mode) data.mode = this.mode;
if (!data.cache && this.cache) data.cahce = this.cache;
if (!data.redirect && this.redirect) data.redirect = this.redirect;
if (!data.referrer && this.referrer) data.referrer = this.referrer;
if (!data.referrerPolicy && this.referrerPolicy) data.referrerPolicy = this.referrerPolicy;
if (!data.signal && this.signal) data.signal = this.signal;
if (!data.integrity && this.integrity) data.integrity = this.integrity;
if (!data.keepAlive && this.keepAlive) data.keepAlive = this.keepAlive;
if (data.contentType) {
data.headers = data.headers || {};
switch (data.contentType) {
case 'js':
data.headers['Content-Type'] = this.mime.js;
break;
case 'xml':
data.headers['Content-Type'] = this.mime.xml;
break;
case 'html':
data.headers['Content-Type'] = this.mime.html;
break;
case 'json':
data.headers['Content-Type'] = this.mime.json;
break;
default:
data.headers['Content-Type'] = data.contentType;
}
}
if (data.acceptType) {
data.headers = data.headers || {};
switch (data.acceptType) {
case 'js':
data.headers['Accept'] = this.mime.js;
break;
case 'xml':
data.headers['Accept'] = this.mime.xml;
break;
case 'html':
data.headers['Accept'] = this.mime.html;
break;
case 'json':
data.headers['Accept'] = this.mime.json;
break;
default:
data.headers['Accept'] = data.acceptType;
}
}
if (typeof this.request === 'function') {
copy = Object.assign({}, data);
return Promise.resolve(this.request(copy)).then(function ($await_40) {
try {
result = $await_40;
if (result === false) {
return $return(data);
}
if (_typeof(result) === 'object') {
Object.assign(data, result);
}
return $If_1.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
function $If_1() {
if (data.body) {
if (data.method === 'GET') {
return Promise.resolve(this.serialize(data.body)).then(function ($await_41) {
try {
data.url = data.url + '?' + $await_41;
return $If_5.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
} else {
if (data.contentType === 'json') {
data.body = JSON.stringify(data.body);
}
return $If_5.call(this);
}
function $If_5() {
return $If_2.call(this);
}
}
function $If_2() {
return Promise.resolve(window.fetch(data.url, Object.assign({}, data))).then(function ($await_42) {
try {
fetched = $await_42;
data.code = fetched.status;
data.message = fetched.statusText;
if (!data.responseType) {
data.body = fetched.body;
return $If_3.call(this);
} else {
return Promise.resolve(fetched[data.responseType === 'buffer' ? 'arrayBuffer' : data.responseType]()).then(function ($await_43) {
try {
data.body = $await_43;
return $If_3.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
function $If_3() {
if (this.response) {
_copy = Object.assign({}, data);
return Promise.resolve(this.response(_copy)).then(function ($await_44) {
try {
_result2 = $await_44;
if (_result2 === false) {
return $return(data);
}
if (_typeof(_result2) === 'object') {
Object.assign(data, _result2);
}
return $If_4.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
function $If_4() {
return $return(data);
}
return $If_4.call(this);
}
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
return $If_2.call(this);
}
return $If_1.call(this);
}.bind(this));
},
post: function post(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'post';
return $return(this.fetch(data));
}.bind(this));
},
get: function get(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'get';
return $return(this.fetch(data));
}.bind(this));
},
put: function put(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'put';
return $return(this.fetch(data));
}.bind(this));
},
head: function head(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'head';
return $return(this.fetch(data));
}.bind(this));
},
patch: function patch(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'patch';
return $return(this.fetch(data));
}.bind(this));
},
delete: function _delete(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'delete';
return $return(this.fetch(data));
}.bind(this));
},
options: function options(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'options';
return $return(this.fetch(data));
}.bind(this));
},
connect: function connect(data) {
return new Promise(function ($return, $error) {
data = typeof data === 'string' ? {
url: data
} : data;
data.method = 'connect';
return $return(this.fetch(data));
}.bind(this));
}
};
function Submit(event) {
return new Promise(function ($return, $error) {
var data, elements, i, l, element, type, binder, value, name, submit, options, result;
if (event.target.hasAttribute('o-submit') === false) {
return $return();
}
event.preventDefault();
data = {};
elements = event.target.querySelectorAll('*');
for (i = 0, l = elements.length; i < l; i++) {
element = elements[i];
type = element.type;
if (!type && name !== 'TEXTAREA' || type === 'submit' || type === 'button' || !type) {
continue;
}
binder = Binder.get('attribute', element, 'o-value');
value = binder ? binder.data : element.value;
name = element.name || (binder ? binder.values[binder.values.length - 1] : i);
if (!name) continue;
data[name] = value;
}
submit = Binder.get('attribute', event.target, 'o-submit');
return Promise.resolve(submit.data.call(submit.container, data, event)).then(function ($await_45) {
try {
options = $await_45;
if (_typeof(options) === 'object') {
options.url = options.url || event.target.getAttribute('o-action');
options.method = options.method || event.target.getAttribute('o-method');
options.contentType = options.contentType || event.target.getAttribute('o-enctype');
return Promise.resolve(Fetcher.fetch(options)).then(function ($await_46) {
try {
result = $await_46;
if (options.handler) {
return Promise.resolve(options.handler(result)).then(function ($await_47) {
try {
return $If_7.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
function $If_7() {
return $If_6.call(this);
}
return $If_7.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
}
function $If_6() {
if (event.target.hasAttribute('o-reset') || _typeof(options) === 'object' && options.reset) {
event.target.reset();
}
return $return();
}
return $If_6.call(this);
} catch ($boundEx) {
return $error($boundEx);
}
}.bind(this), $error);
});
}
function Input(event) {
return new Promise(function ($return, $error) {
if (event.target.type !== 'radio' && event.target.type !== 'option' && event.target.type !== 'checkbox' && event.target.type !== 'select-one' && event.target.type !== 'select-multiple' && 'attributes' in event.target && 'o-value' in event.target.attributes) {
var binder = Binder.get('attribute', event.target, 'o-value');
Binder.render(binder, 'view');
}
return $return();
});
}
function Reset(event) {
return new Promise(function ($return, $error) {
if (event.target.hasAttribute('o-reset') === false) {
return $return();
}
event.preventDefault();
var elements = event.target.querySelectorAll('*');