UNPKG

dabbjs

Version:

general javascript library

171 lines (170 loc) 7.46 kB
/** * Template engine */ class Templates { /** * gets a template * @param key template's key * @returns template text */ static get(key) { return Templates.map.get(key); } /** * saves a template * @param key template's key * @param value template's text */ static set(key, value) { Templates.map.set(key, value); } /** * returns a mount of registered templates */ static get size() { return Templates.map.size; } /** * register a json key-value pair with templates * @param obj key-value pair of template's key with it's text */ static register(obj) { for (let key in obj) { Templates.set(key, obj[key]); } } /** * simple template parser * @param key template's key name * @param obj object to get values from */ static nano(key, obj) { let str = Templates.get(key); return str.replace(/\{\{\s*([\w\.]*)\s*\}\}/g, (_n, t) => { for (var arr = t.split("."), f = obj[arr.shift()], i = 0, len = arr.length; f && len > i; i++) f = f[arr[i]]; return "undefined" != typeof f && null !== f ? f : "null"; }); } /** * full template parser * @param key template's key name * @param obj object to get values from */ static parse(key, obj, beautify) { let xml = XML.parse(Templates.get(key), "text/html"), nsMap = new Map(), getValue = (ns, o) => { for (var arr = ns.split("."), f = o[arr.shift()], i = 0, len = arr.length; f && len > i; i++) f = f[arr[i]]; return f; }, getMappedValue = (ns) => { let value = nsMap.get(ns); !value && nsMap.set(ns, value = getValue(ns, obj)); return value; }, processNode = (node, rootName, arr, ndx) => { let isMatch = false, attributes = Array.from(node.attributes), parseContent = (str) => { let regex = /\{\{\s*([\w\.]*)\s*\}\}/g, match, parsed = "", index = 0; while (match = regex.exec(str)) { parsed += str.substr(index, match.index - index); isMatch = true; if (rootName == match[1]) parsed += arr[ndx]; else if (rootName && match[1].startsWith(rootName + '.')) { parsed += getValue(match[1].substr(rootName.length + 1), arr[ndx]); } else { parsed += getMappedValue(match[1]); } index = match.index + match[0].length; } parsed += str.substr(index, str.length - index); return parsed; }; for (let i = 0; i < attributes.length; i++) { let attr = attributes[i], attrName = attr.name, isIndex = attrName == 'd-for-ndx', removeUndefined = attrName.endsWith('?'), value; isMatch = false; isIndex ? (attrName = attr.value, value = ndx) : (value = parseContent(attr.value), removeUndefined && (attrName = attrName.substr(0, attrName.length - 1))); if (removeUndefined || isIndex) { node.removeAttribute(attr.name); if (value != "undefined") node.setAttribute(attrName, value); } else isMatch && (attr.value = value); } if (!node.children.length) { node.firstChild && (node.firstChild.nodeValue = parseContent(node.firstChild.nodeValue)); } else processChildren(node, rootName, arr, ndx); }, processChild = (child, rootName, arr, ndx) => { var _a; let _for = child.getAttribute('d-for'); if (_for) { if (rootName) throw new Error('nested @for not supported'); child.removeAttribute('d-for'); let match = _for.match(/(\w*)\s+in\s+(\w*)/), array = match ? obj[match[2]] : void 0; if (!array) throw new Error('invalid %for template'); array.forEach((_item, ndx) => { var _a; let node = child.cloneNode(true); (_a = child.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(node, child); processNode(node, match[1], array, ndx); }); (_a = child.parentElement) === null || _a === void 0 ? void 0 : _a.removeChild(child); return array.length; } else { processNode(child, rootName, arr, ndx); return 1; } }, processChildren = (parent, rootName, arr, ndx) => { for (let i = 0, child = parent.children[i]; i < parent.children.length; child = parent.children[i]) { i += processChild(child, rootName, arr, ndx); } }; processChildren(xml.body, void 0, void 0, void 0); return beautify ? XML.prettify(xml.body.firstChild) : xml.body.innerHTML; } } /** * map of all templates */ Templates.map = new Map(); //https://gist.github.com/max-pub/a5c15b7831bbfaba7ad13acefc3d0781 const XML = { parse: (str, type = 'text/xml') => new DOMParser().parseFromString(str, type), // like JSON.parse stringify: (DOM) => new XMLSerializer().serializeToString(DOM), // like JSON.stringify transform: (xml, xsl) => { let proc = new XSLTProcessor(); proc.importStylesheet(typeof xsl == 'string' ? XML.parse(xsl) : xsl); let output = proc.transformToFragment(typeof xml == 'string' ? XML.parse(xml) : xml, document); return typeof xml == 'string' ? XML.stringify(output) : output; // if source was string then stringify response, else return object }, minify: (node) => XML.toString(node, false), prettify: (node) => XML.toString(node, true), toString: (node, pretty, level = 0, singleton = false) => { if (typeof node == 'string') node = XML.parse(node); let tabs = pretty ? Array(level + 1).fill('').join('\t') : '', newLine = pretty ? '\n' : ''; if (node.nodeType == 3) { let nodeContent = (singleton ? '' : tabs) + node.textContent.trim(); return nodeContent.trim() ? nodeContent + (singleton ? '' : newLine) : ""; } if (!node.tagName) return XML.toString(node.firstChild, pretty); let output = tabs + `<${node.tagName}`; // >\n for (let i = 0; i < node.attributes.length; i++) output += ` ${node.attributes[i].name}="${node.attributes[i].value}"`; if (node.childNodes.length == 0) return output + ' />' + newLine; else output += '>'; let onlyOneTextChild = ((node.childNodes.length == 1) && (node.childNodes[0].nodeType == 3)); if (!onlyOneTextChild) output += newLine; for (let i = 0; i < node.childNodes.length; i++) output += XML.toString(node.childNodes[i], pretty, level + 1, onlyOneTextChild); return output + (onlyOneTextChild ? '' : tabs) + `</${node.tagName}>` + newLine; } }; export { Templates, XML };