Permalink
Browse files

Added support for server side HTML generation

  • Loading branch information...
1 parent be10ae1 commit 317d3d55f3e38327698bafcfc25d5a777d99400e @kriszyp committed Aug 22, 2011
Showing with 145 additions and 6 deletions.
  1. +114 −0 html-put.js
  2. +16 −6 put.js
  3. +15 −0 test/html-put.js
View
@@ -0,0 +1,114 @@
+var put = require('./put');
+put.indentation= ' ';
+function Element(tag){
+ this.tag = tag;/*
+ if(tag){
+ this.push((tag == 'html' ? '<!DOCTYPE html>\n<'
+ : '<') + tag, new NamedNodeMap(), '>', new NodeList(), ('</' + tag + '>'));
+ }else{
+ // a fragment, just get the indexes right
+ this.push('', '', '', new NodeList());
+ } */
+}
+var prototype = Element.prototype = [];
+prototype.nodeType = 1;
+var currentIndentation = '';
+prototype.toString = function(){
+ if(put.indentation){
+ // using pretty printing with indentation
+ var lastIndentation = currentIndentation;
+ currentIndentation += put.indentation;
+ var html = (this.tag == 'html' ? '<!DOCTYPE html>\n<html' : '\n' + lastIndentation + '<' + this.tag) +
+ (this.attributes ? this.attributes.join('') : '') +
+ (this.className ? ' class="' + this.className + '"' : '') +
+ (this.innerHTML ? '>' + this.innerHTML.join('') +
+ (!this.mixed ? '\n' +lastIndentation : '') + '</' + this.tag + '>' : ' />');
+
+ currentIndentation = lastIndentation;
+ return html;
+ }
+/* if(this.toStringed){
+ // undo last toString modificatiosn
+ }
+ var className = this.className;
+ if(className){
+ this.setAttribute("class", className);
+ }
+ var html = this.join('');
+ this.toStringed = true;*/
+ return (this.tag == 'html' ? '<!DOCTYPE html>\n<html' : '<' + this.tag) +
+ (this.attributes ? this.attributes.join('') : '') +
+ (this.className ? ' class="' + this.className + '"' : '') +
+ (this.innerHTML ? '>' + this.innerHTML.join('') + '</' + this.tag + '>' : ' />');
+};
+prototype.children = false;
+prototype.attributes = false;
+prototype.setAttribute = function(name, value, escape){
+ var attributes = this.attributes;
+ if(!attributes){
+ attributes = this.attributes = [];
+ }
+ attributes.push(' ' + name + '="' + value + '"');
+};
+prototype.insertBefore = function(child, reference){
+ child.parentNode = this;
+ var children = this.innerHTML;
+ if(!children){
+ children = this.innerHTML = [];
+ }
+ if(reference){
+ for(var i = 0, l = children.length; i < l; i++){
+ if(reference == children[i]){
+ child.nextSibling = reference;
+ if(i > 0){
+ children[i-1].nextSibling = child;
+ }
+ return children.splice(i, 0, child);
+ }
+ }
+ }
+ if(children.length > 0){
+ children[children.length-1].nextSibling = child;
+ }
+ children.push(child);
+};
+prototype.appendChild = function(child, reference){
+ var children = this.innerHTML;
+ if(!children){
+ children = this.innerHTML = [];
+ }
+ if(typeof child == "string"){
+ this.mixed = true;
+ }
+ children.push(child);
+};
+function NamedNodeMap(){
+}
+NamedNodeMap.prototype = [];
+NamedNodeMap.prototype.toString = function(){
+ return this.join('');
+};
+function NodeList(){
+}
+NodeList.prototype = [];
+NodeList.prototype.toString = function(){
+ return this.join('');
+};
+
+var lessThanRegex = /</g, ampersandRegex = /&/g;
+put.setDocument({
+ createElement: function(tag){
+ return new Element(tag);
+ },
+ createTextNode: function(value){
+ return (typeof value == 'string' ? value : ('' + value)).replace(lessThanRegex, "&lt;").replace(ampersandRegex, "&amp;");
+ },
+ createDocumentFragment: function(){
+ return new Element();
+ }
+}, { // fragment heuristic
+ test: function(){
+ return false;
+ }
+});
+module.exports = put;
View
22 put.js
@@ -12,8 +12,9 @@ define([], function(){
var selectorParse = /(([-+])|[,<> ])?\s*(\.|!|#)?([-\w$]+)?(?:\[([^\]=]+)=?['"]?([^\]'"]*)['"]?\])?/g,
fragmentFasterHeuristic = /[-+,> ]/, // if it has any of these combinators, it is probably going to be faster with a document fragment
- doc = document, undefined;
+ doc, undefined;
try{
+ doc = document;
var ieCreateElement = 1;
put('i', {name:'a'});
}catch(e){
@@ -23,7 +24,7 @@ define([], function(){
element.appendChild(doc.createTextNode(text));
}
function put(topReferenceElement){
- var fragment, returnValue, lastArgWasSelector, nextSibling, referenceElement, current,
+ var fragment, returnValue, lastSelectorArg, nextSibling, referenceElement, current,
args = arguments;
function insertLastElement(){
// we perform insertBefore actions after the element is fully created to work properly with
@@ -47,7 +48,6 @@ define([], function(){
var argument = args[i];
if(typeof argument == "object"){
if(argument.nodeType){
- lastArgWasSelector = false;
current = argument;
insertLastElement();
referenceElement = argument;
@@ -58,7 +58,7 @@ define([], function(){
current[key] = argument[key];
}
}
- }else if(lastArgWasSelector){
+ }else if(lastSelectorArg === i - 1){
// a text node should be created
// take a scalar value, use createTextNode so it is properly escaped
// createTextNode is generally several times faster than doing an escaped innerHTML insertion: http://jsperf.com/createtextnode-vs-innerhtml/2
@@ -68,7 +68,7 @@ define([], function(){
// if we are starting with a selector, there is no top element
topReferenceElement = null;
}
- lastArgWasSelector = true;
+ lastSelectorArg = i;
var leftoverCharacters = argument.replace(selectorParse, function(t, combinator, siblingCombinator, prefix, value, attrName, attrValue){
if(combinator){
// insert the last current object
@@ -179,8 +179,18 @@ define([], function(){
return returnValue;
}
put.defaultTag = "div";
+ put.setDocument = function(document, newFragmentHeuristic){
+ doc = document;
+ fragmentFasterHeuristic = newFragmentHeuristic || fragmentFasterHeuristic;
+ }
return put;
});
})(typeof define == "undefined" ? function(deps, factory){
- put = factory();
+ if(typeof module == "undefined"){
+ // plain script
+ put = factory();
+ }else{
+ // CommonJS, probably NodeJS
+ module.exports = factory();
+ }
} : define);
View
@@ -0,0 +1,15 @@
+var assert = require("assert"),
+ put = require("../html-put");
+exports.testSimple = function() {
+ console.log(put('div span.test<').toString());
+};
+exports.testHTML = function() {
+ var page = put('html');
+ put(page, 'head script[src=test.js]');
+ var content = put(page, 'body div.header $+div.content', 'Hello World');
+ put(content, 'div.left', 'Left');
+ put(content, 'div.right', {innerHTML: ['Right']});
+ console.log(page.toString());
+};
+if (require.main === module)
+ require("patr/runner").run(exports);

0 comments on commit 317d3d5

Please sign in to comment.