:[object Object]

[object Object]

HTML output

about JSL:JavaScript Language T1ransformations and T2emplating (JSL2)

/**

JSL is yet another templating framework, inspired by XSL, and the contemporary trend of using JSON data which are transformed to HTML either on the client-side or the server-side. The fundamendal concept is to exploit the powerful modern JavaScript engines in an efficient way to write templates with a minimal footprint.

From JSL's point of view native JavaScript has quite accomodating semantincs for a native template engine.

Consider a function DIV(arg1...argN) that concatenates its arguments into a string and wraps it with the HTML tags <div> and </div>. Now consider the same for all known HTML elements...

Consider also a function jsl.transform that writes HTML source to a stream or DOMNode

What would you expect the following script to do?

*/
var topics=document.querySelectorAll('main > nav a'); jsl.transform( DIV( H1('Is this JSL?'), P('Is it so simple to write an ',I('HTML'),' template?'), P( B(topics.length),' topics of documentation for this?' ), P('What about attributes?',INPUT({type:'checkbox',checked:true})), A({href:"#documentation"},BUTTON('Continue reading...')) ) );

HTML output

documentation: Basic JSL2 example

/**Basic example, illustrating JSL2. What would you expect the following script to do? */ var topics=document.querySelectorAll('main > nav a'); jsl.transform( DIV( H1('Is this JSL?...basic example continued'), P('What about css templates?',{class:'styled-paragraph'}), STYLE(jsl.CSS({ '.styled-paragraph' : { border:"1px solid black" }, '#documentation_out div':{ margin:"0.5em" }, '#documentation_out button':{ minHeight:"2.5em", border:"1px solid", boxSizing:"border-box" }, '#documentation_out button:hover':{ color:'#5e4709', borderWidth:"2px", }, '#dowload-link > button':{ textDecoration:'underline', 'background-color':'inherit' } })), P('What about iterations?'), UL( jsl.forEach(topics,function(topic,index){ if(index<4){ return LI(topic.innerText); } }), LI('...and ',(topics.length-4),'more') ), DIV( BUTTON('What about event handlers?', { onclick:function(event){ alert('Check out in the documentation.'); location.href='#function_properties'; } } ),' ', A({id:"dowload-link",href:"/jsl"+jsl.version+".js",target:"_blank"}, BUTTON('Got it! Just download JSL') ), ), ) );

HTML output

JSL tag-functions: tag-functions for HTML elements

/**Every HTML Element has a corresponding JSL2 tag-function. tag-functions take any number of arguments and return formatted HTML code.

Check in the following sections for a detailed explanation on how the arguments are treated depending on their types.

*/
jsl.transform( BUTTON(), //yields <button></button> BR(), //yields <br/> INPUT(), //yields <input/> TEXTAREA()//yields <textarea></textarea> )

HTML output

tag-function arguments: arguments transform to content

/** The arguments passed to the tag-functions are evaluated and generate the content(innerHTML) of the resulting element. In this example, several nested tag-functions are invoked with string parameters1. */ jsl.transform( DIV( H1('Hello JSL!'), P( 'Every HTML Element has a corresponding jsl tag-function.', 'The arguments passed to the tag-functions are evaluated ', 'and generate the content of the resulting element.' ), P('In this example we are using:'), OL( LI('DIV'), LI('H1'), LI('P'), LI('OL'), LI('LI'), ) ) ); /** 1JSL tag-functions themselves return strings. This allows JSL to work both on the client-side and the server-side without any external dependencies. */

HTML output

primitive arguments:

/** String, Numeric and Boolean arguments of tag-functions are concatenated and become the inner HTML of the resulting html element. Undefined or Null arguments are omitted from the results. For example: */ var x=5.3; jsl.transform(DIV( x //yields '5.3' ,'&lt;' //yields '<' ,x+1 //yields '6.3' ,'?' //yields '?' ,x<x+1 //yields 'true' ,null //yields nothing , //yields nothing ));

HTML output

array arguments:

/** Arrays passed as arguments are flattened recursivelly, and their items are evaluated like the rest of arguments. For example, the following: */ jsl.transform(DIV([1,[2,[3,[4,5],6],7],8])); /* will produce the same results as: */ jsl.transform(DIV( 1, 2, 3, 4, 5, 6, 7, 8));

HTML output

object arguments:objects passed as arguments

/**

The values of the object properties whose property-name is an integer string, are treated as normal arguments.

The named properties of an object passed as argument to a tag-function, are converted to HTML attributes. The name of the property determines the attribute's name, and the value of the property determines the attributes's value.

Properties that are found in multiple object arguments, are concatenated.

For example: */
jsl.transform( //the values of 0:,1: and 2: will be treated as normal arguments H4({0:"This is ",1:"the ",2:["HTML5 ","logo "]}), //an attribute 'src' with value '/html5logo.png' will be generated IMG({src:'/html5logo.png'}), //the class attribute will be the concatenation "blue centered box" DIV( {class:'blue'}, 'blue centered box', {class:'centered'}, {class:'box'} ) );

HTML output

boolean properties:

/**HTML boolean attributes indicate their value with their presence. The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value. Following this premise, boolean property values become corresponding boolean attributes(1) For example: */ jsl.transform( H4('Boolean values result in Boolean attributes',SUP('(1)')), LABEL("Checked and disabled:", INPUT({//will generate <input type="checkbox" checked disabled/> type:"checkbox", checked:true, disabled:true }) ),BR(), LABEL("Unhecked and enabled:", INPUT({//will generate <input type="checkbox"/> type:"checkbox", checked:false, disabled:false }) ),BR() ); /** Important reminder!

The && and || operators actually return the value of one of the specified operands. So if these operators are used with non-Boolean values, they may return a non-Boolean value. To force a property to generate a Boolean Attribute, the property value has to be typecast to Boolean.

In this example the expression (a&&b) evaluates to 0. We are using double negation to make sure that it evaluates as a Boolean.

*/
var a=0,b=false; jsl.transform( H4('The && and || operators are tricky...'), DIV({ hidden :(!!(a&&b)),//'hidden' will be omitted because (!!(a&&b))==false 'data-hidden':(a&&b),//'data-hidden' will be present because typeof (a&&b) == 'number' }, 'You can see this div. (!!(a&&b)) evaluates\ to the Boolean value false. Therefore\ hidden is treated as a Boolean attribute,\ and thus omitted from the HTML markup.\ Had we not used the casting, the generated\ markup would include the attribute hidden="0"\ which by its presence implies to hide the div.' ) ) /** (1)The exception being when the property's name is 'contenteditable' or 'spellcheck'. According to the standards, these two attributes may have the value true or false, thus JSL copies the Boolean value as Attribute value; For example: */ jsl.transform( H4(SUP('(1)'),'Except when the property name is ',I('contenteditable'),' or ',I('spellcheck')), INPUT({spellcheck:false,value:'no spell check heeere.'}) ); /*will generate <input spellcheck="false" value="no spell check heeere."/> keeping the attribute 'spellcheck' with its value set to false. */

HTML output

object properties:

/**

When the property name is data-* the property value(including arrays) is converted to a JSON string attribute.

In all other cases the string representation of the property value is used as attribute value(1)(2).

For example: */
jsl.transform( DIV({ 'class':['blue','centered','box'],//join the items of the array 'data-point':{x:1,y:2},//convert object to JSON and use as attribute value 'data-colors':['r','g','b'],//convert array to JSON and use as attribute value },'div with JSON data') ); /*will generate the equivalent: <div data-point="{"x":1,"y":2}" data-colors="["r","g","b"]"> div with JSON data </div> */ /**

(1)when the property name is style, property values are handled differently.

(2)when the property value is an Array, its items are concatenated using white-space as separator.

*/

HTML output

function properties:

/**Function property values are treated as event handlers. For that, they are wrapped in a call expression, so that at invokation they get the correct context of this and event. For example: */ jsl.transform( INPUT({ value:"click to conceal", onclick:function(event){ this.type="password"; } }) ); /*will generate the equivalent: <button onclick="(function(){ this.disabled=true; }).call(this,event);"> click to conceal </button> */ /**For those cases that we want to pass additional parameters to an event handler, JSL provides the utility function jsl.listener */

HTML output

properties named 'style':

/**When the name of an object property in the arguments of a jsl tag-function is style its value is converted to a CSS style declaration as follows:
  • Primitive values are converted to strings and are treated as if they are correct style definitions.
  • Object values are treated as if they are CSS style declarations. The properties of the object, become CSS properties. You can use the camel case property notation or the css property name notation. In the example below, fontSize corresponds to the CSS property font-size
*/
jsl.transform( DIV({ style : 'font-size:12pt; color:blue' /** style="font-size:12pt; color blue;" */ },'bar'), DIV({ style:{/** style="font-size:20pt;text-align:right; */ fontSize : '20pt', /** font-size:20pt; */ 'text-align': 'right' /** text-align:right */ } },'bar'), );

HTML output

function arguments:

/**

A function object passed as argument to any JSL tag-function other than SCRIPT(...), is treated like all normal object arguments. ...With a useful twist though.

  • The function's comments are used as cdata and copied to the output.
    This feature can be disabled with jsl.options.autocdata
  • The function's name is used as attribute id, if id is not already defined.
    This feature can be disabled with jsl.options.fnametoid
  • The function itself is invoked assuming it is of the form function(tagName, tagAttr, tagBody). This allows it to change the tag-function attributes and body before the tag-function returns.


A function object passed as argument to SCRIPT(...), implies that we want to generate a <script>...</script> tag containing that function.

  • named functions are copied to the output, thus become available in the runtime as normal client-side functions.
  • anonymous functions are wrapped as scripts to be executed in the output document's flow.
For that, JSL uses internally the utility function jsl.javascript

*/
function add(x,y){ var vx=x.r||x,vy=y.r||y,vr=vx+vy; var tx=x.t||x,ty=y.t||y,tr=tx+'+'+ty+'='+vr; return {r:vr, t:tr}; } function targetForScript(){/***The name of this function(targetForScript) will become the id attribute of the generated tag in the following javascript.These comments will become the body of it.
*/
}
; targetForScript.style={color:'red',border:'1px solid'};//add also a property for style jsl.transform( P(targetForScript),//the function 'targetForScript' will be treated as a normal object(...with a twist) SCRIPT( add,//the named function 'add' will be copied to the body of the generated <script> tag function mul(x,y){//locally defined named function 'mul', will be copied to the generated <script> tag var vx=x.r||x,vy=y.r||y,vr=vx*vy; var tx=x.t||x,ty=y.t||y,tr='('+tx+')*('+ty+')='+vr; return {r:vr,t:tr}; }, function(){//anonymous function, will be wrapped as a 'function call' var e=mul(add(1,2),add(3,4)); document.getElementById("targetForScript").innerHTML+=e.t; alert(e.t+"\nCheck out the DOM tree and HTML output"); } ), //using a function to filter the results before output P( "This is a parenthetic remark", filterFunction=function(tagName,tagAttr,tagBody){/*color:black;padding:4pt;border:1px dotted gray;*/ var myComments=tagBody.pop(); var prevMarkup=tagBody.pop(); if(prevMarkup.indexOf("parenthetic")>=0){ tagAttr.style=myComments;//pop my comments from the tagBody, and use them as style tagBody.push('('+prevMarkup+')');//pop the previous argument's construct, and place it in parentheses. }else if(prevMarkup.indexOf("obsolete")<0){ tagBody.push(prevMarkup); }else{ //remove obsolete } }, "The filter-function did the job.", filterFunction,//does nothing "Therefore this text is obsolete", filterFunction//removes the previous line ) );

HTML output

JSL utilities: title

/**JSL provides a number of utilities that in combination with the tag-functions create a powerful framework for creating HTML and CSS templates both on the client-side and the server-side. In all the examples so far you may have noticed frequent calls to jsl.transform and jsl.forEach */

HTML output

jsl.forEach: jsl.forEach(collection,callback,options)

/** collection:: any non primitive object
callback :: function(value,nameOrIndex,source,results){ ... }
should return a new item for the results, or add it manually and return nothing.

The helper function jsl.forEach iterates(1) through the properties of the object provided as first argument and passes them to a callback function as below:

*/
jsl.transform( H3("4 singleton tags"), UL(//iterate through the jsl.defs object and generate a list of the singleton tags jsl.forEach(jsl.defs,function(value,name,source,results){ if((value.singleton)&&(results.length<4)){ return LI(name); } }) ), H3("Triangle sides"), UL(//iterate through an Array and generate some list items jsl.forEach(['A','B','C'],function(item,index,source){ var next=source[(index+1)%(source.length)]; return LI(item,next); }) ), H3("Some custom attributes"), P(//iterate through an object's properties and generate some custom data-attributes 'Should have ', jsl.forEach({x:1,y:2,z:3},function(value,name,source,results){ if(name){ results['data-'+name]=value; } if(results.length){ results.push(name?', ':' and '); } results.push(I("data-"+name)); }), ' attributes.' ) ); /** (1)When you know you are iterating an array, perhaps it is faster to use instead the build in function Array.prototype.map */

HTML output

jsl.transform: writing generated content to the output context

/** jsl.transform transforms its arguments to an HTML fragment, and appends it to a target DOMNode(1) depending on the invokation context(2)
  • Before the document is loaded,jsl.transform appends its fragment to the end of the document using document.write().
  • After the document is loaded, jsl.transform by default inserts its fragment before the end of the document body.
  • The function can be bound using call or apply to a CSS selector string or DOM Node object as this context to define the target where the results should be appended.
  • Alternativelly, the jsl.target property can be used to define the target context.
For example all the following statements are equivalent: */
jsl.transform.call('footer .extra',/**using a css selector as this*/ P('jsl',SUP(jsl.version)) ); jsl.transform.call(document.querySelector('footer .extra'),/**using a DOM Node as this*/ P('jsl',SUP(jsl.version)) ); jsl.target='footer .extra';/**assigning a css selector to jsl.target*/ jsl.transform(P('jsl',SUP(jsl.version))); jsl.target=document.querySelector('footer .extra');/**assigning a DOM Node to jsl.target*/ jsl.transform(P('jsl',SUP(jsl.version))); /**

(1)The function has slightly different usage on the server-side.

(2)in this example, before the invokation, the target of jsl.transform was set via the jsl helper property jsl.target to the panel on the right.

*/

HTML output

server-side: jsl.transform invokation on the server-side

/** JSL on nodejs is quite like the client.

You can set the jsl.target to a response or any object that has a method write before calling jsl.transform

or you can invoke jsl.transform using call or apply binding its 'this' to a response or any object that has a method write

*/
/**A typical initialization of of JSL as nodejs module*/ const jsl=require('jsl2.1.js'); jsl.init(jsl,global); /**A typical way of applying a JSL transformation on a response stream of express, using jsl.transform.call(response,....)*/ const app = express({simQuery:"/mypath/home?w=9em&h=5em"}); app.get('/mypath',function(request, response){ response.status(200); jsl.transform.call(response,HTML({lang:"en-US",dir:"ltr"}, HEAD( TITLE(request.user.name,' home page'), META({name:"generator",content:"jsl2"}), STYLE(//dynamic CSS from request jsl.CSS({/*use the query to create a style sheet*/ 'body':{ backgroundImage:"url("+request.user.pic+")", }, 'p':, 'h1':{color:request.user.colorB}, 'img':{height:request.query.h,width:request.query.w} }),function() ) ), BODY )); response.end(); });

HTML output

jsl.listener: jsl.listener

/**An alternative way to make event handlers is by using the helper function jsl.listener

Calls to jsl.listener have the following format:

jsl.listener(buildTime1...,buildTime1N,function(event,runTime1,...runTime1N){
	...
})
The parameters buildTime1...,buildTime1N are evaluated at build time and are mapped to the event-handler as arguments runTime1...,runTime1N. This allows to make event handlers aware of their environment with a very intuitive way.

Consider the following example and compare how the even figures versus the odd ones generate the onclick event handler. Notice how the even figures generate the onclick event handler using jsl.listener as follows:

onclick: jsl.listener(item,function(event,person){
		...
})
The argument item of jsl.listener is known at the time JSL produces the script. jsl.listener maintains a reference to it, and passes it to the event-handler as param person at the time the click event fires.

*/
jsl.transform(H4("Click to reveal the birthday"),DIV({class:'people'}, jsl.forEach(someDataWePulledEarlierFromTheServer,function(item,index){ switch(index % 2){ case 0://use jsl.listener to marshal the items to the event-handler return FIGURE({ onclick:jsl.listener(item,function(event,person){ alert( "The birthday of "+ person.name+"\nis on "+person.bday ); }) }, IMG({src:item.href}), FIGCAPTION(item.name) ); case 1://use data-* attributes to access the item.* values return FIGURE({ 'data-bday':item.bday, 'data-name':item.name, onclick:function(event){ alert( "The birthday of "+ this.getAttribute("data-name")+ "\nis on "+this.getAttribute("data-bday") ); } }, IMG({src:item.href}), FIGCAPTION(item.name) ); }; }) ) ); /**

For a similar method suitable for generating javascript in <script> tags see also jsl.javascript

JSL is using jsl.listener internally when function properties of object arguments are processed.

*/

HTML output

jsl.cdata: jsl.cdata(fn)

/** fn:: a function object whose first multiline comments are to be converted to character data jsl.cdata allows easy html cdata output without string escaping.
  • If the first multiline comment starts with a single '*' the comment contents are treated as text. Thus the apearence of < is escaped with the sequence &lt;
  • If the first multiline comment starts with a double '**' the comment contents are treated as html.
jsl.cdata is internally invoked when tag-functions contain function arguments */
jsl.transform( DIV( H3("jsl.cdata is cool!"), /** A call to cdata with single '*' */ P(jsl.cdata(function(){/* <h3>jsl.cdata is cool!</h3> <p><i>jsl.cdata</i> can be used to generate cdata whithout having to use <i>strings</i> escaping</p> In this example the comments of the function are conveted to html text. */}) ), /** A similar call to cdata with double '**' */ DIV(jsl.cdata(function(){/** <h3>jsl.rdata is cool!</h3> <p><i>jsl.rdata</i> can be used to generate raw <b>html</b>, whithout having to use <i>strings</i>...</p> In this example the comments of the function are converted to html markup. */}) ) ) );

HTML output

jsl.tdata:

/** jsl.tdata a variation of jsl.cdata that always returns text content*/

HTML output

jsl.rdata:

/** jsl.rdata a variation of jsl.cdata that always returns html content*/

HTML output

jsl.CSS: jsl.CSS(arg1...argN)

/**jsl.CSS is a helper function that converts its arguments to CSS style rules
  • primitive arguments are expected to be valid CSS style rules.
  • function arguments are evaluated repeativelly until they yield a non function
  • object arguments, excluding arrays, are treated as if each property of the argument defines a CSS style rule.
    • the property name defines the CSS selector
    • the property value defines the CSS style declaration of the rule as in the case of style
  • array arguments, the array items are evaluated with the above rules and their results are merged
*/
var data=[]; jsl.transform( STYLE( jsl.CSS({//arg1 of the CSS is an object /*a rule for body > nav */ 'body > main > nav':{ backgroundColor:'#e5dccc' }, /*a rule for body > main > nav li:only-child */ 'body > main > nav li:last-of-type > nav > a':{ backgroundColor:'#f1c778' } }) ) ) /** A quite typical usecase for jsl.CSS is when you want to customize the style to the user preferences. Check for example in the source of the server-side jsl.transform example. */

HTML output

jsl.javascript: jsl.javascript(arg1,arg2,...argN,fn)

/**When we want to pass parameters inside a <script>...<script> we can use the helper function jsl.javascript . fn:: is the function that should be invoked with arguments arg1...argN */ var foo={msg:"hello"}; jsl.transform( SCRIPT(jsl.javascript(foo,function(obj){ alert(obj.msg);/*at run time, obj will refer to foo*/ })) ); /*this flag will make the client use JSON like the server-side would*/ jsl.options.forceJSON=true; jsl.transform( SCRIPT(jsl.javascript(foo,function(obj){ alert(obj.msg); })) ); delete jsl.options.forceJSON; /** For a similar method suitable for generating event handlers, see also jsl.listener */

HTML output

jsl.choose:

/**jsl.choose is a helper function that can be called in two ways:

If the first argument(case) is a Number,Boolean,or a String value, and its second argument an object(cases) then it returns the property of the object cases with property name == case. If no such property exists, and cases has a property named default then jsl.choose will return the default property. Notice that this behavior can be written in pure javascript quite efficiently with javascript expressions, yet JSL provides it for aesthetic reasons.

If the arguments don't match the criteria above, jsl.choose returns its first non void argument

Notice that in all cases the return values are evaluated whether there is a match or not. This may have both performance implications, and lead to unintented errors. So it is strongly recommended to return prefabricated functions/objects, or even avoid jsl.choose.

*/
jsl.transform( //using javascript expressions jsl.forEach(['bold','italic','underline','normal'],(item)=>( ({ 'bold' :B, 'italic' :I, 'underline' :U }[item]||SPAN)('[',item,']') ) ), HR(), //using jsl.choose jsl.forEach(['bold','italic','underline','normal'],(item)=>jsl.choose(item,{ 'bold' :B, 'italic' :I, 'underline' :U, default :SPAN })('[',item,']') ), HR(), //the second mode of usage for jsl.choose: jsl.forEach(['bOld','itaLic','underlime','normal'],(item)=>jsl.choose( jsl.when(item.length==4, B), jsl.when(/^i/.test(item), I), jsl.when(item[0]=='u', U), SPAN, )('[',item,']') ), )

HTML output

jsl.when:

/** jsl.when(test,trueResult,falseResult) is a simple function that takes three arguments, and returns the argument trueResult if the test is non void, or the argument falseResult otherwise.

Notice that this behavior can be written in pure javascript quite efficiently with javascript expressions, using the test?trueResult:falseResult. yet JSL provides it for aesthetic reasons, and because quite often the falseResult is omitted.

Please notice that in JavaScript all the arguments of a function are evaluated before the source of a function is executed. Therefore, jsl.when(false,TAKE_THE_RED_PILL(),TAKE_THE_BLUE_PILL()), will first call both functions and then return the result of the second.

see also jsl.choose for a relevant function.

*/

HTML output

jsl.defs: known tags with a corresponding tag-function

/**

jsl.defs containst the names of the tags with a corresponding JSL tag-function, and a flag indicating whether they are singletons(1).

See below: */
jsl.transform( H3('HTML tags:'), P( jsl.forEach(jsl.defs,function(value,name,defs,results,index){ return [index?', ':'',name,value.singleton&&I('(singleton)')]; }) ) ) /**

(1)singleton tags, e.g. <br/> don't have a closing tag

*/

HTML output

jsl.tags: jsl-functions corresponding to HTML elements

/**jsl.tags contains the jsl-functions corresponding to HTML elements. See below: */ jsl.transform( H3('HTML tags:'), P( jsl.forEach(jsl.tags,function(value,name,defs,results,index){ return [index?', ':'',value().replace(/</g,'&'+'lt;')]; }) ), ) /** Let us know if you are aware of a tag that should be in the list.*/

HTML output

jsl.target: target of jsl.transform

/**

jsl.target can be used to define the target Node of jsl.transform. It can be set to any valid css selector, or to a Node instance. Alternativelly, jsl.transform can be called with call or apply to define its target node.

For example:*/
jsl.target='footer .extra';//jsl.target set to a css selector jsl.transform(P('jsl',SUP(jsl.version))); jsl.target=document.querySelector('footer .extra');//jsl.target set to a DOM node jsl.transform(P('jsl',SUP(jsl.version)));

HTML output

jsl.register: jsl.register(tagName,singleton,namespace)

/** jsl.register(tagName,singleton,namespace)

registers a new tag tagName as a JSL tag-function

singleton is optional, by default false

namespace is an optional object where the new functions will be registered;
by default is the same namespace as with all the rest tag-functions

For example: */
/* register the svg element svg, as a tag-function SVG(...) */ jsl.register('svg'); /* make a namespace object for the rest of svg tag-functions, to keep things tiddy */ var svg={}; /* register the singleton svg element ellipse, as a tag-function svg.ELLIPSE(...) */ jsl.register('ellipse',true,svg); /* register the svg element text, as a tag-function svg.TEXT(...) */ jsl.register('text',false,svg); /* let's declare also a custom function that helps with svg transformations */ svg.translate=function(dx,dy){ return {transform:'translate('+dx+','+dy+')'}; } /* now it is JSL showtime as usual */ jsl.transform( SVG({width:'100%',height:200,style:'border:1px solid black'}, jsl.forEach([0,50,100],function(vx,i){ return jsl.forEach([0,50,100],function(vy,j){ if((i*j)%2){ return; } return svg.ELLIPSE({cx:vx+'%',cy:vy+'%',rx:20,ry:15, style:{ fill:'rgb(255,'+ vx +','+ vy +')' } }, svg.translate((0.5-vx/100)*48,(0.5-vy/100)*38) ) }) }), svg.TEXT({x:'50%',y:'50%','text-anchor':'middle'}, 'This how jsl2svg.js could look like...' ) ) );

HTML output

JSL options:

/**

jsl.options define the performance and code-styling configuration of JSL. The following script generates a list of the current configuration, and the alternative settings:

*/
jsl.transform(TABLE(jsl.forEach(jsl.options,(value,name)=>name&&[ TR( TD(I(name)),TD(B(value==!!value?value:'"'+value+'"')),TD(jsl.describe(name,value)) ), (value===!!value? TR(TD({colspan:3},!value,':',jsl.describe(name,!value))) :null) ]))); /** The options can be set through the jsl.options variable, and alternatively on the client through the calling script tag For example the declaration used to load JSL in this page is:
<script src="jsl2.js?optimize=no&release=yes></ script>
*/

HTML output

JSL templates and examples:

/**

JSL is pure javascript, its tag-functions are pure javascript functions, and accordingly JSL templates are also pure javascript functions.

*/
var people=someDataWePulledEarlierFromTheServer; //let's define a custom template function PERSON(person,index){ return FIGURE({class:'person-figure'}, IMG({src:person.href}),jsl.when(this!=window,this), FIGCAPTION(person.name,'#',index+1,SPAN(person.bday)) ) } //let's try it out... jsl.transform( STYLE(jsl.CSS({ '.person-figure SPAN':{bottom:0,right:0} })), H4("Some imaginary people"), DIV({class:'people'}, jsl.forEach(people.slice(0,2),PERSON) ) ) //now let's put some extra styling in the template var customStyle={style:'outline:4px solid',0:B({style:'bottom:0;'},'nice border')}; /**

We will be binding the template PERSON to the object customStyle. During execution, the PERSON's 'this' will refer to customStyle. In its declaration, the PERSON passes its 'this' binding as an argument to the FIGURE tag-function.

The rest is JSL magic

*/
jsl.transform( STYLE(jsl.CSS({ '.person-figure SPAN':{bottom:0,right:0} })), H4("Some imaginary people"), DIV({class:'people'}, jsl.forEach(people.slice(2,4),PERSON.bind(customStyle)) ) ) /** Now let's define the same template using an arrow function. */ PERSON=(person,index)=>FIGURE({class:'person-figure'}, jsl.when(this!=window,this), IMG({src:person.href}), FIGCAPTION(person.name,'#',index+1,SPAN(person.bday)) ); /** Notice! The binding to the customStyle will not work because of the arrow function in the declaration of PERSON. Arrow functions inherit 'this' from their context of execution. */ jsl.transform( STYLE(jsl.CSS({ '.person-figure SPAN':{bottom:0,right:0} })), H4("Some imaginary people"), DIV({class:'people'}, jsl.forEach(people.slice(4,6),PERSON.bind(customStyle)) ) );

HTML output

this documentation:

/** Download the documentation.js and try your own JSL template on it...

The content you see is generated using a JSL module on the server-side(nodejs).

The execution of the examples, and the DOM tree generation is performed on the browser.

Below is the template function that the server uses to build the navigation bar: */
BUILDMENU=function(fn,name=''){ var ref='#'+name.replace(/\s+/g,'_').replace(/\'|\"/g,''); return NAV( name&&A({href:ref},SPAN({class:"tick-mark"},'✓'),name), OL(jsl.forEach(fn,(f,m)=>(m=='_ref')?null:LI(BUILDMENU(f,m)))) ) }; jsl.transform(SCRIPT({src:'/node_modules/documentation.js?ns=jsldoc',async:true, onload:function(){jsl.transform(BUILDMENU(jsldoc));} }));

HTML output

DOM tree visualization:

/**this is how the DOM Tree is created in all the examples*/ jsl.transform( SCRIPT( function doDomTreeClick(event), BUILDDOMTREE=((node)=>(DOMTREECASES[node.nodeType](node))), ()=>{ DOMTREECASES={ 1: //ELEMENT_NODE (node,name=node.nodeName.toLowerCase(),singleton=!node.hasChildNodes() && jsl.defs[name].singleton)=> DIV({class:'ELEMENT'}, DIV({class:['tag-open',name]},singleton?{class:'tag-close'}:{onclick:doDomTreeClick}, '&','lt;',SPAN(name), jsl.forEach(node.getAttributeNames(),(name) => DIV({class:'tag-attr'},' ',SPAN(name), jsl.when(node.getAttribute(name),['=&','quot;',SPAN(node.getAttribute(name)),'&','quot;']) ) ), (singleton?'/>':'>') ), jsl.forEach(node.childNodes,BUILDDOMTREE), jsl.when(!singleton,DIV({class:'tag-close'},'&','lt;/',SPAN(name),'>')) ), 3: //TEXT_NODE (node)=>SPAN({class:'TEXT'},node.nodeValue.replace(/\</g,"<").replace(/([\r\n\s]+)/g," ")), 4: //CDATA_SECTION_NODE (node)=>SPAN({class:'CDATA'},'<![CDATA['+node.nodeValue+']]>'), 7: //PROCESSING_INSTRUCTION_NODE: (node)=>SPAN({class:'PROCESSING_INSTRUCTION'},'<?',node.nodeValue,'?>'), 8: //COMMENT_NODE (node)=>SPAN({class:'COMMENT'},'<!--',node.nodeValue,'-->'), 9: //DOCUMENT_NODE (node)=>DIV({class:'DOCUMENT'},SPAN('#document'),jsl.forEach(node.childNodes,BUILDDOMTREE)), 10: //DOCUMENT_TYPE_NODE (node)=>DIV({class:'DOCUMENT_TYPE'},'<!doctype ',node.name,'>'), 11: //DOCUMENT_FRAGMENT_NODE (node)=>DIV({class:'DOCUMENT_FRAGMENT'},SPAN('#document-fragment'),jsl.forEach(node.childNodes,BUILDDOMTREE)), }; }, ), STYLE(()=>), ); jsl.transform( DIV( H3('the DOM tree of the footer'), DIV({style:'overflow-x:hidden'}, BUILDDOMTREE(document.querySelector('body footer')) ) ) );

HTML output

data transformation on the go:

/**

In this example we are using jsl.transform to populate the nodes of a tree from the linux distribution as they are pulled from the server.

Notice that instead of XHR we are using the <script>...</script> element to fetch the json data from the server asynchronously. This is a very common approach in JSL;-).

*/
var folder; jsl.transform( SCRIPT( /**load the files of this node, or start from scratch*/ function loadFolder(event){ event && event.stopPropagation && event.stopPropagation(); jsl.transform( (event? /**generate an async scrypt that will load the files
of the current path*/
SCRIPT({src:this.id+"?folder",async:true, /**jsl.listener will make sure that this DOMNode,
will be propagated as param fileItem
to 'doFilesLoaded' and 'doFilesError'. */
onload :jsl.listener(this,doFilesLoaded), onerror:jsl.listener(this,doFilesError),
}
)
: /**start from scratch(event is undefined)*/ DIV("/sys",{class:"file-item pending", id:"/sys",onclick:doFileClick })
)
)
;
}
, /**the subfolder list is loaded, event-handler for script.onload*/ function doFilesLoaded(event,fileItem){ event.stopPropagation(); /** remove the <script>...</ script> tag from the document, we are done with it */ this.outerHTML=''; if(!fileItem.classList.contains('pending')){ return; } fileItem.classList.remove('pending'); /**now that we got the files of let's populate a <ul>...</ul> in fileItem*/ jsl.transform.call(fileItem,UL( jsl.forEach(folder.files,(file,name)=>LI({ class:"file-item pending",id:file.path, onclick:doFileClick },SPAN(name))) ) ); }, /**the subfolder list is failed to load, event-handler for script.onerror*/ function doFilesError(event,fileItem){ event.stopPropagation(); this.outerHTML=''; fileItem.classList.add('error'); }, /**a fileItem was clicked, event-handler for fileItem.onclick*/ function doFileClick(event){ if(this.classList.contains('pending')){ loadFolder.call(this,event); }else if(this.querySelector('ul')){ this.classList.toggle('collapsed'); }; event.stopPropagation(); }, function(){ loadFolder(); }
)
, /** And some CSS for styling the file tree */ STYLE(function())
)

HTML output

DOM tree

ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab