JavaScript template language shootoff

JavaScript performance comparison

Revision 858 of this test case created

Info

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.

Preparation code

<script>
;//../../jin/jin.jam.js?=HNF0UJJC
this.$jin = new function $jin( ){ }

;//../../jin/func/jin_func.jam.js?=HN966P0O
this.$jin.func = {}

this.$jin.func.make = function $jin_func_make( name ){
    eval( 'var func = function ' + name + '( ){\
        return func.execute( this, arguments )\
    }'
)
    return func
}

this.$jin.func.name = function $jin_func_name( func, name ){
    if( arguments.length > 1 ) return func.$jin_func_name = name
    return func.name
    || func.$jin_func_name
    || func.toString().match( /^\s*function\s*([$\w]*)\s*\(/ )[ 1 ]
}

;//../../jin/value/jin-value.jam.js?=HJWMNOGW
this.$jin.value = function $jin_value( value ){
   
    var func = function $jin_value_instance( ){
        return func.$jin_value
    }
   
    func.$jin_value = value
   
    return func
}

;//../../jin/root/jin_root.jam.js?=HJWMOH0O
this.$jin.root = $jin.value( this )

;//../../jin/glob/jin_glob.jam.js?=HJWNFZRC
this.$jin.glob = function $jin_glob( name, value ){
    var keyList = name.split( '_' )
    var current = $jin.root()
    var currentName = ''
   
    while( keyList.length > 1 ){
        var key = keyList.shift() || 'prototype'
        currentName += ( currentName ? '_' : '' ) + ( key === 'prototype' ? '' : key )
       
        if(!( key in current )){
            current[ key ] = $jin.trait.make( currentName )
        }
       
        current = current[ key ]
    }
   
    var key = keyList.shift() || 'prototype'
   
    if( arguments.length > 1 ){
        current[ key ] = value
    } else {
        value = current[ key ]
    }
   
    return value
}

;//../../jin/trait/jin_trait.jam.js?=HN96NIQ8
this.$jin.trait = function $jin_trait( name ){
   
    var trait = $jin.glob( name )
    if( trait ) return trait
   
    trait = $jin.trait.make( name )
   
    return $jin.glob( name, trait )
}

this.$jin.trait.make = function $jin_trait_make( name ){
   
    eval( 'var trait= function ' + name + '( ){ \
        return ( this instanceof trait ) ? this : trait.apply( trait, arguments ) \
    }'
)
   
    trait.name = name
    trait.apply = function( ){ return trait }
   
    return trait
}

;//../../jin/mixin/jin_mixin.jam.js?=HN967LFC
this.$jin.mixin = function( ){ // arguments: sourceName+, targetName
    var trait = $jin.mixin.object.apply( this, arguments )
   
    for( var index = 0; index < arguments.length; ++index ){
        arguments[ index ] += '_'
    }
    $jin.mixin.object.apply( this, arguments )
   
    return trait
}

this.$jin.mixin.object = function( ){ // arguments: sourceName+, targetName
    var sourcePathList = [].slice.call( arguments )
    var targetPath = sourcePathList.pop()
    var target = $jin.trait( targetPath )
   
    sourcePathList.forEach( function( sourcePath ){
        var source = $jin.trait( sourcePath )
        source.$jin_mixin_slaveList = source.$jin_mixin_slaveList || []
        if( ~source.$jin_mixin_slaveList.indexOf( targetPath ) ) return
        source.$jin_mixin_slaveList.push( targetPath )
       
        for( var key in source ){
            var func = source[ key ]
            if( typeof func !== 'function' ) continue
            if( !func.$jin_method_name ) continue
           
            var methodName = func.$jin_method_name.replace( /^([$\w]+_)+/, '' )
            $jin.method.define( targetPath + '_' + methodName, func )
        }
    })
   
    return target
}

;//../../jin/method/jin_method.jam.js?=HNH59NNS
this.$jin.method = function $jin_method( ){ // arguments: resolveName*, func
    var resolveList = [].slice.call( arguments )
    var func = resolveList.pop()
   
    var name = $jin.func.name( func )
    if( !name ) throw new Error( 'Can not register anonymous function' )
   
    func.$jin_method_name = name
   
    func.$jin_method_resolveList = resolveList
   
    $jin.method.define( name, func )
}

this.$jin.method.define = function $jin_method_define( name, func ){
    var funcName = func.$jin_method_name
    if( !funcName ) throw new Error( '$jin_method_name is not defined in [' + func + ']' )
   
    var nameList = name.split( '_' )
    var methodName = nameList.pop()
    var ownerPath = nameList.join( '_' )
    var owner = $jin.trait( ownerPath )
    var slaveList = owner.$jin_mixin_slaveList
   
    owner[ funcName ]= func
   
    if( slaveList ) slaveList.forEach( function( slavePath ){
        $jin.method.define( slavePath + '_' + methodName, func )
    })
   
    var existFunc = owner[ methodName ]
    checkConflict: {
       
        if( existFunc === void 0 ) break checkConflict
       
        if( typeof existFunc !== 'function' ){
            throw new Error( 'Can not redefine [' + existFunc + '] by [' + funcName + ']' )
        }
       
        if( func === existFunc ) return existFunc
       
        if( !existFunc.$jin_method_name ) break checkConflict
       
        func = $jin.method.merge( existFunc, func, name )
    }
   
    owner[ methodName ]= func
   
    if( slaveList ) slaveList.forEach( function( slavePath ){
        $jin.method.define( slavePath + '_' + methodName, func )
    })
   
    return func
}

this.$jin.method.merge = function $jin_method_merge( left, right, name ){
    var leftConflicts = left.$jin_method_conflictList || [ left ]
    var rightConflicts = right.$jin_method_conflictList || [ right ]
    var conflictList = leftConflicts.concat( rightConflicts )

    var leftResolves = left.$jin_method_resolveList || []
    var rightResolves = right.$jin_method_resolveList || []
    var resolveList = leftResolves.concat( rightResolves )
   
    conflictList = conflictList.filter( function( conflict ){
        return !~resolveList.indexOf( conflict.$jin_method_name )
    })
   
    if( conflictList.length === 0 ){
        throw new Error( 'Can not resolve conflict ' + name + ' because cyrcullar resolving' )
    } else if( conflictList.length === 1 ){
        var func = conflictList[0]
    } else if( conflictList.length > 1 ){
        var func = $jin.func.make( name )
        func.execute = function( ){
            throw new Error( "Conflict in [" + func.$jin_method_name + "] by [" + func.$jin_method_conflictList + "]" )
        }
        func.$jin_method_name = name
        func.$jin_method_conflictList = conflictList
    }
   
    func.$jin_method_resolveList = resolveList
   
    return func
}

;//../../jin/property/jin_property.jam.js?=HLBXYT4G
this.$jin.property = function $jin_property( ){ // arguments: resolveName*, filter
    var resolveList = [].slice.call( arguments )
    var filter = resolveList.pop()
   
    var name = filter.name = filter.name || filter.toString().match( /^\s*function\s*([$\w]*)\s*\(/ )[ 1 ]
    if( !name ) throw new Error( 'Can not register anonymous property' )
   
    $jin.property.define.apply( $jin.property, resolveList.concat([ name, filter ]) )
}

this.$jin.property.define = function $jin_property_define( ){ // arguments: resolveName*, name, filter
    var resolveList = [].slice.call( arguments )
    var filter = resolveList.pop()
    var name = resolveList.pop()
   
    var fieldName = '_' + name
   
    eval( 'var property = function ' + name + '( ){ \
        return property.apply( this, arguments ) \
    }'
)
   
    property.apply = function $jin_property_apply( obj, args ){
        if( args.length ){
            if( args[0] === void 0 ){
                obj[ fieldName ] = void 0
            } else {
                obj[ fieldName ] = property.$jin_property_filter.apply( obj, args )
            }
            return obj
        } else {
            if( obj[ fieldName ] === void 0 ){
                return obj[ fieldName ] = property.$jin_property_filter.apply( obj, args )
            } else {
                return obj[ fieldName ]
            }
        }
    }
   
    property.$jin_property_filter = filter || function( value ){ return value }
   
    return $jin.method.apply( $jin, resolveList.concat([ property ]) )
}

;//../../jin/support/jin_support.env=web.jam.js?=HKMWXZ1K
$jin.property( function $jin_support_xmlModel( ){
    return ( window.DOMParser && window.XMLSerializer && window.XSLTProcessor ) ? 'w3c' : 'ms'
} )

$jin.property( function $jin_support_htmlModel( ){
    return document.createElement( 'html:div' ).namespaceURI !== void 0 ? 'w3c' : 'ms'
} )

$jin.property( function $jin_support_eventModel( ){
    return ( 'addEventListener' in document.createElement( 'div' ) ) ? 'w3c' : 'ms'
} )

$jin.property( function $jin_support_textModel( ){
    return ( 'createRange' in document ) ? 'w3c' : 'ms'
} )

$jin.property( function $jin_support_vml( ){
    return /*@cc_on!@*/ false
} )

;//../../jin/klass/jin_klass.jam.js?=HNILZW2W
$jin.klass = function $jin_klass( ){ // arguments: sourceName*, targetName
    $jin.mixin.apply( this, arguments )
   
    var name = arguments[ arguments.length - 1 ]
    return $jin.mixin( '$jin_klass', name )
}

$jin.method( function $jin_klass_apply( context, args ){
    var obj = new this
    obj.init.apply( obj, args )
    return obj
} )

$jin.method( function $jin_klass_id( ){
    return this.$jin_method_name || this.name
} )

$jin.method( function $jin_klass_toString( ){
    return this.id()
} )

$jin.method( function $jin_klass__init( json ){
    return this.json( json )
} )

$jin.property.define( '$jin_klass__entangleList', Array )
$jin.method( function $jin_klass__entangle( value ){
    this.entangleList().push( value )
    return this
} )

$jin.method( function $jin_klass__destroy( ){
   
    this.entangleList().forEach( function( entangle ){
       entangle.destroy()
    } )
   
    for( var key in this ){
        delete this[ key ]
    }
   
    return this
} )

$jin.method( function $jin_klass__json( json ){
    if( !arguments.length ) return null
   
    if( !json ) return this
   
    for( var key in json ){
        this[ key ]( json[ key ] )
    }
   
    return this
} )

$jin.property.define( '$jin_klass__methodList', Object )
$jin.method( function $jin_klass__method( name ){
    var hash = this.methodHash()
   
    var method = hash[ '_' + name ]
    if( method ) return method
   
    method = function $jin_klass__method_instance( ){
        return method.content[ method.methodName ].call( method.content, arguments )
    }
   
    return hash[ '_' + name ] = method
} )

;//../../jin/wrapper/jin_wrapper.jam.js?=HNIHPODS
$jin.wrapper = function $jin_wrapper( ){ // arguments: sourceName*, targetName
    $jin.mixin.apply( this, arguments )
   
    var name = arguments[ arguments.length - 1 ]
    return $jin.mixin( '$jin_wrapper', name )
}

$jin.mixin( '$jin_klass', '$jin_wrapper' )
$jin.property.define( '$jin_wrapper__raw', null )

$jin.method( '$jin_klass_apply', function $jin_wrapper_apply( wrapper, args ){
    var obj = args[0]
    if( obj instanceof wrapper ) return obj
    if( obj.$jin_wrapper__raw ) obj = args[0] = obj.raw()
    return wrapper.$jin_klass_apply( wrapper, args )
} )

$jin.method( '$jin_klass__init', function $jin_wrapper__init( obj ){
    this.raw( obj )
    return this
} )

;//../../jin/event/jin_event.jam.js?=HM9HTEKW
$jin.klass( '$jin_event' )

$jin.property( function $jin_event_type( ){
    return String( this )
} )

$jin.method( function $jin_event_listen( crier, handler ){
    return crier.listen( this.type(), handler )
} )


$jin.property.define( '$jin_event__target', null )
$jin.property.define( '$jin_event__catched', Boolean )
   
$jin.property( function $jin_event__type( type ){
    if( arguments.length ) return String( type )
    return String( this.constructor )
} )

$jin.method( function $jin_event__scream( crier ){
    crier.scream( this )
    return this
} )

;//../../jin/dom/event/jin_dom_event.env=web.jam.js?=HKWZJK20
$jin.klass( '$jin_wrapper', '$jin_event', '$jin_dom_event' )

$jin.property( '$jin_dom_event_bubbles', Boolean )
$jin.property( '$jin_dom_event_cancelable', Boolean )

$jin.method( function $jin_dom_event__nativeEvent( ){
    var raw = this.raw()
    if( raw ) return raw
   
    var Event = this.constructor
    var type = Event.type()
    var bubbles = Event.bubbles()
    var cancelable = Event.cancelable()
   
    if( $jin.support.eventModel() === 'ms' ){
        raw= document.createEventObject()
        raw.type = type
        raw.bubbles = bubbles
        raw.cancelable = cancelable
    } else {
        raw = document.createEvent( 'Event' )
        raw.initEvent( type, bubbles, cancelable )
    }
   
    this.raw( raw )
   
    return raw
} )

$jin.method( '$jin_event__target', function $jin_dom_event__target( ){
    return this.nativeEvent().target
} )

$jin.method( '$jin_event__type', function $jin_dom_event__type( type ){
    var nativeEvent = this.nativeEvent()
    type = String( type )
   
    if( !arguments.length ){
        return nativeEvent.$jin_dom_event_type || nativeEvent.type
    }
   
    nativeEvent.initEvent( type, this.bubbles(), this.cancelable() )
    nativeEvent.$jin_dom_event_type= nativeEvent.type= type
   
    return this
} )

$jin.method( function $jin_dom_event__bubbles( bubbles ){
    var nativeEvent = this.nativeEvent()
   
    if( !arguments.length ){
        return nativeEvent.bubbles
    }
   
    nativeEvent.initEvent( this.type(), Boolean( bubbles ), this.cancelable() )
   
    return this
} )

$jin.method( function $jin_dom_event__cancelable( cancelable ){
    var nativeEvent = this.nativeEvent()
   
    if( !arguments.length ){
        return nativeEvent.cancelable
    }
   
    nativeEvent.initEvent( this.type(), this.bubbles(), Boolean( cancelable ) )
   
    return this
} )

$jin.method( function $jin_dom_event__catched( catched ){
    var nativeEvent = this.nativeEvent()
   
    if( !arguments.length ){
        return nativeEvent.defaultPrevented || nativeEvent.$jin_dom_event_catched
    }
   
    nativeEvent.returnValue= !catched
   
    if( catched && nativeEvent.preventDefault ){
        nativeEvent.preventDefault()
    }
   
    nativeEvent.$jin_dom_event_catched = nativeEvent.defaultPrevented = !!catched
   
    return this
} )

$jin.method( function $jin_dom_event__keyCode( ){
    return this.nativeEvent().keyCode
} )

$jin.method( function $jin_dom_event__mouseButton( ){
    return this.nativeEvent().button
} )

;//../../jin/env/jin_env.jam.js?=HJWSZD88
this.$jin.env = $jin.value( function(){ return this }() )

;//../../jin/listener/jin_listener.jam.js?=HMDEZN88
$jin.klass( '$jin_listener' )

$jin.property.define( '$jin_listener__crier', null )
$jin.property.define( '$jin_listener__eventName', String )
$jin.property.define( '$jin_listener__handler', null )

$jin.method( function $jin_listener__forget( ){
    this.crier().forget( this.eventName(), this.handler() )
    return this
} )

$jin.method( '$jin_klass__destroy',  function $jin_listener__destroy( ){
    this.forget()
    this.$jin_klass__destroy()
} )

;//../../jin/dom/jin_dom.env=web.jam.js?=HLCC248O
$jin.method( function $jin_dom__html( html ){
    if( arguments.length ){
        this.nativeNode().innerHTML = html
        return this
    } else {
        return this.nativeNode().innerHTML
    }
} )

if( $jin.support.xmlModel() === 'ms' ){
   
    $jin.mixin( '$jin_dom_ms', '$jin_dom' )
   
    $jin.method( '$jin_dom__toString', function $jin_dom_ms__toString( ){
        return String( this.nativeNode().xml )
    } )

    // works incorrectly =( use render instead
    $jin.method( '$jin_dom__transform', function $jin_dom_ms__transform( stylesheet ){
        var result= this.nativeNode().transformNode( $jin.dom( stylesheet ).nativeNode() )
        return $jin.dom.parse( result )
    } )

    $jin.method( '$jin_dom__render', function $jin_dom_ms__render( from, to ){
        from= $jin.dom( from ).nativeNode()
        to= $jin.dom( to ).nativeNode()
       
        to.innerHTML= from.transformNode( this.nativeDoc() )
    } )
   
    $jin.method( '$jin_dom__text', function $jin_dom_ms__text( value ){
        if( arguments.length ){
            return this.$jin_dom__text( value )
        } else {
            return this.nativeNode().text
        }
    } )
   
    $jin.method( '$jin_dom__select', function $jin_dom_ms__select( xpath ){
        var list= []
       
        var found= this.nativeNode().selectNodes( xpath )
        for( var i= 0; i < found.length; ++i ) list.push( $jin.dom( found[ i ] ) )
       
        return list
    } )

    $jin.method( '$jin_dom_parse', function $jin_dom_ms_parse( str ){
        var doc= new ActiveXObject( 'MSXML2.DOMDocument' )
        doc.async= false
        doc.loadXML( str )
        return $jin.dom( doc.documentElement || doc )
    } )

}

if( $jin.support.eventModel() === 'ms' ){

    $jin.method( '$jin_dom_listen', function $jin_dom_ms__listen( eventName, handler ){
        eventName = this.normalizeEventName( eventName )
        this.nativeNode().attachEvent( eventName, function( ){
            var ev = $jin.dom.event( event )
            if( event.type() !== eventName ) return
            return handler( event )
        } )
        return this
    } )
   
    $jin.method( '$jin_dom_forget', function $jin_dom_ms__forget( eventName, handler ){
        eventName = this.normalizeEventName( eventName )
        this.nativeNode().detachEvent( eventName, handler )
        return this
    } )
   
    $jin.method( '$jin_dom_scream', function $jin_dom_ms__scream( event ){
        event = $jin.dom.event( event )
        var eventName = this.normalizeEventName( event.type() )
        this.nativeNode().fireEvent( eventName, event.nativeEvent() )
        return this
    } )

    $jin.method( function $jin_dom_ms__normalizeEventName( eventName ){
        return /^[a-zA-Z]+$/.test( eventName ) ? 'on' + eventName : 'onbeforeeditfocus'
    } )
   
}
;//../../jin/dom/jin_dom.jam.js?=HNSTQEI0
$jin.klass( '$jin_wrapper', '$jin_dom' )

$jin.method( '$jin_wrapper_apply', function $jin_dom_apply( wrapper, args ){
    var obj = args[0]
    if( obj instanceof wrapper ) return obj
    if( obj[ wrapper ] instanceof wrapper ) return obj[ wrapper ]
    if( obj.$jin_wrapper__raw ) obj = args[0] = obj.raw()
    obj = wrapper.$jin_klass_apply( wrapper, args )
    var node = obj.nativeNode()
    try {
        node[ wrapper ] = obj
    } catch( e ){}
    return obj
} )

$jin.method( function $jin_dom_env( ){
    return $jin.env()
} )

$jin.method( function $jin_dom_parse( str ){
    var parser = new( $jin.dom.env().DOMParser )
    var doc = parser.parseFromString( String( str ), 'text/xml' )
    return $jin.dom( doc.documentElement || doc )
} )

$jin.method( function $jin_dom_escape( val ){
    return val.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' ).replace( /"/g, '&quot;' )
} )


$jin.method( function $jin_dom__nativeDoc( ){
    var node = this.raw()
    return node.ownerDocument || node
} )
   
$jin.method( function $jin_dom__nativeNode( ){
    return this.raw()
} )
   
$jin.method( function $jin_dom__toString( ){
    var serializer= new( $jin.dom.env().XMLSerializer )
    return serializer.serializeToString( this.nativeNode() )
} )
   
$jin.method( function $jin_dom__transform( stylesheet ){
    var proc= new( $jin.dom.env().XSLTProcessor )
    proc.importStylesheet( $jin.dom( stylesheet ).nativeDoc() )
    var doc= proc.transformToDocument( this.nativeNode() )
    return $jin.dom( doc )
} )
   
$jin.method( function $jin_dom__render( from, to ){
    from= $jin.dom( from ).nativeNode()
    to= $jin.dom( to ).nativeNode()
   
    var proc= new( $jin.dom.env().XSLTProcessor )
    proc.importStylesheet( this.nativeDoc() )
    var res= proc.transformToFragment( from, to.ownerDocument )
    to.innerHTML= ''
    to.appendChild( res )
   
    return this
} )
   
$jin.method( function $jin_dom__name( ){
    return this.nativeNode().nodeName
} )

$jin.method( function $jin_dom__attr( name, value ){
    if( arguments.length > 1 ){
        if( value == null ) this.nativeNode().removeAttribute( name )
        else this.nativeNode().setAttribute( name, value )
        return this
    } else {
        return this.nativeNode().getAttribute( name )
    }
} )
   
$jin.method( function $jin_dom__attrList( ){
    var nodes= this.nativeNode().attributes
   
    if( !nodes ) return []
   
    var list= []
    for( var i= 0; i < nodes.length; ++i ){
        list.push( $jin.dom( nodes[ i ] ) )
    }
   
    return list
} )

$jin.method( function $jin_dom__text( value ){
    if( arguments.length ){
        this.nativeNode().textContent = String( value )
        return this
    } else {
        return this.nativeNode().textContent
    }
} )

$jin.method( function $jin_dom__clear( ){
    var node = this.nativeNode()
    var child
    while( child= node.firstChild ){
        node.removeChild( child )
    }
    return this
} )

$jin.method( function $jin_dom__parent( parent ){
    var node = this.nativeNode()
    if( arguments.length ){
        if( parent == null ){
            parent= node.parentNode
            if( parent ) parent.removeChild( node )
        } else {
            $jin.dom( parent ).nativeNode().appendChild( node )
        }
        return this
    } else {
        parent= node.parentNode || node.ownerElement
        return parent ? $jin.dom( parent ) : parent
    }
} )

$jin.method( function $jin_dom__next( next ){
    var node = this.nativeNode()
    if( !arguments.length ){
        var next = node.nextSibling
        if( next ) next = $jin.dom( next )
        return next
    }
    throw new Error( 'Not implemented' )
    this.parent().nativeNode().insertBefore( $jin.dom( prev ).nativeNode(), node.nextSibling )
    return this
} )

$jin.method( function $jin_dom__prev( prev ){
    var node = this.nativeNode()
    if( !arguments.length ){
        var prev = node.previousSibling
        if( prev ) prev = $jin.dom( prev )
        return prev
    }
    this.parent().nativeNode().insertBefore( $jin.dom( prev ).nativeNode(), node )
    return this
} )

$jin.method( function $jin_dom__head( dom ){
    var node = this.nativeNode()
    if( !arguments.length ){
        var node = node.firstChild
        if( node ) node = $jin.dom( node )
        return node
    }
    node.insertBefore( $jin.dom( dom ).nativeNode(), this.head().nativeNode() )
    return this
} )

$jin.method( function $jin_dom__tail( dom ){
    var node = this.nativeNode()
    if( !arguments.length ){
        var node = node.lastChild
        if( node ) node = $jin.dom( node )
        return node
    }
    $jin.dom( dom ).parent( this )
    return this
} )

$jin.method( function $jin_dom__childList( ){
    var nodes= this.nativeNode().childNodes
   
    var list= []
    for( var i= 0; i < nodes.length; ++i ){
        list.push( $jin.dom( nodes[ i ] ) )
    }
   
    return list
} )

$jin.method( function $jin_dom__select( xpath ){
    var list= []
   
    var found= this.nativeDoc().evaluate( xpath, this.nativeNode(), null, null, null )
    for( var node; node= found.iterateNext(); ) list.push( $jin.dom( node ) )
   
    return list
} )


$jin.method( function $jin_dom__clone( ){
    return $jin.dom( this.nativeNode().cloneNode() )
} )

$jin.method( function $jin_dom__cloneTree( ){
    return $jin.dom( this.nativeNode().cloneNode( true ) )
} )


$jin.method( function $jin_dom__makeText( value ){
    return $jin.dom( this.nativeDoc().createTextNode( value ) )
} )

$jin.method( function $jin_dom__makeFragment( ){
    return $jin.dom( this.nativeDoc().createDocumentFragment() )
} )

$jin.method( function $jin_dom__makePI( name, content ){
    return $jin.dom( this.nativeDoc().createProcessingInstruction( name, content ) )
} )

$jin.method( function $jin_dom__makeElement( name, ns ){
    if( arguments.length > 1 ){
        return $jin.dom( this.nativeDoc().createElementNS( ns, name ) )
    } else {
        return $jin.dom( this.nativeDoc().createElement( name ) )
    }
} )

$jin.method( function $jin_dom__makeTree( json ){
    if( !json ) return this.makeFragment()
    if( ~[ 'string', 'number' ].indexOf( typeof json ) ) return this.makeText( json )
   
    var result = this.makeFragment()
    for( var key in json ){
        if( !json.hasOwnProperty( key ) ) continue
       
        var val = json[ key ]
        if( !key || Number( key ) == key ){
            this.makeTree( val ).parent( result )
        } else {
            var dom = this.makeElement( key )
            this.makeTree( val ).parent( dom )
            dom.parent( result )
        }
    }
    return result
} )

$jin.method( function $jin_dom__listen( eventName, handler ){
    var wrappedHandler = function( event ){
        return handler( $jin.dom.event( event ) )
    }
    this.nativeNode().addEventListener( eventName, wrappedHandler, false )
    return $jin.listener().crier( this ).eventName( eventName ).handler( wrappedHandler )
} )

$jin.method( function $jin_dom__forget( eventName, handler ){
    this.nativeNode().removeEventListener( eventName, handler, false )
    return this
} )

$jin.method( function $jin_dom__scream( event ){
    event = $jin.dom.event( event )
    this.nativeNode().dispatchEvent( event.nativeEvent() )
    return this
} )

;//../../jin/sample/jin_sample.jam.js?=HNSOBVEW
$jin.method( function $jin_sample( name ){
    var proto = document.getElementById( name )
    var sample = $jin.sample.make( proto )
    return sample()
})

$jin.method( function $jin_sample_make( proto ){
    proto = $jin.dom( proto )
   
    return function( ){
        var root = proto.cloneTree()
       
        var elems = {}
       
        function collect( node ){
            var childs = node.childNodes
            for( var i = 0; i < childs.length; ++i ) collect( childs[ i ] )
           
            var attrs = node.attributes
            if( !attrs ) return
            for( var i = 0; i < attrs.length; ++i ) elems[ attrs[i].nodeName.toLowerCase() ] = ( node )
        }
       
        collect( root.nativeNode() )
       
        return function( name ){
            if( name ) return elems[ name.toLowerCase() ]
            else return root
        }
    }
} )
</script>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.2.1/lodash.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.1.2/handlebars.min.js"></script>

<script src="http://cdn.kendostatic.com/2013.2.716/js/kendo.all.min.js"></script>

<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

<script src="http://www.jsviews.com/download/jsrender.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/hogan.js/2.0.0/hogan.js"></script>
<script src="https://raw.github.com/twitter/hogan.js/master/lib/compiler.js"></script>
<script src="http://olado.github.io/doT/doT.min.js">
</script>
<div id="jin_sample_root">
    <h1 jin_sample_header></h1>
    <h2 jin_sample_header2></h2>
    <h3 jin_sample_header3></h3>
    <h4 jin_sample_header4></h4>
    <h5 jin_sample_header5></h5>
    <h6 jin_sample_header6></h6>
    <ul jin_sample_list></ul>
</div>
<li id="jin_sample_item" jin_sample_item></li>
<!--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: "\"'><i>if italic then need escaping",
   list: ['<b>if bold then need escaping', '2', '3', '4', '5', '6', '7', '8', '9', '10']
  };
 

window.jinTemplateRoot = $jin.sample.make( document.getElementById( 'jin_sample_root' ) )
window.jinTemplateItem = $jin.sample.make( document.getElementById( 'jin_sample_item' ) )

  //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' title='{{>header6}}' name=\"{{>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' title='{{header6}}' name=\"{{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' title='{{header6}}' name=\"{{header6}}\">{{header6}}</h6><ul class='list'>{{#each list}}<li class='item'>{{this}}</li>{{/each}}</ul></div>");
 
  window.kendouiTemplate = kendo.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>", {useWithBlock:true});
 
  window.kendouiTemplate2 = kendo.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>", {useWithBlock:false});
 
  //Use external template definition
  window.kendoUIAlt = kendo.template($("#kendoUIextTemplate").html());
  window.kendoUIAlt2 = kendo.template($("#kendoUIextTemplate").html(), {useWithBlock:false});
 
  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' title='<%-header6%>' name=\"<%-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' title='<%-data.header6%>' name=\"<%-data.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>", {append:false});

 
  //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>");

  window.hoganTemplate = Hogan.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' title='{{> header6}}' name=\"{{> header6}}\">{{> header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{> .}}</li>{{/list}}</ul></div>");

window.xslTemplate = $jin.dom.parse(
'<xsl:stylesheet\
    version="1.0"\
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\
    >\
    <xsl:template match="root">\
        <div><xsl:apply-templates /></div>\
    </xsl:template>\
    <xsl:template match="header">\
        <h1 class="header"><xsl:apply-templates /></h1>\
    </xsl:template>\
    <xsl:template match="header2">\
        <h2 class="header2"><xsl:apply-templates /></h2>\
    </xsl:template>\
    <xsl:template match="header3">\
        <h3 class="header3"><xsl:apply-templates /></h3>\
    </xsl:template>\
    <xsl:template match="header4">\
        <h4 class="header4"><xsl:apply-templates /></h4>\
    </xsl:template>\
    <xsl:template match="header5">\
        <h5 class="header5"><xsl:apply-templates /></h5>\
    </xsl:template>\
    <xsl:template match="header6">\
        <h6 class="header6" title="{.}" name="{.}"><xsl:apply-templates /></h6>\
    </xsl:template>\
    <xsl:template match="list">\
        <ul class="list"><xsl:apply-templates /></ul>\
    </xsl:template>\
    <xsl:template match="item">\
        <li class="item"><xsl:apply-templates /></li>\
    </xsl:template>\
</xsl:stylesheet>\
'
)

var root = {}
for( var key in sharedVariables ) root[ key ] = sharedVariables[ key ]
root.list = root.list.map( function( item ){ return { item: item } } )
var sharedVariablesAsDom = $jin.dom.parse('<x/>').makeTree({ root: root })

</script>

<div id="template" style="display:none"></div>
<div id="output"></div>
<script>
window.output = document.getElementById('output')
window.wrappedOutput = $jin.dom( output )
</script>
<script>
Benchmark.prototype.setup = function() {
    output.innerHTML = ''
};
</script>

Preparation code output

  • Test runner

    Warning! For accurate results, please disable Firebug before running the tests. (Why?)

    Java applet disabled.

    Testing in unknown unknown
    Test Ops/sec
    doT.js
    // broken output
    // output.innerHTML = doTemplate(sharedVariables);
    pending…
    Kendo UI Templates (Default)
    // no escaped output
    // output.innerHTML = kendouiTemplate(sharedVariables);
     
    pending…
    Kendo UI Templates (No "with" block)
    // no escaped output
    // output.innerHTML = kendouiTemplate2(sharedVariables);
     
    pending…
    Handlebars.js
    output.innerHTML = handlebarsTemplate(sharedVariables);
     
    pending…
    Underscore.js Template
    output.innerHTML = underscoreTemplate(sharedVariables);
     
    pending…
    Resig Micro Templates (modified)
    // no escaped output
    // output.innerHTML = resig(sharedVariables);
     
    pending…
    Resig Micro Templates (No "with" block)
    // no escaped output
    // output.innerHTML = resig2(sharedVariables);
     
    pending…
    Underscore.js Template (No "with")
    output.innerHTML = underscoreTemplateNoWith(sharedVariables);
     
    pending…
    Hogan.js
    // broken output
    // output.innerHTML = hoganTemplate.render(sharedVariables);
     
    pending…
    JsRender
    output.innerHTML = jsRenderTemplate.render(sharedVariables);
     
    pending…
    Mustache.js Template
    output.innerHTML = Mustache.to_html(mustacheTemplate, sharedVariables);
     
    pending…
    jin_sample
    var root = jinTemplateRoot()
    root( 'jin_sample_header'  ).textContent = sharedVariables.header
    root( 'jin_sample_header2' ).textContent = sharedVariables.header2
    root( 'jin_sample_header3' ).textContent = sharedVariables.header3
    root( 'jin_sample_header4' ).textContent = sharedVariables.header4
    root( 'jin_sample_header5' ).textContent = sharedVariables.header5

    var h6 = root( 'jin_sample_header6' )
    h6.textContent = sharedVariables.header6
    h6.setAttribute( 'title', sharedVariables.header6 )
    h6.setAttribute( 'name', sharedVariables.header6 )

    var list = root( 'jin_sample_list' )
    for( var i = 0; i < sharedVariables.list.length; ++i ){
        var item = jinTemplateItem()
        item().textContent = sharedVariables.list[ i ]
        list.appendChild(item())
    }

    output.textContent = ''
    output.appendChild( root() )
    pending…
    jin_dom__render (xslt)
    xslTemplate.render( sharedVariablesAsDom, wrappedOutput )
     
    pending…

    Compare results of other browsers

    Revisions

    You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

    0 comments

    Add a comment