jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
A limited comparison of some popular JavaScript templating engines on a short template: 6 header tags, and 10 list items. Compared templating engines:
Same as 412 but excludes DOM node generation.
<script>
/*
|-----------------------------------------------------------------------------|
| jsToolkit 2.0 |
|-----------------------------------------------------------------------------|
| Developed By: |
| Joshua Blackburn |
| |
|-----------------------------------------------------------------------------|
| JavaScript tools to increase productivity |
| IE 6 |
|-----------------------------------------------------------------------------|
| September 2004 | jsToolkit 1.0 |
| October 2005 | jsToolkit 2.0. Added Perl like functions map/grep. |
|-----------------------------------------------------------------------------|
*/
var jss = {
block : '',
obj : '',
params : {},
page : ''
};
function vGetParams() {
var d = document.URL.split('?'), pn = window.location.pathname;
var dn = pn.substring(pn.lastIndexOf('/') + 1);
jss.page = dn.replace(/[\.\:\\]/g,'');
if ( d[1] ) {
var argList = d[1].split('&');
var vars = [];
if ( typeof(argList) == 'object' ) {
for ( var stuff in argList ) {
vars = argList[stuff].split('=');
//vars = d[1].split('=');
jss.params[vars[0]] = vars[1];
}
} else {
vars = d[1].split('=');
jss.params[vars[0]] = vars[1];
}
//jss.page = dn.replace('.', '');
return jss.params;
}
}
function vCount() {
var obj = arguments[0];
var i = 0;
for ( var c in obj ) {
if ( c == 'toXMLRPC' ) continue;
i++;
}
obj = null;
return i;
}
function stringToHash(str, del1, del2) {
var argList = str.split(del1);
var vars = [], obj = {};
if ( typeof(argList) == 'object' ) {
for ( var stuff in argList ) {
vars = argList[stuff].split(del2);
//vars = d[1].split('=');
obj[vars[0]] = vars[1];
}
} else {
vars = str.split(del2);
obj[vars[0]] = vars[1];
}
return obj;
}
// Converts a hash to a string with given delimeters. delimeters default to = (del1) and & (del2)
function hashToString(obj, del1, del2) {
var str = [];
var count = vCount(obj), counter = 1;
for ( var i in obj ) {
if ( typeof obj[i] == 'string' || typeof obj[i] == 'number' ) str.push(i + (del1 || "=") + obj[i] + (counter != count ? (del2 || "&") : ''));
++counter;
}
return str.join('');
}
function vTypeof( obj ) {
var vType = typeof obj;
if( vType == "function" ) {
var vFunction = obj.toString();
if( ( /^\/.*\/[gi]??[gi]??$/ ).test( vFunction ) ) {
return "regex";
} else if( ( /^\[object.*\]$/i ).test( vFunction ) ) {
vType = "object"
}
}
if( vType != "object" ) {
return vType;
}
switch( obj ) {
case null:
return "null";
case window:
return "window";
case window.event:
return "event";
}
if( window.event && ( event.type == obj.type ) ) {
return "event";
}
try {
if ( obj.join(',') ) return "array";
} catch(e) {}
var fConstructor = obj.constructor;
if( fConstructor != null ) {
switch( fConstructor ) {
case Array:
vType = "array";
break;
case Date:
return "date";
case RegExp:
return "regex";
case Object:
vType = "object";
break;
case ReferenceError:
return "error";
default:
var sConstructor = fConstructor.toString();
var aMatch = sConstructor.match( /\s*function (.*)\(/ );
if( aMatch != null ) {
return 'object';
}
}
}
var nNodeType = obj.nodeType;
if( nNodeType != null ) {
switch( nNodeType ) {
case 1:
if( obj.item == null ) {
return "domelement";
}
break;
case 3:
return "textnode";
}
}
if( obj.toString != null ) {
var sExpression = obj.toString();
var aMatch = sExpression.match( /^\[object (.*)\]$/i );
if( aMatch != null ) {
var sMatch = aMatch[ 1 ];
switch( sMatch.toLowerCase() ) {
case "event":
return "event";
case "math":
return "math";
case "error":
return "error";
case "mimetypearray":
return "mimetypecollection";
case "pluginarray":
return "plugincollection";
case "windowcollection":
return "window";
case "nodelist":
case "htmlcollection":
case "elementarray":
return "domcollection";
}
}
}
if( obj.moveToBookmark && obj.moveToElementText ) {
return "textrange";
} else if( obj.callee != null ) {
return "array";
} else if( obj.item != null ) {
return "domcollection";
}
return vType;
}
function rtrim(str) {
try {
return str.replace(/\s+$/,'');
} catch(e) { return ''; }
}
function ltrim(str) {
try {
return str.replace(/^\s+/,'');
} catch(e) { return ''; }
}
function trim(str) {
try {
return str.replace(/(^\s+|\s+$)/g, '');
} catch(e) { return ''; }
}
// Check if a value is in an array
function contains(ary, val) {
//if ( vTypeof(ary) != 'array' || vTypeof(ary) != 'arguments' ) return false;
if ( vTypeof(val) == 'object' ) {
// Extract first key and value from the value and assign to temp array
var v = [];
for ( var m in val ) {
v[0] = m;
v[1] = val[m];
break;
}
// Iterate over array and check for hashes
for ( var i = 0; i < ary.length; i++ ) {
if ( vTypeof(ary[i]) == 'object' ) {
for ( var j in ary[i] ) {
//alert("j: " + j + " val: " + ary[i][j] + " *" + v[0] + "**" + v[1] + "*");
if ( j == v[0] ) {
if ( trim(ary[i][j]) == trim(v[1]) ) return ( i + 1);
}
}
}
}
} else {
for ( var i = 0; i < ary.length; i++ ) {
if ( ary[i] == val ) return (i + 1);
}
}
return -1;
}
function vGet(id) {
return document.getElementById(id);
}
document.getElementsByClassName = function(cName) {
var domE;
if ( arguments[1] ) {
if ( vTypeof(arguments[1]) == 'string' ) {
if ( document.getElementById(arguments[1]) ) {
domE = document.getElementById(arguments[1]).getElementsByTagName("*");
} else { return false; }
} else {
domE = arguments[1];
}
} else {
if ( document.all ) {
domE = document.all;
} else {
domE = document.getElementsByTagName("*");
}
}
var yourElements = [];
//for ( var a = domE.length - 1; a >= 0; a--) {
for ( var a in domE ) {
if ( ( new RegExp("^(?:[^\\s]+\\s)*"+cName+"(?:\\s.+)?$") ).test(domE[a].className ) ) {
yourElements[yourElements.length] = domE[a];
}
}
domE = null;
return yourElements;
}
document.getElementsByID = function(cName) {
var domE;
arguments[1] ? ( vTypeof(arguments[1]) == 'string' ? domE = (document.getElementById(arguments[1]) ? document.getElementById(arguments[1]).getElementsByTagName("*") : null) : domE = arguments[1] ) : ( document.all ? domE = document.all : domE = document.getElementsByTagName("*") );
if ( vTypeof(domE) == 'null' ) return false;
var yourElements = [];
for ( var a = 0; a < domE.length; a++) {
if (domE[a].id == cName) {
yourElements[yourElements.length] = domE[a];
}
}
return yourElements;
}
document.remove = function() {
$map(arguments, function (i) { $(i).parentNode.removeChild( $(i) ); });
}
function $() {
//From prototype
var ary = [];
for ( var i = 0; i < arguments.length; i++ ) {
if ( typeof arguments[i] == 'string' ) {
if ( arguments.length == 1) {
return document.getElementById(arguments[i]);
} else {
ary.push(document.getElementById(arguments[i]));
}
}
}
return ary;
}
function $map(ary) {
var mapped = [];
var actions = arguments;
for (var i = 0; i < ary.length; i++ ) {
for ( var x = 0; x < actions.length; x++ ) {
if ( x != 0 ) try{mapped.push(actions[x](ary[i], i));}catch(e){return ary;}
}
}
return mapped;
}
function $grep(ary, action) {
var mapped = [];
var result;
for (var i = 0; i < ary.length; i++ ) {
try {
result = action(ary[i], i);
if ( result ) mapped.push(result);
} catch(e){return ary;};
}
return mapped;
}
function $mapHash(obj, action) {
var mapped = {};
var actions = arguments, counter = 0, key;
for (var i in obj ) {
for ( var x = 0; x < actions.length; x++ ) {
if ( x != 0 )
try {
key = i;
if ( x > 1 ) {
while ( mapped[key] ) {
key = i + counter;
++counter;
}
counter = 0;
}
mapped[key] = actions[x](obj[i], i);
} catch(e){return obj;}
}
}
return mapped;
}
function $grepHash(obj, action) {
var mapped = {};
var result;
for (var i in obj ) {
try {
result = action(obj[i], i);
if ( result ) mapped[i] = result;
} catch(e){return obj;};
}
return mapped;
}
function $dcopy(obj) {
var objType = vTypeof(obj);
var temp;
switch ( objType ) {
case 'array':
temp = [];
for ( var i = 0; i < obj.length; i++ ) {
if ( vTypeof(obj[i]) == 'array' || vTypeof(obj[i]) == 'object' ) {
temp.push($dcopy(obj[i]));
} else {
temp.push(obj[i]);
}
}
return temp;
break;
case 'object':
temp = {};
for ( var i in obj ) {
if ( vTypeof(obj[i]) == 'array' || vTypeof(obj[i]) == 'object' ) {
temp[i] = $dcopy(obj[i]);
} else {
temp[i] = obj[i];
}
}
return temp;
default:
return obj;
}
}
function $concat(obj) {
var mapped = obj;
var hashes = arguments, counter = 0, key;
for ( var x = 1; x < hashes.length; x++ ) {
try {
for (var i in hashes[x] ) {
key = i;
if ( x > 0 ) {
while ( mapped[key] ) {
key = i + counter;
++counter;
}
counter = 0;
}
mapped[key] = hashes[x][i];
}
} catch(e){continue;}
}
return mapped;
}
function $renameKey(hash, oldKey, newKey) {
try {
if ( hash[oldKey] ) {
hash[newKey] = hash[oldKey];
delete hash[oldKey];
return true;
} else {
return false;
}
} catch(e) { return false; }
}
/* ============================== END Tools ================================= */
/* =============== Session ====================== */
/*
|-----------------------------------------------------------------------------|
| vSession 1.0 |
|-----------------------------------------------------------------------------|
| Developed By: |
| Joshua Blackburn |
| |
|-----------------------------------------------------------------------------|
| Client side "session". JavaScript data store. |
| IE 6 |
|-----------------------------------------------------------------------------|
| September 2004 | vSession 1.0 |
|-----------------------------------------------------------------------------|
*/
function vSessionGet() {
var objRef = "top.vSession.";
objRef += arguments[0].replace(/\s/g, '1s1');
try {
var val = eval(objRef);
return val;
} catch(e) {
//alert("vSessionGet:False");
return false;
}
}
function vSessionGetCount() {
var obj = vSessionGet(arguments[0]);
var i = 0;
for ( var c in obj ) {
if ( c == "toXMLRPC" ) continue;
i++;
}
obj = null;
return i;
}
function vSessionInsert() {
if ( ! top.vSession ) top.vSession = {};
vSessionRandom = new Date();
arguments[0] ? keyName = arguments[0] : keyName = "v" + Math.floor(vSessionRandom.getTime());
arguments[1] ? keyValue = arguments[1] : keyValue = "";
arguments[2] ? skipInsert = true : skipInsert = false;
if ( top.vSession[keyName] ) {
if( !skipInsert ) alert('vSessionInsert:\rThe vSession key ' + keyName + ' already exists.\rUse vDump to view all keys in vSession or choose another key name.');
return false;
} else {
top.vSession[keyName] = keyValue;
return keyName;
}
}
function vSessionUpdate() {
if ( ! top.vSession ) top.vSession = {};
var vSessionRandom = new Date();
arguments[0] ? keyName = arguments[0] : keyName = "v" + vSessionGet("current_time").time();
keyName = keyName.replace(/\s/g, '1s1');
arguments[1] ? keyValue = arguments[1] : keyValue = "";
if ((/\./).test(keyName)) {
var sub = true;
var keyNames = keyName.split(".");
} else {
var sub = false;
}
if ( sub == true ) {
var objRef = new Object();
var kLength = keyNames.length;
for (i = 0; i < kLength; i++) {
if ( i == 0 ) {
if ( ! top.vSession[keyNames[i]] ) { top.vSession[keyNames[i]] = new Object(); }
if ( typeof(top.vSession[keyNames[i]]) != "object" ) { top.vSession[keyNames[i]] = null; top.vSession[keyNames[i]] = new Object(); }
objRef = top.vSession[keyNames[i]];
} else {
if ( i == kLength - 1 ) {
if ( objRef[keyNames[i]] ) { objRef[keyNames[i]] = null; }
objRef[keyNames[i]] = keyValue;
} else {
if ( ! objRef[keyNames[i]] ) { objRef[keyNames[i]] = new Object(); }
if ( typeof(objRef[keyNames[i]]) != "object" ) { objRef[keyNames[i]] = null; objRef[keyNames[i]] = new Object(); }
objRef = objRef[keyNames[i]];
}
}
}
} else {
top.vSession[keyName] = null;
top.vSession[keyName] = keyValue;
}
return keyName;
}
function vSessionDelete() {
if ( arguments[0] ) {
vKey = arguments[0];
} else {
vAlert('vSessionDelete:\rYou must pass the name of the key you\rwish to delete.');
return false;
}
if ( ( /\./ ).test(vKey) ) {
var objRef = 'top.vSession.';
objRef += vKey;
try { eval(objRef + " = null"); eval("delete " + objRef); objRef = null; return true; } catch(e){};
} else {
if ( top.vSession[vKey] ) {
top.vSession[vKey] = null;
objRef = null;
return true;
} else {
return false;
}
}
}
function vSessionFlush() {
top.vSession = null;
top.vSession = new Object();
}
/* ============================== END Session ================================= */
/* =============== jsSimple ====================== */
/*
|-----------------------------------------------------------------------------|
| jsSimple 2.1 |
|-----------------------------------------------------------------------------|
| Developed By: |
| Joshua Blackburn |
| |
|-----------------------------------------------------------------------------|
| JavaScript DOM formatting. |
| IE 6+ + Firefox + Safari + Chrome + Opera |
|-----------------------------------------------------------------------------|
| September 2004 | jsSimple 1.0 |
| March 2005 | jsSimple 2.0. Complete recode and optimization. |
| July 2009 | jsSimple 2.1. Cross browser compatibility |
|-----------------------------------------------------------------------------|
*/
/*
JS.Simple
******************
Example of use tag ( insert directly below <html> )
<uses tags="replace" ids=""/>
- ids are only used for tags that cannot be wrapped with <simple:replace> tags.
*******************
********************
- ALL SIMPLE TAGS MUST HAVE A UNIQUE ID
********************
********************
Example of simple replace tag
<simple:replace structure="object" id="someID" where="key=value">
<simple:replace structure="{pointer}.object" id="someID2" where="key=value">
</simple:replace>
</simple:replace>
- Structures should only be a hash or an array of hashes.
Ex 1:
var hash = {
key : value,
key : value
}
EX 2:
var arrayHashes = [
{
key : value
},
{
key : value
}
]
- Usage of where: specify one key with specific value that must be present in the current hash to be displayed.
- Usage of {pointer}: used in nested tags. {pointer} is replaced with the current object path in the replace. Allows you to use objects within a hash for example:
var arrayHashes = [
{
key : value
myKey : {
key : value
}
},
{
key : value
}
]
Allows you to use myKey by using {pointer}.myKey
+++++++++++++++++++++NOTES+++++++++++++
- If replacing the value of an input text box, HTML parsers will strip the "" off the value if it doesn't contain a space, therefore, after simple:replace, strings are not treated correctly. To avoid this, insert a space after your tag. Ex:
BAD:
<input type="text" value="{myValue}" />
GOOD:
<input type="text" value="{myValue} " />
This will assure that all characters are replaced correctly.
*/
//var orderBy, ASC, DESC, domMapped = false; // <- Used by _jsSimple
var _jsSimple = {
debug : false,
clean : true, // <- Defines if cleanup is called or supressed
setup : function() {
vGetParams();
this._orderBy = '';
this.ASC = '';
this.DESC = '';
this.domMapped = false;
this.block = '';
this.tag = '';
this.page = jss.page;
this.tagNames = (document.getElementsByTagName('uses')[0] ? ( document.getElementsByTagName('uses')[0].tags ? document.getElementsByTagName('uses')[0].tags.split(',') : ["replace"] ) : ["replace"]);
this.simpleIDS = (document.getElementsByTagName('uses')[0] ? ( document.getElementsByTagName("uses")[0].getAttribute('ids') ? document.getElementsByTagName("uses")[0].getAttribute('ids').split(',') : [] ) : []);
//sDumper("simpleIDS", this.simpleIDS, this.tagNames);
this.tags = []; // <- Stores all the tags on the page
this.passedIDS = []; // <- Stores all ids passed as arguments
this.cleanRegex = /\{[a-zA-Z0-9_-]+\}/ig;
this.custom = {}; // <- Allows for extended functionality. Define custom functions to handle certain data types
this._override = {};
this.mapDOM();
this.r = {
nestedRegex : /\<\?xml[^\>]+\>/g,
pointerRegex : /\{pointer\}/,
pathRegex : /\{path\}/g,
positionRegex: /\{position\}/g,
myHTML : [],
position : 0,
pointer : '',
path : '',
nestedIDS : []
}
},
mapDOM : function () {
//Perform following check to allow stand alone operation
if ( ! top.vSession ) top.vSession = {};
var simpleTags, tags = this.tagNames, ids = this.simpleIDS;
if ( ! this.domMapped && arguments.length < 1 ) {
// Add all simple tags to DOM map
for ( var i in tags ) {
simpleTags = document.getElementsByTagName( trim(tags[i]) );
for ( var single = 0; single < simpleTags.length; single++ ) {
this.tags.push(simpleTags[single]); // Add all tag references to tag array
// If tag has id, use that for dom map id, if not, just use the position in the array
if ( simpleTags[single].getAttribute('id') ) {
vSessionUpdate("DOM_MAP."+this.page+'.'+simpleTags[single].id, simpleTags[single].innerHTML);
} else {
vSessionUpdate("DOM_MAP."+this.page+'.'+single, simpleTags[single].innerHTML);
}
}
}
// Add all regular tags specified as a simple tag
for ( var j in ids ) {
try {
vSessionUpdate( "DOM_MAP."+this.page+'.'+ trim(ids[j]), document.getElementById( trim(ids[j]) ).innerHTML );
} catch(e) {}
}
this.domMapped = true; // <- Set global variable to signify that the DOM is already mapped and don't do it again.
}
if ( arguments.length > 0 ) {
var myObj;
for ( var i = 0; i < arguments.length; i++ ) {
if ( myObj = document.getElementById(arguments[i]) ) { // <- Make sure the element exists first
// If this tag has not already been mapped, then map it
if ( ! vSessionGet("DOM_MAP."+this.page+'.'+trim(arguments[i])) ) vSessionUpdate( "DOM_MAP."+this.page+'.'+ trim(arguments[i]), myObj.innerHTML );
}
}
myObj = null;
}
simpleTags = null;
},
format : function () {
this.passedIDS = [];
// Check for passed arguments
for ( var a = 0; a < arguments.length; a++ ) {
if ( typeof(arguments[a]) == 'number' ) {
this.clean = false; // Supress cleanup
} else {
this.passedIDS.push(arguments[a]);
}
}
if ( this.debug ) alert("Passed " + this.passedIDS.length + ' IDs');
for ( var i in ( obj = ( this.passedIDS[0] ? this.passedIDS : this.tags.concat(this.simpleIDS) ) ) ) {
if ( typeof( obj[i] ) == 'string' ) {
this.tag = document.getElementById( obj[i] );
if ( ! this.tag ) { // <- Protect against bad Id's
if ( this.debug ) alert('Was passed a bad id: ' + obj[i] );
return;
}
if ( ! this._override[obj[i]] ) { // <- Allows you to override a skipped tag
if ( this.debug ) alert('Tag has no override applied');
if ( this.tag.getAttribute('skip') || this.tag.getAttribute('nested') ) { this.simpleCleanup(this.tag.id);continue; } // <- Tag is skipped, but still do cleanup
} else {
delete this.override[this.tag.id]; // <- Delete after being overridden
}
this.block = vSessionGet( 'DOM_MAP.'+this.page+"."+(obj[i]) );
if ( this.debug ) alert("was passed a string. Block: " + this.block + ' Id being looked up: ' + obj[i] + ' The current page is: ' + this.page);
this.tag.getAttribute('simple') ? this.replace() : this.replace();
} else {
this.tag = obj[i];
if ( ! this._override[this.tag.id] ) { // <- Allows you to override a skipped tag
if ( this.debug ) alert('Tag has no override applied');
if ( this.tag.getAttribute('skip') || this.tag.getAttribute('nested') ) { this.simpleCleanup(this.tag.id);continue; } // <- Tag is skipped, but still do cleanup
} else {
delete this.override[this.tag.id]; // <- Delete after being overridden
}
this.block = vSessionGet( 'DOM_MAP.'+this.page+"."+(obj[i].getAttribute('id') ? obj[i].getAttribute('id') : i) );
if ( this.debug ) alert("was passed an object. block: " + this.block);
//this[ this.tag.nodeName ]();
this.replace();
}
}
},
replace : function () {
try { // <- Check for invalid structure
if ( ! top[this.tag.getAttribute('structure')] ) {
if ( this.debug ) alert('The structure | ' + this.tag.getAttribute('structure') + " | is invalid or does not exist");
this.simpleCleanup(this.tag.id); // <- Cleanup up block before returning
return;
}
} catch(e) {
if ( this.debug ) alert('The structure | ' + this.tag.getAttribute('structure') + " | is invalid or does not exist");
this.simpleCleanup(this.tag.id); // <- Cleanup up block before returning
return;
}
this.r['structure'] = ( this.tag.getAttribute('structure') ? /*eval( this.tag.getAttribute('structure') )*/top[this.tag.getAttribute('structure')] : {} );
this.r['whereClause'] = ( this.tag.getAttribute('where') ? this.tag.getAttribute('where') : false );
this._orderBy = ( this.tag.getAttribute('orderBy') ? this.tag.getAttribute('orderBy') : false );
this.ASC = ( this.tag.getAttribute('ASC') ? true : false );
this.DESC = ( this.tag.getAttribute('DESC') ? true : false );
this.r['dynamic'] = ( this.tag.getAttribute('dynamic') ? true : false );
var wc = this.parseWhereClause(), transformedData, objType, struct, re, tempBlock, nestedTags, tagObj;
// ------+ Handle Nesting +---------
if ( arguments[0] ) this.tag.setAttribute("nested", "true"); // <- Replace has been called for a nested tag. Mark so it is not executed more than once
if ( ! arguments[0] && this.tag.getAttribute("nested") ) return; // <- Don't continue if the tag has already been marked nested
// ++++++++++++++++++++- Start Replacing -+++++++++++++++++++++++++++++++++++++++++++++++++++++
if ( this.debug ) alert("The type of structure is: " + vTypeof(this.r.structure));
if ( vTypeof(this.r.structure) == 'array' ) {
// =========================- ARRAY -==========================
// If there is an orderBy clause, go ahead and sort the array of hashes before starting the loop
if ( this._orderBy ) {
if ( this.debug ) alert("The structure is ordered by: " + this._orderBy);
if ( this.custom['orderBy'] ) { // <- See if there is a custom function defined to handle the order by
this.r.structure = this.r.structure.sort(this.custom.orderBy); // <- Sort using custom function if it exists
} else {
this.r.structure = this.r.structure.sort(this.orderBy); // <- Use default sorting
}
}
for ( var c = 0; c < (struct = this.r.structure).length; c++ ) {
if ( this.debug ) alert("Walking the array and currently on " + c + " with a value of " + struct[c]);
if ( struct[c] ) { // <- Make sure the key exists
// Update pointer and position variables that custom transforms may need
this.r.pointer = this.tag.getAttribute('structure') + "[" + c + "]";
this.r.path = this.tag.getAttribute('structure') + "[" + c + "]";
this.r.position = c;
// Do pointer replace for nested tags. Then do count replace for nested tags.
tempBlock = this.block.replace(this.r.pointerRegex, this.tag.getAttribute('structure') + "[" + c + "]");
tempBlock = tempBlock.replace(this.r.positionRegex, c);
tempBlock = tempBlock.replace(this.r.pathRegex, this.r.path);
// If there is a where clause, make sure it matches, if it does, move on, if not, move on to the next hash
try {
if ( this.r.whereClause ) {
if ( ! eval(wc) ) {
if ( this.debug ) alert("The following where clause did not match: " + wc);
continue;
} else {
if ( this.debug ) alert("The following where clause matched: " + wc);
}
} else {
if ( this.debug ) alert("No where clause found. Continuing");
}
} catch(e) {
if ( this.debug ) alert('You have a malformed where clause in the tag ' + this.tag.getAttribute('id') + "\n or a key you checked against does not exist in the structure\n" + wc);
}
for ( var key in struct[c] ) { // <- Go through all the keys in the hash
if ( this.debug ) alert("Type of value is: " + typeof(struct[c][key]) + " vTypeof value is: " + vTypeof(struct[c][key]));
if ( typeof(struct[c][key]) == 'string' || typeof(struct[c][key]) == 'number' || vTypeof(struct[c][key]) == 'date' || typeof(struct[c][key]) == 'boolean' ) {
if ( this.debug ) alert('key: ' + key + ' value: ' + struct[c][key]);
transformedData = struct[c][key];
// ------------------------= Custom Transforms =--------------------
//objType = vTypeof(transformedData);
objType = vTypeof(transformedData);
if ( typeof(transformedData) != 'boolean' ) if ( this.custom[key] ) transformedData = this.custom[key](transformedData, struct[c], this); // <- Value sent to custom function and returned for replacement. Also pass reference to jsSimple just so they have it.
if ( this.custom[objType] && ! this.custom[key] ) transformedData = this.custom[objType](transformedData, struct[c], this); // <- Value sent to custom function and returned for replacement. Also pass reference to jsSimple just so they have it.
// ------------------------= End Custom Transforms =--------------------
re = new RegExp("\\{" + key + "\\}", "ig");
tempBlock = tempBlock.replace(re, transformedData);
if ( this.tag.nodeName.toLowerCase() == 'select' ) {
if ( this.tag.getAttribute('defaultOption') ) { if ( this.tag.getAttribute('defaultOption') == struct[c][key] && isNaN(parseInt(this.tag.getAttribute('defaultOption')))) { tempBlock = tempBlock.replace(/(.*\<option)(.*\>)/,'$1' + ' selected ' + '$2');} else if ( !isNaN(parseInt(this.tag.getAttribute('defaultOption'))) ) { if ( c == parseInt(this.tag.getAttribute('defaultOption')) ) tempBlock = tempBlock.replace(/(.*\<option)(.*\>)/,'$1' + ' selected ' + '$2');} }
}
if ( this.debug ) alert('current temp block: ' + tempBlock);
}
}
this.r.myHTML.push( tempBlock );
}
}
} else if ( typeof(this.r.structure) == 'object' ) {
// =========================- HASH -==========================
tempBlock = this.block;
for ( var key in (struct = this.r.structure) ) {
try {
//if ( struct[key] ) {
if ( this.debug ) alert("Type of value is: " + typeof(struct[key]) + " vTypeof value is: " + vTypeof(struct[key]));
if ( typeof(struct[key]) == 'string' || typeof(struct[key]) == 'number' || vTypeof(struct[key]) == 'date' || typeof(struct[key]) == 'boolean') {
transformedData = struct[key];
// ------------------------= Custom Transforms =--------------------
objType = vTypeof(transformedData);
if ( typeof(transformedData) != 'boolean' ) if ( this.custom[key] ) transformedData = this.custom[key](transformedData, struct, this); // <- Value sent to custom function and returned for replacement
if ( this.custom[objType] && ! this.custom[key] ) transformedData = this.custom[objType](transformedData, struct, this); // <- Value sent to custom function and returned for replacement
// ------------------------= End Custom Transforms =--------------------
//Debug
if ( this.debug ) alert('The current hash key is: |' + key + '| and the value is: |' + struct[key] + '| and value after transform is: ' + transformedData);
re = new RegExp("\\{" + key + "\\}",'ig');
tempBlock = tempBlock.replace(re, transformedData);
if ( this.tag.nodeName.toLowerCase() == 'select' ) {
if ( this.tag.getAttribute('defaultOption') ) if ( this.tag.getAttribute('defaultOption') == struct[key]) tempBlock = tempBlock.replace(/(.*\<option)(.*\>)/,'$1' + ' selected ' + '$2');
}
}
//}
} catch (e) { continue; }
}
this.r.myHTML.push( tempBlock );
}
// ++++++++++++++++++++- End Replacing -+++++++++++++++++++++++++++++++++++++++++++++++++++++
if ( this.debug ) alert("The code before cleanup: " + this.r.myHTML.join(''));
this.tag.innerHTML = this.cleanup(this.r.myHTML.join('')); // <- Clean the block and put the HTML back inside the tag
if ( this.tag.nodeName.toLowerCase() == 'select' ) {
this.simpleCleanup();
//this.tag.add(new Option());
//this.tag.options[this.tag.options.length - 1].selected = true;
}
this.r.myHTML = null; this.r.myHTML = []; this.r.whereClause = false; // <- Reset HTML holder and whereClause. Bad things happen if not reset
// If tag is dynamic, run dynamic piece. This will redisplay the block of code inside the simple tag.
if ( this.r.dynamic ) this.tag.style.display = 'block';
//if ( this.r.dynamic ) this.tag.innerHTML = this.tag.innerHTML.replace( /(\<\!\-\-)|(\-\-\>)/g, '');
// Handle tag events (Ex: onfinish)
if ( this.tag.getAttribute('onfinish') ) eval(this.tag.onfinish);
// +++++++++++++++++++++++++++- Nested Tags -+++++++++++++++++++++++++++++++++++++++++++++++++++
nestedTags = this.tag.getElementsByTagName('replace');
if ( nestedTags.length > 0 ) {
for ( var j = 0; j < nestedTags.length; j++ ) {
if ( nestedTags[j].getAttribute('nested') || nestedTags[j].getAttribute('skip') ) continue; // <- If tag has already been executed as nested or is skipped, continue
if ( this.debug ) alert("Nested tag: " + j);
this.block = vSessionGet( 'DOM_MAP.'+this.page+"."+(nestedTags[j].getAttribute('id') ? nestedTags[j].getAttribute('id') : j) );
this.tag = nestedTags[j];
this.replace('nested');
}
nestedTags = null;
}
// +++++++++++++++++++++++++++- End Nested Tags -+++++++++++++++++++++++++++++++++++++++++++++++++++
},
parseWhereClause : function () {
if ( this.r.whereClause ) {
/*
* Example where clauses
* where="keyName = 'string'"
* where="keyName = 'string' AND keyName = 'string'"
* where="keyName = 'string' OR keyName = 'string'"
* where="keyName = 'string' OR keyName = 'string' AND keyName = number"
* where="keyName ! 'string'"
* where="[keyName = [0-9]{4}]"
*/
return this.r.whereClause.replace(/\s?AND\s?/g, "&&").replace(/\s?OR\s?/g, "||").replace(/\s/g, "").replace(/\[([^=]+)\=[\'\"]?([^\'\"]+)[\'\"]?\]/g, "(/$2/i).test(trim(struct[c]['$1']))").replace(/\=/g, " ==").replace(/\!/g," !=").replace(/(\w+)\s/g, "(struct[c]['$1']) && (trim(struct[c]['$1']))");
//return ( ( trim(this.r.whereClause) ).split('=') ); // <- Only supports one simple where clause. Ex: where="key=value"
} else {
return false;
}
},
orderBy : function (a, b) {
try {
if ( (! this.ASC && a[this._orderBy] < b[this._orderBy]) || (this.ASC && a[this._orderBy] > b[this._orderBy] ) || (! a[this._orderBy] ) ) {
return 1;
} else if ( a[this._orderBy] == b[this._orderBy] || ( !a[this._orderBy] && !b[this._orderBy]) ) {
return 0;
} else {
return -1;
}
} catch (e) {
return 0;
}
},
override : function () {
for ( var i = 0; i < arguments.length; i++ ) {
try { this._override[arguments[i]] = "OVERRIDE"; } catch(e) {}
}
},
cleanup : function () {
//alert('cleaning');
if ( arguments.length > 0 ) { // <- Only cleanup tags specified
return arguments[0].replace(this.cleanRegex, '');
} else { // <- Cleanup all tags
return '';
}
},
simpleCleanup : function () {
//alert('simpleCleanup');
try {
if ( arguments.length > 0 ) {
for ( var i = 0; i < arguments.length; i++ ) {
document.getElementById(arguments[i]).innerHTML = document.getElementById(arguments[i]).innerHTML.replace(this.cleanRegex, '');
}
} else {
var dE = document.body.innerHTML;
document.body.innerHTML = dE;
}
} catch(e) {}
},
sort : function (tag, key, pos, col) {
var tagObj;
if ( (tagObj = document.getElementsByName(tag)).length > 1 ) {
arguments[2] ? tagObj = tagObj[ parseInt(arguments[2]) ] : tagObj = tagObj[0];
} else {
tagObj = tagObj[0];
}
//try {((document.getElementById('_jsSimpleSort')).parentNode).removeChild(document.getElementById('_jsSimpleSort'));} catch(e) {};
try {document.remove('_jsSimpleSort');} catch(e) {};
if ( tagObj.getAttribute('ASC') ) {
tagObj.removeAttribute('ASC');
tagObj.setAttribute('DESC', 'true');
if ( col ) col.innerHTML = col.innerHTML + "<img id='_jsSimpleSort' src='images/arrow_down.gif'>";
} else if ( tagObj.getAttribute('DESC') ) {
tagObj.removeAttribute('DESC');
tagObj.setAttribute('ASC', 'true');
if ( col ) col.innerHTML = col.innerHTML + "<img id='_jsSimpleSort' src='images/arrow_up.gif'>";
} else {
tagObj.setAttribute('DESC', 'true');
if ( col ) col.innerHTML = col.innerHTML + "<img id='_jsSimpleSort' src='images/arrow_down.gif'>";
}
if ( tagObj.getAttribute('orderBy') ) {
tagObj.orderBy = key;
} else {
tagObj.setAttribute('orderBy', key);
}
if ( tagObj.getAttribute('nested') ) tagObj.removeAttribute('nested');
this.format(tagObj);
},
kill : function () {
try{
if ( ! top.vSession ||
! top.vSession.DOM_MAP ||
! top.vSession.DOM_MAP[this.page] ) return;
}catch(Exception){
return;
}
for ( var i in top.vSession.DOM_MAP[this.page] ) {
top.vSession.DOM_MAP[this.page][i] = null;
}
top.vSession.DOM_MAP[this.page] = null;
delete top.vSession.DOM_MAP[this.page];
for ( var i in jsSimple ) {
jsSimple[i] = null;
}
jsSimple = null;
},
xmlhttp : function() {
if ( ! this._xmlhttp ) this._xmlhttp = undefined;
if (window.XMLHttpRequest) {
this._xmlhttp = new XMLHttpRequest();
if ( typeof this.xmlhttp.overrideMimeType != 'undefined') {
this._xmlhttp.overrideMimeType('text/xml');
}
} else if (window.ActiveXObject) {
this._xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} else {
alert('Perhaps your browser does not support xmlhttprequests?');
}
return this._xmlhttp;
},
jsonFetch : function(url) {
},
jsonFetchAndUpdate : function(url, id) {
//alert(url);
this.xmlhttp();
this._xmlhttp.onreadystatechange = function() {
if (this.readyState == 4) {
// do something with the results
if ( ! top[(document.getElementById(id)).getAttribute('structure')] ) {
return;
} else {
top[(document.getElementById(id)).getAttribute('structure')] = eval(this.responseText);
//sDumper(eval(this.responseText));
alert(top.struct.length);
_jsSimple.format(id);
}
}
};
//sDumper(this._xmlhttp);
this._xmlhttp.open('GET', url, true);
this._xmlhttp.send();
}
}
/* ============================== END jsSimple ================================= */
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="https://raw.github.com/documentcloud/underscore/master/underscore.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>
<script src="https://github.com/downloads/wycats/handlebars.js/handlebars.1.0.0.beta.3.js"></script>
<script src="http://borismoore.github.com/jsrender/jsrender.js"></script>
<script src="https://github.com/olado/doT/raw/master/doT.js">
</script>
<!--External Template Definitions-->
<script type="text/x-kendo-template" id="kendoUIextTemplate">
<div>
<h1 class='header'>#= data.header #</h1>
<h2 class='header2'>#= data.header2 #</h2>
<h3 class='header3'>#= data.header3 #</h3>
<h4 class='header4'>#= data.header4 #</h4>
<h5 class='header5'>#= data.header5 #</h5>
<h6 class='header6'>#= data.header6 #</h6>
<ul class='list'>
# for (var i = 0, l = data.list.length; i < l; i++) { #
<li class='item'>#= data.list[i] #</li>
# } #
</ul>
</div>
</script>
<script>
window.sharedVariables = {
header: "Header",
header2: "Header2",
header3: "Header3",
header4: "Header4",
header5: "Header5",
header6: "Header6",
list: ['1000000000', '2', '3', '4', '5', '6', '7', '8', '9', '10']
};
//JsRender compiled template (no encoding)
window.jsRenderTemplate = $.templates("<div><h1 class='header'>{{:header}}</h1><h2 class='header2'>{{:header2}}</h2><h3 class='header3'>{{:header3}}</h3><h4 class='header4'>{{:header4}}</h4><h5 class='header5'>{{:header5}}</h5><h6 class='header6'>{{:header6}}</h6><ul class='list'>{{for list}}<li class='item'>{{:#data}}</li>{{/for}}</ul></div>");
window.mustacheTemplate = "<div><h1 class='header'>{{{header}}}</h1><h2 class='header2'>{{{header2}}}</h2><h3 class='header3'>{{{header3}}}</h3><h4 class='header4'>{{{header4}}}</h4><h5 class='header5'>{{{header5}}}</h5><h6 class='header6'>{{{header6}}}</h6><ul class='list'>{{#list}}<li class='item'>{{{.}}}</li>{{/list}}</ul></div>";
window.handlebarsTemplate = Handlebars.compile("<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#each list}}<li class='item'>{{this}}</li>{{/each}}</ul></div>");
//Use external template definition
window.underscoreTemplate = _.template("<div><h1 class='header'><%= header %></h1><h2 class='header2'><%= header2 %></h2><h3 class='header3'><%= header3 %></h3><h4 class='header4'><%= header4 %></h4><h5 class='header5'><%= header5 %></h5><h6 class='header6'><%= header6 %></h6><ul class='list'><% for (var i = 0, l = list.length; i < l; i++) { %><li class='item'><%= list[i] %></li><% } %></ul></div>");
window.underscoreTemplateNoWith = _.template("<div><h1 class='header'><%= data.header %></h1><h2 class='header2'><%= data.header2 %></h2><h3 class='header3'><%= data.header3 %></h3><h4 class='header4'><%= data.header4 %></h4><h5 class='header5'><%= data.header5 %></h5><h6 class='header6'><%= data.header6 %></h6><ul class='list'><% for (var i = 0, l = data.list.length; i < l; i++) { %><li class='item'><%= data.list[i] %></li><% } %></ul></div>", null, {variable: 'data'});
window.baseHtml = "<div><h1 class='header'></h1><h2 class='header2'></h2><h3 class='header3'></h3><h4 class='header4'></h4><h5 class='header5'></h5><h6 class='header6'></h6><ul class='list'><li class='item'></li></ul></div>";
window.doTtemplate = doT.template("<div><h1 class='header'>{{=it.header}}</h1><h2 class='header2'>{{=it.header2}}</h2><h3 class='header3'>{{=it.header3}}</h3><h4 class='header4'>{{=it.header4}}</h4><h5 class='header5'>{{=it.header5}}</h5><h6 class='header6'>{{=it.header6}}</h6><ul class='list'>{{for(var i = 0,l=it.list.length;i<l;i++) { }}<li class='item'>{{=it.list[i]}}</li>{{ } }}</ul></div>");
//Resig Template Function (modified to support ')
function tmpl(str) {
var strFunc =
"var p=[];" +
"with(obj){p.push('" +
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^#]*#>)/g, "\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<#=(.+?)#>/g, "',$1,'")
.split("<#").join("');")
.split("#>").join("p.push('")
+ "');}return p.join('');";
return new Function("obj", strFunc);
}
window.resig = tmpl("<div><h1 class='header'><#= header #></h1><h2 class='header2'><#= header2 #></h2><h3 class='header3'><#= header3 #></h3><h4 class='header4'><#= header4 #></h4><h5 class='header5'><#= header5 #></h5><h6 class='header6'><#= header6 #></h6><ul class='list'><# for (var i = 0, l = list.length; i < l; i++) { #><li class='item'><#= list[i] #></li><# } #></ul></div>");
//Resig modified template function (no "with" block)
function tmpl2(str) {
var strFunc =
"var p=[];" +
"p.push('" +
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^#]*#>)/g, "\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<#=(.+?)#>/g, "',$1,'")
.split("<#").join("');")
.split("#>").join("p.push('")
+ "');return p.join('');";
return new Function("data", strFunc);
}
window.resig2 = tmpl2("<div><h1 class='header'><#= data.header #></h1><h2 class='header2'><#= data.header2 #></h2><h3 class='header3'><#= data.header3 #></h3><h4 class='header4'><#= data.header4 #></h4><h5 class='header5'><#= data.header5 #></h5><h6 class='header6'><#= data.header6 #></h6><ul class='list'><# for (var i = 0, l = data.list.length; i < l; i++) { #><li class='item'><#= data.list[i] #></li><# } #></ul></div>");
</script>
<div id="jsSimpleTemplate" structure="sharedVariables" style="display:none">
<h1 class='header'>{header}</h1>
<h2 class='header2'>{header2}</h2>
<h3 class='header3'>{header3}</h3>
<h4 class='header4'>{header4}</h4>
<h5 class='header5'>{header5}</h5>
<h6 class='header6'>{header6}</h6>
<ul id="hello" class='list' structure="list">
<li class='item'>{item}</li>
</ul>
</div>
<div id="template" style="display:none"></div>
<script>
window.jsSimple = function() {
_jsSimple.format('jsSimpleTemplate');
_jsSimple.format('hello');
};
window.list = sharedVariables.list.map(function(item) { return { item: item }; });
_jsSimple.setup();
</script>
Ready to run.
Test | Ops/sec | |
---|---|---|
doT.js |
| ready |
Kendo UI Templates (Default) |
| ready |
Kendo UI Templates (No "with" block) |
| ready |
Handlebars.js |
| ready |
Underscore.js Template |
| ready |
Resig Micro Templates (modified) |
| ready |
Resig Micro Templates (No "with" block) |
| ready |
Underscore.js Template (No "with") |
| ready |
Hogan.js |
| ready |
JsRender |
| ready |
Mustache.js Template |
| ready |
JSSimple |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.