/** * Copyright (c) 2017 ESHA Research * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Declarative, persistent DOM content. * * <input store name="whatever"> * <div store="somekey" store-area="session" contenteditable>Some content</div> * * Status: BETA - uses store, doesn't extend it, deserves standalone project */ ;(function(document, store, _, Array) { // expose internal functions on store._.dom for extensibility var DOM = _.dom = function() { var nodes = document.querySelectorAll(DOM.selector), array = Array.prototype.slice.call(nodes); for (var i=0; i<array.length; i++) { DOM.node(array[i], i); } return array; }; DOM.selector = '[store],[store-area]'; DOM.event = 'input';// beforeunload is tempting DOM.node = function(node, i) { var key = DOM.key(node, i), area = DOM.area(node), value = area(key); if (value == null) { value = DOM.get(node); } else { DOM.set(node, value); } if (!node.storeListener) { node.addEventListener(DOM.event, function() { area(key, DOM.get(node)); }); node.storeListener = true; } }; DOM.area = function(node) { return store[node.getAttribute('store-area') || 'local']; }; // prefer store attribute value, then name attribute, use nodeName+index as last resort DOM.key = function(node, i) { return node.getAttribute('store') || node.getAttribute('name') || ('dom.'+node.nodeName.toLowerCase() + (i||'')); }; // both get and set should prefer value property to innerHTML DOM.get = function(node) { return node.value || node.innerHTML; }; DOM.set = function(node, value) { if ('value' in node) { node.value = value; } else { node.innerHTML = typeof value === "string" ? value : _.stringify(value); } }; // initialize document.addEventListener('DOMContentLoaded', DOM); })(document, window.store, window.store._, Array);