JavaScript template language shootoff (v864)

Revision 864 of this benchmark created on


Description

A limited comparison of some popular JavaScript templating engines on a short template: 6 header tags, and 10 list items. Compared templating engines:

Preparation HTML

<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?=HO0FZ4PK
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' )||( !func.$jin_method_name )){
                if(!( key in target )) target[ key ] = void 0
                continue
            }
            
            var methodName = func.$jin_method_name.replace( /^([$\w]+_)+/, '' )
            $jin.method.define( targetPath + '_' + methodName, func )
        }
    })
    
    return target
}

;//../../jin/method/jin_method.jam.js?=HNUF10CW
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 ) func.$jin_method_name = name
    //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?=HO0FS3MG
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
    
    var ownerName = name.replace( /_[a-z0-9]+$/i, '' )
    $jin.trait( ownerName )[ fieldName ] = void 0
    
    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/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/env/jin_env.jam.js?=HJWSZD88
this.$jin.env = $jin.value( function(){ 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/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/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/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.jam.js?=HO0G94A0
$jin.klass( '$jin_wrapper', '$jin_dom' )

$jin.method( '$jin_wrapper_apply', function $jin_dom_apply( wrapper, args ){
    var node = args[0]
    if( node instanceof wrapper ) return node
    if( node[ wrapper ] instanceof wrapper ) return node[ wrapper ]
    if( node.$jin_wrapper__raw ) node = args[0] = node.raw()
    var obj = this.$jin_klass_apply( wrapper, args )
    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 ){
    var node = this.nativeNode()
    if( arguments.length ){
        if( 'textContent' in node ) node.textContent = String( value )
        else if( 'innerText' in node ) node.innerText = String( value )
        else node.text = String( value )
        return this
    } else {
        return ( 'textContent' in node ) ? node.textContent : ( 'innerText' in node ) ? node.innerText : node.text
    }
} )

$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__xpath( xpath ){
    var node= this.nativeDoc().evaluate( xpath, this.nativeNode(), null, null, null ).iterateNext()
    if( !node ) return node
    return $jin.dom( node )
} )

$jin.method( function $jin_dom__css( css ){
    var node = this.nativeNode().querySelector( css )
    if( !node ) return node
    return $jin.dom( node )
} )


$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/dom/jin_dom.env=web.jam.js?=HNUI9BUW
$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 ){
        var node = this.nativeNode()
        if( arguments.length ){
            node.innerText = value
            return this
        } else {
            return node.innerText
        }
    } )
    */
    $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/sample/jin_sample.jam.js?=HO0F4RCG
$jin.method( function $jin_sample( name ){
    return $jin.sample.make( document.getElementById( name ) )
})

$jin.method( function $jin_sample_make( proto ){
    //proto = $jin.dom( proto )
    return function( ){
        return proto.cloneNode(true)
    }
} )

</script>

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

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

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

<script src="https://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="https://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 class="jin_sample_header"></h1>
    <h2 class="jin_sample_header2"></h2>
    <h3 class="jin_sample_header3"></h3>
    <h4 class="jin_sample_header4"></h4>
    <h5 class="jin_sample_header5"></h5>
    <h6 class="jin_sample_header6"></h6>
    <ul class="jin_sample_list"></ul>
</div>
<li id="jin_sample_item" class="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( 'jin_sample_root' )
window.jinTemplateItem = $jin.sample( 'jin_sample_item' )

var sampleRender = function( output, data ){
        output.textContent = ''
        output.appendChild( sampleRender.root( jinTemplateRoot(), data ) )
        return output
}

sampleRender.root = function( root, data ){
        root.querySelector( '.jin_sample_header'  ).textContent = data.header
        root.querySelector( '.jin_sample_header2' ).textContent = data.header2
        root.querySelector( '.jin_sample_header3' ).textContent = data.header3
        root.querySelector( '.jin_sample_header4' ).textContent = data.header4
        root.querySelector( '.jin_sample_header5' ).textContent = data.header5

        var h6 = root.querySelector( '.jin_sample_header6' )
        h6.textContent = data.header6
        h6.setAttribute( 'title', data.header6 )
        h6.setAttribute( 'name', data.header6 )
        
        this.list( root.querySelector( '.jin_sample_list' ), data.list )
        
        return root
}

sampleRender.list = function( list, data ){
        for( var i = 0; i < data.length; ++i ){
                var item = jinTemplateItem()
                item.textContent= data[ i ]
                list.appendChild( item )
        }
        return list
}


var sampleRenderReuse = function( output, data ){
        var root = output.querySelector( '.jin_sample_root'  )
        if( root ){
                sampleRenderReuse.root( root, data )
        } else {
                output.textContent = ''
                output.appendChild( sampleRender.root( jinTemplateRoot(), data ) )
        }
        return output
}

sampleRenderReuse.root = function( root, data ){
        this.text( root.querySelector( '.jin_sample_header'  ), data.header )
        this.text( root.querySelector( '.jin_sample_header2' ), data.header2 )
        this.text( root.querySelector( '.jin_sample_header3' ), data.header3 )
        this.text( root.querySelector( '.jin_sample_header4' ), data.header4 )
        this.text( root.querySelector( '.jin_sample_header5' ), data.header5 )

        var h6 = root.querySelector( '.jin_sample_header6' )
        this.text( h6, data.header6 )
        this.attr( h6, 'title', data.header6 )
        this.attr( h6, 'name', data.header6 )
        
        this.list( root.querySelector( '.jin_sample_list' ), data.list )
        
        return root
}

sampleRenderReuse.list = function( list, data ){
        for( var i = 0; i < list.childNodes.length; ++i ){
                var item = list.childNodes[i]
                this.text( item, data[ i ] )
        }
        for( var i; i < data.length; ++i ){
                var item = jinTemplateItem()
                item.textContent= data[ i ]
                list.appendChild( item )
        }
        for( var i; i > list.childNodes.length; --i ){
                var item = list.childNodes[i]
                list.removeChild( item )
        }
        return list
}

sampleRenderReuse.text = function( text, data ){
        if( text.textContent !== data ) text.textContent = data
        return text
}

sampleRenderReuse.attr = function( attr, key, data ){
        if( attr.getAttribute( key ) !== data ) attr.setAttribute( key, data )
        return text
}

  //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="data">\
        <div>\
                        <h1 class="header"><xsl:value-of select="@header" /></h1>\
                        <h2 class="header2"><xsl:value-of select="@header2" /></h2>\
                        <h3 class="header3"><xsl:value-of select="@header3" /></h3>\
                        <h4 class="header4"><xsl:value-of select="@header4" /></h4>\
                        <h5 class="header5"><xsl:value-of select="@header5" /></h5>\
                        <h6 class="header6" title="{@header6}" name="{@header6}"><xsl:value-of select="@header6" /></h6>\
                        <ul class="list"><xsl:apply-templates mode="list" /></ul>\
                </div>\
    </xsl:template>\
    <xsl:template match="item" mode="list">\
        <li class="item"><xsl:value-of select="." /></li>\
    </xsl:template>\
</xsl:stylesheet>\
')


var root = $jin.dom.parse('<data/>')

root.attr( 'header', sharedVariables[ 'header' ] )
root.attr( 'header2', sharedVariables[ 'header2' ] )
root.attr( 'header3', sharedVariables[ 'header3' ] )
root.attr( 'header4', sharedVariables[ 'header4' ] )
root.attr( 'header5', sharedVariables[ 'header5' ] )
root.attr( 'header6', sharedVariables[ 'header6' ] )

for(var i = 0; i<sharedVariables.list.length;++i){
  var item = sharedVariables.list[i]
        root.makeElement( 'item' ).text( item ).parent( root )
}
window.sharedVariablesAsDom = 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>

Setup

output.innerHTML = ''

Test runner

Ready to run.

Testing in
TestOps/sec
jin_sample (reuse dom)
sampleRenderReuse( output, sharedVariables )
 
ready
jin_sample
sampleRender( output, sharedVariables )
ready
jin_dom__render (xslt)
xslTemplate.render( sharedVariablesAsDom, wrappedOutput )
 
ready
Handlebars
output.innerHTML = handlebarsTemplate(sharedVariables);
 
ready
Underscore
output.innerHTML = underscoreTemplate(sharedVariables);
 
ready
JsRender
output.innerHTML = jsRenderTemplate.render(sharedVariables);
 
ready
Underscore (no with)
output.innerHTML = underscoreTemplateNoWith(sharedVariables);
 
ready
Mustache
output.innerHTML = Mustache.to_html(mustacheTemplate, sharedVariables);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.