dbjs-dom
Version:
dbjs DOM bindings
223 lines (214 loc) • 8.09 kB
JavaScript
'use strict';
var contains = require('es5-ext/array/#/contains')
, uniq = require('es5-ext/array/#/uniq')
, remove = require('es5-ext/array/#/remove')
, constant = require('es5-ext/function/constant')
, assign = require('es5-ext/object/assign')
, normalizeOptions = require('es5-ext/object/normalize-options')
, Set = require('es6-set')
, d = require('d')
, autoBind = require('d/auto-bind')
, makeElement = require('dom-ext/document/#/make-element')
, extend = require('dom-ext/element/#/extend')
, removeEl = require('dom-ext/element/#/remove')
, setPresenceEl = require('dom-ext/element/#/set-presence')
, getId = require('dom-ext/html-element/#/get-id')
, isAnchor = require('dom-ext/html-anchor-element/is-html-anchor-element')
, resolveOptions = require('../utils/resolve-options')
, DOMInput = require('../_controls/input')
, stringify = JSON.stringify
, getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
, Input;
module.exports = Input = function (document, type/*, options*/) {
var options = arguments[2];
this.items = [];
this.removeButtons = [];
this.make = makeElement.bind(document);
this.type = type;
options = resolveOptions(options, type);
delete options.multiple;
if (options.minInputsCount) {
this.minInputsCount = options.minInputsCount >>> 0;
delete options.minInputsCount;
}
if (options.addLabel) {
this.addLabel = (typeof options.addLabel === 'function')
? options.addLabel() : options.addLabel;
delete options.addLabel;
} else if (typeof this.addLabel === 'function') {
this.addLabel = this.addLabel();
}
if (options.deleteLabel) {
this.deleteLabel = (typeof options.deleteLabel === 'function')
? options.deleteLabel : constant(options.deleteLabel);
delete options.deleteLabel;
}
this.options = normalizeOptions(options.item);
this.options.required = false;
this.options.dbOptions = options.dbOptions;
this.options.control = Object(this.options.control);
this.options.toDOMInput = (typeof options.toDOMInput === 'function') && options.toDOMInput;
DOMInput.call(this, document, type, options);
};
Input.prototype = Object.create(DOMInput.prototype, assign({
constructor: d(Input),
_value: d(null),
controlAttributes: d({}),
minInputsCount: d(0),
onChange: d(function () {
var value, changed, valid, emitChanged, emitValid, form;
if (this.items[0]) {
form = this.items[0].form;
if (form) {
if (this.form !== form) {
if (this.form) this.form.removeEventListener('reset', this._onReset, false);
this.form = form;
this.form.addEventListener('reset', this._onReset, false);
}
}
}
value = this.value;
changed = this.items.some(function (item) { return item.changed; });
valid = this.items.every(function (item) { return item.valid; });
if (valid && this.required) valid = Boolean(value.length);
if (this.changed !== changed) {
this.changed = changed;
emitChanged = true;
}
if (this.valid !== valid) {
this.valid = valid;
emitValid = true;
}
this.emit('change', value);
if (emitChanged) this.emit('change:changed', this.changed);
if (emitValid) this.emit('change:valid', this.valid);
}),
name: d.gs(getName, function (name) {
this._name = name;
name = this.name;
this.items.forEach(function (input) { input.name = name; });
}),
inputValue: d.gs(function () {
return uniq.call(this.items.map(function (item) { return item.value; })
.filter(function (value) { return value != null; }));
}, function (value) {
var length, item, index = -1, nestedMap;
if (value == null) {
value = new Set();
} else if ((value.key === 'map') && value.owner && this.type.database.NestedMap &&
(value.owner instanceof this.type.database.NestedMap)) {
nestedMap = value.owner;
value = nestedMap.ordered;
}
this._value = value;
value.forEach(function (value) {
var item = this.items[++index], hiddenInput;
if (!item) item = this.addItem();
if (nestedMap) {
hiddenInput = this.document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = nestedMap.map.__id__ + '[]';
hiddenInput.value = value.__id__;
item.dom.appendChild(hiddenInput);
}
item.index = index;
item.value = value;
}, this);
length = value.size;
while (this.items[length]) this.removeItem(this.items[length]);
while (length < this.minInputsCount) {
item = this.addItem();
item.index = length++;
}
}),
value: d.gs(function () { return this.inputValue; }, function (value) {
this.inputValue = value;
}),
castControlAttribute: d(function (name, value) {
if (name === 'required') return;
this.options.control[name] = value;
this.items.forEach(function (input) {
input.castControlAttribute(name, value);
});
if (this.templateInput) this.templateInput.castControlAttribute(name, value);
if (name === 'disabled') {
if (this.addButton) setPresenceEl.call(this.addButton, !value);
this.removeButtons.forEach(function (btn) {
setPresenceEl.call(btn, !value);
});
}
}),
addLabel: d('Add'),
deleteLabel: d(constant('x')),
_render: d(function (/*options*/) {
var el = this.make, options = Object(arguments[0]), templateItem, templateDom;
this.domList = el('ul');
this.addButton = this.addLabel;
templateItem = this.renderItem();
this.templateInput = templateItem.input;
templateItem.input.name = options.name;
templateItem.input.index = true;
templateDom = el('ul', { class: 'template' }, templateItem.dom);
if (!isAnchor(this.addButton)) {
this.addButton = el('a', { class: 'dbjs-multiple-button-add' }, this.addButton);
}
this.addButton.setAttribute('onclick', 'document.getElementById(' +
stringify(getId.call(this.domList)) + ').appendChild(document.getElementById(' +
stringify(getId.call(templateDom)) + ').firstChild.cloneNode(true));' +
'this.className = this.className');
this.addButton.onclick = this.addItem;
this.dom = el('div', { class: 'dbjs multiple' }, this.domList,
el('div', { class: 'controls' }, this.addButton), templateDom);
}),
safeRemoveItem: d(function (input) {
if (this.domList.childNodes.length <= this.minInputsCount) return;
this.removeItem(input);
}),
removeItem: d(function (input) {
if (!contains.call(this.items, input)) return;
removeEl.call(input.listItem);
remove.call(this.items, input);
input.destroy();
this.items.forEach(function (item, index) { item.index = index; });
this.onChange();
}),
renderItem: d(function () {
var el = this.make, dom, input, removeButton;
if (this.options.toDOMInput) {
input = this.options.toDOMInput.call(this.type, this.document, this.options);
} else {
input = this.type.toDOMInput(this.document, this.options);
}
dom = el('li');
removeButton = this.deleteLabel();
if (!isAnchor(removeButton)) {
removeButton = el('a', { class: 'dbjs-multiple-button-remove' }, removeButton);
}
removeButton.setAttribute('onclick', 'this.parentNode.parentNode.removeChild(this.parentNode)');
removeButton.onclick = this.safeRemoveItem.bind(this, input);
extend.call(dom, input, removeButton);
this.removeButtons.push(removeButton);
if (this.options.control.disabled) setPresenceEl.call(removeButton, false);
return { dom: dom, input: input };
})
}, autoBind({
addItem: d(function () {
var data = this.renderItem(), input = data.input, dom = data.dom;
if (this.name) input.name = this.name;
input.index = this.items.length;
input.parent = this;
this.items.push(input);
input.listItem = dom;
input.on('change', this.onChange);
this.domList.appendChild(dom);
this.onChange();
return input;
}),
_onReset: d(function (e) {
var control = this.dom.getElementsByTagName('input')[0];
if (!control) control = this.dom.getElementsByTagName('select')[0];
if (!control) control = this.dom.getElementsByTagName('textarea')[0];
if (!control) return;
this.inputValue = this._value;
})
})));