UNPKG

application-prototype

Version:
1,622 lines (1,505 loc) • 39.8 kB
/** * @memberof JSTemplate * @typedef {Object} JSTemplateModule * @property {JSTemplate.JSTemplateParseContent} parseContent * @property {object} config * @property {number} [config.RENDER_FPS=15] * @property {number} [config.REMOVE_EMPTY_NODES=true] */ /** * Module used for template rendering * @example * Application.require('js-template').then(function (jsTemplate) { * jsTemplate.parseContent( * document.body, * function (err, config) { console.log(config) }, * { context: {}, args: { item: 'sample reference' }} * ); * }, console.error); * @interface JSTemplate * @returns {JSTemplate.JSTemplateModule} */ var libs; /** * @typedef {Object} jsTemplate_textResult * @memberof JSTemplate * @property {string} [type='text'] * @property {JSTemplate.jsTemplate_textResultData} data */ /** * @typedef {Object} jsTemplate_textResultData * @memberof JSTemplate * @property {Array<Text>} nodes * @property {Array<Text>} initialNodes * @property {string} code */ /** * @typedef {Object} parseTextNodesConfig * @memberof JSTemplate * @property {object} [args={}] arguments * @property {object} [context={}] execution context * @property {string} [start='{{'] start token * @property {string} [end='}}'] end token * @property {Array<JSTemplate.jsTemplate_textResult>} [textNodes] array of TextNodes * @property {Array<Text>} [buffer] (technical property) buffer * @property {boolean} [opened=false] (technical property) * @property {Array<string>} [__argsNames] (technical property) * @property {Array<any>} [__argsValues] (technical property) */ /** * Expression Builder * @protected * @function * @memberof JSTemplate * @param {string} code * @param {JSTemplate.parseTextNodesConfig} config */ var expressionBuilder = function (code, config) { var expressionCall; /* jshint -W054 */ try { expressionCall = ( Function.apply( config.context || {}, config.__argsNames.concat( [ "return (" + code + " );" ] ) ) ); } catch (er) { expressionCall = function () { return [ "{{ ", er, ":\n", code, " }}" ]; }; } /* jshint +W054 */ return expressionCall; }; /** * @protected * @function * @memberof JSTemplate * @param {Array<Text>} bf * @param {parseTextNodesConfig} config * @returns {JSTemplate.jsTemplate_textResult} */ var textParser = function (bf, config) { var er; var code = []; bf.forEach(function (node) { if (node.parentNode) { code.push(node.data); } }); /* jshint +W054 */ /* jshint -W014 */ var _methods = { type: 'text', data: { nodes: bf, initialNodes: bf, code: code.join("") } }; return _methods; }; /** * @protected * @function * @param {JSTemplate.jsTemplate_textResult} item * @param {JSTemplate.parseTextNodesConfig} config * @param {function(Error, any): void} cb */ textParser.value = function (item, config, cb) { var expressionCall = expressionBuilder(item.data.code, config); var value = expressionCall.apply(config.context, config.__argsValues); if ( value && typeof(value) === "object" && typeof(value.then) === "function" && typeof(value.catch) === "function" ) { value.then(function (result) { cb(null, result); }, function (err) { cb(err, null); }); } else { cb(null, value); } }; /** * @protected * @function * @param {JSTemplate.jsTemplate_textResult} item * @param {Array<Node|Text>|Text|Node} val * @param {function(Error): void} cb */ textParser.update = function (item, val, cb) { var nNodes = [], er; try { if (typeof (val) !== "undefined") { if (!Array.isArray(val)) { val = [val]; } val.forEach(function (v) { if (v instanceof Node) { nNodes.push(v); } else { //@ts-ignore if (v && typeof (v.toString) === "function") { //@ts-ignore nNodes.push(document.createTextNode(v.toString())); } else { nNodes.push(document.createTextNode("" + v)); } } }); } } catch (er) { console.error(er); } if (!nNodes.length) { nNodes.push(document.createTextNode("")); } // removing old nodes // TODO divList[0].isEqualNode(divList[2]) var equalNodesArr = true; if (item.data.nodes.length !== nNodes.length) { equalNodesArr = false; } else { item.data.nodes.forEach(function (n, i) { if (!n.isEqualNode(nNodes[i])) { equalNodesArr = false; } }); } if (equalNodesArr) { cb(null); return; } var nodes = item.data.nodes; item.data.nodes = nNodes; var markNode = document.createTextNode(""); nodes[0].parentNode.insertBefore(markNode, nodes[0]); nNodes.forEach(function (node) { markNode.parentNode.insertBefore(node, markNode); }); markNode.parentNode.removeChild(markNode); nodes.filter(function (node) { var found = false; var i; for (i = 0; i < nNodes.length; i++) { if (node === nNodes[i]) { found = true; break; } } return !found; }).forEach(function (node) { if (node.parentNode) { node.parentNode.removeChild(node); } }); if (cb) { cb(null); } }; /** * @callback parseTextNodesCallback * @memberof JSTemplate * @param {Error} err * @param {JSTemplate.parseTextNodesConfig} config */ /** * @protected * @function * @memberof JSTemplate * @param {HTMLElement|Node|Text} textNode * @param {JSTemplate.parseTextNodesCallback} cb * @param {JSTemplate.parseTextNodesConfig} config */ var parseTextNodes = function (textNode, cb, config) { if (!config && typeof(cb) !== "function") { config = cb; cb = function () {}; } if (!config) { config = { opened: false, buffer: [], args: {}, context: {}, start: "{{", end: "}}", textNodes: [] }; } else { config.opened = false; config.buffer = []; config.args = (config.args || {}); config.context = (config.context || {}); config.start = (config.start || '{{'); config.end = (config.end || '}}'); config.textNodes = []; } /** * nodes parser * @private * @function * @param {(ChildNode|Node|Text)} textNode */ var ate = function (textNode) { //@ts-ignore var text = (textNode ? (textNode.data || '') : '') + ''; var firstNode, nextNode, index, cNode; if (textNode && textNode.nodeType === Node.TEXT_NODE) { /** * @private * @type {string} */ //@ts-ignore text = textNode.data; } if (config.opened) { //@ts-ignore index = (textNode === null ? "" : text).indexOf(config.end); //@ts-ignore if (index === -1 || textNode === null) { if ( !textNode || !textNode.previousSibling || textNode.previousSibling.nodeType !== Node.TEXT_NODE ) { config.buffer = []; config.opened = false; } else { //@ts-ignore if (textNode !== null) { //@ts-ignore config.buffer.push(textNode); } } } else { config.opened = false; if (index) { //@ts-ignore firstNode = document.createTextNode(text.subs(index)); config.buffer.push(firstNode); } if (index + config.end.length < text.length) { //@ts-ignore nextNode = document.createTextNode(text.subs(index + config.end.length, 0)); } if (firstNode) { textNode.parentNode.insertBefore(firstNode, textNode); } cNode = textParser(config.buffer.map(function (v) { return v; }), { start: config.start, end: config.end }); config.textNodes.push(cNode); if (nextNode) { textNode.parentNode.insertBefore(nextNode, textNode); } config.buffer = []; textNode.parentNode.removeChild(textNode); if (nextNode) { ate(nextNode); } } } else if (textNode) { index = text.indexOf(config.start); if (index !== -1) { config.opened = true; config.buffer = []; if (index) { //@ts-ignore firstNode = document.createTextNode(text.subs(index)); } if (index + config.end.length < text.length) { //@ts-ignore nextNode = document.createTextNode(text.subs(index + config.end.length, 0)); // config.buffer.push(nextNode); } if (firstNode) { textNode.parentElement.insertBefore(firstNode, textNode); } if (nextNode) { textNode.parentElement.insertBefore(nextNode, textNode); } textNode.parentNode.removeChild(textNode); if (nextNode) { ate(nextNode); } else { ate(textNode.nextSibling || null); } } else { ate(textNode.nextSibling || null); } } }; if (textNode.nodeType === Node.ELEMENT_NODE) { if (textNode.childNodes[0]) { ate(textNode.childNodes[0]); } } if (textNode.nodeType === Node.TEXT_NODE) { ate(textNode); } if (typeof (cb) === "function") { cb(null, config); } return config; }; /** * @typedef {Object} jsTemplate_Attribute * @memberof JSTemplate * @property {string} name * @property {string} value */ /** * @typedef {Object} jsTemplate_attrResultAttributeData * @memberof JSTemplate * @property {string} name attribute name * @property {string} code executable code * @property {HTMLElement} node node element * @property {any} [buffer] ( technical property ) * @property {boolean} [inline=false] should be value be parsed * @property {boolean} [postProcess=false] should be value be parsed */ /** * @typedef {Object} jsTemplate_attrResult * @memberof JSTemplate * @property {('event'|'attribute'|'binding'|'macro')} type * @property {JSTemplate.jsTemplate_Attribute} attr * @property {JSTemplate.jsTemplate_attrResultAttributeData} data */ /** * @typedef {Object} jsTemplateAttrData * @memberof JSTemplate * @property {Array<JSTemplate.jsTemplate_attrResult>} nodes * @property {Array<JSTemplate.jsTemplate_textResult>} texts * @property {Array<JSTemplate.jsTemplateAttrData>} children * @property {Object<string,JSTemplate.jsTemplate_attrResult>} _macro * @property {boolean} [HAS_POST_PROCESS=false] */ /** * Parsing NodeElement Attribute * @protected * @memberof JSTemplate * @param {JSTemplate.jsTemplate_Attribute} attr * @returns {JSTemplate.jsTemplate_attrResult} */ var attrParser = function (attr, node) { /** * @private * @type {JSTemplate.jsTemplate_attrResult} */ var attrResult = { type: null, attr: attr, data: null }; if (attr.name.match(/^ev-.+/)) { attrResult.type = 'event'; attrResult.data = { name: attr.name.substring(3), code: 'function (event) { ' + attr.value + ' }', node: node }; } else if (attr.name.match(/^\((.+)\)$/)) { attrResult.type = 'event'; attrResult.data = { name: attr.name.match(/^\((.+)\)$/)[1], code: 'function (event) { ' + attr.value + ' }', node: node }; } else if (attr.name.match(/^\[\((.+)\)\]$/)) { attrResult.type = 'binding'; attrResult.data = { name: attr.name.match(/^\[\((.+)\)\]$/)[1], code: 'function (event) { if (event.__args) with (event.__args) { ' + attr.value + ' = event.__value; }; return ( ' + attr.value + ' ); }', node: node }; } else if (attr.name.match(/^\[(.+)\]$/)) { attrResult.type = 'attribute'; attrResult.data = { name: attr.name.match(/^\[(.+)\]$/)[1], code: attr.value, node: node }; } else if (attr.name.match(/^\*(.*)$/)) { attrResult.type = 'macro'; attrResult.data = { name: attr.name.match(/^\*(.*)$/)[1].toLowerCase(), code: attr.value, node: node }; if (['for', 'template'].indexOf(attrResult.data.name) !== -1) { attrResult.data.postProcess = true; } } else if (attr.name.match(/^js-.+$/)) { attrResult.type = 'attribute'; attrResult.data = { name: attr.name.substring(3), code: attr.value, inline: true, node: node }; } else if (attr.value.match(/\{\{.+\}\}/)) { attrResult.type = 'attribute'; attrResult.data = { name: attr.name, inline: true, code: attr.value, node: node }; } else { attrResult = null; } if (attrResult) { attrResult.data.buffer = null; } return attrResult; }; /** * @protected * @function * @param {JSTemplate.jsTemplate_attrResult} item * @param {(Node|Text|Array<Node|Text>|function)} value * @param {function (Error, boolean): void} cb * @param {JSTemplate.parseTextNodesConfig} config */ attrParser.update = function (item, value, config, cb) { var status = true; switch (item.type) { case "attribute": if (value === null) { if (item.data.node.hasAttribute(item.data.name)) { item.data.node.removeAttribute(item.data.name); } } else { if ( value !== item.data.buffer ) { item.data.buffer = value; item.data.node.setAttribute( item.data.name, //@ts-ignore value ); } } break; case "macro": switch (item.data.name) { case "if": if (item.data.buffer === null) { item.data.buffer = { current: item.data.node, empty: document.createTextNode(''), valid: item.data.node }; } if (value) { if (item.data.buffer.current !== item.data.buffer.valid) { item.data.buffer.current.parentElement.insertBefore( item.data.buffer.valid, item.data.buffer.current ); item.data.buffer.current.parentElement.removeChild( item.data.buffer.current ); item.data.buffer.current = item.data.buffer.valid; } } else { status = false; if (item.data.buffer.current !== item.data.buffer.empty) { item.data.buffer.current.parentElement.insertBefore( item.data.buffer.empty, item.data.buffer.current ); item.data.buffer.current.parentElement.removeChild( item.data.buffer.current ); item.data.buffer.current = item.data.buffer.empty; } } break; case "class": if (value === null) { if (item.data.buffer !== value) { item.data.buffer = value; if ( item.data.node.hasAttribute(item.data.name) ) { item.data.node.removeAttribute(item.data.name); } } } else if (typeof (value) === "string") { if (item.data.buffer !== value) { item.data.buffer = value; item.data.node.setAttribute(item.data.name, value); } } else if (typeof (value) === "object") { var name, result = []; for (name in value) { if (value[name]) { result.push(name); } } var resultValue = result.sort().join(' '); if (item.data.buffer !== resultValue) { item.data.buffer = resultValue; if (resultValue) { item.data.node.setAttribute(item.data.name, resultValue); } else if ( item.data.node.hasAttribute(item.data.name) ) { item.data.node.removeAttribute(item.data.name); } } } else { console.warn("šŸ› Incorrect value for JSTemplate:class:item ", item, "; value: ", value); } break; case "for": if (item.data.buffer === null) { var _key = ( //@ts-ignore item.data.node.attrdata.__JS_TEMPLATE._macro['key'] || { data: { code: 'key' } } ).data.code || 'key'; var _ref = ( //@ts-ignore item.data.node.attrdata.__JS_TEMPLATE._macro['ref'] || { data: { code: 'item' } } ).data.code || 'item'; item.data.buffer = { template: item.data.node, current: [], reference: document.createTextNode(''), cache: [], createNode: function (args) { var _item = { node: item.data.node.cloneNode(true), args: Object.assign({}, config.args, args) }; //@ts-ignore _item.node.renderJs( config.context, _item.args, function (err) { if (err) console.error(err); } ); item.data.buffer.reference.parentNode.insertBefore( _item.node, item.data.buffer.reference ); return _item; }, allocNode: function (args) { var _item; if (item.data.buffer.cache.length) { _item = item.data.buffer.cache.shift(); Object.assign(_item.args, args); // console.log(_item, {..._item.args}, args, config.context); _item.node.renderJs( config.context, _item.args, function (err) { if (err) console.error(err); } ); item.data.buffer.reference.parentNode.insertBefore( _item.node, item.data.buffer.reference ); } else { _item = item.data.buffer.createNode(args); } item.data.buffer.current.push(_item); return _item; }, cacheCurrent: function (length) { var _item; while (item.data.buffer.current.length > length) { _item = item.data.buffer.current.pop(); if (_item) { item.data.buffer.cache.push( _item ); _item.node.parentElement.removeChild(_item.node); } } }, update: function (value) { if (Array.isArray(value)) { value.forEach(function (_item, index) { var args = {}; args[_key] = index; args[_ref] = _item; if (!item.data.buffer.current[index]) { item.data.buffer.allocNode(args); } else { Object.assign( item.data.buffer.current[index].args, args ); item.data.buffer.current[index].node.renderJs( config.context, item.data.buffer.current[index].args, function (err) { if (err) console.error(err); } ); } }); item.data.buffer.cacheCurrent(value.length); } else if (value && typeof (value) === "object") { var _keys = Object.keys(value); _keys.sort().forEach(function (indexKey, index) { var args = {}; args[_key] = indexKey; args[_ref] = value[indexKey]; if (!item.data.buffer.current[index]) { item.data.buffer.allocNode(Object.assign({}, args)); } else { Object.assign( item.data.buffer.current[index].args, args ); // item.data.buffer.current[index].node.renderJs(); item.data.buffer.current[index].node.renderJs( config.context, item.data.buffer.current[index].args, function (err) { if (err) console.error(err); } ); } }); item.data.buffer.cacheCurrent(_keys.length); } } }; item.data.buffer.current = []; item.data.node.parentElement.insertBefore( item.data.buffer.reference, item.data.node ); item.data.node.parentElement.removeChild( item.data.node ); } item.data.buffer.update(value); break; } break; case "event": if (!item.data.buffer) { item.data.buffer = value; if (typeof (value) === "function") { //@ts-ignore item.data.node.addEventListener( item.data.name, function (event) { attrParser.value(item, config, function (err, value) { if (err) { console.error(err); return; } if (value.apply( config.context, [event] ) !== false) { config.__redraw(); } }); } ); // } else if ( // //@ts-ignore // value && typeof (value) === "object" && typeof (value.emit) === "function" // ) { // item.data.node.addEventListener( // item.data.name, // function (event, node) { // //@ts-ignore // value.emit(item.data.name, [event, node]); // } // ); } else { console.warn( "šŸ› Unknown JSTemplate:event:item destination: ", item, "; for value: ", value, ";\n\nℹ Please use a function or ApplicationPrototype Instance" ); } } break; case "binding": switch (item.data.name) { case "model": if (!item.data.buffer) { item.data.buffer = value; if (typeof (value) === "function") { /** * @private * @type {HTMLTextAreaElement} */ var node = item.data.node; switch (node.tagName.toLowerCase()) { case "input": /** * @private * @var {HTMLInputElement} node */ var inputType = node.getAttribute('type').toLowerCase(); if (inputType === "radio") { console.warn( "šŸ› Input[type=\"radio\"] JSTemplate:binding:item not supported: ", item ); break; } else if (inputType === "checkbox") { //@ts-ignore node.checked = value.apply(config.context, [{}]); node.addEventListener('change', function (event) { //@ts-ignore event.__args = config.args; //@ts-ignore event.__value = !!event.target.checked; value.apply(config.context, [event]); config.__redraw(); }); break; } else if (inputType === "file") { //@ts-ignore node.files = value.apply(config.context, [{}]); node.addEventListener('change', function (event) { //@ts-ignore event.__args = config.args; //@ts-ignore event.__value = event.target.files; value.apply(config.context, [event]); config.__redraw(); }); break; } else { /** * @private * @var {HTMLInputElement} node */ node.value = value({}); node.addEventListener('input', function (event) { //@ts-ignore event.__args = config.args; //@ts-ignore event.__value = event.target.value; value.apply(config.context, [event]); config.__redraw(); }); } break; case "textarea": /** * @private * @var {HTMLTextAreaElement} node */ node.value = value({}); node.addEventListener('input', function (event) { //@ts-ignore event.__args = config.args; //@ts-ignore event.__value = event.target.value; value.apply(config.context, [event]); config.__redraw(); }); break; case "select": /** * @private * @var {HTMLSelectElement} node */ node.value = value({}); node.addEventListener('change', function (event) { //@ts-ignore event.__args = config.args; //@ts-ignore event.__value = event.target.value; value.apply(config.context, [event]); config.__redraw(); }); break; } //@ts-ignore item.data.node.addEventListener( item.data.name, function (event) { attrParser.value(item, config, function (err, value) { if (err) { console.error(err); return; } value.apply( config.context, [event] ); }); } ); } } else { /** * @private * @type {HTMLInputElement} */ var node = item.data.node; var _newValue = null; switch (node.tagName.toLowerCase()) { case 'input': var inputType = node.getAttribute('type').toLowerCase(); if (inputType === "radio") { // TODO } else if (inputType === "checkbox") { _newValue = !!value({}); if (node.checked !== _newValue) { node.checked = _newValue; } } else if (inputType === "file") { _newValue = value({}); if (node.files !== _newValue) { node.files = _newValue; } } else { _newValue = value({}); if (node.value !== _newValue) { node.value = _newValue; } } break; case "textarea": _newValue = value({}); if (node.value !== _newValue) { node.value = _newValue; } break; case "select": _newValue = value({}); if (node.value !== _newValue) { node.value = _newValue; } break; } } break; default: if (!item.data.buffer) { item.data.buffer = true; console.warn( "🚧 In Construction JSTemplate:binding:item ", item, "; for value: ", value ); } break; } break; } cb(null, status); return status; }; /** * @protected * @function * @param {JSTemplate.jsTemplate_attrResult} item * @param {JSTemplate.parseTextNodesConfig} config * @param {function (Error, any): void} cb */ attrParser.value = function (item, config, cb) { /** * @private * @type {jsTemplate_attrResultAttributeData} */ //@ts-ignore var data = item.data; var value; if (data.inline) { var parts = []; data.code.split(config.start) .forEach(function (part) { parts.push.apply(parts, part.split(config.end)); }); value = parts.map(function (part, index) { if (index % 2) { return expressionBuilder(part, config) .apply(config.context, config.__argsValues); } return part; }).join(''); cb(null, value); } else { var expressionCall = expressionBuilder(data.code, config); /* jshint -W054 */ try { expressionCall = ( Function.apply({}, config.__argsNames.concat( [ "return (" + //@ts-ignore item.data.code + " );" ] ) ) ); } catch (er) { console.error("JSTemplate::expressionBuilder", er, item); expressionCall = function () { return [ //@ts-ignore "{{ ", er, ":\n", item.data.code, " }}" ]; }; } /* jshint +W054 */ value = expressionCall.apply(config.context, config.__argsValues); if ( value && typeof (value) === "object" && typeof (value.then) === "function" && typeof (value.catch) === "function" ) { value.then(function (result) { cb(null, result); }, function (err) { cb(err, null); }); } else if (typeof(value) === "function") { cb(null, function () { return value.apply(config.context, arguments); }); } else { cb(null, value); } } }; /** * @protected * @callback nodeParserCallback * @memberof JSTemplate * @param {Error} err * @param {JSTemplate.parseTextNodesConfig} config * @returns {JSTemplate.parseTextNodesConfig} */ /** * @protected * @callback JSTemplateParseContent * @memberof JSTemplate * @param {HTMLElement} nodeElement * @param {JSTemplate.nodeParserCallback} cb * @param {JSTemplate.parseTextNodesConfig} config * @returns {JSTemplate.parseTextNodesConfig} */ /** * @protected * @function * @memberof JSTemplate * @param {HTMLElement} nodeElement * @param {JSTemplate.nodeParserCallback} cb * @param {JSTemplate.parseTextNodesConfig} config * @returns {JSTemplate.parseTextNodesConfig} */ var nodeParser = function (nodeElement, cb, config) { if (typeof (cb) !== "function") cb = function () {}; if (!config) { config = { args: {}, context: {}, RENDER_FPS: module.exports.config.RENDER_FPS || 15, REMOVE_EMPTY_NODES: module.exports.config.REMOVE_EMPTY_NODES || false }; } else { config.args = (config.args || {}); config.context = (config.context || {}); config.RENDER_FPS = config.RENDER_FPS || module.exports.config.RENDER_FPS || 15; config.REMOVE_EMPTY_NODES = ( typeof (config.REMOVE_EMPTY_NODES) === "boolean" ? config.REMOVE_EMPTY_NODES : module.exports.config.REMOVE_EMPTY_NODES ); } var argsNames = []; var argsValues = [];; ((function (o) { if (o && typeof (o) === "object") { var i; for (i in o) { argsNames.push(i); argsValues.push(o[i]); } } })(config.args)); /** * @private * @function * @param {(HTMLElement)} nodeElement * @returns {JSTemplate.jsTemplateAttrData} */ var ate = function (nodeElement) { //@ts-ignore nodeElement.attrdata.__JS_TEMPLATE = nodeElement.attrdata.__JS_TEMPLATE || { target: nodeElement, nodes: [], texts: [], children: [], _macro: {}, HAS_POST_PROCESS: false }; //@ts-ignore nodeElement.attrdata.__JS_TEMPLATE.nodes = nodeElement.attrdata.__JS_TEMPLATE.nodes || []; //@ts-ignore nodeElement.attrdata.__JS_TEMPLATE.texts = nodeElement.attrdata.__JS_TEMPLATE.texts || []; /** * @private * @type {JSTemplate.jsTemplateAttrData} */ //@ts-ignore var __JS_TEMPLATE = nodeElement.attrdata.__JS_TEMPLATE; var i; /** * @private * @type {JSTemplate.jsTemplate_attrResult} */ var attrResult; //@ts-ignore for (i = 0; i < nodeElement.attributes.length; i++) { //@ts-ignore attrResult = attrParser(nodeElement.attributes[i], nodeElement); if ( attrResult ) { __JS_TEMPLATE.nodes.push(attrResult); if (attrResult.type === 'macro') { __JS_TEMPLATE._macro[attrResult.data.name] = attrResult; } if (attrResult.data.postProcess) { __JS_TEMPLATE.HAS_POST_PROCESS = true; } } } __JS_TEMPLATE.nodes.forEach(function (item) { nodeElement.removeAttribute(item.attr.name); }); if (__JS_TEMPLATE.HAS_POST_PROCESS) { cb(null, __JS_TEMPLATE); return __JS_TEMPLATE; } /** * Allowed Types * * Node.ELEMENT_NODE, * Node.TEXT_NODE, * Node.CDATA_SECTION_NODE, * // PROCESSING_INSTRUCTION_NODE * Node.COMMENT_NODE, * // Node.DOCUMENT_NODE, * // Node.DOCUMENT_TYPE_NODE, * // Node.DOCUMENT_FRAGMENT_NODE */ /** * @private * @type {NodeList} */ var children = Array.prototype.slice.call(nodeElement.childNodes) .filter(function ( /** @type {Node} */ node) { if ( node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE || node.nodeType === Node.COMMENT_NODE ) { // Text Content Parsing } if (node.nodeType === Node.ELEMENT_NODE) { return true; } return false; }); children.forEach(function (node) { //@ts-ignore if (!__JS_TEMPLATE.children.find(function (item) { return item.target === node; })) { __JS_TEMPLATE.children.push(ate(node)); } }); parseTextNodes(nodeElement, function (err, data) { if (err) { console.error("JSTemplate::parseTextNodes ", err); } data.textNodes.forEach(function (itemText) { __JS_TEMPLATE.texts.push(itemText); }); if (config.REMOVE_EMPTY_NODES) { __JS_TEMPLATE.children = __JS_TEMPLATE.children.filter( function (item) { return (item.children.length || item.nodes.length || item.texts.length); } ); } if (cb) { cb(null, __JS_TEMPLATE); } }, config); // console.log("šŸš€", __JS_TEMPLATE); return __JS_TEMPLATE; }; var _time = new Date().valueOf(); var _timer = null; var _timerTime = 0; var _methods = { __requireRedraw: null, __renderStarted: false, items: ate(nodeElement), _config: config, redraw: function (cb, context, args) { var DEBUG_MODE = 0; if (!cb) cb = function () { if (DEBUG_MODE) { console.log("JSTemplate Redraw", nodeElement); } }; var _callbackProtection = function (callback) { if (!DEBUG_MODE) return callback; var _calls = 0; return function (a0, a1, a2, a3) { if (_calls) { _calls++; console.warn('Multiple callback arguments ', arguments); throw Error('Multiple callback calls ' + _calls); } _calls++; callback(a0, a1, a2, a3); }; }; cb = _callbackProtection(cb); if (typeof (context) !== "undefined") { config.context = context; } if (typeof (args) === "object" && args) { config.args = args; } var time = new Date().valueOf(); var timerWaitedTime = 0; if (_timer !== null) { timerWaitedTime = time - _timerTime; if (DEBUG_MODE) { console.warn("šŸŽØ JSTemplate::Redraw Replaces; another render request waited ", timerWaitedTime, "ms", { node: nodeElement }); } } var _delay = Math.max( 0, Math.max(1000 / config.RENDER_FPS - (time - _time), 0) - timerWaitedTime ); if (_methods.__renderStarted) { if (DEBUG_MODE) { console.warn( "šŸŽØ JSTemplate::Redraw Omitted: ", _delay, { node: nodeElement } ); } if (_methods.__requireRedraw) { _methods.__requireRedraw.cb(Error('JSTemplate::Redraw Omitted')); } _methods.__requireRedraw = { cb: cb, context: context, args: args }; // @TODO Check return; } if (_timer) { clearTimeout(_timer); _timer = null; } _timerTime = time; _timer = setTimeout(function () { if (_methods.__renderStarted) { return false; } _methods.__renderStarted = true; _timer = null; config.__argsNames = []; config.__argsValues = []; ((function (o) { if (o && typeof (o) === "object") { var i; for (i in o) { config.__argsNames.push(i); config.__argsValues.push(o[i]); } } })(config.args)); /** * @function * @private * @param {JSTemplate.jsTemplateAttrData} item * @param {function():void} cb */ var renderItem = function (item, cb) { cb = _callbackProtection(cb); var renderStop = false; var renderChildren = true; var handleError = function (instance) { if (instance && Array.isArray(instance.errors) && instance.errors.length) { instance.errors.forEach(function (err) { console.error('JSTemplate::itemChild error: ', err); }); } }; if (DEBUG_MODE >= 2) { console.log(" ⬇ Render Items"); } if (item.HAS_POST_PROCESS) { renderChildren = false; }; libs.async.forEach( item.nodes, function (next, itemNode) { next = _callbackProtection(next); if (DEBUG_MODE >= 3) { console.log(" āž” Item", itemNode); } if (renderStop || (item.HAS_POST_PROCESS && !itemNode.data.postProcess)) { next(); return; } attrParser.value( itemNode, config, function (err, value) { if (err) { //@ts-ignore err.item = itemNode; next(err); renderStop = true; } else { attrParser.update( itemNode, value, config, function (err, state) { if (err) { //@ts-ignore err.item = itemNode; renderStop = true; next(err); if (DEBUG_MODE >= 3) { console.log("āŒ renderStop = ", true); } } else { if (!state) { if (DEBUG_MODE >= 3) { console.log("āŒ renderChildren = ", false); } renderChildren = false; } next(); } } ); } } ); }, function () { handleError(this); if (!renderChildren || renderStop) { cb(); } else { if (DEBUG_MODE >= 2) { console.log(" ⬇ Render Texts"); } libs.async.forEach( item.texts, function (next, itemText) { next = _callbackProtection(next); if (DEBUG_MODE >= 3) { console.log(" āž” Text", itemText); } textParser.value( itemText, config, function (err, value) { if (err) { //@ts-ignore err.item = itemText; next(err); } else { textParser.update( itemText, value, function (err) { if (err) { //@ts-ignore err.item = itemText; next(err); } else { next(); } } ); } } ); }, function () { handleError(this); if (!renderChildren || renderStop) { cb(); } else { if (DEBUG_MODE >= 2) { console.log(" ⬇ Render Children"); } if (!item.children.length) { cb(); return; } libs.async.forEach( item.children, function (next, itemChild) { next = _callbackProtection(next); if (DEBUG_MODE >= 3) { console.log(" āž” Child", itemChild); } renderItem(itemChild, next); }, function () { handleError(this); cb(); } ).on('error', function (err) { console.error('JSTemplate::itemChild error: ', err); }); } } ).on('error', function (err) { console.error('JSTemplate::itemText error: ', err); }); } } ).on('error', function (err) { console.error('JSTemplate::itemNode error: ', err); }); }; if (DEBUG_MODE) { console.info("šŸŽØ JSTemplate::Start delay: ", _delay, { node: nodeElement }); } var sTime = new Date().valueOf(); try { renderItem( _methods.items, function () { _methods.__renderStarted = false; if (_methods.__requireRedraw) { _methods.redraw(_methods.__requireRedraw.cb, _methods.__requireRedraw.context, _methods.__requireRedraw.args); _methods.__requireRedraw = null; } if (DEBUG_MODE) { console.info("šŸŽØ JSTemplate::Finish delay: ", (Math.floor(_delay * 100) / 100), "; time: ", (new Date().valueOf() - sTime), "ms;", { node: nodeElement }); } } ); } catch (err) { _methods.__renderStarted = false; if (_methods.__requireRedraw) { _methods.redraw(_methods.__requireRedraw.cb, _methods.__requireRedraw.context, _methods.__requireRedraw.args); _methods.__requireRedraw = null; } if (DEBUG_MODE) { console.warn("šŸŽØ JSTemplate::Render finished with error: ", (Math.floor(_delay * 100) / 100), "; time: ", (new Date().valueOf() - sTime), "ms;", { node: nodeElement }); } } }, _delay); } }; config.__redraw = function () { _methods.redraw(); }; _methods.redraw(); return _methods; }; //@ts-ignore Element.prototype.renderJs = function (context, args, cb) { //@ts-ignore this.renderJsTemplate(context, args, cb); }; //@ts-ignore Element.prototype.renderJsTemplate = function (context, args, cb) { //@ts-ignore if (!this.__renderContent) { //@ts-ignore this.__renderContent = module.exports.parseContent(this, cb, { context: (context || this), args: (args || {}) }); } else { //@ts-ignore this.__renderContent.redraw(cb, context, args); } }; //@ts-ignore Element.prototype.renderJsArgs = function (context, args, cb) { //@ts-ignore if (!this.__renderContent) { console.log("⚠ Deprecated: Element.prototype.renderJsArgs"); //@ts-ignore this.__renderContent = module.exports.parseContent(this, cb, { context: (context || this), args: (args || {}) }); } else { //@ts-ignore this.__renderContent.redraw(cb, context, args); } }; // @ts-ignore Application.require( [ "extensions/prototype", "async" ] ).then(function () { libs = arguments[0]; module.exports = { parseContent: nodeParser, parseAttributes: nodeParser, config: { RENDER_FPS: 15, REMOVE_EMPTY_NODES: true } }; });