{namespace my.app.namespace} #Namespace # #Template files begin with a namespace and end with a .xjs extension. # #If you instantiate the compiler with global=>true (default), then #you can call window.my.app.namespace in a browser, or in the global scope #of where your template is executed. You can optionally set global=>false, in #this case, X would return an object, and the namespaces would be assigned to #said object. # #Whitespace isn't allowed before a namespace declaration. The idea is to enforce #A coding style. #Comments # #This is a comment. Comments in X borrow from shell scripting. #Import # #You may use imports to combine other template files. When you import another #file, all of the templates in that file are added to the resulting namespace #specified by the compiler options (global namespace or a newly returned #namespace object). None of the variables defined in an imported file are #available in an importing file. # #You can specify a relative path I.E. ../../file.xjs, or an absolute path I.E. #/file.xjs. This example is using relative path. Relative paths are always #relative to the document declaring the import statement, so profile_sample.xjs #is a sibling file in the parent directory of the current file. {import profile_sample.xjs} #GlobalVariable # #This is a global variable. Only templates within this file have access to it. {var boo 5} #VariableAssignment # #Variables don't need to have an assigned value. Variables may only be #initialized. Compile time errors will occur if a reference is found to a #variable that hasn't been declared, or if an attempt is made to declare a #variable more than once. Variables without a value are undefined. {var zamboni}#undefined value FOREVER! #Referencing Variables # #Variables must be declared before they may be referenced. Now that we've #declared 'boo', it can be referenced. References begin with the '@' symbol. {var zoo @boo} #GlobalVariable Types # #Global variables, unlike variables that appear within a template, are limited #to the following types and expressions: #numbers {var coo 1} {var cooo 0x4a09} {var coooo 1.345} {var coooo9 1.345e-1234} #strings {var doo 'string'} {var dooo "string"} #null, and booleans {var woo null} {var eooo true} {var eoooo false} #expressions #X allows most ECMAScript operators to appear in expression statements. The #value of any variable may be derived form a short-circuited expression. #The following is a valid expression: {var wooo 1 || (typeof 2 === 'number' && ~~3 && !@woo) && #comments ending with \n are #considered white space everywhere!!! (((('nested parens'||'not nested'))))#etc. }#Can you guess what the value of @wooo is now? #Operators # #The following operators (separated by space), are directly output to the #compiled result. The precedence is exactly what it is in ECMAScript, and is #not given in any intentional order herein: # #Logical: #== === != !== || && < <= > >= # #Mathmatical: #+ - % * / # #Unary: #typeof !!! ~~~ # #Parenthetical: #(((expression))) # #Templates # #You may define as many templates as you wish within a file. Templates are #assigned to the namespace declared in their file. Templates end up as methods #within their namespace, so in the case of wow (assuming the namespace is #applied globally, you would call it like this: #my.app.namespace.wow(data, params); {template wow}{/template} #As you can see, wow doesn't do very much. wow2 prints: Hello! {template wow2} Hello! {/template} #ContextSelector # #When calling templates, the first parameter is the data, or context with which #the template uses for processing. Here are some valid context selectors: # {template contextSelectorTest} # #Property Selectors #Property Selectors follow the same naming conventions that exist in #ECMAScript E.G. /[a-zA-Z_$][a-zA-Z_$0-9]*/. You may select any property by name #like this: {Tables} # #To drill down: {tables.first.name} # #If a property name is a reserved word or contains invalid characters, you may #use dynamic refinement like this: {['table-inventory']} # #To driill down: {['table-inventory']['count']} # #Mixing static and dynamic refinement: {tables['table-inventory'].first[0+4]} # #Any valid expression is allowable inside dynamic refinement. The following is #perfectly valid: {tables[^encodeURIComponent('/////').replace('%2F', '')][@woo||2&&3===(3-5)]} # #Current Context #The context at the template level is always the first argument do the template. #You can directly access the current context using one of the following #selectors: {.} {current()} # #Global Context #Use the caret symbol to access global variables and functions. This makes #templates extremely flexible, and permits helpers to be accessed from any #template without a registration mechanism. Please give feedback on this #approach. Referencing globals is generally a bad practive, but to avoid #verbosity it is allowed. #Example: {^Date.now()}#print the current timestamp. # #Functions. The following functions are built-ins and are reserved. Some of #them really only take on meaning inside foreach statements. # #count(contextSelector) #Returns the number of items in an array or properties in an object. {count(.)} # #name() #Outputs the name of the current item in a foreach loop. This may be a #number if iterating over an array, or a string if iterating over an object. {name()} # #position() #Outputs the current index in the foreach loop. This may be random #when looping over properties in an object. {position()} # #last() #Returns the highest possible index in the current iteration in a foreach #statement. {last()}#:P {/template} #Params # #When calling templates, the second parameter represents the params you wish to #make available to your templates. In order to access params, you must declare #them at the top of your templates. Param values are the same as any other #template variable. {template params} {param foo} {param too 5}#if too isn't supplied, then the default is number 5. {/template} #Variables # #Variables and Params within a template inherit the same rules that Global #Variables follow, and additionally allow context selectors. {template vars} {var foo1 .} {var foo2 ['5']} {/template} #Helpers # #You can directly call any VariableReference, or ContextSelector in the same #way you would normal javascript methods / functions. I.E. @boo(5,4) && #property.name[0](). See ContextSelector for an example of calling global #references I.E. {^call.my.helper()} #PrintStatement # #To directly print the same type of values allowed for variables, you can wrap #the value in curly braces. Doing so outputs the value. # #You can optionally modify the output with PrintModifiers. Valid PrintModifiers #are: #e: Disables XSS escaping. #E: Enables XSS escaping. {template print} {var greeting 'Top of the morning to ya!'} {@greeting}#output the value of greeting. {hello}#output the value of hello in the current context. {hello || hola}#If hello is falsey, output the value of hola. {^Date.now()}#Print the current timestamp. See ContextSelector #Example of PrintModifiers {hello |e}#disable XSS escaping {hello |E}#enable XSS escaping {/template} #InputTokens # #Any value between } and { in a template will be output. Certain values must #be escaped with the '\' character. The following characters need to be escaped #as input tokens: # and {. An unescaped '#' results in a comment that is #considered to be space. An unescaped '{' that does not appear as a valid #opening tag results in a compile time error. {template inputTokens} <h1>A header</h1> #Escaped comment. { {/template} #If # #If statements allow for control flow within a template. To facilitate else if, #and else, continuation statements of the form {:elif} and {:else} are used. #Note that {:elif} and {:else} are optional. {template IF} {if someValue}#Do something {:elif anotherValue}#Do something else {:elif anotherValue||anyExpression}#Do something else yet again. {:else}#Otherwise {/if} {/template} #Foreach # #You can loop over arrays and objects using foreach. Foreach establishes a new #context, so the item you iterate over becomes the new value of '.' and #'current()'. {template foreach} {foreach person} {age} {/foreach} {/template} #Sort # #Must appear as the first statement within a foreach. Sorting items does NOT #affect the original object. You can sort on any context selector to control #the ordering of items. The sort algorithm used (for ascending and descending #sorts), is guaranteed to preserve an item's index while sorting. # #The following types of sort are allowed: #asc desc rand # #The following sort modifiers may be applied to "asc" and "desc": # #i Makes the sort case-insensitive. #c Shifts lowercase items leftward where possible. #C Shifts uppercase items leftward where possible. #n Shifts numbers leftward. # {template sort} {foreach person} {sort hobbies.favorite |asc} {/foreach} {foreach person} #This sort statement does the following: # #1. Sort persons by their favorite hobby #2. Hobbies are sorted case insensitively ascendingly. #3. Uppercase hobbies are then promoted upward. {sort hobbies.favorite |asc|iC} My name is: {name()} {/foreach} {foreach person}#multiple sorts allowed! {sort hobbies.favorite |asc} {sort hobbies.name |desc} Show all of me: {.} {/foreach} {/template} #Text # #To avoid excessive escaping on InputTokens, you can use the text statement. {template text} {text} ' #'{ {/text} {/template} #Log # #Use console.log under the hood. By default logs are removed. You must #configure the compiler accordingly if you wish to use log statements. {template log} {log 5} {/template} #Render # #To render a template within the current namespace, do this: {template renderTemplateFromSameNamespace} #partial is declared below {render partial/} {/template} # #You can render any template defined in an imported file, or the #current file. If the imported file defines a namespace different than the #importing file, then you must use the full path to the namespace I.E. {template renderTemplateFromAnImportedFileWithADifferentNamespace} {render sample.buildProfile/} {/template} # #otherwise you can supply the short form for any template defined in the current #namespace. Compile time checking will insure that the desired template has #been declared. # #To set the context, declare a second argument in the render statement I.E. {template setContextOnRenderExample} #This changes the context of partial to "fred" {render partial fred/}. {/template} # #Prints: I'm a partial5 when rendered from renderPartial. {template partial} {param foo} I'm a partial{@foo}. {/template} # #You can also pass params to other templates, in addition to changing the #context with the render statement. {template renderPartial} {render partial/} {render partial} {param foo 5} {/render} {/template}