/**
*  Contemplate
*  Light-weight Object-Oriented Template Engine for PHP, Python, JavaScript
*
*  @version: 1.6.0
*  https://github.com/foo123/Contemplate
*
*  @inspired by : Simple JavaScript Templating, John Resig - http://ejohn.org/ - MIT Licensed
*  http://ejohn.org/blog/javascript-micro-templating/
*
**/
!function(root, name, factory) {
"use strict";
if (('undefined'!==typeof Components)&&('object'===typeof Components.classes)&&('object'===typeof Components.classesByID)&&Components.utils&&('function'===typeof Components.utils['import'])) /* XPCOM */
    (root.$deps = root.$deps||{}) && (root.EXPORTED_SYMBOLS = [name]) && (root[name] = root.$deps[name] = factory.call(root));
else if (('object'===typeof module)&&module.exports) /* CommonJS */
    (module.$deps = module.$deps||{}) && (module.exports = module.$deps[name] = factory.call(root));
else if (('function'===typeof define)&&define.amd&&('function'===typeof require)&&('function'===typeof require.specified)&&require.specified(name) /*&& !require.defined(name)*/) /* AMD */
    define(name,['module'],function(module){factory.moduleUri = module.uri; return factory.call(root);});
else if (!(name in root)) /* Browser/WebWorker/.. */
    (root[name] = factory.call(root)||1)&&('function'===typeof(define))&&define.amd&&define(function(){return root[name];} );
}(  /* current root */          'undefined' !== typeof self ? self : this,
    /* module name */           "Contemplate",
    /* module factory */        function ModuleFactory__Contemplate(undef) {
"use strict";
/////////////////////////////////////////////////////////////////////////////////////
//
//  Contemplate Engine Main Class
//
//////////////////////////////////////////////////////////////////////////////////////
// private vars
var __version__ = "1.6.0", Contemplate,
    PROTO = 'prototype', Obj = Object, Arr = Array,
    HAS = Obj[PROTO].hasOwnProperty, toString = Obj[PROTO].toString,
    NOP = function( ){ },
    isXPCOM = ("undefined" !== typeof Components) && ("object" === typeof Components.classes) && ("object" === typeof Components.classesByID) && Components.utils && ("function" === typeof Components.utils['import']),
    isNode = "undefined" !== typeof(global) && '[object global]' === toString.call(global),
    $Scope = this,
    Cu = isXPCOM ? Components.utils : null,
    Cc = isXPCOM ? Components.classes : null,
    Ci = isXPCOM ? Components.interfaces : null,
    import_ = isXPCOM ? Cu['import'] : (isNode ? require : NOP),
    fs = isNode ? import_('fs') : null,
    import_module = isXPCOM
    ? function import_module(name, path) {import_(path, $Scope); return $Scope[name];}
    : (isNode
    ? function import_module(name, path) {return import_(path);}
    : NOP),
    XHR = function() {
    return window.XMLHttpRequest
        // code for IE7+, Firefox, Chrome, Opera, Safari
        ? new XMLHttpRequest()
        // code for IE6, IE5
        : new ActiveXObject("Microsoft.XMLHTTP") // or ActiveXObject("Msxml2.XMLHTTP"); ??
    ;
    },
    $__isInited = false,
    $__leftTplSep = "<%", $__rightTplSep = "%>", $__tplStart = "", $__tplEnd = "",
    // https://nodejs.org/api/os.html#os_os_eol
    //
    $__EOL = "\n", $__TEOL = /*isNode ? import_('os').EOL :*/ "\n", $__escape = true,
    $__preserveLinesDefault = "' + \"\\n\" + '", $__preserveLines = '',
    $__level = 0, $__pad = "    ", $__idcnt = 0,
    $__locals, $__variables, $__loops = 0, $__ifs = 0, $__loopifs = 0, $__forType = 2,
    $__allblocks = null, $__allblockscnt = null,  $__openblocks = null,
    $__currentblock, $__startblock = null, $__endblock = null, $__blockptr = -1,
    $__extends = null, $__uses = null, $__strings = null,
    $__ctx, $__global, $__context, $__uuid = 0,
    UNDERLN = /[\W]+/g, NEWLINE = /\n\r|\r\n|\n|\r/g, SQUOTE = /'/g,
    DS_RE = /[\/\\]/, BASENAME_RE = /[\/\\]?[^\/\\]+$/,
    TAG_RE = /<\/?[a-zA-Z0-9:_\-]+[^<>]*>/gm, AMP_RE = /&+/g,
    ALPHA = /^[a-zA-Z_]/, NUM = /^[0-9]/, ALPHANUM = /^[a-zA-Z0-9_]/i,
    SPACE = /^\s/, ALL_SPACE = /^\s+$/,
    INDENT = /^(postdent|predent)\((-?\d+)\):/,
    re_controls = /(\t|\s?)\s*((#ID_(continue|endblock|elsefor|endfor|endif|break|else|fi)#(\s*\(\s*\))?)|(#ID_([^#]+)#\s*(\()))(.*)$/g,
    $__reserved_var_names = [
    'Contemplate', 'self', 'this', 'data', '__p__', '__i__', '__ctx'
    ],
    $__directives = [
    'set', 'unset', 'isset',
    'if', 'elseif', 'else', 'endif',
    'for', 'elsefor', 'endfor',
    'extends', 'block', 'endblock',
    'include', 'super', 'getblock', 'iif', 'empty', 'continue', 'break', 'local_set', 'get', 'local'
    ],
    $__directive_aliases = {
     'elif'     : 'elseif'
    ,'fi'       : 'endif'
    },
    $__aliases = {
     'cc'       : 'concat'
    ,'j'        : 'join'
    ,'dq'       : 'qq'
    ,'now'      : 'time'
    ,'template' : 'tpl'
    },
    // generated cached tpl class code as a "heredoc" template (for Node cached templates)
    TT_ClassCode,
    // generated cached tpl block method code as a "heredoc" template (for Node cached templates)
    TT_BlockCode, TT_BLOCK,
    TT_FUNC, TT_RCODE
;
if (isXPCOM)
{
    // do some necessary imports
    import_("resource://gre/modules/NetUtil.jsm");
    import_("resource://gre/modules/FileUtils.jsm");
}
/*function HAS(o, x)
{
    return !!(o && Object.prototype.hasOwnProperty.call(o, x));
}*/
function reset_state()
{
    $__loops = 0; $__ifs = 0; $__loopifs = 0; $__forType = 2; $__level = 0;
    $__allblocks = []; $__allblockscnt = {}; $__openblocks = [[null, -1]];
    $__extends = null; $__uses = []; $__locals = {}; $__variables = {}; $__currentblock = '_';
    $__locals[$__currentblock] = $__locals[$__currentblock] || {};
    $__variables[$__currentblock] = $__variables[$__currentblock] || {};
}
function clear_state()
{
    $__loops = 0; $__ifs = 0; $__loopifs = 0; $__forType = 2; $__level = 0;
    $__allblocks = null; $__allblockscnt = null; $__openblocks = null;
    $__locals = null; $__variables = null; $__currentblock = null;
    $__idcnt = 0; $__strings = null;
    /*$__extends = null; $__uses = [];*/
}
function push_state()
{
    return [$__loops, $__ifs, $__loopifs, $__forType, $__level,
    $__allblocks, $__allblockscnt, $__openblocks, $__extends, $__locals, $__variables, $__currentblock, $__uses];
}
function pop_state(state)
{
    $__loops = state[0]; $__ifs = state[1]; $__loopifs = state[2]; $__forType = state[3]; $__level = state[4];
    $__allblocks = state[5]; $__allblockscnt = state[6]; $__openblocks = state[7];
    $__extends = state[8]; $__locals = state[9]; $__variables = state[10]; $__currentblock = state[11]; $__uses = state[12];
}
function remove_initial_space(s)
{
    var l = s.length, sl, i, initial_space, c, pos;
    if (l)
    {
        initial_space = '';
        for (i=0; i<l; ++i)
        {
            c = s.charAt(i);
            if (' ' === c || "\t" === c) initial_space += c;
            else break;
        }
        sl = initial_space.length;
        if (sl)
        {
            s = s.slice(sl);
            pos = s.indexOf("\n", 0);
            while (-1 !== pos)
            {
                if (initial_space === s.slice(pos+1, pos+1+sl))
                {
                    s = s.slice(0, pos+1) + s.slice(pos+1+sl);
                }
                pos = s.indexOf("\n", pos + 1);
            }
        }
    }
    return s;
}
function remove_blank_lines(s)
{
    var lines = s.split("\n"), n = lines.length, start = 0, end = n-1, i, l;
    for (i=0; i<n; ++i)
    {
        l = lines[i];
        if (l.length && !ALL_SPACE.test(l))
        {
            start = i;
            break;
        }
    }
    for (i=n-1; i>=start; --i)
    {
        l = lines[i];
        if (l.length && !ALL_SPACE.test(l))
        {
            end = i;
            break;
        }
    }
    return lines.slice(start, end+1).join("\n");
}
function align(s, level)
{
    // pad lines to generate formatted code
    if (2 > arguments.length) level = $__level;
    s = remove_initial_space(s);
    var aligned = '', alignment, c, i, l = s.length, is_line_start;
    if (l && (0 < level))
    {
        alignment = new Arr(level+1).join($__pad);
        aligned = alignment;
        is_line_start = true;
        for (i=0; i<l; ++i)
        {
            c = s.charAt(i);
            if ("\n" === c)
            {
                aligned += "\n" + alignment;
                is_line_start = true;
            }
            else if (is_line_start)
            {
                // consistently replace tabs with our tabbed spaces
                if (SPACE.test(c))
                {
                    aligned += "\t" === c ? $__pad : c;
                }
                else
                {
                    aligned += c;
                    is_line_start = false;
                }
            }
            else
            {
                aligned += c;
            }
        }
    }
    else
    {
        aligned = s;
    }
    return aligned;
}
function merge()
{
    var args = arguments, l = args.length;
    if (l < 1) return;
    var merged = args[0], i, k, o;
    for (i=1; i<l; ++i)
    {
        o = args[i];
        if (o) for (k in o) if (HAS.call(o,k)) merged[k] = o[k];
    }
    return merged;
}
function get_separators(text, separators)
{
    var line, seps, pos, i, l;
    if (separators)
    {
        seps = trim(separators).split(" ");
        $__leftTplSep = trim(seps[0]);
        $__rightTplSep = trim(seps[1]);
    }
    else
    {
        // tpl separators are defined on 1st (non-empty) line of tpl content
        l = text.length; i = 0; pos = 0; line = "";
        while (i < l && -1 < pos && !line.length)
        {
            pos = text.indexOf("\n", i);
            line = -1 < pos ? trim(text.slice(i, pos+1)) : "";
            i = pos+1;
        }
        if (line.length)
        {
            seps = line.split(" ");
            $__leftTplSep = trim(seps[0]);
            $__rightTplSep = trim(seps[1]);
            text = text.slice(pos+1);
        }
    }
    return text;
}
function split_arguments(args, delim)
{
    args = trim(args);
    if (!args.length) return [''];
    if (arguments.length < 2) delim = ',';
    var a = [], paren = [], s = '', i = 0, l = args.length, c;
    while (i < l)
    {
        c = args.charAt(i++);
        if (delim === c && !paren.length)
        {
            a.push(trim(s));
            s = '';
            continue;
        }
        s += c;
        if ('(' === c)
        {
            paren.unshift(')');
        }
        else if ('{' === c)
        {
            paren.unshift('}');
        }
        else if ('[' === c)
        {
            paren.unshift(']');
        }
        else if (')' === c || '}' === c || ']' === c)
        {
            if (!paren.length || paren[0] !== c) break;
            paren.shift();
        }
    }
    if (s.length) a.push(trim(s));
    if (i < l) a.push(trim(args.slice(i)));
    return a;
}
function local_variable(variable, block, literal)
{
    if (null == variable)
    {
        return '_loc_' + (++$__idcnt);
    }
    else
    {
        if (null == block) block = $__currentblock;
        if (!HAS.call($__locals[block], $__variables[block][variable]))
            $__locals[block][$__variables[block][variable]] = literal ? 2 : 1;
        return variable;
    }
}
function is_local_variable(variable, block)
{
    if (null == block) block = $__currentblock;
    //if ('_loc_' === variable.slice(0, 5)) return 1;
    return HAS.call($__locals[block], $__variables[block][variable]) ? $__locals[block][$__variables[block][variable]] : 0;
}
//
// Control structures
//
function t_include(id, cb)
{
    var tpl, state, ch, contx = $__context;
    id = trim(id);
    if ($__strings && HAS.call($__strings, id)) id = $__strings[id];
    ch = id.charAt(0);
    if (('"' === ch || "'" === ch) && (ch === id.charAt(id.length-1))) id = id.slice(1, -1); // quoted id
    if ('function' === typeof cb)
    {
        // cache it
        if (!HAS.call(contx.partials, id) /*&& !HAS.call($__global.partials,id)*/)
        {
            get_template_contents(id, contx, function(err, tpl) {
                if (err)
                {
                    cb(err, null);
                    return;
                }
                tpl = get_separators(tpl);
                state = push_state();
                reset_state();
                parse(tpl, $__leftTplSep, $__rightTplSep, false, function(err, text) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    contx.partials[id] = [" " + text +  "';" + $__TEOL, $__uses ? $__uses.slice() : []];
                    pop_state(state);
                    // add usedTpls used inside include tpl to current usedTpls
                    for (var uses = contx.partials[id][1],usedTpl=0; usedTpl<uses.length; ++usedTpl)
                    {
                        if (-1 === $__uses.indexOf(uses[usedTpl]))
                            $__uses.push(uses[usedTpl]);
                    }
                    cb(null, align(contx.partials[id][0]));
                } );
            });
        }
        else
        {
            // add usedTpls used inside include tpl to current usedTpls
            for (var uses = contx.partials[id][1],usedTpl=0; usedTpl<uses.length; ++usedTpl)
            {
                if (-1 === $__uses.indexOf(uses[usedTpl]))
                    $__uses.push(uses[usedTpl]);
            }
            cb(null, align(contx.partials[id][0]));
        }
        return '';
    }
    else
    {
        // cache it
        if (!HAS.call(contx.partials, id) /*&& !HAS.call($__global.partials,id)*/)
        {
            tpl = get_template_contents(id, contx);
            tpl = get_separators(tpl);
            state = push_state();
            reset_state();
            contx.partials[id] = [" " + parse(tpl, $__leftTplSep, $__rightTplSep, false) + "';" + $__TEOL, $__uses ? $__uses.slice() : []];
            pop_state(state);
        }
        // add usedTpls used inside include tpl to current usedTpls
        for (var uses = contx.partials[id][1],usedTpl=0; usedTpl<uses.length; ++usedTpl)
        {
            if (-1 === $__uses.indexOf(uses[usedTpl]))
                $__uses.push(uses[usedTpl]);
        }
        return align(contx.partials[id][0] /*|| $__global.partials[id][0]*/);
    }
}
function t_block(block)
{
    block = block.split(',');
    var echoed = !(block.length>1 ? "false"===trim(block[1]) : false);
    block = trim(block[0]);
    if ($__strings && HAS.call($__strings, block)) block = $__strings[block];
    var ch = block.charAt(0);
    if (('"' === ch || "'" === ch) && (ch === block.charAt(block.length-1))) block = block.slice(1, -1); // quoted block
    $__allblocks.push([block, -1, -1, 0, $__openblocks[0][1], echoed]);
    $__allblockscnt[block] = $__allblockscnt[block] ? ($__allblockscnt[block]+1) : 1;
    $__blockptr = $__allblocks.length;
    $__openblocks.unshift([block, $__blockptr-1]);
    $__startblock = block;
    $__endblock = null;
    $__currentblock = block;
    $__locals[$__currentblock] = $__locals[$__currentblock] || {};
    $__variables[$__currentblock] = $__variables[$__currentblock] || {};
    return "' +  #BLOCK_" + block + "#";
}
function t_endblock()
{
    if (1 < $__openblocks.length)
    {
        var block = $__openblocks.shift();
        $__endblock = block[0];
        $__blockptr = block[1]+1;
        $__startblock = null;
        $__currentblock = $__openblocks.length ? $__openblocks[0][0] : '_';
        return "#/BLOCK_" + block[0] + "#";
    }
    else
    {
        $__currentblock = '_';
    }
    return '';
}
//
// auxilliary parsing methods
function parse_constructs_async(s, cb)
{
    // reset lastIndex of regex, else it may fail where it should succeed
    re_controls.lastIndex = 0;
    var match = re_controls.exec(s), s2;
    if (!match)
    {
        cb(null, s);
        return;
    }
    parse_constructs(match, function(err, replace) {
        if (err)
        {
            cb(err, null);
            return;
        }
        s2 = s.slice(0, match.index) + replace;
        // continue until end
        parse_constructs_async(s.slice(match.index+match[0].length), function(err, replace2) {
            if (err)
            {
                cb(err, null);
                return;
            }
            cb(null, s2+replace2);
        });
    });
}
function parse_constructs(match, cb)
{
    cb = 'function' === typeof cb ? cb : null;
    /*
    main probelm with this function running async is the "include" directive
    which needs to read external files and may need to do it async,
    else rest parsing can be sync, specificaly args parsing can be synced even if original call is async since we can reasonably assume args will NOT contain "include" directive
    */
    var match0 = match[0],
        match1 = match[1],
        match2 = match[2],
        match3 = match[3],
        match4 = match[4],
        match5 = match[5],
        match6 = match[6],
        match7 = match[7],
        match8 = match[8],
        match9 = match[9],
        prefix = match1 || '',
        ctrl = match4 || match7 || '',
        rest = match9 || '',
        startParen = match8 || false,
        args = '',  out = '', paren = 0, l, i, ch, m, err,
        varname, tplvarname, expr, args2, usedTpl,
        parse_constructs_sync = function(m0,m1,m2,m3,m4,m5,m6,m7,m8,m9) {
            return parse_constructs([m0,m1,m2,m3,m4,m5,m6,m7,m8,m9]);
        };
    // parse parentheses and arguments, accurately
    if (startParen && startParen.length)
    {
        paren = 1; l = rest.length; i = 0;
        while (i < l && paren > 0)
        {
            ch = rest.charAt(i++);
            if ('(' === ch) ++paren;
            else if (')' === ch) --paren;
            if (paren > 0) args += ch;
        }
        rest = rest.slice(args.length+1);
    }
    args = trim(args);
    if (HAS.call($__directive_aliases, ctrl)) ctrl = $__directive_aliases[ctrl];
    m = $__directives.indexOf(ctrl);
    if (-1 < m)
    {
        switch (m)
        {
            case 22 /*'local'*/:
                varname = trim(args);
                tplvarname = $__variables[$__currentblock][varname];
                if (-1 !== $__reserved_var_names.indexOf(tplvarname))
                {
                    // should be different from 'this', 'data', .. as these are used internally
                    err = new Contemplate.Exception('Contemplate Parse: Use of reserved name as local variable name "'+tplvarname+'"');
                    if (cb)
                    {
                        cb(err, null);
                        return;
                    }
                    else
                    {
                        throw err;
                    }
                }
                local_variable(varname, null, true); // make it a literal local variable
                out = "';" + $__TEOL + align('var ' + varname + ';') + $__TEOL;
                break;
            case 0 /*'set'*/:
            case 20 /*'local_set'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                args = split_arguments(args, ',');
                varname = trim(args.shift());
                expr = trim(args.join(','));
                if (20 === m && !is_local_variable(varname))
                {
                    local_variable(varname); // make it a local variable
                    varname = 'var ' + varname;
                }
                out = "';" + $__TEOL + align(varname + ' = ('+ expr +');') + $__TEOL;
                break;
            case 21 /*'get'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                out = prefix + 'Contemplate.get(' + args + ')';
                break;
            case 1 /*'unset'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                varname = args;
                if (varname && varname.length)
                {
                    varname = trim(varname);
                    out = "';" + $__TEOL + align('if ("undefined" !== typeof(' + varname + ')) delete ' + varname + ';') + $__TEOL;
                }
                else
                {
                    out = "';" + $__TEOL;
                }
                break;
            case 2 /*'isset'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                varname = args;
                out = '("undefined" !== typeof(' + varname + ') && null !== ' + varname + ')';
                break;
            case 3 /*'if'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                out = "';" + align([
                                ""
                                ,"if ("+args+")"
                                ,"{"
                                ,""
                            ].join($__TEOL));
                ++$__ifs;
                ++$__level;
                break;
            case 4 /*'elseif'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                --$__level;
                out = "';" + align([
                                ""
                                ,"}"
                                ,"else if ("+args+")"
                                ,"{"
                                ,""
                            ].join($__TEOL));
                ++$__level;
                break;
            case 5 /*'else'*/:
                --$__level;
                out = "';" + align([
                                ""
                                ,"}"
                                ,"else"
                                ,"{"
                                ,""
                            ].join($__TEOL));
                ++$__level;
                break;
            case 6 /*'endif'*/:
                --$__ifs;
                --$__level;
                out = "';" + align([
                                ""
                                ,"}"
                                ,""
                            ].join($__TEOL));
                break;
            case 7 /*'for'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                var for_expr = args, is_php_style = for_expr.indexOf(' as '),
                    is_python_style = for_expr.indexOf(' in '),
                    o, _o, kv, isAssoc
                ;
                if (-1 < is_python_style)
                {
                    for_expr = [for_expr.slice(0, is_python_style), for_expr.slice(is_python_style+4)];
                    o = trim(for_expr[1]);
                    _o = local_variable();
                    kv = for_expr[0].split(',');
                }
                else/*if ( -1 < is_php_style )*/
                {
                    for_expr = [for_expr.slice(0, is_php_style), for_expr.slice(is_php_style+4)];
                    o = trim(for_expr[0]);
                    _o = local_variable();
                    kv = for_expr[1].split('=>');
                }
                isAssoc = kv.length >= 2
                // http://jsperf.com/values-extraction/5
                // raw 'in' loop with .hasOwnProperty is faster than looping over Object.keys
                if (isAssoc)
                {
                    var k = trim(kv[0]),
                        v = trim(kv[1]),
                        _oK = local_variable(),
                        _k = local_variable(),
                        _l = local_variable()
                    ;
                    out = "';";
                    if (!is_local_variable(k))
                    {
                        local_variable(k);
                        out += $__TEOL + align('var ' + k + ';');
                    }
                    if (!is_local_variable(v))
                    {
                        local_variable(v);
                        out += $__TEOL + align('var ' + v + ';');
                    }
                    out += align([
                                    ""
                                    ,"var "+_o+" = "+o+", "+_oK+" = "+_o+" ? Object.keys("+_o+") : null,"
                                    ,"    "+_k+", "+_l+" = "+_o+" ? "+_oK+".length : 0;"
                                    ,"if ("+_l+")"
                                    ,"{"
                                    ,"    for ("+_k+"=0; "+_k+"<"+_l+"; ++"+_k+")"
                                    ,"    {"
                                    ,"        "+k+" = "+_oK+"["+_k+"]; "+v+" = "+_o+"["+k+"];"
                                    ,"        "
                                    ,""
                                ].join($__TEOL));
                    $__forType = 2;
                    $__level+=2;
                }
                else
                {
                    var v = trim(kv[0]),
                        _oV = local_variable(),
                        _arr = local_variable(),
                        _k = local_variable(),
                        _kk = local_variable(),
                        _l = local_variable()
                    ;
                    out = "';";
                    if (!is_local_variable(v))
                    {
                        local_variable(v);
                        out += $__TEOL + align('var ' + v + ';');
                    }
                    out += align([
                                    ""
                                    ,"var "+_o+" = "+o+", "+_arr+" = !!"+_o+".forEach,"
                                    ,"    "+_oV+" = "+_o+" ? ("+_arr+" ? "+_o+" : Object.keys("+_o+")) : null,"
                                    ,"    "+_k+", "+_kk+", "+_l+" = "+_oV+" ? "+_oV+".length : 0;"
                                    ,"if ("+_l+")"
                                    ,"{"
                                    ,"    for ("+_k+"=0; "+_k+"<"+_l+"; ++"+_k+")"
                                    ,"    {"
                                    ,"        "+_kk+" = "+_oV+"["+_k+"];"
                                    ,"        "+v+" = "+_arr+" ? "+_kk+" : "+_o+"["+_kk+"];"
                                    ,"        "
                                    ,""
                                ].join($__TEOL));
                    $__forType = 1;
                    $__level+=2;
                }
                ++$__loops;  ++$__loopifs;
                break;
            case 8 /*'elsefor'*/:
                /* else attached to  for loop */
                if (2 === $__forType)
                {
                    --$__loopifs;
                    $__level+=-2;
                    out = "';" + align([
                                    ""
                                    ,"    }"
                                    ,"}"
                                    ,"else"
                                    ,"{  "
                                    ,""
                                ].join($__TEOL));
                    ++$__level;
                }
                else
                {
                    --$__loopifs;
                    $__level+=-2;
                    out = "';" + align([
                                    ""
                                    ,"    }"
                                    ,"}"
                                    ,"else"
                                    ,"{  "
                                    ,""
                                ].join($__TEOL));
                    ++$__level;
                }
                break;
            case 9 /*'endfor'*/:
                if ($__loopifs === $__loops)
                {
                    if (2 === $__forType)
                    {
                        --$__loops; --$__loopifs;
                        $__level+=-2;
                        out = "';" + align([
                                        ""
                                        ,"    }"
                                        ,"}"
                                        ,""
                                    ].join($__TEOL));
                    }
                    else
                    {
                        --$__loops; --$__loopifs;
                        $__level+=-2;
                        out = "';" + align([
                                        ""
                                        ,"    }"
                                        ,"}"
                                        ,""
                                    ].join($__TEOL));
                    }
                }
                else
                {
                    --$__loops;
                    --$__level;
                    out = "';" + align([
                                    ""
                                    ,"}"
                                    ,""
                                ].join($__TEOL));
                }
                break;
            case 10 /*'extends'*/:
                var id = trim(args);
                if ($__strings && HAS.call($__strings, id)) id = $__strings[id];
                var ch = id.charAt(0);
                if (('"' === ch || "'" === ch) && (ch === id.charAt(id.length-1))) id = id.slice(1, -1); // quoted id
                $__extends = id;
                out = "';" + $__TEOL;
                break;
            case 11 /*'block'*/:
                out = t_block(args);
                break;
            case 12 /*'endblock'*/:
                out = t_endblock();
                break;
            case 13 /*'include'*/:
                out = cb ? '' : t_include(args);
                break;
            case 14 /*'super'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                out = prefix + 'self.sprblock(' + args + ', data)';
                break;
            case 15 /*'getblock'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                out = prefix + '__i__.block(' + args + ', data)';
                break;
            case 16 /*'iif'*/:
                args = split_arguments(args.replace(re_controls, parse_constructs_sync), ',');
                out = prefix + "(("+args[0]+") ? ("+args[1]+") : ("+args[2]+"))";
                break;
            case 17 /*'empty'*/:
                args = args.replace(re_controls, parse_constructs_sync);
                out = prefix + '(("undefined" === typeof(' + args + ')) || (null === ' + args + ') || Contemplate.empty(' + args + '))';
                break;
            case 18 /*'continue'*/:
            case 19 /*'break'*/:
                out = "';" + $__TEOL + align(18 === m ? 'continue;' : 'break;') + $__TEOL;
                break;
        }
        if (cb)
        {
            if (13 === m)/*'include'*/
            {
                // include may be async now
                t_include(args, function(err, out) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    parse_constructs_async(rest, function(err, rest2) {
                        if (err)
                        {
                            cb(err, null);
                            return;
                        }
                        cb(null, out + rest2);
                    });
                });
            }
            else
            {
                parse_constructs_async(rest, function(err, rest2) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    cb(null, out + rest2);
                });
            }
            return;
        }
        else
        {
            return out + rest.replace(re_controls, parse_constructs_sync);
        }
    }
    if (HAS.call($__context.plugins, ctrl) || HAS.call($__global.plugins, ctrl))
    {
        // allow custom plugins as template functions
        var pl = $__context.plugins[ctrl] || $__global.plugins[ctrl];
        args = args.replace(re_controls, parse_constructs_sync);
        out = pl instanceof Contemplate.InlineTemplate ? pl.render([args].concat(split_arguments(args, ','))) : 'Contemplate.plg_("' + ctrl + '"' + (!args.length ? '' : ','+args) + ')';
        if (cb)
        {
            parse_constructs_async(rest, function(err, rest2) {
                if (err)
                {
                    cb(err, null);
                    return;
                }
                cb(null, prefix + out + rest2);
            });
            return;
        }
        else
        {
            return prefix + out + rest.replace(re_controls, parse_constructs_sync);
        }
    }
    if (HAS.call($__aliases, ctrl)) ctrl = $__aliases[ctrl];
    args = args.replace(re_controls, parse_constructs_sync);
    // aliases and builtin functions
    switch (ctrl)
    {
        case 's': out = 'String(' + args + ')'; break;
        case 'n': out = 'parseInt(' + args + ')'; break;
        case 'f': out = 'parseFloat(' + args + ')'; break;
        case 'q': out = '"\'"+(' + args + ')+"\'"'; break;
        case 'qq': out = '\'"\'+(' + args + ')+\'"\''; break;
        case 'concat': out = 'String('+split_arguments(args, ',').join(')+String(')+')'; break;
        case 'is_array':
            args = split_arguments(args, ',');
            if (args.length > 1)
                out = "(("+args[1]+") ? '[object Array]' === Object.prototype.toString.call("+args[0]+") : '[object Array]' === Object.prototype.toString.call("+args[0]+") || '[object Object]' === Object.prototype.toString.call("+args[0]+"))";
            else
                out = "('[object Array]'===Object.prototype.toString.call("+args[0]+")||'[object Object]'===Object.prototype.toString.call("+args[0]+"))";
            break;
        case 'in_array':
            args = split_arguments(args, ',');
            out = '(-1<('+args[1]+').indexOf('+args[0]+'))';
            break;
        case 'tpl':
            args2 = split_arguments(args, ',');
            usedTpl = args2[0];
            if ('#STR_' === usedTpl.slice(0, 5) && HAS.call($__strings, usedTpl))
            {
                // only literal string support here
                usedTpl = $__strings[usedTpl].slice(1, -1); // without quotes
                if (-1 === $__uses.indexOf(usedTpl))
                    $__uses.push(usedTpl);
            }
            // no break
        default:
            if (HAS.call(Contemplate, ctrl) && ('function' === typeof Contemplate[ctrl]))
            {
                out = 'Contemplate.' + ctrl + '(' + args + ')';
            }
            else
            {
                out = ctrl + (startParen ? '('+args+')' : '');
            }
    }
    if (cb)
    {
        parse_constructs_async(rest, function(err, rest2) {
            if (err)
            {
                cb(err, null);
                return;
            }
            cb(null, prefix + out + rest2);
        });
        return;
    }
    else
    {
        return prefix + out + rest.replace(re_controls, parse_constructs_sync);
    }
}
function parse_blocks(s)
{
    var blocks = [], bl = $__allblocks.length,
        block, delims, tag, rep, tl, rl,
        pos1, pos2, off, containerblock, echoed, EOL = $__TEOL
    ;
    while (bl--)
    {
        delims = $__allblocks[bl];
        block = delims[0];
        pos1 = delims[1];
        pos2 = delims[2];
        off = delims[3];
        containerblock = delims[4];
        echoed = delims[5];
        tag = "#BLOCK_" + block + "#";
        rep = echoed ? "__i__.block('" + block + "', data);" : "'';";
        tl = tag.length; rl = rep.length;
        if (-1 < containerblock)
        {
            // adjust the ending position of the container block (if nested)
            // to compensate for the replacements in this (nested) block
            $__allblocks[containerblock][3] += rl - (pos2-pos1+1);
        }
        // adjust the ending position of this block (if nested)
        // to compensate for the replacements of any (nested) block(s)
        pos2 += off;
        if (1 === $__allblockscnt[block])
        {
            // 1st occurance, block definition
            blocks.push([block, TT_BLOCK.render({
             'BLOCKCODE'    : s.slice( pos1+tl, pos2-tl-1 ) + "';"
            })]);
        }
        s = s.slice(0, pos1) + rep + s.slice(pos2+1);
        if (1 <= $__allblockscnt[block]) --$__allblockscnt[block];
    }
    //$__allblocks = null; $__allblockscnt = null; $__openblocks = null;
    return [s, blocks];
}
function parse_variable(s, i, l)
{
    if (ALPHA.test(s[i]))
    {
        var strings = {}, variables = [], subvariables,
            id, variable, property, variable_raw, variable_main, variable_rest,
            len, lp, bracket, delim, ch,
            str_, q, escaped, si, is_prop_access, tok,
            strid, sub, space = 0, hasStrings = false
        ;
        // main variable
        variable = s.charAt(i++);
        while (i < l && ALPHANUM.test(ch=s.charAt(i)))
        {
            variable += ch;
            ++i;
        }
        variable_raw = variable;
        // transform into tpl variable
        //variable_main = "data['"+variable_raw+"']";
        variable_main = "data."+variable_raw;
        variable_rest = '';
        ++$__idcnt;
        id = "#VAR_"+$__idcnt+"#";
        len = variable_raw.length;
        $__variables[$__currentblock][id] = variable_raw;
        // extra space
        space = 0;
        while (i < l && SPACE.test(s.charAt(i)))
        {
            ++space;
            ++i;
        }
        // optional properties
        while (i < l && ('.' === s.charAt(i) || '[' === s.charAt(i) || '->' === s.substring(i, i+2)))
        {
            delim = s.charAt(i++);
            // -> (php) object notation property
            if ('-' === delim) delim += s.charAt(i++);
            // extra space
            while (i < l && SPACE.test(s.charAt(i)))
            {
                ++space;
                ++i;
            }
            // alpha-numeric dot property
            if ('.' === delim)
            {
                // property
                property = '';
                while (i < l && ALPHANUM.test(s.charAt(i)))
                {
                    property += s.charAt(i++);
                }
                lp = property.length;
                if (lp)
                {
                    // transform into tpl variable bracketed property
                    //variable_rest += "['" + property + "']";
                    variable_rest += "." + property;
                    len += space + 1 + lp;
                    space = 0;
                }
                else
                {
                    break;
                }
            }
            // alpha-numeric (php) object notation property
            else if ('->' === delim /*&& ALPHA.test(s.charAt(i))*/)
            {
                // property
                property = '';
                while (i < l && ALPHANUM.test(s.charAt(i)))
                {
                    property += s.charAt(i++);
                }
                lp = property.length;
                if (lp)
                {
                    // transform into tpl variable object property
                    //variable_rest += "['" + property + "']";
                    variable_rest += "." + property;
                    len += space + 2 + lp;
                    space = 0;
                }
                else
                {
                    break;
                }
            }
            // bracketed property
            else if ('[' === delim)
            {
                bracket = '';
                while (i < l)
                {
                    ch = s.charAt(i);
                    // spaces
                    if (SPACE.test(ch))
                    {
                        ++space;
                        ++i;
                    }
                    // literal string property
                    else if ('"' === ch || "'" === ch)
                    {
                        //property = parse_string(s, ch, i+1, l);
                        str_ = q = ch; escaped = false; si = i+1;
                        while (si < l)
                        {
                            str_ += (ch=s.charAt(si++));
                            if (q === ch && !escaped)  break;
                            escaped = (!escaped && '\\' === ch);
                        }
                        property = str_;
                        ++$__idcnt;
                        strid = "#STR_"+$__idcnt+"#";
                        strings[strid] = property;
                        lp = property.length;
                        i += lp;
                        len += space + lp;
                        space = 0;
                        hasStrings = true;
                        bracket += strid;
                    }
                    // numeric array property
                    else if (NUM.test(ch))
                    {
                        property = s.charAt(i++);
                        while (i < l && NUM.test(s.charAt(i)))
                        {
                            property += s.charAt(i++);
                        }
                        lp = property.length;
                        len += space + lp;
                        space = 0;
                        bracket += property;
                    }
                    // sub-variable as property
                    else if ('$' === ch)
                    {
                        sub = s.slice(i+1);
                        subvariables = parse_variable(sub, 0, sub.length);
                        if (subvariables)
                        {
                            // transform into tpl variable property
                            property = subvariables[subvariables.length-1];
                            lp = property[4];
                            i += lp + 1;
                            len += space + 1 + lp;
                            space = 0;
                            variables = variables.concat(subvariables);
                            hasStrings = hasStrings || property[5];
                            bracket += property[0];
                        }
                        else
                        {
                            bracket += ch;
                            ++len;
                            ++i;
                        }
                    }
                    // identifiers
                    else if (ALPHA.test(ch))
                    {
                        len += space + 1;
                        ++i;
                        if (space > 0)
                        {
                            bracket += " ";
                            space = 0;
                        }
                        is_prop_access = (2 < i && '-' === s.charAt(i-3) && '>' === s.charAt(i-2));
                        tok = ch;
                        while (i < l && ALPHANUM.test(ch=s.charAt(i)))
                        {
                            ++i;
                            ++len;
                            tok += ch;
                        }
                        if (!is_prop_access && 'as' !== tok && 'in' !== tok && 'null' !== tok && 'false' !== tok && 'true' !== tok)
                        {
                            tok = '#ID_'+tok+'#';
                        }
                        bracket += tok;
                    }
                    // close bracket
                    else if (']' === ch)
                    {
                        variable_rest += delim + bracket.replace(re_controls, function(m0,m1,m2,m3,m4,m5,m6,m7,m8,m9) {
                            return parse_constructs([m0,m1,m2,m3,m4,m5,m6,m7,m8,m9]);
                        }) + ch;
                        len += space + 2;
                        space = 0;
                        ++i;
                        break;
                    }
                    // rest
                    else
                    {
                        bracket += ch;
                        ++len;
                        ++i;
                    }
                }
            }
            // extra space
            while (i < l && SPACE.test(s.charAt(i)))
            {
                ++space;
                ++i;
            }
        }
        variables.push([id, variable_raw, variable_main, variable_rest, len, hasStrings, strings]);
        return variables
    }
    return null;
}
var str_re = /#STR_\d+#/g;
function parse_async(tpl, leftTplSep, rightTplSep, withblocks, cb)
{
    var t1, t2, p1, p2, l1, l2, len, parsed, s, i,
        tag, tagTpl, strings, variables, hasVariables, hasStrings, varname, id,
        countl, index, ch, out, tok, v, tokv,
        multisplit_re = InlineTemplate.multisplit_re,
        ind, q, str_, escaped, si, space,
        blockTag, hasBlock, notFoundBlock,
        special_chars = "$'\" \n\r\t\v\0%",
        non_compatibility_mode = true,
        is_prop_access, isphp, isjs, ispy,
        code, indent, l3, indenttype, m
    ;
    t1 = leftTplSep; l1 = t1.length;
    t2 = rightTplSep; l2 = t2.length;
    parsed = '';
    var parse_chunk = function parse_chunk() {
        if (!tpl || !tpl.length)
        {
            parse_finish();
            return;
        }
        p1 = tpl.indexOf(t1);
        if (-1 === p1)
        {
            s = tpl;
            if ($__escape) s = s.split("\\").join("\\\\"); // escape escapes
            parsed += s
                .split("'").join("\\'")  // escape single quotes accurately (used by parse function)
                .split(/*"\n"*/ /\n/).join($__preserveLines) // preserve lines
            ;
            parse_finish();
            return;
        }
        p2 = tpl.indexOf(t2, p1+l1);
        if (-1 === p2) p2 = tpl.length;
        if (p1 > 0)
        {
            s = tpl.slice(0, p1);
            if ($__escape) s = s.split("\\").join("\\\\"); // escape escapes
            parsed += s
                .split("'").join("\\'")  // escape single quotes accurately (used by parse function)
                .split(/*"\n"*/ /\n/).join($__preserveLines) // preserve lines
            ;
        }
        // php literal code block
        isphp = 'php:' === tpl.slice(p1+l1, p1+l1+4);
        // js literal code block
        isjs = 'js:' === tpl.slice(p1+l1, p1+l1+3);
        // py literal code block
        ispy = 'py:' === tpl.slice(p1+l1, p1+l1+3);
        if (isphp || isjs || ispy)
        {
            // include if in same language else ignore
            if (isjs)
            {
                if ('=' === tpl.slice(p1+l1+3, p1+l1+4))
                {
                    parsed += "';" + align("\n/* js code start */") + align("\n__p__ += String(" + trim(tpl.slice(p1+l1+4, p2)) + ");") + align("\n/* js code end */\n__p__ += '");
                }
                else
                {
                    indent = 0; l3 = 0; indenttype = 'none';
                    if (m = tpl.slice(p1+l1+3).match(INDENT))
                    {
                        indenttype = m[1];
                        indent = parseInt(m[2]);
                        l3 = m[0].length;
                    }
                    code = remove_blank_lines(tpl.slice(p1+l1+3+l3, p2));
                    if ('predent' === indenttype)
                    {
                        $__level = Math.max(0, $__level + indent);
                    }
                    parsed += "';" + align("\n/* js code start */");
                    if (trim(code).length)
                    {
                        parsed += "\n" + align(code);
                    }
                    if ('postdent' === indenttype)
                    {
                        $__level = Math.max(0, $__level + indent);
                    }
                    parsed += align("\n/* js code end */\n__p__ += '");
                }
            }
            tpl = tpl.slice(p2+l2);
            parse_chunk();
            return;
        }
        // template TAG
        s = tpl.slice(p1+l1, p2); tpl = tpl.slice(p2+l2);
        // parse each template tag section accurately
        // refined parsing
        countl = s.length;
        variables = [];
        strings = {};
        hasVariables = false;
        hasStrings = false;
        hasBlock = false;
        index = 0;
        space = 0;
        ch = '';
        out = '';
        while (index < countl)
        {
            ch = s.charAt(index++);
            ind = special_chars.indexOf(ch);
            if (-1 < ind)
            {
                // variable
                if (0 === ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    tok = parse_variable(s, index, countl);
                    if (tok)
                    {
                        for (v=0,len=tok.length; v<len; ++v)
                        {
                            tokv = tok[v];
                            id = tokv[0];
                            //$__variables[$__currentblock][id] = tokv[1];
                            if (tokv[5]) strings = merge(strings, tokv[6]);
                        }
                        out += id;
                        index += tokv[4];
                        variables = variables.concat(tok);
                        hasVariables = true;
                        hasStrings = hasStrings || tokv[5];
                    }
                    else
                    {
                        out += '$';
                    }
                }
                // literal string
                else if (3 > ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    //tok = parse_string(s, ch, index, countl);
                    str_ = q = ch; escaped = false; si = index;
                    while (si < countl)
                    {
                        str_ += (ch=s.charAt(si++));
                        if (q === ch && !escaped)  break;
                        escaped = (!escaped && '\\' === ch);
                    }
                    tok = str_;
                    ++$__idcnt;
                    id = "#STR_"+$__idcnt+"#";
                    strings[id] = tok;
                    out += id;
                    index += tok.length-1;
                    hasStrings = true;
                }
                // spaces
                else if (9 > ind)
                {
                    ++space;
                }
                // directive or identifier or atom in compatibility mode
                else//if (9 === ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    q = ch;
                    if (non_compatibility_mode || index >= countl || !ALPHA.test(ch=s.charAt(index)))
                    {
                        out += q;
                        continue;
                    }
                    ++index;
                    tok = ch;
                    while (index < countl && ALPHANUM.test(ch = s.charAt(index)))
                    {
                        ++index;
                        tok += ch;
                    }
                    tok = '#ID_'+tok+'#';
                    out += tok;
                }
            }
            // directive or identifier or atom and not variable object property access
            else if (non_compatibility_mode && ALPHA.test(ch))
            {
                if (space > 0)
                {
                    out += " ";
                    space = 0;
                }
                is_prop_access = (2 < index && '-' === s.charAt(index-3) && '>' === s.charAt(index-2));
                tok = ch;
                while (index < countl && ALPHANUM.test(ch=s.charAt(index)))
                {
                    ++index;
                    tok += ch;
                }
                if (!is_prop_access && 'as' !== tok && 'in' !== tok && 'null' !== tok && 'false' !== tok && 'true' !== tok)
                {
                    tok = '#ID_'+tok+'#';
                }
                out += tok;
            }
            // rest, bypass
            else
            {
                if (space > 0)
                {
                    out += " ";
                    space = 0;
                }
                out += ch;
            }
        }
        // fix literal data notation, not needed here
        //out = str_replace(array('{', '}', '[', ']', ':'), array('array(', ')','array(', ')', '=>'), out);
        // fix pending "->" arrow-notation for object variable
        out = out.split('->').join('.');
        tag = "\t" + out + "\v";
        $__startblock = null;  $__endblock = null; $__blockptr = -1;
        $__strings = strings;
        parse_controls();
    };
    var after_parse_controls = function after_parse_controls() {
        // check for blocks
        if ($__startblock)
        {
            $__startblock = "#BLOCK_"+$__startblock+"#";
            hasBlock = true;
        }
        else if ($__endblock)
        {
            $__endblock = "#/BLOCK_"+$__endblock+"#";
            hasBlock = true;
        }
        notFoundBlock = hasBlock;
        // replacements
        /*.replace( re_repls, "' + ($1) + '" );*/
        if (9 === tag.charCodeAt(0) && 11 === tag.charCodeAt(tag.length-1))
            tag = "' + ("+trim(tag.slice(1, -1))+") + '";
        if (hasVariables)
        {
            // replace variables
            for (v=variables.length-1; v>=0; --v)
            {
                id = variables[v][0]; varname = variables[v][1];
                tag = tag
                    .split(id+'__RAW__').join(varname)
                    .split(id).join((
                        HAS.call($__locals[$__currentblock], varname)
                        ? ((2 === $__locals[$__currentblock][varname] ? '' : '_loc_') + varname) /* local (loop) variable */
                        : (variables[v][2]) /* default (data) variable */
                        ) + variables[v][3])
                ;
            }
        }
        if (hasStrings)
        {
            // replace strings (accurately)
            tagTpl = multisplit_re(tag, str_re);
            tag = '';
            for (v=0,len=tagTpl.length; v<len; ++v)
            {
                if (tagTpl[v][0])
                {
                    // and replace blocks (accurately)
                    if (notFoundBlock)
                    {
                        if ($__startblock)
                        {
                            blockTag = tagTpl[v][1].indexOf($__startblock);
                            if (-1 !== blockTag)
                            {
                                $__allblocks[$__blockptr-1][1] = blockTag + parsed.length + tag.length;
                                notFoundBlock = false;
                            }
                        }
                        else//if ($__endblock)
                        {
                            blockTag = tagTpl[v][1].indexOf($__endblock);
                            if (-1 !== blockTag)
                            {
                                $__allblocks[$__blockptr-1][2] = blockTag + parsed.length + tag.length + $__endblock.length;
                                notFoundBlock = false;
                            }
                        }
                    }
                    tag += tagTpl[v][1];
                }
                else
                {
                    tag += strings[tagTpl[v][1]];
                }
            }
        }
        else if (hasBlock)
        {
            // replace blocks (accurately)
            if ($__startblock)
                $__allblocks[$__blockptr-1][1] = parsed.length + tag.indexOf($__startblock);
            else//if ($__endblock)
                $__allblocks[$__blockptr-1][2] = parsed.length + tag.indexOf($__endblock) + $__endblock.length;
        }
        // replace tpl separators
        if (/*"\v"*/11 === tag.charCodeAt(tag.length-1))
        {
            tag = tag.slice(0, -1) + align($__tplEnd);
        }
        if (/*"\t"*/9 === tag.charCodeAt(0))
        {
            tag = $__tplStart + tag.slice(1);
            if (hasBlock)
            {
                // update blocks (accurately)
                blockTag = $__tplStart.length-1;
                if ($__startblock)
                    $__allblocks[$__blockptr-1][1] += blockTag;
                else//if ($__endblock)
                    $__allblocks[$__blockptr-1][2] += blockTag;
            }
        }
        parsed += tag;
        // continue until end
        parse_chunk();
    };
    var parse_controls = function parse_controls() {
        // replace constructs, functions, etc..
        parse_constructs_async(tag, function(err, repl) {
            if (err)
            {
                cb(err, null);
                return;
            }
            tag = repl;
            after_parse_controls();
        });
    };
    var parse_finish = function parse_finish() {
        cb(null, false !== withblocks ? ($__allblocks.length>0 ? parse_blocks(parsed) : [parsed, []]) : parsed);
    };
    parse_chunk();
}
function parse(tpl, leftTplSep, rightTplSep, withblocks, cb)
{
    if ('function' === typeof cb)
    {
        parse_async(tpl, leftTplSep, rightTplSep, withblocks, cb);
        return;
    }
    var t1, t2, p1, p2, l1, l2, len, parsed, s, i,
        tag, tagTpl, strings, variables, hasVariables, hasStrings, varname, id,
        countl, index, ch, out, tok, v, tokv,
        multisplit_re = InlineTemplate.multisplit_re,
        ind, q, str_, escaped, si, space,
        blockTag, hasBlock, notFoundBlock,
        special_chars = "$'\" \n\r\t\v\0%",
        non_compatibility_mode = true,
        is_prop_access, isphp, isjs, ispy,
        code, indent, l3, indenttype, m
    ;
    t1 = leftTplSep; l1 = t1.length;
    t2 = rightTplSep; l2 = t2.length;
    parsed = '';
    while (tpl && tpl.length)
    {
        p1 = tpl.indexOf(t1);
        if (-1 === p1)
        {
            s = tpl;
            if ($__escape) s = s.split("\\").join("\\\\"); // escape escapes
            parsed += s
                .split("'").join("\\'")  // escape single quotes accurately (used by parse function)
                .split(/*"\n"*/ /\n/).join($__preserveLines) // preserve lines
            ;
            break;
        }
        p2 = tpl.indexOf(t2, p1+l1);
        if (-1 === p2) p2 = tpl.length;
        if (p1 > 0)
        {
            s = tpl.slice(0, p1);
            if ($__escape) s = s.split("\\").join("\\\\"); // escape escapes
            parsed += s
                .split("'").join("\\'")  // escape single quotes accurately (used by parse function)
                .split(/*"\n"*/ /\n/).join($__preserveLines) // preserve lines
            ;
        }
        // php literal code block
        isphp = 'php:' === tpl.slice(p1+l1, p1+l1+4);
        // js literal code block
        isjs = 'js:' === tpl.slice(p1+l1, p1+l1+3);
        // py literal code block
        ispy = 'py:' === tpl.slice(p1+l1, p1+l1+3);
        if (isphp || isjs || ispy)
        {
            // include if in same language else ignore
            if (isjs)
            {
                if ('=' === tpl.slice(p1+l1+3, p1+l1+4))
                {
                    parsed += "';" + align("\n/* js code start */") + align("\n__p__ += String(" + trim(tpl.slice(p1+l1+4, p2)) + ");") + align("\n/* js code end */\n__p__ += '");
                }
                else
                {
                    indent = 0; l3 = 0; indenttype = 'none';
                    if (m = tpl.slice(p1+l1+3).match(INDENT))
                    {
                        indenttype = m[1];
                        indent = parseInt(m[2]);
                        l3 = m[0].length;
                    }
                    code = remove_blank_lines(tpl.slice(p1+l1+3+l3, p2));
                    if ('predent' === indenttype)
                    {
                        $__level = Math.max(0, $__level + indent);
                    }
                    parsed += "';" + align("\n/* js code start */");
                    if (trim(code).length)
                    {
                        parsed += "\n" + align(code);
                    }
                    if ('postdent' === indenttype)
                    {
                        $__level = Math.max(0, $__level + indent);
                    }
                    parsed += align("\n/* js code end */\n__p__ += '");
                }
            }
            tpl = tpl.slice(p2+l2);
            continue;
        }
        // template TAG
        s = tpl.slice(p1+l1, p2); tpl = tpl.slice(p2+l2);
        // parse each template tag section accurately
        // refined parsing
        countl = s.length;
        variables = [];
        strings = {};
        hasVariables = false;
        hasStrings = false;
        hasBlock = false;
        index = 0;
        space = 0;
        ch = '';
        out = '';
        while (index < countl)
        {
            ch = s.charAt(index++);
            ind = special_chars.indexOf(ch);
            if (-1 < ind)
            {
                // variable
                if (0 === ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    tok = parse_variable(s, index, countl);
                    if (tok)
                    {
                        for (v=0,len=tok.length; v<len; ++v)
                        {
                            tokv = tok[v];
                            id = tokv[0];
                            //$__variables[$__currentblock][id] = tokv[1];
                            if (tokv[5]) strings = merge(strings, tokv[6]);
                        }
                        out += id;
                        index += tokv[4];
                        variables = variables.concat(tok);
                        hasVariables = true;
                        hasStrings = hasStrings || tokv[5];
                    }
                    else
                    {
                        out += '$';
                    }
                }
                // literal string
                else if (3 > ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    //tok = parse_string(s, ch, index, countl);
                    str_ = q = ch; escaped = false; si = index;
                    while (si < countl)
                    {
                        str_ += (ch=s.charAt(si++));
                        if (q === ch && !escaped)  break;
                        escaped = (!escaped && '\\' === ch);
                    }
                    tok = str_;
                    ++$__idcnt;
                    id = "#STR_"+$__idcnt+"#";
                    strings[id] = tok;
                    out += id;
                    index += tok.length-1;
                    hasStrings = true;
                }
                // spaces
                else if (9 > ind)
                {
                    ++space;
                }
                // directive or identifier or atom in compatibility mode
                else//if (9 === ind)
                {
                    if (space > 0)
                    {
                        out += " ";
                        space = 0;
                    }
                    q = ch;
                    if (non_compatibility_mode || index >= countl || !ALPHA.test(ch=s.charAt(index)))
                    {
                        out += q;
                        continue;
                    }
                    ++index;
                    tok = ch;
                    while (index < countl && ALPHANUM.test(ch = s.charAt(index)))
                    {
                        ++index;
                        tok += ch;
                    }
                    tok = '#ID_'+tok+'#';
                    out += tok;
                }
            }
            // directive or identifier or atom and not variable object property access
            else if (non_compatibility_mode && ALPHA.test(ch))
            {
                if (space > 0)
                {
                    out += " ";
                    space = 0;
                }
                is_prop_access = (2 < index && '-' === s.charAt(index-3) && '>' === s.charAt(index-2));
                tok = ch;
                while (index < countl && ALPHANUM.test(ch=s.charAt(index)))
                {
                    ++index;
                    tok += ch;
                }
                if (!is_prop_access && 'as' !== tok && 'in' !== tok && 'null' !== tok && 'false' !== tok && 'true' !== tok)
                {
                    tok = '#ID_'+tok+'#';
                }
                out += tok;
            }
            // rest, bypass
            else
            {
                if (space > 0)
                {
                    out += " ";
                    space = 0;
                }
                out += ch;
            }
        }
        // fix literal data notation, not needed here
        //out = str_replace(array('{', '}', '[', ']', ':'), array('array(', ')','array(', ')', '=>'), out);
        // fix pending "->" arrow-notation for object variable
        out = out.split('->').join('.');
        tag = "\t" + out + "\v";
        $__startblock = null;  $__endblock = null; $__blockptr = -1;
        $__strings = strings;
        // replace constructs, functions, etc..
        tag = tag.replace(re_controls, function(m0,m1,m2,m3,m4,m5,m6,m7,m8,m9) {
            return parse_constructs([m0,m1,m2,m3,m4,m5,m6,m7,m8,m9]);
        });
        // check for blocks
        if ($__startblock)
        {
            $__startblock = "#BLOCK_"+$__startblock+"#";
            hasBlock = true;
        }
        else if ($__endblock)
        {
            $__endblock = "#/BLOCK_"+$__endblock+"#";
            hasBlock = true;
        }
        notFoundBlock = hasBlock;
        // replacements
        /*.replace( re_repls, "' + ($1) + '" );*/
        if (9 === tag.charCodeAt(0) && 11 === tag.charCodeAt(tag.length-1))
            tag = "' + ("+trim(tag.slice(1,-1))+") + '";
        if (hasVariables)
        {
            // replace variables
            for (v=variables.length-1; v>=0; --v)
            {
                id = variables[v][0]; varname = variables[v][1];
                tag = tag
                    .split(id+'__RAW__').join(varname)
                    .split(id).join((
                        HAS.call($__locals[$__currentblock], varname)
                        ? ((2 === $__locals[$__currentblock][varname] ? '' : '_loc_') + varname) /* local (loop) variable */
                        : (variables[v][2]) /* default (data) variable */
                        ) + variables[v][3])
                ;
            }
        }
        if (hasStrings)
        {
            // replace strings (accurately)
            tagTpl = multisplit_re(tag, str_re);
            tag = '';
            for (v=0,len=tagTpl.length; v<len; ++v)
            {
                if (tagTpl[v][0])
                {
                    // and replace blocks (accurately)
                    if (notFoundBlock)
                    {
                        if ($__startblock)
                        {
                            blockTag = tagTpl[v][1].indexOf($__startblock);
                            if (-1 !== blockTag)
                            {
                                $__allblocks[$__blockptr-1][1] = blockTag + parsed.length + tag.length;
                                notFoundBlock = false;
                            }
                        }
                        else//if ($__endblock)
                        {
                            blockTag = tagTpl[v][1].indexOf($__endblock);
                            if (-1 !== blockTag)
                            {
                                $__allblocks[$__blockptr-1][2] = blockTag + parsed.length + tag.length + $__endblock.length;
                                notFoundBlock = false;
                            }
                        }
                    }
                    tag += tagTpl[v][1];
                }
                else
                {
                    tag += strings[tagTpl[v][1]];
                }
            }
        }
        else if (hasBlock)
        {
            // replace blocks (accurately)
            if ($__startblock)
                $__allblocks[$__blockptr-1][1] = parsed.length + tag.indexOf($__startblock);
            else//if ($__endblock)
                $__allblocks[$__blockptr-1][2] = parsed.length + tag.indexOf($__endblock) + $__endblock.length;
        }
        // replace tpl separators
        if (/*"\v"*/11 === tag.charCodeAt(tag.length-1))
        {
            tag = tag.slice(0,-1) + align($__tplEnd);
        }
        if (/*"\t"*/9 === tag.charCodeAt(0))
        {
            tag = $__tplStart + tag.slice(1);
            if (hasBlock)
            {
                // update blocks (accurately)
                blockTag = $__tplStart.length-1;
                if ($__startblock)
                    $__allblocks[$__blockptr-1][1] += blockTag;
                else//if ($__endblock)
                    $__allblocks[$__blockptr-1][2] += blockTag;
            }
        }
        parsed += tag;
    }
    return false !== withblocks ? ($__allblocks.length>0 ? parse_blocks(parsed) : [parsed, []]) : parsed;
}
function get_cached_template_name(id, ctx, cacheDir)
{
    var filename, path;
    if ((isNode || isXPCOM) && (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\')))
    {
        filename = basename(id);
        path = trim(dirname(id), '/\\');
        if (path.length) path += '/';
    }
    else
    {
        filename = id;
        path = '';
    }
    return cacheDir + path + filename.replace(UNDERLN, '_') + '_tpl__' + ctx.replace(UNDERLN, '_') + '.js';
}
function get_cached_template_class(id, ctx)
{
    var filename;
    if ((isNode || isXPCOM) && (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\')))
    {
        filename = basename(id);
    }
    else
    {
        filename = id;
    }
    return 'Contemplate_' + filename.replace(UNDERLN, '_') + '__' + ctx.replace(UNDERLN, '_');
}
function get_template_contents(id, contx, cb)
{
    cb = 'function' === typeof cb ? cb : null;
    var proceed = function() {
        var template = contx.templates[id] || $__global.templates[id] || null;
        if (!template)
        {
            if (cb)
            {
                // async
                cb(null, '');
            }
            return '';
        }
        if (template[1]) //inline tpl
        {
            if (cb)
            {
                // async
                cb(null, template[0]);
                return '';
            }
            else
            {
                // sync
                return template[0];
            }
        }
        else
        {
            // nodejs, xpcom
            if (isNode || isXPCOM)
            {
                if (cb)
                {
                    // async
                    fread_async(template[0], contx.encoding, function(err, data) {
                        if (err)
                        {
                            cb(err, '');
                        }
                        else
                        {
                            cb(null, data);
                        }
                    });
                    return '';
                }
                else
                {
                    // sync
                    return fread(template[0], contx.encoding);
                }
            }
            // client-side js and #id of DOM script-element given as template holder
            else if ('#' === template[0].charAt(0))
            {
                if (cb)
                {
                    // async
                    cb(null, window.document.getElementById(template[0].slice(1)).innerHTML || '');
                    return '';
                }
                else
                {
                    // sync
                    return window.document.getElementById(template[0].slice(1)).innerHTML || '';
                }
            }
            // client-side js and url given as template location
            else
            {
                if (cb)
                {
                    // async
                    fread_async(template[0], contx.encoding, function(err, data) {
                        if (err)
                        {
                            cb(err, null);
                        }
                        else
                        {
                            cb(null, data);
                        }
                    });
                    return '';
                }
                else
                {
                    // sync
                    return fread(template[0], contx.encoding);
                }
            }
        }
    };
    if (!Contemplate.hasTpl(id, contx.id))
    {
        if (cb)
        {
            Contemplate.findTpl(id, contx.id, function(err, found) {
                if (err || !found)
                {
                    cb(null, '');
                    return;
                }
                var tpldef = {};
                tpldef[id] = found;
                Contemplate.add(tpldef, contx.id);
                proceed();
            });
        }
        else
        {
            // supposed to be sync operation if no callback given
            var found = Contemplate.findTpl(id, contx.id);
            if (!found) return '';
            var tpldef = {};
            tpldef[id] = found;
            Contemplate.add(tpldef, contx.id);
            return proceed();
        }
    }
    else
    {
        return proceed();
    }
}
function create_template_render_function(id, contx, seps, cb)
{
    cb = 'function' === typeof cb ? cb : null;
    var tpl, blocks, funcs = {}, b, bl, func, renderf, EOL = $__TEOL;
    if (cb)
    {
        get_template_contents(id, contx, function(err, tpl) {
            if (err)
            {
                cb(err, null);
                return;
            }
            tpl = get_separators(tpl, seps);
            reset_state();
            parse(tpl, $__leftTplSep, $__rightTplSep, true, function(err, blocks) {
                if (err)
                {
                    cb(err, null);
                    return;
                }
                clear_state();
                renderf = blocks[0];
                blocks = blocks[1];
                bl = blocks.length;
               // Convert the template into pure JavaScript
                func = TT_FUNC.render({
                 'FCODE'         : $__extends ? "__p__ = '';" : "__p__ = '" + renderf + "';"
                });
                // defined blocks
                for (b=0; b<bl; ++b) funcs[blocks[b][0]] = FUNC("Contemplate,data,self,__i__", blocks[b][1]);
                cb(null, [FUNC("Contemplate", func), funcs]);
            });
        });
        return null;
    }
    else
    {
        tpl = get_template_contents(id, contx);
        tpl = get_separators(tpl, seps);
        reset_state();
        blocks = parse(tpl, $__leftTplSep, $__rightTplSep, true);
        clear_state();
        renderf = blocks[0];
        blocks = blocks[1];
        bl = blocks.length;
       // Convert the template into pure JavaScript
        func = TT_FUNC.render({
         'FCODE'         : $__extends ? "__p__ = '';" : "__p__ = '" + renderf + "';"
        });
        // defined blocks
        for (b=0; b<bl; ++b) funcs[blocks[b][0]] = FUNC("Contemplate,data,self,__i__", blocks[b][1]);
        return [FUNC("Contemplate", func), funcs];
    }
}
function create_cached_template(id, contx, filename, classname, seps, cb)
{
    cb = 'function' === typeof cb ? cb : null;
    var tpl, funcs = {}, prefixCode, extendCode, renderCode,
        b, bl, sblocks, blocks, renderf, EOL = $__TEOL;
    if (cb)
    {
        get_template_contents(id, contx, function(err, tpl) {
            if (err)
            {
                cb(err, null);
                return;
            }
            tpl = get_separators(tpl, seps);
            reset_state();
            parse(tpl, $__leftTplSep, $__rightTplSep, true, function(err, blocks) {
                if (err)
                {
                    cb(err, null);
                    return;
                }
                clear_state();
                renderf = blocks[0];
                blocks = blocks[1];
                bl = blocks.length;
                // tpl-defined blocks
                sblocks = [];
                for (b=0; b<bl; ++b)
                    sblocks.push(EOL + TT_BlockCode.render({
                     'BLOCKNAME'            : blocks[b][0]
                    ,'BLOCKMETHODNAME'      : blocks[b][0]
                    ,'BLOCKMETHODCODE'      : align(blocks[b][1], 1)
                    }));
                sblocks = sblocks.length ? EOL + "self._blocks = {" + EOL + sblocks.join(',' + EOL) + EOL + "};" + EOL : '';
                renderCode = TT_RCODE.render({
                 'RCODE'                : $__extends ? "__p__ = '';" : "__p__ += '" + renderf + "';"
                });
                //extendCode = $__extends ? "self.extend('" + $__extends + "');" : '';
                extendCode = $__extends ? "self._extendsTpl = '" + $__extends + "';" : '';
                extendCode += EOL + "self._usesTpl = [" + ($__uses.length ? "'"+$__uses.join("','")+"'" : '') + "];";
                prefixCode = contx.prefix ? contx.prefix : '';
              // generate tpl class
                var classCode = TT_ClassCode.render({
                 'CLASSNAME'            : classname
                ,'TPLID'                : id
                ,'PREFIXCODE'           : prefixCode
                ,'EXTENDCODE'           : align(extendCode, 1)
                ,'BLOCKS'               : align(sblocks, 1)
                ,'RENDERCODE'           : align(renderCode, 1)
                });
                fwrite_async(filename, classCode, contx.encoding, function(err, res) {
                    cb(err, res);
                });
            });
        });
        return null;
    }
    else
    {
        tpl = get_template_contents(id, contx);
        tpl = get_separators(tpl, seps);
        reset_state();
        blocks = parse(tpl, $__leftTplSep, $__rightTplSep, true);
        clear_state();
        renderf = blocks[0];
        blocks = blocks[1];
        bl = blocks.length;
        // tpl-defined blocks
        sblocks = [];
        for (b=0; b<bl; ++b)
            sblocks.push(EOL + TT_BlockCode.render({
             'BLOCKNAME'            : blocks[b][0]
            ,'BLOCKMETHODNAME'      : blocks[b][0]
            ,'BLOCKMETHODCODE'      : align(blocks[b][1], 1)
            }));
        sblocks = sblocks.length ? EOL + "self._blocks = {" + EOL + sblocks.join(',' + EOL) + EOL + "};" + EOL : '';
        renderCode = TT_RCODE.render({
         'RCODE'                : $__extends ? "__p__ = '';" : "__p__ += '" + renderf + "';"
        });
        //extendCode = $__extends ? "self.extend('" + $__extends + "');" : '';
        extendCode = $__extends ? "self._extendsTpl = '" + $__extends + "';" : '';
        extendCode += EOL + "self._usesTpl = [" + ($__uses.length ? "'"+$__uses.join("','")+"'" : '') + "];";
        prefixCode = contx.prefix ? contx.prefix : '';
      // generate tpl class
        var classCode = TT_ClassCode.render({
         'CLASSNAME'            : classname
        ,'TPLID'                : id
        ,'PREFIXCODE'           : prefixCode
        ,'EXTENDCODE'           : align(extendCode, 1)
        ,'BLOCKS'               : align(sblocks, 1)
        ,'RENDERCODE'           : align(renderCode, 1)
        });
        return fwrite(filename, classCode, contx.encoding);
    }
}
function get_cached_template(id, contx, options, cb)
{
    cb = 'function' === typeof cb ? cb : null;
    var template, tplclass, tpl, sprTpl, funcs, cachedTplFile, cachedTplClass, stat, stat2,
        exists, fname, fpath, parsed;
    template = contx.templates[id] || $__global.templates[id] || null;
    if (!template)
    {
        if (cb)
        {
            cb(null, null);
        }
        return null;
    }
    options = options || {context:contx.id,autoUpdate:false};
    parsed = options.parsed || null;
    if (HAS.call(options, 'parsed')) delete options.parsed;
    if (cb)
    {
        var setUsedTpls = function(tpl, cb) {
            var usedTpls = tpl._usesTpl && tpl._usesTpl.length ? tpl._usesTpl : null;
            if (usedTpls)
            {
                var i = 0, load_one = function load_one() {
                    if (i >= usedTpls.length)
                    {
                        cb(null, tpl);
                        return;
                    }
                    Contemplate.tpl(usedTpls[i], null, options, function(err, usedtpl) {
                        ++i;
                        load_one();
                    });
                };
                load_one();
            }
            else
            {
                cb(null, tpl);
            }
        };
        // inline templates saved only in-memory
        if (template[1])
        {
            var setSuper = function(tpl, cb) {
                sprTpl = $__extends || tpl._extendsTpl;
                if (sprTpl)
                {
                    Contemplate.tpl(sprTpl, null, options, function(err, spr) {
                        if (err)
                        {
                            cb(err, null);
                            return;
                        }
                        tpl.extend(spr);
                        cb(null, tpl);
                    });
                }
                else
                {
                    cb(null, tpl);
                }
            };
            // dynamic in-memory caching during page-request
            tpl = new Contemplate.Template(id).ctx(contx);
            if (parsed)
            {
                // already parsed code was given
                tpl.setRenderFunction(FUNC("Contemplate", parsed));
                setSuper(tpl, function(err, ctpl) {
                   if (!err) setUsedTpls(ctpl, cb);
                   else cb(err, null);
                });
            }
            else
            {
                // parse code and create template class
                create_template_render_function(id, contx, options.separators, function(err, funcs) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    tpl.setRenderFunction(funcs[0]).setBlocks(funcs[1]).usesTpl($__uses);
                    setSuper(tpl, function(err, ctpl) {
                       if (!err) setUsedTpls(ctpl, cb);
                       else cb(err, null);
                    });
                });
            }
        }
        else
        {
            if (!isNode && !isXPCOM) contx.cacheMode = Contemplate.CACHE_TO_DISK_NONE;
            var create_and_load_tpl = function(do_create, check_exists) {
                if (false === do_create)
                {
                    if (false === check_exists)
                    {
                        tplclass = import_module(cachedTplClass, cachedTplFile)(Contemplate);
                        tpl = (new tplclass(id))/*.setId(id)*/.ctx(contx);
                        if (sprTpl = tpl._extendsTpl)
                        {
                            Contemplate.tpl(sprTpl, null, options, function(err, spr) {
                                if (err)
                                {
                                    cb(err, null);
                                    return;
                                }
                                tpl.extend(spr);
                                setUsedTpls(tpl, cb);
                            });
                        }
                        else
                        {
                            setUsedTpls(tpl, cb);
                        }
                    }
                    else
                    {
                        fexists_async(cachedTplFile, function(err, exists) {
                            if (err || !exists)
                            {
                                cb(err || new Error('Could not create or read file "'+cachedTplFile+'"!'), null);
                                return;
                            }
                            tplclass = import_module(cachedTplClass, cachedTplFile)(Contemplate);
                            tpl = (new tplclass(id))/*.setId(id)*/.ctx(contx);
                            if (sprTpl = tpl._extendsTpl)
                            {
                                Contemplate.tpl(sprTpl, null, options, function(err, spr) {
                                    if (err)
                                    {
                                        cb(err, null);
                                        return;
                                    }
                                    tpl.extend(spr);
                                    setUsedTpls(tpl, cb);
                                });
                            }
                            else
                            {
                                setUsedTpls(tpl, cb);
                            }
                        } );
                    }
                }
                else
                {
                    create_cached_template(id, contx, cachedTplFile, cachedTplClass, options.separators, function(err, res) {
                        if (err)
                        {
                            cb(err, null);
                            return;
                        }
                        fexists_async(cachedTplFile, function(err, exists) {
                            if (err || !exists)
                            {
                                cb(err || new Error('Could not create or read file "'+cachedTplFile+'"!'), null);
                                return;
                            }
                            tplclass = import_module(cachedTplClass, cachedTplFile)(Contemplate);
                            tpl = (new tplclass(id))/*.setId(id)*/.ctx(contx);
                            if (sprTpl = tpl._extendsTpl)
                            {
                                Contemplate.tpl(sprTpl, null, options, function(err, spr) {
                                    if (err)
                                    {
                                        cb(err, null);
                                        return;
                                    }
                                    tpl.extend(spr);
                                    setUsedTpls(tpl, cb);
                                });
                            }
                            else
                            {
                                setUsedTpls(tpl, cb);
                            }
                        } );
                    } );
                }
            };
            if (true !== options.autoUpdate && Contemplate.CACHE_TO_DISK_NOUPDATE === contx.cacheMode)
            {
                cachedTplFile = get_cached_template_name(id, contx.id, contx.cacheDir);
                cachedTplClass = get_cached_template_class(id, contx.id);
                fexists_async(cachedTplFile, function(err, exists) {
                    if (!exists)
                    {
                        if (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\'))
                        {
                            fname = basename(id);
                            fpath = trim(dirname(id), '/\\');
                        }
                        else
                        {
                            fname = id;
                            fpath = '';
                        }
                        if (fpath.length)
                        {
                            create_path(fpath, contx.cacheDir, parseInt('0755', 8), function(err, res) {
                                if (err)
                                {
                                    cb(err, null);
                                    return;
                                }
                                create_and_load_tpl();
                            });
                        }
                        else
                        {
                            create_and_load_tpl();
                        }
                    }
                    else
                    {
                        create_and_load_tpl(false, false);
                    }
                });
            }
            else if (true === options.autoUpdate || Contemplate.CACHE_TO_DISK_AUTOUPDATE === contx.cacheMode)
            {
                cachedTplFile = get_cached_template_name(id, contx.id, contx.cacheDir);
                cachedTplClass = get_cached_template_class(id, contx.id);
                fexists_async(cachedTplFile, function(err, exists) {
                    if (!exists)
                    {
                        // if tpl not exist create it
                        if (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\'))
                        {
                            fname = basename(id);
                            fpath = trim(dirname(id), '/\\');
                        }
                        else
                        {
                            fname = id;
                            fpath = '';
                        }
                        if (fpath.length)
                        {
                            create_path(fpath, contx.cacheDir, parseInt('0755',8), function(err, res) {
                                if (err)
                                {
                                    cb(err, null);
                                    return;
                                }
                                create_and_load_tpl();
                            });
                        }
                        else
                        {
                            create_and_load_tpl();
                        }
                    }
                    else
                    {
                        fstat_async(cachedTplFile, function(err, stat) {
                           if (err)
                           {
                               cb(err, null);
                               return;
                           }
                           fstat_async(template[0], function(err, stat2) {
                                if (err)
                                {
                                    cb(err, null);
                                    return;
                                }
                                if (stat.mtime.getTime() <= stat2.mtime.getTime())
                                {
                                    // is out-of-sync re-create it
                                    create_and_load_tpl();
                                }
                                else
                                {
                                    create_and_load_tpl(false, false);
                                }
                           });
                        });
                    }
                } );
                return null;
            }
            else
            {
                // dynamic in-memory caching during page-request
                create_template_render_function(id, contx, options.separators, function(err, funcs) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    tpl = (new Contemplate.Template(id)).ctx(contx).setRenderFunction(funcs[0]).setBlocks(funcs[1]).usesTpl($__uses);
                    sprTpl = $__extends;
                    if (sprTpl)
                    {
                        Contemplate.tpl(sprTpl, null, options, function(err, spr) {
                            if (err)
                            {
                                cb(err, null);
                                return;
                            }
                            tpl.extend(spr);
                            setUsedTpls(tpl, cb);
                        });
                    }
                    else
                    {
                        setUsedTpls(tpl, cb);
                    }
                });
            }
        }
    }
    else
    {
        // inline templates saved only in-memory
        if (template[1])
        {
            // dynamic in-memory caching during page-request
            tpl = (new Contemplate.Template(id)).ctx(contx);
            if (parsed)
            {
                // already parsed code was given
                tpl.setRenderFunction(FUNC("Contemplate", parsed));
            }
            else
            {
                // parse code and create template class
                funcs = create_template_render_function(id, contx, options.separators);
                tpl.setRenderFunction(funcs[0]).setBlocks(funcs[1]).usesTpl($__uses);
            }
            sprTpl = $__extends || tpl._extendsTpl;
            if (sprTpl) tpl.extend(Contemplate.tpl(sprTpl, null, options));
            return tpl;
        }
        else
        {
            if (!isNode && !isXPCOM) contx.cacheMode = Contemplate.CACHE_TO_DISK_NONE;
            if (true !== options.autoUpdate && Contemplate.CACHE_TO_DISK_NOUPDATE === contx.cacheMode)
            {
                cachedTplFile = get_cached_template_name(id, contx.id, contx.cacheDir);
                cachedTplClass = get_cached_template_class(id, contx.id);
                exists = fexists(cachedTplFile);
                if (!exists)
                {
                    if (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\'))
                    {
                        fname = basename(id);
                        fpath = trim(dirname(id), '/\\');
                    }
                    else
                    {
                        fname = id;
                        fpath = '';
                    }
                    if (fpath.length) create_path(fpath, contx.cacheDir, parseInt('0755', 8));
                    create_cached_template(id, contx, cachedTplFile, cachedTplClass, options.separators);
                }
                if (fexists(cachedTplFile))
                {
                    tplclass = import_module(cachedTplClass, cachedTplFile)(Contemplate);
                    tpl = (new tplclass(id))/*.setId(id)*/.ctx(contx);
                    if (tpl._extendsTpl) tpl.extend(Contemplate.tpl(tpl._extendsTpl, null, options));
                    return tpl;
                }
                return null;
            }
            else if (true === options.autoUpdate || Contemplate.CACHE_TO_DISK_AUTOUPDATE === contx.cacheMode)
            {
                cachedTplFile = get_cached_template_name(id, contx.id, contx.cacheDir);
                cachedTplClass = get_cached_template_class(id, contx.id);
                exists = fexists(cachedTplFile);
                if (!exists)
                {
                    // if tpl not exist create it
                    if (-1 !== id.indexOf('/') || -1 !== id.indexOf('\\'))
                    {
                        fname = basename(id);
                        fpath = trim(dirname(id), '/\\');
                    }
                    else
                    {
                        fname = id;
                        fpath = '';
                    }
                    if (fpath.length) create_path(fpath, contx.cacheDir, parseInt('0755', 8));
                    create_cached_template(id, contx, cachedTplFile, cachedTplClass, options.separators);
                }
                else
                {
                    stat = fstat(cachedTplFile); stat2 = fstat(template[0]);
                    if (stat.mtime.getTime() <= stat2.mtime.getTime())
                    {
                        // is out-of-sync re-create it
                        create_cached_template(id, contx, cachedTplFile, cachedTplClass, options.separators);
                    }
                }
                if (fexists(cachedTplFile))
                {
                    tplclass = import_module(cachedTplClass, cachedTplFile)(Contemplate);
                    tpl = (new tplclass(id))/*.setId(id)*/.ctx(contx);
                    if (tpl._extendsTpl) tpl.extend(Contemplate.tpl(tpl._extendsTpl, null, options));
                    return tpl;
                }
                return null;
            }
            else
            {
                // dynamic in-memory caching during page-request
                funcs = create_template_render_function(id, contx, options.separators);
                tpl = (new Contemplate.Template(id)).ctx(contx).setRenderFunction(funcs[0]).setBlocks(funcs[1]).usesTpl($__uses);
                sprTpl = $__extends || tpl._extendsTpl;
                if (sprTpl) tpl.extend(Contemplate.tpl(sprTpl, null, options));
                return tpl;
            }
        }
    }
}
function split_and_filter(r, s, regex)
{
    return s.split(r).map(function(x) {return trim(x);}).filter(function(x) {return 0<x.length;});
}
function create_path(path, root, mode, cb)
{
    path = trim(path);
    if (!path.length) return;
    mode = mode || parseInt('0755', 8);
    root = root || '';
    var i, l,
        parts = split_and_filter(DS_RE, path),
        current = rtrim(root, '/\\'), exists0 = true;
    if ('function' === typeof cb)
    {
        i = 0; l = parts.length;
        current += '/' + parts[i];
        var create_one_level = function create_one_level() {
            if (i >= l)
            {
                cb(null, true);
                return;
            }
            if (false === exists0)
            {
                fmkdir_async(current, mode, function(err, res) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    if (i+1 < l)
                    {
                        ++i;
                        current += '/' + parts[i];
                        create_one_level();
                    }
                    else
                    {
                        cb(null, true);
                    }
                });
            }
            else
            {
                fexists_async(current, function(err, exists) {
                    if (err)
                    {
                        cb(err, null);
                        return;
                    }
                    if (!exists) exists0 = false;
                    fmkdir_async(current, mode, function(err, res) {
                        if (err)
                        {
                            cb(err, null);
                            return;
                        }
                        if (i+1 < l)
                        {
                            ++i;
                            current += '/' + parts[i];
                            create_one_level();
                        }
                        else
                        {
                            cb(null, true);
                        }
                    });
                });
            }
        };
        create_one_level();
    }
    else
    {
        for(i=0,l=parts.length; i<l; ++i)
        {
            current += '/' + parts[i];
            if (!fexists(current) /*&& !fis_dir(current)*/)
                fmkdir(current, mode);
        }
    }
}
function ContemplateException(msg)
{
    this.name = 'ContemplateException';
    this.message = msg;
}
ContemplateException[PROTO] = Object.create(Error[PROTO]);
// can use inline templates for plugins etc.. to enable non-linear plugin compile-time replacement
function InlineTemplate(tpl, replacements, compiled)
{
    var self = this;
    if (!(self instanceof InlineTemplate)) return new InlineTemplate(tpl, replacements, compiled);
    self.id = null;
    self._renderer = null;
    self._parsed = false; // lazy init, only if needed, as and when needed
    self._args = [tpl, replacements, compiled];
    self.tpl = null;
}
InlineTemplate.multisplit = function multisplit(tpl, reps, as_array) {
    var r, sr, s, i, j, a, b, c, al, bl/*, as_array = is_array(reps)*/;
    as_array = !!as_array;
    a = [[1, tpl]];
    for (r in reps)
    {
        if (!HAS.call(reps, r)) continue;
        c = []; sr = as_array ? reps[r] : r; s = [0, reps[r]];
        for (i=0,al=a.length; i<al; ++i)
        {
            if (1 === a[i][0])
            {
                b = a[i][1].split(sr); bl = b.length;
                c.push([1, b[0]]);
                if (bl > 1)
                {
                    for (j=0; j<bl-1; ++j)
                    {
                        c.push(s);
                        c.push([1, b[j+1]]);
                    }
                }
            }
            else
            {
                c.push(a[i]);
            }
        }
        a = c;
    }
    return a;
};
InlineTemplate.multisplit_re = function multisplit_re(tpl, re) {
    re = re.global ? re : new RegExp(re.source, re.ignoreCase ? "gi" : "g"); /* make sure global flag is added */
    var a = [], i = 0, m;
    while (m = re.exec(tpl))
    {
        a.push([1, tpl.slice(i, re.lastIndex - m[0].length)]);
        a.push([0, m[1] ? m[1] : m[0]]);
        i = re.lastIndex;
    }
    a.push([1, tpl.slice(i)]);
    return a;
};
InlineTemplate.compile = function(tpl) {
    var l = tpl.length,
        i, notIsSub, s, out = '"use strict";' + "\n" + 'return (';
    ;
    for (i=0; i<l; ++i)
    {
        notIsSub = tpl[i][0]; s = tpl[i][1];
        if (notIsSub) out += "'" + s.replace(SQUOTE, "\\'").replace(NEWLINE, "' + \"\\n\" + '") + "'";
        else out += " + String(args['" + s + "']) + ";
    }
    out += ');';
    return FUNC('args', out);
};
InlineTemplate[PROTO] = {
    constructor: InlineTemplate
    ,id: null
    ,tpl: null
    ,_renderer: null
    ,_parsed: false
    ,_args: null
    ,dispose: function() {
        var self = this;
        self.id = null;
        self.tpl = null;
        self._renderer = null;
        self._parsed = null;
        self._args = null;
        return self;
    }
    ,render: function(args) {
        var self = this;
        args = args || [];
        if (!self._parsed) // lazy init, only if needed, as and when needed
        {
            var tpl = self._args[0], replacements = self._args[1], compiled = self._args[2];
            self.tpl = replacements instanceof RegExp
                ? InlineTemplate.multisplit_re(tpl||'', replacements)
                : InlineTemplate.multisplit(tpl||'', replacements||{});
            if (true === compiled)
            {
                self._renderer = InlineTemplate.compile(self.tpl);
                self.render = self._renderer;
            }
            self._args = null;
            self._parsed = true;
        }
        if (is_callable(self._renderer)) return self._renderer(args);
        var tpl = self.tpl, l = tpl.length,
            i, notIsSub, s, out = ''
        ;
        for (i=0; i<l; ++i)
        {
            notIsSub = tpl[i][0]; s = tpl[i][1];
            out += (notIsSub ? s : args[s]);
        }
        return out;
    }
};
function Template(id)
{
    var self = this;
    if (!(self instanceof Template)) return new Template(id);
    self._renderer = null;
    self._blocks = null;
    self._extends = null;
    self._extendsTpl = null;
    self._usesTpl = null;
    self._ctx = null;
    self._autonomus = false;
    self.id = null;
    if (id) self.id = id;
}
Template.spr = function(data, __i__) {
    var self = this, r, __ctx = false;
    !__i__&&(__i__=self)&&(self._autonomus||(__ctx=Contemplate._set_ctx(self._ctx)));
    r = self._extends.render(data, __i__);
    __ctx&&Contemplate._set_ctx(__ctx);
    return r;
};
Template.fixr = function(tpl) {
    tpl.render = tpl._extends instanceof Template
                ? Template.spr
                : (is_callable(tpl._renderer)
                ? tpl._renderer
                : tpl.constructor[PROTO].render);
    return tpl;
};
Template[PROTO] = {
    constructor: Template
    ,id: null
    ,_renderer: null
    ,_blocks: null
    ,_extends: null
    ,_extendsTpl: null
    ,_usesTpl: null
    ,_ctx: null
    ,_autonomus: false
    // public methods
    ,dispose: function() {
        var self = this;
        self._renderer = null;
        self._blocks = null;
        self._extends = null;
        self._extendsTpl = null;
        self._usesTpl = null;
        self._ctx = null;
        self._autonomus = null;
        self.id = null;
        return self;
    }
    ,setId: function(id) {
        if (id) this.id = id;
        return this;
    }
    ,ctx: function(ctx) {
        this._ctx = ctx;
        return this;
    }
    ,autonomus: function(enable) {
        this._autonomus = !arguments.length ? true : !!enable;
        return this;
    }
    ,extend: function(tpl) {
        var self = this;
        self._extends = tpl && tpl.substr
                    ? Contemplate.tpl(tpl)
                    : (tpl instanceof Template
                    ? tpl
                    : null);
        Template.fixr(self);
        return self;
    }
    ,usesTpl: function(usedTpls) {
        var self = this;
        self._usesTpl = [].concat(usedTpls);
        return self;
    }
    ,setRenderFunction: function(renderfunc) {
        var self = this;
        self._renderer = is_callable(renderfunc)
                    ? renderfunc(Contemplate)
                    : null;
        Template.fixr(self);
        return self;
    }
    ,setBlocks: function(blocks) {
        var self = this;
        if ('object' === typeof blocks)
            self._blocks = merge(self._blocks || {}, blocks);
        return self;
    }
    ,sprblock: function(block, data/*, __i__*/) {
        var self = this;
        //__i__ = __i__ || self;
        if (self._extends) return self._extends.block(block, data, self._extends);
        return '';
    }
    ,block: function(block, data, __i__) {
        var self = this, r = '', __ctx = false, blocks = self._blocks;
        !__i__&&(__i__=self)&&(self._autonomus||(__ctx=Contemplate._set_ctx(self._ctx)));
        if (blocks && HAS.call(blocks, block)) r = blocks[block](Contemplate, data, self, __i__);
        else if (self._extends) r = self._extends.block(block, data, __i__);
        __ctx&&Contemplate._set_ctx(__ctx);
        return r;
    }
    ,render: function(data, __i__) {
        return '';
    }
}
// aliases
Template[PROTO].renderBlock = Template[PROTO].block;
Template[PROTO].renderSuperBlock = Template[PROTO].sprblock;
function Ctx(id)
{
    var self = this;
    self.id               = id;
    self.cacheDir         = './';
    self.cacheMode        = 0;
    self.cache            = {};
    self.templateDirs     = [];
    self.templateFinder   = null;
    self.templates        = {};
    self.partials         = {};
    self.plugins          = {};
    self.prefix           = '';
    self.encoding         = isXPCOM ? 'UTF-8' : 'utf8';
}
Ctx[PROTO] = {
    constructor: Ctx
    ,id: null
    ,cacheDir: null
    ,cacheMode: null
    ,cache: null
    ,templateDirs: null
    ,templateFinder: null
    ,templates: null
    ,partials: null
    ,plugins: null
    ,prefix: null
    ,encoding: null
    ,dispose: function() {
        var self = this;
        self.id = null;
        self.cacheDir = null;
        self.cacheMode = null;
        self.templateDirs = null;
        self.templateFinder = null;
        self.templates = null;
        self.partials = null;
        self.plugins = null;
        self.prefix = null;
        self.encoding = null;
        if (self.cache)
        {
            for(var tpl in self.cache)
                if (HAS.call(self.cache, tpl))
                    self.cache[tpl].dispose();
        }
        self.cache = null;
    }
};
Contemplate = {
    // constants
    VERSION: __version__
    ,CACHE_TO_DISK_NONE: 0
    ,CACHE_TO_DISK_AUTOUPDATE: 2
    ,CACHE_TO_DISK_NOUPDATE: 4
    ,Exception: ContemplateException
    ,Template: Template
    ,InlineTemplate: InlineTemplate
    ,Ctx: Ctx
    ,init: function() {
        if ($__isInited) return;
        // a default global context
        $__global = new Ctx('global');
        $__ctx = {
        'global'  : $__global
        };
        $__context = $__global;
        // pre-compute the needed regular expressions
        $__preserveLines = $__preserveLinesDefault;
        $__tplStart = "';" + $__TEOL;
        $__tplEnd = $__TEOL + "__p__ += '";
        // make compilation templates
        TT_ClassCode = new InlineTemplate([
            "#PREFIXCODE#"
            ,"!function(root, name, factory) {"
            ,"\"use strict\";"
            ,"if (('undefined'!==typeof Components)&&('object'===typeof Components.classes)&&('object'===typeof Components.classesByID)&&Components.utils&&('function'===typeof Components.utils['import'])) /* XPCOM */"
            ,"    (root.$deps = root.$deps||{}) && (root.EXPORTED_SYMBOLS = [name]) && (root[name] = root.$deps[name] = factory.call(root));"
            ,"else if (('object'===typeof module)&&module.exports) /* CommonJS */"
            ,"    (module.$deps = module.$deps||{}) && (module.exports = module.$deps[name] = factory.call(root));"
            ,"else if (('function'===typeof define)&&define.amd&&('function'===typeof require)&&('function'===typeof require.specified)&&require.specified(name) /*&& !require.defined(name)*/) /* AMD */"
            ,"    define(name,['module'],function(module){factory.moduleUri = module.uri; return factory.call(root);});"
            ,"else if (!(name in root)) /* Browser/WebWorker/.. */"
            ,"    (root[name] = factory.call(root)||1)&&('function'===typeof(define))&&define.amd&&define(function(){return root[name];} );"
            ,"}('undefined' !== typeof self ? self : this,'#CLASSNAME#',function() {"
            ,"\"use strict\";"
            ,"return function(Contemplate) {"
            ,"/* Contemplate cached template '#TPLID#', constructor */"
            ,"function #CLASSNAME#(id)"
            ,"{"
            ,"    var self = this;"
            ,"    Contemplate.Template.call(self, id);"
            ,"    /* tpl-defined blocks render code starts here */"
            ,"#BLOCKS#"
            ,"    /* tpl-defined blocks render code ends here */"
            ,"    /* extend tpl assign code starts here */"
            ,"#EXTENDCODE#"
            ,"    /* extend tpl assign code ends here */"
            ,"}"
            ,"/* extends main Contemplate.Template class */"
            ,"#CLASSNAME#.prototype = Object.create(Contemplate.Template.prototype);"
            ,"/* render method */"
            ,"#CLASSNAME#.prototype.render = function(data, __i__) {"
            ,"    \"use strict\";"
            ,"    var self = this, __p__ = '', __ctx = false;"
            ,"    !__i__&&(__i__=self)&&(self._autonomus||(__ctx=Contemplate._set_ctx(self._ctx)));"
            ,"    /* tpl main render code starts here */"
            ,"#RENDERCODE#"
            ,"    /* tpl main render code ends here */"
            ,"    __ctx&&Contemplate._set_ctx(__ctx);"
            ,"    return __p__;"
            ,"};"
            ,"// export it"
            ,"return #CLASSNAME#;"
            ,"};"
            ,"});"
            ,""
        ].join($__TEOL), {
             "#PREFIXCODE#"         : "PREFIXCODE"
            ,"#CLASSNAME#"          : "CLASSNAME"
            ,"#TPLID#"              : "TPLID"
            ,"#BLOCKS#"             : "BLOCKS"
            ,"#EXTENDCODE#"         : "EXTENDCODE"
            ,"#RENDERCODE#"         : "RENDERCODE"
        }, false);
        TT_BlockCode = new InlineTemplate([
            ""
            ,"/* tpl block render method for block '#BLOCKNAME#' */"
            ,"'#BLOCKMETHODNAME#': function(Contemplate, data, self, __i__) {"
            ,"#BLOCKMETHODCODE#"
            ,"}"
            ,""
        ].join($__TEOL), {
             "#BLOCKNAME#"          : "BLOCKNAME"
            ,"#BLOCKMETHODNAME#"    : "BLOCKMETHODNAME"
            ,"#BLOCKMETHODCODE#"    : "BLOCKMETHODCODE"
        }, false);
        TT_BLOCK = new InlineTemplate([
            "\"use strict\";"
            ,"var __p__ = '';"
            ,"#BLOCKCODE#"
            ,"return __p__;"
            ,""
        ].join($__TEOL), {
             "#BLOCKCODE#"          : "BLOCKCODE"
        }, false);
        TT_FUNC = new InlineTemplate([
            "return function(data, __i__){"
            ,"\"use strict\";"
            ,"var self = this, __p__ = '', __ctx = false;"
            ,"!__i__&&(__i__=self)&&(self._autonomus||(__ctx=Contemplate._set_ctx(self._ctx)));"
            ,"#FCODE#"
            ,"__ctx&&Contemplate._set_ctx(__ctx);"
            ,"return __p__;"
            ,"};"
        ].join($__TEOL), {
             "#FCODE#"              : "FCODE"
        }, false);
        TT_RCODE = new InlineTemplate([
            ""
            ,"#RCODE#"
            ,""
        ].join($__TEOL), {
             "#RCODE#"              : "RCODE"
        }, false);
        clear_state();
        $__isInited = true;
    }
    ,_set_ctx: function(ctx) {
        var contx = $__context;
        /*if ( ctx instanceof Ctx ) $__context = ctx;
        else if ( ctx && HAS.call($__ctx,ctx) ) $__context = $__ctx[ctx];
        else $__context = $__global;*/
        $__context = ctx ? ctx : $__global;
        return contx;
    }
    //
    // Main API methods
    //
    ,createCtx: function(ctx) {
        if (ctx && 'global' !== ctx && !HAS.call($__ctx,ctx)) $__ctx[ctx] = new Ctx(ctx);
    }
    ,disposeCtx: function(ctx) {
        if (ctx && 'global' !== ctx && HAS.call($__ctx, ctx))
        {
            $__ctx[ctx].dispose();
            delete $__ctx[ctx];
        }
    }
    ,setTemplateSeparators: function(seps) {
        if (seps)
        {
            if (seps['left'])  $__leftTplSep = ''+seps['left'];
            if (seps['right']) $__rightTplSep = ''+seps['right'];
        }
    }
    ,setPreserveLines: function(enable) {
        if (arguments.length < 1) enable = true;
        $__preserveLines = !!enable ? $__preserveLinesDefault : '';
    }
    ,hasPlugin: function(name, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        return !!name && (HAS.call(contx.plugins, name) || HAS.call($__global.plugins, name));
    }
    ,addPlugin: function(name, pluginCode, ctx) {
        var contx;
        if (name && pluginCode)
        {
            if (arguments.length < 2) ctx = 'global';
            contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
            contx.plugins[name] = pluginCode;
        }
    }
    ,plg_: function(plg) {
        var args = arguments;
        if (HAS.call($__context.plugins, plg) && is_callable($__context.plugins[plg]))
        {
            return $__context.plugins[plg].apply(null, slice.call(args, 1));
        }
        else if (HAS.call($__global.plugins, plg) && is_callable($__global.plugins[plg]))
        {
            return $__global.plugins[plg].apply(null, slice.call(args, 1));
        }
        return '';
    }
    ,setPrefixCode: function(preCode, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        if (preCode) contx.prefix = '' + preCode;
    }
    ,setEncoding: function(encoding, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx,ctx) ? $__ctx[ctx] : $__context;
        contx.encoding = encoding;
    }
    ,setCacheDir: function(dir, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        contx.cacheDir = rtrim(dir, '/\\') + '/';
    }
    ,setCacheMode: function(mode, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        contx.cacheMode = isNode || isXPCOM ? mode : Contemplate.CACHE_TO_DISK_NONE;
    }
    ,setTemplateDirs: function(dirs, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        contx.templateDirs = [].concat(dirs);
    }
    ,getTemplateDirs: function(ctx) {
        var contx;
        if (arguments.length < 1) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        return contx.templateDirs;
    }
    ,setTemplateFinder: function(finder, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        contx.templateFinder = is_callable(finder) ? finder : null;
    }
    ,clearCache: function(all, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        contx.cache = {};
        if (all) contx.partials = {};
    }
    ,add: function(tpls, ctx) {
        var contx, tplID;
        if (tpls && ("object" === typeof tpls))
        {
            if (arguments.length < 2) ctx = 'global';
            contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
            for (tplID in tpls)
            {
                if (!HAS.call(tpls, tplID)) continue;
                if (is_array(tpls[tplID]))
                {
                    // unified way to add tpls both as reference and inline
                    // inline tpl, passed as array
                    if (tpls[tplID][0])
                        contx.templates[tplID] = [tpls[tplID][0], true];
                }
                else
                {
                    contx.templates[tplID] = [tpls[tplID], false];
                }
            }
        }
    }
    ,hasTpl: function(tpl, ctx) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        return !!tpl && (HAS.call(contx.templates, tpl) || HAS.call($__global.templates, tpl));
    }
    ,getTemplateContents: function(id, ctx, cb) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        if (is_callable(cb))
        {
            get_template_contents(id, contx, function(err, tpl) {
                cb(err, tpl);
            });
        }
        else
        {
            return get_template_contents(id, contx);
        }
    }
    ,getTemplateContentsPromise: function(id, ctx) {
        if ('function' === typeof Promise)
        {
            return new Promise(function(resolve, reject) {
                Contemplate.getTemplateContents(id, ctx, function(err, data) {
                    if (err) reject(err);
                    else resolve(data);
                });
            });
        }
        return null;
    }
    ,findTpl: function(tpl, ctx, cb) {
        var contx;
        if (arguments.length < 2) ctx = 'global';
        contx = ctx && HAS.call($__ctx, ctx) ? $__ctx[ctx] : $__context;
        if (is_callable(cb))
        {
            var templateDirs, filename, dir,
                search_one = function search_one() {
                    if (dir >= templateDirs.length)
                    {
                        cb(null, null);
                        return;
                    }
                    var path = rtrim(templateDirs[dir],'/\\') + '/' + filename;
                    fexists_async(path, function(err, exists) {
                        if (!err && exists)
                        {
                            cb(null, path);
                        }
                        else
                        {
                            ++dir;
                            search_one();
                        }
                    });
                };
            if (is_callable(contx.templateFinder))
            {
                // supposed to be async operation with callback given
                contx.templateFinder(tpl, function(found) {
                    cb(null, found);
                });
                return;
            }
            if (contx.templateDirs && contx.templateDirs.length)
            {
                templateDirs = contx.templateDirs;
                filename = ltrim(tpl,'/\\');
                dir = 0;
                search_one();
                return;
            }
            if (contx != $__global)
            {
                contx = $__global;
                if (is_callable(contx.templateFinder))
                {
                    // supposed to be async operation with callback given
                    contx.templateFinder(tpl, function(found) {
                        cb(null, found);
                    });
                    return;
                }
                if (contx.templateDirs && contx.templateDirs.length)
                {
                    templateDirs = contx.templateDirs;
                    filename = ltrim(tpl,'/\\');
                    dir = 0;
                    search_one();
                    return;
                }
            }
            cb(null, null);
        }
        else
        {
            var filename, path, dir, l;
            if (is_callable(contx.templateFinder))
            {
                // supposed to be sync operation if no callback provided
                return contx.templateFinder(tpl);
            }
            if (contx.templateDirs && contx.templateDirs.length)
            {
                filename = ltrim(tpl, '/\\');
                for(dir=0,l=contx.templateDirs.length; dir<l; ++dir)
                {
                    path = rtrim(contx.templateDirs[dir],'/\\') + '/' + filename;
                    if (fexists(path)) return path;
                }
                return null;
            }
            if (contx != $__global)
            {
                contx = $__global
                if (is_callable(contx.templateFinder))
                {
                    // supposed to be sync operation if no callback provided
                    return contx.templateFinder(tpl);
                }
                if (contx.templateDirs && contx.templateDirs.length)
                {
                    filename = ltrim(tpl, '/\\');
                    for(dir=0,l=contx.templateDirs.length; dir<l; ++dir)
                    {
                        path = rtrim(contx.templateDirs[dir],'/\\') + '/' + filename;
                        if (fexists(path)) return path;
                    }
                    return null;
                }
            }
        }
        return null;
    }
    ,findTplPromise: function(tpl, ctx) {
        if ('function' === typeof Promise)
        {
            if (arguments.length < 2) ctx = 'global';
            return new Promise(function(resolve, reject) {
                Contemplate.findTpl(tpl, ctx, function(err, found) {
                    if (err) reject(err);
                    else resolve(found);
                });
            });
        }
        return null;
    }
    ,parseTpl: function(tpl, options, cb) {
        var parsed, leftSep, rightSep, separators, _ctx, contx = null;
        // see what context this template may use
        if (options && options.substr)
        {
            if (HAS.call($__ctx, options))
                contx = $__ctx[options]; // preset context
            else
                contx = $__global; // global context
            options = {};
        }
        options = merge({
            'separators': null
        }, options);
        if (options.context)
        {
            if (HAS.call($__ctx, options.context))
                contx = $__ctx[options.context]; // preset context
            else if (!contx)
                contx = $__global; // global context
            delete options.context;
        }
        if (!contx) contx = $__global; // global context
        leftSep = $__leftTplSep; rightSep = $__rightTplSep;
        separators = options && options.separators ? options.separators : null;
        if (separators)
        {
            leftSep = separators[0];
            rightSep = separators[1];
        }
        if (is_callable(cb))
        {
            _ctx = $__context;
            $__context = contx;
            reset_state();
            parse(tpl, leftSep, rightSep, true, function(err, parsed) {
                clear_state();
                $__context = _ctx;
                cb(err, parsed);
            });
            return null;
        }
        else
        {
            _ctx = $__context;
            $__context = contx;
            reset_state();
            parsed = parse(tpl, leftSep, rightSep, true);
            clear_state();
            $__context = _ctx;
            return parsed;
        }
    }
    ,parseTplPromise: function(tpl, options) {
        if ('function' === typeof Promise)
        {
            return new Promise(function(resolve, reject) {
                Contemplate.parseTpl(tpl, options, function(err, parsed) {
                    if (err) reject(err);
                    else resolve(parsed);
                });
            });
        }
        return null;
    }
    //
    // Main Template functions
    //
    ,tpl: function(tpl, data, options, cb) {
        cb = is_callable(cb) ? cb : null;
        var tmpl, contx, _ctx;
        if (tpl instanceof Contemplate.Template)
        {
            tmpl = tpl;
            if (cb)
            {
                // Provide some basic currying to the user
                cb(null, data && ("object" === typeof data) ? tmpl.render(data) : tmpl);
                return null;
            }
            else
            {
                // Provide some basic currying to the user
                return data && ("object" === typeof data) ? tmpl.render(data) : tmpl;
            }
        }
        else
        {
            // see what context this template may use
            contx = null;
            if (null == options) options = {};
            if (options && options.substr)
            {
                if (HAS.call($__ctx, options))
                    contx = $__ctx[options]; // preset context
                else
                    contx = $__context; // current context
                options = {};
            }
            options = merge({
                 'separators': null
                ,'autoUpdate': false
                ,'refresh': false
                ,'escape': true
                ,'standalone': false
            }, options);
            if (options.context)
            {
                if (HAS.call($__ctx, options.context))
                    contx = $__ctx[options.context]; // preset context
                else if (!contx)
                    contx = $__context; // current context
                delete options.context;
            }
            if (!contx) contx = $__context; // current context
            $__escape = false !== options.escape;
            if (cb)
            {
                var proceed = function() {
                    // asynchronous loading, parsing and writing
                    if (!!options.refresh || (!contx.cache[tpl] && !$__global.cache[tpl]))
                    {
                        _ctx = $__context;
                        $__context = contx;
                        get_cached_template(tpl, contx, options, function(err, ctpl) {
                            $__context = _ctx;
                            if (err)
                            {
                                cb(err, null);
                                return;
                            }
                            contx.cache[tpl] = ctpl;
                            tmpl = contx.cache[tpl] || $__global.cache[tpl];
                            tmpl.autonomus(options.standalone);
                            cb(null, data && ("object"===typeof data) ? tmpl.render(data) : tmpl);
                        });
                    }
                    else
                    {
                        tmpl = contx.cache[tpl] || $__global.cache[tpl];
                        tmpl.autonomus(options.standalone);
                        cb(null, data && ("object"===typeof data) ? tmpl.render(data) : tmpl);
                    }
                };
                if (null == options['parsed'] && !Contemplate.hasTpl(tpl, contx.id))
                {
                    // async operation
                    Contemplate.findTpl(tpl, contx.id, function(err, path) {
                        if (!path)
                        {
                            cb(null, data && "object"===typeof data ? '' : null);
                            return;
                        }
                        var tpldef = {};
                        tpldef[tpl] = path;
                        Contemplate.add(tpldef, contx.id);
                        proceed();
                    });
                }
                else
                {
                    proceed();
                }
                return null;
            }
            else
            {
                if (null == options['parsed'] && !Contemplate.hasTpl(tpl, contx.id))
                {
                    // sync operation
                    var path = Contemplate.findTpl(tpl, contx.id);
                    if (!path) return data && "object"===typeof data ? '' : null;
                    var tpldef = {};
                    tpldef[tpl] = path;
                    Contemplate.add(tpldef, contx.id);
                }
                // Figure out if we're getting a template, or if we need to
                // load the template - and be sure to cache the result.
                if (!!options.refresh || (!contx.cache[tpl] && !$__global.cache[tpl]))
                {
                    _ctx = $__context;
                    $__context = contx;
                    contx.cache[tpl] = get_cached_template(tpl, contx, options);
                    $__context = _ctx;
                }
                tmpl = contx.cache[tpl] || $__global.cache[tpl];
                tmpl.autonomus(options.standalone);
                // Provide some basic currying to the user
                return data && ("object"===typeof data) ? tmpl.render(data) : tmpl;
            }
        }
    }
    ,tplPromise: function(tpl, data, options) {
        if ('function' === typeof Promise)
        {
            return new Promise(function(resolve, reject) {
                Contemplate.tpl(tpl, data, options, function(err, tpl) {
                    if (err) reject(err);
                    else resolve(tpl);
                });
            });
        }
        return null;
    }
    ,inline: function(tpl, reps, compiled) {
        return (tpl instanceof Contemplate.InlineTemplate)
            ? tpl.render(reps)
            : Contemplate.InlineTemplate(tpl, reps, compiled);
    }
    ,concat: function() {
        return join.call(arguments, '');
    }
    ,join: function(sep, args, skip_empty) {
        if (null == args) return '';
        skip_empty = true === skip_empty;
        if ('object' !== typeof args) return skip_empty&&!String(args).length ? '' : String(args);
        if (null == sep) sep = '';
        var i, l = args.length, s,
            out = l > 0 ? ('object'===typeof args[0] ? Contemplate.join(sep, args[0], skip_empty) : (skip_empty&&(null==args[0]||!String(args[0]).length) ? '' : String(args[0]))) : '';
        for (i=1; i<l; ++i)
        {
            s = 'object' === typeof args[i] ? Contemplate.join(sep, args[i], skip_empty) : (skip_empty&&(null==args[i]||!String(args[i]).length) ? '' : String(args[i]));
            if (!skip_empty || s.length > 0) out += sep + s;
        }
        return out;
    }
    ,keys: function(o) {
        return o ? array_keys(o) : [];
    }
    ,values: function(o) {
        return o ? array_values(o) : [];
    }
    ,items: function(o) {
        return o;
    }
    ,count: count
    ,is_array: function(v, strict) {
        var to_string = toString.call(v);
        return strict ? '[object Array]' === to_string : ('[object Array]' === to_string) || ('[object Object]' === to_string);
    }
    ,in_array: function(v, a) {
        return -1 < a.indexOf(v);
    }
    ,is_list: is_array
    ,haskey: function(v/*, key1, key2, etc.. */) {
        var args, i, tmp;
        if (!v || !Contemplate.is_array(v)) return false;
        args = arguments; tmp = v;
        for (i=1; i<args.length; ++i)
        {
            if (null == tmp || !Contemplate.is_array(tmp) || !HAS.call(tmp,args[i])) return false;
            tmp = tmp[args[i]];
        }
        return true;
    }
    ,time: php_time
    ,date: function(format, timestamp) {
        if (arguments.length < 2) timestamp = php_time();
        return php_date(format, timestamp);
    }
    ,lowercase: function(s) {
        return String(s).toLowerCase();
    }
    ,uppercase: function(s) {
        return String(s).toUpperCase();
    }
    ,striptags: function(s) {
        return s.replace(TAG_RE, '');
    }
    ,e: function(s, entities) {
        // http://jsperf.com/split-join-vs-regex-replace/10
        var i = 0, l = s.length, r = '', c, cd;
        if (entities)
        {
            for (i=0; i<l; ++i)
            {
                c = s.charAt(i); cd = c.charCodeAt(0);
                switch (cd)
                {
                    case 34: r += """; break;
                    case 38: r += "&"; break;
                    case 39: r += "'"; break;
                    case 60: r += "<"; break;
                    case 62: r += ">"; break;
                    default: r += c;
                }
            }
        }
        else
        {
            for (i=0; i<l; ++i)
            {
                c = s.charAt(i); cd = c.charCodeAt(0);
                switch (cd)
                {
                    case 34: case 38: case 39: case 60: case 62:
                        r += "&#"+cd+";"; break;
                    default:
                        r += c;
                }
            }
        }
        return r;
    }
    ,json_encode: function(v) {
        return JSON.stringify(v);
    }
    ,json_decode: function(v) {
        return JSON.parse(v);
    }
    ,urlencode: urlencode
    ,urldecode: urldecode
    ,buildquery: function(data) {
        return http_build_query(data, '&');
    }
    ,parsequery: function(str) {
        return parse_str(str);
    }
    ,queryvar: function(url, add_keys, remove_keys) {
        var keys, key, value, k, l, last, q, to_str;
        if (null != remove_keys)
        {
            // https://davidwalsh.name/php-remove-variable
            keys = [].concat(remove_keys);
            l = keys.length;
            for (k=0; k<l; ++k)
            {
                url = url.replace(RE('(\\?|&)' + urlencode( keys[k] ).replace(re_9, '\\$1') + '(\\[[^\\[\\]]*\\])*(=[^&]+)?','g'), '$1');
            }
            url = url.replace(AMP_RE, '&').replace('?&', '?');
            last = url.slice(-1);
            if ('?' === last || '&' === last)
            {
                url = url.slice(0,-1);
            }
        }
        if (!!add_keys)
        {
            keys = Keys(add_keys);
            l = keys.length;
            q = -1 === url.indexOf('?') ? '?' : '&';
            for(k=0; k<l; ++k)
            {
                key = keys[k]; value = add_keys[key];
                key = urlencode(key);
                to_str = toString.call(value);
                if ('[object Array]' === to_str || '[object Object]' === to_str)
                {
                    if ('[object Array]' === to_str)
                    {
                        for (var v=0,vl=value.length; v<vl; ++v)
                        {
                            url += q + key + '[]=' + urlencode(value[v]);
                            q = '&';
                        }
                    }
                    else
                    {
                        for (var kvalue=Keys(value),v=0,vl=kvalue.length; v<vl; ++v)
                        {
                            url += q + key + '[' + urlencode(kvalue[v]) + ']=' + urlencode(value[kvalue[v]]);
                            q = '&';
                        }
                    }
                }
                else
                {
                    url += q + key + '=' + urlencode(value);
                }
                q = '&';
            }
        }
        return url;
    }
    ,get: function(v, keys, default_value) {
        default_value = null != default_value ? default_value : null;
        if (!Contemplate.is_array(keys, true)) keys = [keys];
        var o = v, k = 0, l = keys.length, found = 1, key, keyGetter;
        for (k=0; k<l; ++k)
        {
            key = String(keys[k]);
            if (HAS.call(o, key))
            {
                o = o[key];
            }
            else
            {
                keyGetter = 'get' + key.charAt(0).toUpperCase() + key.substring(1);
                if (/*HAS.call(o, keyGetter) &&*/ is_callable(o[keyGetter]))
                {
                    o = o[keyGetter]();
                }
                else
                {
                    found = 0;
                    break;
                }
            }
        }
        return found ? o : default_value;
    }
    ,uuid: function(namespace) {
        return [namespace||'UUID', ++$__uuid, php_time()].join('_');
    }
    ,merge: merge
    //,local_variable: local_variable
    //,is_local_variable: is_local_variable
    // extra for js version
    ,empty: empty
    ,trim: trim
    ,ltrim: ltrim
    ,rtrim: rtrim
    ,sprintf: sprintf
    ,vsprintf: function(fmt, args) {
        return sprintf.apply(null, [fmt].concat(args||[]));
    }
};
// Template Engine end here
//
//
var default_date_locale = {
     meridian: {am:'am', pm:'pm', AM:'AM', PM:'PM'}
    ,ordinal: {ord:{1:'st',2:'nd',3:'rd'}, nth:'th'}
    ,timezone: ['UTC','EST','MDT']
    ,timezone_short: ['UTC','EST','MDT']
    ,day: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
    ,day_short: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
    ,month: ['January','February','March','April','May','June','July','August','September','October','November','December']
    ,month_short: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
    },
    Keys = Obj.keys, join = Arr[PROTO].join, slice = Arr[PROTO].slice, floor = Math.floor, round = Math.round, abs = Math.abs,
    re_1 = /([\s\S]*?)(&(?:#\d+|#x[\da-f]+|[a-zA-Z][\da-z]*);|$)/g,
    re_2 = /!/g, re_3 = /'/g, re_4 = /\(/g, re_5 = /\)/g, re_6 = /\*/g, re_7 = /%20/g,
    re_8 = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g,
    re_9 = /([\[\]\(\)\.\?\/\*\{\}\+\$\^\:\\])/g,
    rtrim_re = /[ \s\u00A0]+$/g,
    ltrim_re = /^[ \s\u00A0]+/g,
    trim_re = /^[ \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+|[ \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+$/g,
    fileurl_2_nsfile = function(file_uri) {
        // NetUtil.newURI(file_uri).QueryInterface(Ci.nsIFileURL).file
        // http://stackoverflow.com/q/24817347/3591273
        /*var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService),
            url = ios.newURI(file_uri, null, null), // url is a nsIURI
            // file is a nsIFile
            file = url.QueryInterface(Ci.nsIFileURL).file;*/
        return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService).newURI(file_uri, null, null).QueryInterface(Ci.nsIFileURL).file;
    },
    fexists = isXPCOM
    ? function fexists(file) {
        // file is URI, i.e file://...
        return fileurl_2_nsfile(file).exists();
    }
    : (isNode
    ? function fexists(file) {
        return fs.existsSync(file);
    }
    : function fexists(file) {
        return true;
    }),
    fexists_async = isXPCOM
    ? function fexists_async(file, cb) {
        // file is URI, i.e file://...
        if (cb) cb(null, fexists(file));
    }
    : (isNode
    ? function fexists_async(file, cb) {
        // exists is deprecated due to incompatible callback signature
        //fs.exists(file, cb);
        fs.stat(file, function(err, stat) {
            if (!cb) return;
            if (!err)
            {
                // exists
                cb(null, true);
            }
            else if ('ENOENT' === err.code)
            {
                // file does not exist
                cb(null, false);
            }
            else // some other error
            {
                cb(err, null);
            }
        });
    }
    : function fexists_async(file, cb) {
        if (cb) cb(null, true);
    }),
    fis_dir = isXPCOM
    ? function fis_dir(file) {
        return fileurl_2_nsfile(file).isDirectory();
    }
    : (isNode
    ? function fis_dir(file) {
        return fs.lstatSync(file).isDirectory();
    }
    : function fis_dir(file) {
        return false;
    }),
    fis_dir_async = isXPCOM
    ? function fis_dir_async(file, cb) {
        if (cb) cb(null, fileurl_2_nsfile(file).isDirectory());
    }
    : (isNode
    ? function fis_dir_async(file, cb) {
        fs.lstat(file, function(err, stats){
            if (cb) cb(err, err ? null : stats.isDirectory());
        });
    }
    : function fis_dir_async(file, cb) {
        if (cb) cb(null, false);
    }),
    fmkdir = isXPCOM
    ? function fmkdir(file, mode) {
        var nsfile = fileurl_2_nsfile(file);
        return nsfile.create(nsfile.DIRECTORY_TYPE, mode);
    }
    : (isNode
    ? function fmkdir(file, mode) {
        return fs.mkdirSync(file, mode);
    }
    : function fmkdir(file, mode) {
        // do nothing
        return false;
    }),
    fmkdir_async = isXPCOM
    ? function fmkdir_async(file, mode, cb) {
        var res = fmkdir(file, mode);
        if (cb) cb(null, res);
    }
    : (isNode
    ? function fmkdir_async(file, mode, cb) {
        fs.mkdir(file, mode, function(err) {
            if (cb) cb(err, err ? false : true);
        });
    }
    : function fmkdir_async(file, mode, cb) {
        // do nothing
        if (cb) cb(null, false);
    }),
    fstat = isXPCOM
    ? function fstat(file) {
        // file is URI, i.e file://...
        var mtime = fileurl_2_nsfile(file).lastModifiedTime;
        return {mtime: !!mtime ? new Date(mtime) : false};
    }
    : (isNode
    ? function fstat(file) {
        return fs.statSync(file);
    }
    : function fstat(file) {
        // http://stackoverflow.com/a/5748207/3591273
        var xhr = XHR(), mtime, stats = {mtime: false};
        xhr.open('HEAD', file, false);  // 'false' makes the request synchronous
        xhr.send(null);
        if (200 === xhr.status)
        {
            mtime = new Date(xhr.getResponseHeader('Last-Modified'));
            if (xhr.toString() === 'Invalid Date') mtime = false;
            stats.mtime = mtime;
        }
        return stats;
    }),
    fstat_async = isXPCOM
    ? function fstat_async(file, cb) {
        if (cb) cb(null, fstat(file));
    }
    : (isNode
    ? function fstat_async(file, cb) {
        fs.stat(file, cb);
    }
    : function fstat_async(file, cb) {
        // http://stackoverflow.com/a/5748207/3591273
        var xhr = XHR(), mtime, stats = {mtime: false};
        xhr.open('HEAD', file, true);  // 'true' makes the request asynchronous
        xhr.onload = function() {
            if (200 === xhr.status)
            {
                mtime = new Date(xhr.getResponseHeader('Last-Modified'));
                if (mtime.toString() === 'Invalid Date') mtime = false;
                stats.mtime = mtime;
            }
            if (cb) cb(null, stats);
        };
        xhr.send(null);
    }),
    fread = isXPCOM
    ? function fread(file, enc) {
        // file is URI, i.e file://...
        var data = '', stream, conv, len, str = {value:''}, read = 0;
        // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O
        stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
        conv = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
        stream.init(fileurl_2_nsfile(file), -1, 0, 0);
        conv.init(stream, enc||'UTF-8', 0, 0);
        do {
            // read as much as we can and put it in str.value
            read = conv.readString(0xffffffff, str);
            data += str.value;
        } while (0 != read);
        conv.close(); // this closes stream
        return data;
    }
    : (isNode
    ? function fread(file, enc) {
        return fs.readFileSync(file, {encoding:enc||'utf8'})/*.toString()*/;
    }
    : function fread(file, enc) {
        var xhr = XHR();
        // plain text with enc encoding format
        xhr.open('GET', file, false);  // 'false' makes the request synchronous
        // http://stackoverflow.com/questions/9855127/setting-xmlhttprequest-responsetype-forbidden-all-of-a-sudden
        xhr.setRequestHeader("Content-Type", "text/plain; charset="+(enc||'utf8')+"");
        xhr.overrideMimeType("text/plain; charset="+(enc||'utf8')+"");
        xhr.send(null);
        return 200 === xhr.status ? xhr.responseText : '';
    }),
    fread_async = isXPCOM
    ? function fread_async(file, enc, cb) {
        // file is URI, i.e file://...
        // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O
        NetUtil.asyncFetch(fileurl_2_nsfile(file), function(stream, status) {
            var err = !Components.isSuccessCode(status),
                data = err
                ? ''
                : NetUtil.readInputStreamToString(stream, stream.available(), {charset:enc||'UTF-8'});
            if (cb) cb(err, data);
        });
    }
    : (isNode
    ? function fread_async(file, enc, cb) {
        fs.readFile(file, {encoding:enc||'utf8'}, cb);
    }
    : function fread_async(file, enc, cb) {
        var xhr = XHR();
        // plain text with enc encoding format
        xhr.open('GET', file, true);  // 'true' makes the request asynchronous
        xhr.responseType = "text";
        xhr.setRequestHeader("Content-Type", "text/plain; charset="+(enc||'utf8')+"");
        xhr.overrideMimeType("text/plain; charset="+(enc||'utf8')+"");
        xhr.onload = function() {
            var err = 200 !== xhr.status,
                data = err ? '' : xhr.responseText;
            if (cb) cb(err, data);
        };
        xhr.send(null);
    }),
    // https://github.com/moxystudio/node-proper-lockfile
    // https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Overlay_Extensions/Firefox_addons_developer_guide/Using_XPCOM%E2%80%94Implementing_advanced_processes
    // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions
    fwrite = isXPCOM
    ? function fwrite(file, data, enc) {
        // file is URI, i.e file://...
        var stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream),
            conv = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
        // use 0x02 | 0x10 to open file for appending.
        // write, create, truncate
        // In a c file operation, we have no need to set file mode with or operation,
        // directly using "r" or "w" usually.
        // if you are sure there will never ever be any non-ascii text in data you can
        // also call foStream.write(data, data.length) directly
        // https://bugzilla.mozilla.org/show_bug.cgi?id=572890
        stream.init(fileurl_2_nsfile( file ), 0x02|0x08|0x20, 0x1B6/*0666*/, 0);
        conv.init(stream, enc||"UTF-8", 0, 0);
        conv.writeString(data);
        conv.close(); // this closes stream
    }
    : (isNode
    ? function fwrite(file, data, enc) {
        fs.writeFileSync(file, data, {encoding:enc||'utf8'})/*.toString()*/;
    }
    : function fwrite(file, data, enc) {
    }),
    fwrite_async = isXPCOM
    ? function fwrite_async(file, data, enc, cb) {
        // file is URI, i.e file://...
        // http://stackoverflow.com/questions/9777773/reading-writing-file-on-local-machine
        var istream, ostream, conv;
        ostream = FileUtils.openSafeFileOutputStream(fileurl_2_nsfile(file));
        conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
        conv.charset = enc||"UTF-8";
        istream = conv.convertToInputStream(data);
        NetUtil.asyncCopy(istream, ostream, function(status) {
            if (cb) cb(Components.isSuccessCode(status));
        });
    }
    : (isNode
    ? function fwrite_async(file, data, enc, cb) {
        fs.writeFile(file, data, {encoding: enc||'utf8'}, function(err,res) {
            cb(err,res);
        });
    }
    : function fwrite_async(file, data, enc, cb) {
        if (cb) cb(null);
    }),
    FUNC = isXPCOM
    ? function FUNC(a, f) {
        // create new sandbox instance
        // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.Sandbox
        /*system principal*/ /*null*/ /*null principal*/
        var principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal),
            sandbox = new Cu.Sandbox(principal, {
                sandboxName: 'contemplate_sandbox_' + $__context.id,
                sameZoneAs: Contemplate,
                wantComponents: false,
                wantExportHelpers: false,
                wantXrays: true,
                wantGlobalProperties: []
            }), fn_uuid = Contemplate.uuid('dyna_func');
        sandbox.Contemplate = Contemplate;
        /*return */Cu.evalInSandbox(';function '+fn_uuid+'('+a+'){'+f+'};', sandbox);
        return sandbox[fn_uuid];
    }
    : function FUNC(a, f) {
        return new Function(a, f);
    }/*,
    ASYNC_SUPPORTED = isXPCOM ? false : (function() {
        var test = null;
        try {
            test = (new Function('', 'return async function(){};'))();
        } catch (e) {
            return false;
        }
        return is_callable(test) && is_asyncf(test);
    })(),
    ASYNC_FUNC = ASYNC_SUPPORTED
    ? function ASYNC_FUNC(a, f) {
        return FUNC('', 'return async function('+a+'){'+f+'};')();
    }
    : FUNC*/
;
// utilities
function RE(r, f)
{
    return new RegExp(r, f||'');
}
function empty(o)
{
    // exactly like php's empty function
    if (!o || !Boolean(o) || "0" === o) return true;
    var to_string = toString.call(o);
    if ((o instanceof Array || o instanceof String || '[object Array]' === to_string || '[object String]' === to_string) && !o.length) return true;
    if ((o instanceof Object || '[object Object]' === to_string) && !array_keys(o).length ) return true;
    return false;
}
// php-like functions, mostly adapted and optimised from phpjs project, https://github.com/kvz/phpjs
function is_callable(o)
{
    return 'function' === typeof o;
}
/*function is_asyncf(o)
{
    return /*('function' === typeof o) &&* ('AsyncFunction' === o.constructor.name);
}*/
// http://jsperf.com/instanceof-array-vs-array-isarray/6
function is_array(o)
{
    return o && (/*(o.constructor === Arr)*/(o instanceof Arr) || ('[object Array]' === toString.call(o)));
}
function is_object(o)
{
    return o && (/*(o.constructor === Obj)(o instanceof Obj) ||*/ ('[object Object]' === toString.call(o)));
}
function count(mixed_var)
{
    return null == mixed_var
    ? 0
    : (is_array(mixed_var)
    ? mixed_var.length
    : (is_object(mixed_var)
    ? array_keys(mixed_var).length
    : 1));
}
function array_keys(o)
{
    if (is_callable(Object.keys)) return Object.keys(o);
    var v, k, l;
    if (is_array(o))
    {
        v = new Array(l=o.length);
        for(k=0; k<l; ++k)
            v[k] = String(k);
    }
    else
    {
        v = [];
        for (k in o)
            if (HAS.call(o, k))
                v.push(k);
    }
    return v;
}
function array_values(o)
{
    if (is_array(o)) return o;
    if (is_callable(Object.values)) return Object.values(o);
    var v = [], k;
    for (k in o)
        if (HAS.call(o, k))
            v.push(o[k]);
    return v;
}
function is_numeric_array(o)
{
    if (is_array(o)) return true;
    if (is_object(o))
    {
        var k = array_keys(o), i, l = k.length;
        for (i=0; i<l; ++i)
            if (i !== +k[i]) return false;
        return true;
    }
    return false;
}
function ltrim(str, charlist)
{
    return (str+'').replace(
        !charlist ? ltrim_re : RE('^[' + (charlist+'').replace(re_9, '\\$1') + ']+', 'g'),
    '');
}
function rtrim(str, charlist)
{
    return (str+'').replace(
        !charlist ? rtrim_re : RE('[' + (charlist+'').replace(re_9, '\\$1') + ']+$', 'g'),
    '');
}
function trim(str, charlist)
{
    return (str+'').replace(
        !charlist ? trim_re : RE('^[' + (charlist=(charlist+'').replace(re_9, '\\$1')) + ']+|[' + charlist + ']+$', 'g'),
    '');
}
function basename(s)
{
    var lastChar = s.charAt(s.length-1);
    if ('/' === lastChar || '\\' === lastChar)
    {
        s = s.slice(0, -1);
    }
    return s.split(DS_RE).pop();
}
function dirname(s)
{
    var lastChar = s.charAt(s.length-1);
    if ('/' === lastChar || '\\' === lastChar)
    {
        s = s.slice(0, -1);
    }
    return s.replace(BASENAME_RE, '');
}
function pad(s, len, ch)
{
    var sp = s.toString(), n = len-sp.length;
    return n > 0 ? new Array(n+1).join(ch||' ')+sp : sp;
}
function rawurlencode(str)
{
    // Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
    // PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
    return encodeURIComponent('' + str).replace(re_2, '%21').replace(re_3, '%27').replace(re_4, '%28').
    replace(re_5, '%29').replace(re_6, '%2A');
}
function urlencode(str)
{
    // Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
    // PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
    return encodeURIComponent('' + str).replace(re_2, '%21').replace(re_3, '%27').replace(re_4, '%28').
    replace(re_5, '%29').replace(re_6, '%2A').replace(re_7, '+');
}
function rawurldecode(str)
{
    return decodeURIComponent(''+str);
}
function urldecode(str)
{
    return rawurldecode(('' + str).split('+').join('%20'));
}
function parse_str(str)
{
    var strArr = str.replace(/^&+|&+$/g, '').split('&'),
        sal = strArr.length,
        i, j, ct, p, lastObj, obj, chr, tmp, key, value,
        postLeftBracketPos, keys, keysLen, lastkey,
        array = {}, possibleLists = [], prevkey, prevobj
    ;
    for (i=0; i<sal; ++i)
    {
        tmp = strArr[i].split('=');
        key = rawurldecode(trim(tmp[0]));
        value = (tmp.length < 2) ? '' : rawurldecode(trim(tmp[1]));
        j = key.indexOf('\x00');
        if (j > -1) key = key.slice(0, j);
        if (key && '[' !== key.charAt(0))
        {
            keys = [];
            postLeftBracketPos = 0;
            for (j=0; j<key.length; ++j)
            {
                if ('[' === key.charAt(j)  && !postLeftBracketPos)
                {
                    postLeftBracketPos = j + 1;
                }
                else if (']' === key.charAt(j))
                {
                    if (postLeftBracketPos)
                    {
                        if (!keys.length)
                        {
                            keys.push(key.slice(0, postLeftBracketPos - 1));
                        }
                        keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos));
                        postLeftBracketPos = 0;
                        if ('[' !== key.charAt(j + 1)) break;
                    }
                }
            }
            if (!keys.length) keys = [key];
            for (j=0; j<keys[0].length; ++j)
            {
                chr = keys[0].charAt(j);
                if (' ' === chr || '.' === chr || '[' === chr)
                {
                    keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
                }
                if ('[' === chr) break;
            }
            obj = array; key = null; lastObj = obj;
            lastkey = keys.length ? trim(keys[keys.length-1].replace(/^['"]|['"]$/g, '')) : null;
            for (j=0, keysLen=keys.length; j<keysLen; ++j)
            {
                prevkey = key;
                key = keys[ j ].replace(/^['"]|['"]$/g, '');
                prevobj = lastObj;
                lastObj = obj;
                if ('' !== trim(key) || 0 === j)
                {
                    if (!HAS.call(obj, key)) obj[key] = (j+1 === keysLen-1) && (''===lastkey) ? [] : {};
                    obj = obj[key];
                }
                else
                {
                    // To insert new dimension
                    /*ct = -1;
                    for ( p in obj )
                    {
                        if ( HAS.call(obj,p) )
                        {
                            if ( +p > ct && p.match(/^\d+$/g) )
                            {
                                ct = +p;
                            }
                        }
                    }
                    key = ct + 1;*/
                    key = true;
                }
            }
            if (true === key)
            {
                lastObj.push(value);
            }
            else
            {
                if (key == +key)
                    possibleLists.push({key:prevkey, obj:prevobj});
                lastObj[key] = value;
            }
        }
    }
    for (i=possibleLists.length-1; i>=0; --i)
    {
        // safe to pass multiple times same obj, it is possible
        obj = possibleLists[i].key ? possibleLists[i].obj[possibleLists[i].key] : possibleLists[i].obj;
        if (is_numeric_array(obj))
        {
            obj = array_values(obj);
            if (possibleLists[i].key)
                possibleLists[i].obj[possibleLists[i].key] = obj;
            else
                array = obj;
        }
    }
    return array;
}
function http_build_query_helper(key, val, arg_separator, PHP_QUERY_RFC3986)
{
    var k, tmp, encode = PHP_QUERY_RFC3986 ? rawurlencode : urlencode;
    if (true === val) val = "1";
    else if (false === val) val = "0";
    if (null != val)
    {
        if ("object" === typeof(val))
        {
            tmp = [];
            for (k in val)
            {
                if (HAS.call(val, k) && null != val[k])
                {
                    tmp.push(http_build_query_helper(key + "[" + k + "]", val[k], arg_separator, PHP_QUERY_RFC3986));
                }
            }
            return tmp.join(arg_separator);
        }
        else
        {
            return encode(key) + "=" + encode(val);
        }
    }
    else
    {
        return '';
    }
}
function http_build_query(data, arg_separator, PHP_QUERY_RFC3986)
{
    var value, key, query, tmp = [];
    if (arguments.length < 2) arg_separator = "&";
    if (arguments.length < 3) PHP_QUERY_RFC3986 = false;
    for (key in data)
    {
        if (!HAS.call(data, key)) continue;
        value = data[key];
        query = http_build_query_helper(key, value, arg_separator, PHP_QUERY_RFC3986);
        if ('' != query) tmp.push(query);
    }
    return tmp.join(arg_separator);
}
function php_time()
{
    return floor(new Date().getTime() / 1000);
}
function php_date(format, timestamp)
{
    var formatted_datetime, f, i, l, jsdate,
        locale = default_date_locale
    ;
    // JS Date
    if (timestamp instanceof Date) jsdate = new Date(timestamp);
    // UNIX timestamp (auto-convert to int)
    else if ("number" === typeof timestamp) jsdate = new Date(timestamp * 1000);
    // undefined
    else/*if ( null === timestamp  || undef === timestamp )*/ jsdate = new Date();
    var D = {}, tzo = jsdate.getTimezoneOffset(), atzo = abs(tzo), m = jsdate.getMonth(), jmod10;
    // 24-Hours; 0..23
    D.G = jsdate.getHours();
    // Day of month; 1..31
    D.j = jsdate.getDate(); jmod10 = D.j%10;
    // Month; 1...12
    D.n = m + 1;
    // Full year; e.g. 1980...2010
    D.Y = jsdate.getFullYear();
    // Day of week; 0[Sun]..6[Sat]
    D.w = jsdate.getDay();
    // ISO-8601 day of week; 1[Mon]..7[Sun]
    D.N = D.w || 7;
    // Day of month w/leading 0; 01..31
    D.d = pad(D.j, 2, '0');
    // Shorthand day name; Mon...Sun
    D.D = locale.day_short[D.w];
    // Full day name; Monday...Sunday
    D.l = locale.day[D.w];
    // Ordinal suffix for day of month; st, nd, rd, th
    D.S = locale.ordinal.ord[D.j] ? locale.ordinal.ord[D.j] : (locale.ordinal.ord[jmod10] ? locale.ordinal.ord[jmod10] : locale.ordinal.nth);
    // Day of year; 0..365
    D.z = round((new Date(D.Y, m, D.j) - new Date(D.Y, 0, 1)) / 864e5);
    // ISO-8601 week number
    D.W = pad(1 + round((new Date(D.Y, m, D.j - D.N + 3) - new Date(D.Y, 0, 4)) / 864e5 / 7), 2, '0');
    // Full month name; January...December
    D.F = locale.month[m];
    // Month w/leading 0; 01...12
    D.m = pad(D.n, 2, '0');
    // Shorthand month name; Jan...Dec
    D.M = locale.month_short[m];
    // Days in month; 28...31
    D.t = (new Date(D.Y, m+1, 0)).getDate();
    // Is leap year?; 0 or 1
    D.L = D.Y % 4 === 0 & D.Y % 100 !== 0 | D.Y % 400 === 0;
    // ISO-8601 year
    D.o = D.Y + (11 === m && D.W < 9 ? 1 : (0 === m && D.W > 9 ? -1 : 0));
    // Last two digits of year; 00...99
    D.y = D.Y.toString().slice(-2);
    // am or pm
    D.a = D.G > 11 ? locale.meridian.pm : locale.meridian.am;
    // AM or PM
    D.A = D.G > 11 ? locale.meridian.PM : locale.meridian.AM;
    // Swatch Internet time; 000..999
    D.B = pad(floor((jsdate.getUTCHours() * 36e2 + jsdate.getUTCMinutes() * 60 + jsdate.getUTCSeconds() + 36e2) / 86.4) % 1e3, 3, '0');
    // 12-Hours; 1..12
    D.g = (D.G % 12) || 12;
    // 12-Hours w/leading 0; 01..12
    D.h = pad(D.g, 2, '0');
    // 24-Hours w/leading 0; 00..23
    D.H = pad(D.G, 2, '0');
    // Minutes w/leading 0; 00..59
    D.i = pad(jsdate.getMinutes(), 2, '0');
    // Seconds w/leading 0; 00..59
    D.s = pad(jsdate.getSeconds(), 2, '0');
    // Microseconds; 000000-999000
    D.u = pad(jsdate.getMilliseconds() * 1000, 6, '0');
    // Timezone identifier; e.g. Atlantic/Azores, ...
    // The following works, but requires inclusion of the very large
    // timezone_abbreviations_list() function.
    /*              return that.date_default_timezone_get();
    */
    D.e = '';
    // DST observed?; 0 or 1
    D.I = ((new Date(D.Y, 0) - Date.UTC(D.Y, 0)) !== (new Date(D.Y, 6) - Date.UTC(D.Y, 6))) ? 1 : 0;
    // Difference to GMT in hour format; e.g. +0200
    D.O = (tzo > 0 ? "-" : "+") + pad(floor(atzo / 60) * 100 + atzo % 60, 4, '0');
    // Difference to GMT w/colon; e.g. +02:00
    D.P = (D.O.substr(0, 3) + ":" + D.O.substr(3, 2));
    // Timezone abbreviation; e.g. EST, MDT, ...
    D.T = 'UTC';
    // Timezone offset in seconds (-43200...50400)
    D.Z = -tzo * 60;
    // Seconds since UNIX epoch
    D.U = jsdate / 1000 | 0;
    // ISO-8601 date. 'Y-m-d\\TH:i:sP'
    D.c = D.Y+'-'+D.m+'-'+D.d+'\\'+D.T+D.H+':'+D.i+':'+D.s+D.P;
    // RFC 2822 'D, d M Y H:i:s O'
    D.r = D.D+', '+D.d+' '+D.M+' '+D.Y+' '+D.H+':'+D.i+':'+D.s+' '+D.O;
    formatted_datetime = '';
    for (i=0,l=format.length; i<l; ++i)
    {
        f = format.charAt(i);
        formatted_datetime += HAS.call(D, f) ? D[f] : f;
    }
    return formatted_datetime;
}
function pad_(s, n, c, right)
{
    if (null == c) c = ' ';
    var str = String(s), l = str.length, p = l < n ? new Array(n-l+1).join(c) : '';
    return !!right ? str+p : p+str;
}
function justify(value, prefix, leftJustify, minWidth, zeroPad, customPadChar)
{
    var sv = String(value), diff = minWidth - sv.length;
    if (diff > 0)
    {
        if (leftJustify || !zeroPad)
            sv = pad_(sv, minWidth, customPadChar, leftJustify);
        else
            sv = sv.slice(0, prefix.length) + pad_('', diff, '0', true) + sv.slice(prefix.length);
    }
    return sv;
}
function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad)
{
    // Note: casts negative numbers to positive ones
    var number = value >>> 0;
    prefix = prefix && number && {
    '2': '0b',
    '8': '0',
    '16': '0x'
    }[base] || '';
    value = prefix + pad_(number.toString(base), precision||0, '0', false);
    return justify(value, prefix, leftJustify, minWidth, zeroPad);
}
function formatString(value, leftJustify, minWidth, precision, zeroPad, customPadChar)
{
    if (null != precision) value = value.slice(0, precision);
    return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
}
function sprintf()
{
    /*
     * More info at: http://phpjs.org
     *
     * This is version: 3.24
     * php.js is copyright 2011 Kevin van Zonneveld.
     */
    // http://kevin.vanzonneveld.net
    // +   original by: Ash Searle (http://hexmen.com/blog/)
    // + namespaced by: Michael White (http://getsprink.com)
    // +    tweaked by: Jack
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Paulo Freitas
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Dj
    // +   improved by: Allidylls
    // *     example 1: sprintf("%01.2f", 123.1);
    // *     returns 1: 123.10
    // *     example 2: sprintf("[%10s]", 'monkey');
    // *     returns 2: '[    monkey]'
    // *     example 3: sprintf("[%'#10s]", 'monkey');
    // *     returns 3: '[####monkey]'
    // *     example 4: sprintf("%d", 123456789012345);
    // *     returns 4: '123456789012345'
    var i = 1, fmt = arguments[0], a = arguments;
    var do_format = function do_format(substring, valueIndex, flags, minWidth, _, precision, type) {
        var number, prefix, method, textTransform, value;
        if ('%%' == substring) return '%';
        // parse flags
        var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false,
            j, customPadChar = ' ', flagsl = flags.length;
        for (j=0; flags && j < flagsl; ++j)
        {
            switch (flags.charAt(j))
            {
                case ' ':
                    positivePrefix = ' ';
                    break;
                case '+':
                    positivePrefix = '+';
                    break;
                case '-':
                    leftJustify = true;
                    break;
                case "'":
                    customPadChar = flags.charAt(j + 1);
                    break;
                case '0':
                    zeroPad = true;
                    break;
                case '#':
                    prefixBaseX = true;
                    break;
            }
        }
        // parameters may be null, undefined, empty-string or real valued
        // we want to ignore null, undefined and empty-string values
        if (!minWidth) minWidth = 0;
        else if ('*' == minWidth) minWidth = +a[i++];
        else if ('*' == minWidth.charAt(0)) minWidth = +a[minWidth.slice(1, -1)];
        else minWidth = +minWidth;
        // Note: undocumented perl feature:
        if (0 > minWidth)
        {
            minWidth = -minWidth;
            leftJustify = true;
        }
        if (!isFinite(minWidth))
        {
            throw new Error('sprintf: (minimum-)width must be finite');
        }
        if (!precision) precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
        else if ('*' == precision) precision = +a[i++];
        else if ('*' == precision.charAt(0)) precision = +a[precision.slice(1, -1)];
        else precision = +precision;
        // grab value using valueIndex if required?
        value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
        switch (type)
        {
            case 's':
                return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
            case 'c':
                return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
            case 'b':
                return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'o':
                return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'x':
                return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'X':
                return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
            case 'u':
                return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'i':
            case 'd':
                number = +value || 0;
                number = Math.round(number - number % 1); // Plain Math.round doesn't just truncate
                prefix = number < 0 ? '-' : positivePrefix;
                value = prefix + pad_(String(Math.abs(number)), precision, '0', false);
                return justify(value, prefix, leftJustify, minWidth, zeroPad);
            case 'e':
            case 'E':
            case 'f': // Should handle locales (as per setlocale)
            case 'F':
            case 'g':
            case 'G':
                number = +value;
                prefix = number < 0 ? '-' : positivePrefix;
                method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                value = prefix + Math.abs(number)[method](precision);
                return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
            default:
                return substring;
        }
    };
    return fmt.replace(sprintf.format_re, do_format);
}
sprintf.format_re = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g;
// init the engine on load
Contemplate.init();
// export it
return Contemplate;
}); 
  |