UNPKG

lazo-next

Version:

A client-server web framework built on Node.js that allows front-end developers to easily create a 100% SEO compliant, component MVC structured web application with an optimized first page load.

157 lines (129 loc) 5.68 kB
define(['underscore', 'utils/treeMixin'], function (_, tree) { 'use strict'; return _.extend({ // Matches an HTML tag in a string of HTML based on an attribute and value. getInsertIndex: function (attr, val, html) { var regex = new RegExp('<[^<]*' + attr + '+=["||\']' + val + '["||\']+.*?>'); return html.match(regex); }, getTreeHtml: function (tree, nodeId, nodeType, callback) { var rootNode = tree; var self = this; this._getNodesHtml(rootNode, function (tree) { var html; // view called render; strip off surrounding component markup if (nodeType === 'view') { // null out the parent for view rendering tree.parent = null; html = self._renderTree(tree); var match = self.getInsertIndex(self._attrs.cmpId, rootNode.cid, html); html = html.substr(match[0].length, (html.length - (match[0].length + self.closingTagLength))); } else { html = self._renderTree(tree); } callback(html); }); }, closingTagLength: 6, _renderTree: function (component) { var self = this; return (function render(node, html) { html = self._assembleNodeHtml(node, html); if (_.size(node.children)) { var children = node.children; for (var k in children) { for (var i = children[k].length - 1; i >= 0; i--) { html = render(children[k][i], html); } } } return html; })(component, ''); }, _assembleNodeHtml: function (component, html) { var match; var siblings = this._getCmpSiblings(component); var cmpHtml = component.html.substr(0, (component.html.length - this.closingTagLength)) + component.currentView.html + '</div>'; if (siblings && siblings.nodes.length) { var i = 0; while (!match && siblings.nodes[i]) { // find index of last cmp added to parent container match = this.getInsertIndex(this._attrs.cmpId, siblings.nodes[i].cid, html); if (match) { return html.substr(0, match.index) + cmpHtml + html.substr(match.index); } i++; } } if (!match && siblings && siblings.container) { match = this.getInsertIndex(this._attrs.compContainerName, siblings.container, html); } if (!match && component.parent && siblings && siblings.nodes.length > 1) { var errMsg = 'component ' + component.name; throw 'The parent node for ' + errMsg + ' was not found.'; // root component or render called from a view } else if (!match && (!component.parent || (component.parent && siblings && siblings.nodes.length === 1))) { return cmpHtml; } var open = html.substr(0, match.index + match[0].length); var close = html.substr(match.index + match[0].length); return open + cmpHtml + close; }, _getCmpSiblings: function (component) { var children = component.parent && component.parent.children; var siblings; for (var k in children) { if ((siblings = _.find(children[k], function (cmp) { return cmp.cid === component.cid; }))) { siblings = { container: k, nodes: children[k] }; break; } } return siblings; }, _getNodesHtml: function (tree, callback) { var self = this; var nodesToBeProcessed = 0; var nodesProcessed = 0; function getNodeHtml(node, callback) { var html; if (self.getNodeType(node) === 'component') { callback('<div ' + self._attrs.cmpName + '="' + node.name + '" ' + self._attrs.cmpId + '="' + node.cid + '"></div>'); } else { node.getHtml({ error: function (err) { throw err; }, success: function (html) { callback(html); } }); } } (function processNode(node) { var children = self.getNodeChildren(node); nodesToBeProcessed++; if (children.length) { for (var i = 0; i < children.length; i++) { processNode(children[i]); } } getNodeHtml(node, function (html) { node.html = html; getNodeHtml(node.currentView, function (html) { node.currentView.html = html; nodesProcessed++; if (nodesProcessed === nodesToBeProcessed) { callback(tree); } }); }); })(tree); } }, tree); });