/**
* TrimPath Template. Release 1.0.38.
* Copyright (C) 2004, 2005 Metaha.
* 
* TrimPath Template is licensed under the GNU General Public License
* and the Apache License, Version 2.0, as follows:
*
* This program is free software; you can redistribute it and/or 
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 
* This program is distributed WITHOUT ANY WARRANTY; without even the 
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var TrimPath;


// TODO: Debugging mode vs stop-on-error mode - runtime flag.
// TODO: Handle || (or) characters and backslashes.
// TODO: Add more modifiers.


(function()
{               // Using a closure to keep global namespace clean.

    if (TrimPath == null)

        TrimPath = new Object();

    if (TrimPath.evalEx == null)
    {
        TrimPath.evalEx = function(src)
        {
            //document.getElementById("test").value=src;
            return eval(src);
        };

    }

    var UNDEFINED;
    if (Array.prototype.pop == null)  // IE 5.x fix from Igor Poteryaev.
        Array.prototype.pop = function()
        {
            if (this.length === 0) { return UNDEFINED; }
            return this[--this.length];
        };
    if (Array.prototype.push == null) // IE 5.x fix from Igor Poteryaev.
        Array.prototype.push = function()
        {
            for (var i = 0; i < arguments.length; ++i) { this[this.length] = arguments[i]; }
            return this.length;
        };

    TrimPath.parseTemplate = function(tmplContent, optTmplName, optEtc)
    {

        if (optEtc == null)

            optEtc = TrimPath.parseTemplate_etc;

        var funcSrc = parse(tmplContent, optTmplName, optEtc);
        var func = TrimPath.evalEx(funcSrc, optTmplName, 1);
        if (func != null)
            return new optEtc.Template(optTmplName, tmplContent, funcSrc, func, optEtc);
        return null;
    }


    try
    {
        String.prototype.process = function(context, optFlags)
        {
            var template = TrimPath.parseTemplate(this, null);
            if (template != null)
                return template.process(context, optFlags);
            return this;
        }
    } catch (e)
    { // Swallow exception, such as when String.prototype is sealed.
    }



    /* begin * config *   modified by Wei.Zijun */

    TrimPath.tagMaxLength = 16;
    //TrimPath.keepWhitespace=true;
    var selfOUT = " var _OUT_arr = []; var _OUT = { write: function(m) { if (m) _OUT_arr.push(m); } }; ";
    var selfOUTResult = "_OUT_arr.join('')";

    TrimPath.replaceAll = function(exstr, ov, value)
    {
        var gc = ov.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1");
        if (gc == null || gc == "")
        {
            return exstr;
        }
        var reReplaceGene = "/" + gc + "/gm";
        var r = null;
        var cmd = "r=exstr.replace(" + reReplaceGene + ",\"" + value + "\")";
        eval(cmd);
        return r;
    }



    /* end * config *   modified by Wei.Zijun */





    TrimPath.parseTemplate_etc = {};            // Exposed for extensibility.


    // deleted by Wei.Zijun
    //TrimPath.parseTemplate_etc.statementTag = "forelse|for|if|elseif|else|var|macro";


    TrimPath.parseTemplate_etc.statementDef = { // Lookup table for statement tags.

        "if": { delta: 1, prefix: "if (", suffix: ") {", paramMin: 1 },
        "else": { delta: 0, prefix: "} else {" },
        "elseif": { delta: 0, prefix: "} else if (", suffix: ") {", paramDefault: "true" },
        "/if": { delta: -1, prefix: "}" },

        "var": { delta: 0, prefix: "var ", suffix: ";" },
        "macro": { delta: 1,
            prefixFunc: function(stmtParts, state, tmplName, etc)
            {
                /* something replaced by  selfOUT ,  selfOUTResult */
                var macroName = stmtParts[1].split('(')[0];
                return ["var ", macroName, " = function",
                                   stmtParts.slice(1).join(' ').substring(macroName.length),
                                   "{ " + selfOUT].join('');
            } 
        },
        "/macro": { delta: -1, prefix: " return " + selfOUTResult + "; };" },

        /* begin * statementDef *   modified by Wei.Zijun */

        "switch": { delta: 1, prefix: "switch (", suffix: ") { case \"${this_case_never_run__don`t_use_this_string_in_your_codes!!!}\" :", paramMin: 1 },
        "case": { delta: 0, prefix: "  case ", suffix: " :", paramMin: 1 },
        "default": { delta: 0, prefix: "  default : " },
        "/switch": { delta: -1, prefix: "}" },

        "while": { delta: 1, prefix: "while (", suffix: ") {", paramMin: 1, paramDefault: "false" },
        "/while": { delta: -1, prefix: "}" },
        // for        ( != old for )
        "for": { delta: 1, prefix: "for ( var for_index=0 ; for_index<", suffix: " ; for_index++ ) {", paramMin: 1 },
        "/for": { delta: -1, prefix: "}" },
        // foreach  ( == old for )
        "foreach": { delta: 1, paramMin: 3,
            prefixFunc: function(stmtParts, state, tmplName, etc)
            {
                if (stmtParts[2] != "in")
                    throw new etc.ParseError(tmplName, state.line, "bad for loop statement: " + stmtParts.join(' '));
                var iterVar = stmtParts[1];
                var listVar = "__LIST__" + iterVar;
                return ["var ", listVar, " = ", stmtParts[3], ";",
                // Fix from Ross Shaull for hash looping, make sure that we have an array of loop lengths to treat like a stack.
                             "var __LENGTH_STACK__;",
                             "if (typeof(__LENGTH_STACK__) == 'undefined' || !__LENGTH_STACK__.length) __LENGTH_STACK__ = new Array();",
                             "__LENGTH_STACK__[__LENGTH_STACK__.length] = 0;", // Push a new for-loop onto the stack of loop lengths.
                             "if ((", listVar, ") != null) { ",
                             "var ", iterVar, "_ct = 0;",       // iterVar_ct variable, added by B. Bittman     
                             "for (var ", iterVar, "_index in ", listVar, ") { ",
                             iterVar, "_ct++;",
                             "if (typeof(", listVar, "[", iterVar, "_index]) == 'function') {continue;}", // IE 5.x fix from Igor Poteryaev.
                             "__LENGTH_STACK__[__LENGTH_STACK__.length - 1]++;",
                             "var ", iterVar, " = ", listVar, "[", iterVar, "_index];"].join("");
            } 
        },
        "forelse": { delta: 0, prefix: "} } if (__LENGTH_STACK__[__LENGTH_STACK__.length - 1] == 0) { if (", suffix: ") {", paramDefault: "true" },
        "/foreach": { delta: -1, prefix: "} }; delete __LENGTH_STACK__[__LENGTH_STACK__.length - 1];" }, // Remove the just-finished for-loop from the stack of loop lengths.
        //break & continue
        "break": { delta: 0, prefix: "break ", suffix: ";" },
        "continue": { delta: 0, prefix: "continue ", suffix: ";" },

        // run javascript    {exec somevar="changeme";alert(somevar) }
        "exec": { delta: 0, prefix: "", suffix: ";" },

        // inline     it's like block-function "minify".but it's can include jstemplate statement.
        "inline": { delta: 1, prefix: " _OUT.write( scrubWhiteSpace( (function(){ " + selfOUT + " " },
        "/inline": { delta: -1, prefix: " return " + selfOUTResult + "; })() ) );" },
        //    minifymacro        it's  macro + inline.
        "minifymacro": { delta: 1,
            prefixFunc: function(stmtParts, state, tmplName, etc)
            {
                var macroName = stmtParts[1].split('(')[0];
                return ["var ", macroName, " = function",
                                   stmtParts.slice(1).join(' ').substring(macroName.length),
                                   "{ " + selfOUT].join('');
            } 
        },
        "/minifymacro": { delta: -1, prefix: " return scrubWhiteSpace(" + selfOUTResult + "); };" }
    }

    // auto-build statement-RegExp
    TrimPath.parseTemplate_etc.statementTag = function()
    {
        var rex = "";
        for (var key in TrimPath.parseTemplate_etc.statementDef)
        {
            if (key.indexOf("/") != 0)
            {
                rex += ("|" + key);
            }
        }
        rex = rex.substr(1);
        return rex;
    } ();

    /* end * statementDef *   modified by Wei.Zijun */



    TrimPath.parseTemplate_etc.modifierDef = {

        "eat": function(v) { return ""; },

        "escape": function(s) { return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); },

        "capitalize": function(s) { return String(s).toUpperCase(); },

        "default": function(s, d) { return s != null ? s : d; },

        /* begin * modifierDef *   added by Wei.Zijun */

        "transform": function()
        {
            /* 

            a)example 1: ${somebodyVar|transform:"hello,$this!"}

            if somebodyVar is null or "" :  ""
            if somebodyVar=="Wei Zijun"  :  hello,Wei Zijun!


            b)example 2: ${somebodyVar|transform:"hello,$this!","nobody"}

            if somebodyVar is null or "" :  nobody
            if somebodyVar=="Wei Zijun"  :  hello,Wei Zijun!

            c)example 3: ${somebodyVar|transform:expression,"hello,$this!","nobody"}

            if expression is true  :  ("hello" + somebodyVar)
            if expression is false :  nobody
            
            */
            var s;
            var b;
            var y = "";
            var n = "";
            var rs;

            s = arguments[0];
            if (arguments.length == 2)
            {
                b = (s != null && s.length > 0);
                y = arguments[1];
            } else if (arguments.length == 3)
            {
                b = (s != null && s.length > 0);
                y = arguments[1];
                n = arguments[2];
            } else if (arguments.length == 4)
            {
                b = arguments[1];
                y = arguments[2];
                n = arguments[3];
            }

            if (b)
            {
                y = TrimPath.replaceAll(y, "$this", s + "");
                rs = y;
            } else
            {
                n = TrimPath.replaceAll(n, "$this", s + "");
                rs = n;
            }
            return rs;

        },
        "times": function(s, t, d)
        {
            /*
            ${"P"|times:3}    :   PPP
            ${"P"|times:3."-"}   :   P-P-P   ( not P-P-P- )
            */
            if (t < 1)
            {
                t = 1;
            }
            if (typeof (d) == "undefined" || d == null)
            {
                d = "";
            }
            var rs = "";
            for (var i = 0; i < t - 1; i++)
            {
                rs = rs + s + d;
            }
            rs = rs + s;
            return rs;
        },
        "attribute": function(s, b)
        {
            /*
            It's used to build the attributes of HTML Elements .

                s is a map var  ( s={ ...keyX : valueX...} ) 
            b is a boolean var. (b = true / b= false )

                ${map|attribute:true} :     key1="value1" key2="" key3="value3"  (value2==null)

                ${map|attribute}  :   key1="value1" key3="value3"            (value2==null)
            */
            var res = "";
            var a;
            for (var attribute in s)
            {
                var attributeValue = s[attribute];
                a = "";
                if (attributeValue != null && attributeValue.length > 0)
                {
                    a = attribute + '="' + attributeValue + '" ';
                } else if (b)
                {
                    a = attribute + '="" ';
                }
                res += a;
            }

            return res;

        },
        "minify": function(s) { return scrubWhiteSpace(s); },
        "inline": function(s) { return String(s).replace(/\n/g, ""); }
        /* end * modifierDef *   added by Wei.Zijun */


    }


    /* begin * blockrx *   modified by Wei.Zijun */
    TrimPath.parseTemplate_etc.blockrxDef = {
        "cdata": function(blockText, funcText)
        {
            emitText(blockText, funcText);
        },
        "minify": function(blockText, funcText)
        {
            emitText(scrubWhiteSpace(blockText), funcText);
        },
        "eval": function(blockText, funcText)
        {
            if (blockText != null && blockText.length > 0) // From B. Bittman, eval should not execute until process().
                funcText.push('_OUT.write( (function() { ' + blockText + ' })() );');
        }

    };

    TrimPath.parseTemplate_etc.blockrxRex = function()
    {
        var rex = "";
        for (var key in TrimPath.parseTemplate_etc.blockrxDef)
        {
            rex += ("|" + key);
        }
        rex = rex.substr(1);
        eval("rex=/^\\{(" + rex + ")$/;");
        return rex;
    } ();

    /* end * blockrx *   modified by Wei.Zijun */



    TrimPath.parseTemplate_etc.Template = function(tmplName, tmplContent, funcSrc, func, etc)
    {
        this.process = function(context, flags)
        {

            if (context == null)

                context = {};

            if (context._MODIFIERS == null)

                context._MODIFIERS = {};
            if (context.defined == null)
                context.defined = function(str) { return (typeof (context[str]) != undefined); };

            for (var k in etc.modifierDef)
            {

                if (context._MODIFIERS[k] == null)

                    context._MODIFIERS[k] = etc.modifierDef[k];

            }

            if (flags == null)

                flags = {};

            var resultArr = [];
            var resultOut = { write: function(m) { resultArr.push(m); } };

            try
            {
                func(resultOut, context, flags);

            } catch (e)
            {

                if (flags.throwExceptions == true)

                    throw e;

                var result = new String(resultArr.join("") + "[ERROR: " + e.toString() + (e.message ? '; ' + e.message : '') + "]");
                result["exception"] = e;
                return result;

            }
            return resultArr.join("");
        }

        this.name = tmplName;

        this.source = tmplContent;

        this.sourceFunc = funcSrc;

        this.toString = function() { return "TrimPath.Template [" + tmplName + "]"; }
    }
    TrimPath.parseTemplate_etc.ParseError = function(name, line, message)
    {

        this.name = name;
        this.line = line;
        this.message = message;
    }
    TrimPath.parseTemplate_etc.ParseError.prototype.toString = function()
    {
        return ("TrimPath template ParseError in " + this.name + ": line " + this.line + ", " + this.message);
    }




    var parse = function(body, tmplName, etc)
    {
        body = cleanWhiteSpace(body);
        var funcText = ["var TrimPath_Template_TEMP = function(_OUT, _CONTEXT, _FLAGS) { with (_CONTEXT) {"];
        var state = { stack: [], line: 1 };                              // TODO: Fix line number counting.
        var endStmtPrev = -1;
        while (endStmtPrev + 1 < body.length)
        {
            var begStmt = endStmtPrev;
            // Scan until we find some statement markup.
            begStmt = body.indexOf("{", begStmt + 1);
            while (begStmt >= 0)
            {
                var endStmt = body.indexOf('}', begStmt + 1);
                var stmt = body.substring(begStmt, endStmt);


                /* begin * blockrx *   modified by Wei.Zijun */
                var blockrx = stmt.match(TrimPath.parseTemplate_etc.blockrxRex);
                /* end * blockrx *   modified by Wei.Zijun */

                if (blockrx)
                {
                    var blockType = blockrx[1];
                    var blockMarkerBeg = begStmt + blockType.length + 1;
                    var blockMarkerEnd = body.indexOf('}', blockMarkerBeg);
                    if (blockMarkerEnd >= 0)
                    {
                        var blockMarker;
                        if (blockMarkerEnd - blockMarkerBeg <= 0)
                        {
                            blockMarker = "{/" + blockType + "}";
                        } else
                        {
                            blockMarker = body.substring(blockMarkerBeg + 1, blockMarkerEnd);
                        }

                        var blockEnd = body.indexOf(blockMarker, blockMarkerEnd + 1);
                        if (blockEnd >= 0)
                        {
                            emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText);

                            var blockText = body.substring(blockMarkerEnd + 1, blockEnd);

                            /* begin * blockrx *   modified by Wei.Zijun */
                            for (var bkey in TrimPath.parseTemplate_etc.blockrxDef)
                            {
                                if (blockType == bkey)
                                {
                                    TrimPath.parseTemplate_etc.blockrxDef[bkey](blockText, funcText);
                                }
                            }
                            /* end * blockrx *   modified by Wei.Zijun */

                            begStmt = endStmtPrev = blockEnd + blockMarker.length - 1;
                        }
                    }


                } else if (body.charAt(begStmt - 1) != '$' &&               // Not an expression or backslashed,
                           body.charAt(begStmt - 1) != '\\')
                {              // so check if it is a statement tag.

                    var offset = (body.charAt(begStmt + 1) == '/' ? 2 : 1); // Close tags offset of 2 skips '/'.

                    if (body.substring(begStmt + offset, begStmt + TrimPath.tagMaxLength + offset).search(TrimPath.parseTemplate_etc.statementTag) == 0)
                    {
                        break;
                    } // Found a match.
                }

                begStmt = body.indexOf("{", begStmt + 1);

            }




            if (begStmt < 0)                              // In "a{for}c", begStmt will be 1.
                break;
            var endStmt = body.indexOf("}", begStmt + 1); // In "a{for}c", endStmt will be 5.
            if (endStmt < 0)
                break;
            emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText);
            emitStatement(body.substring(begStmt, endStmt + 1), state, funcText, tmplName, etc);
            endStmtPrev = endStmt;
        }


        emitSectionText(body.substring(endStmtPrev + 1), funcText);
        if (state.stack.length != 0)
            throw new etc.ParseError(tmplName, state.line, "unclosed, unmatched statement(s): " + state.stack.join(","));
        funcText.push("}}; TrimPath_Template_TEMP;");
        return funcText.join("");
    }

    var emitStatement = function(stmtStr, state, funcText, tmplName, etc)
    {
        var parts = stmtStr.slice(1, -1).split(' ');
        var stmt = etc.statementDef[parts[0]]; // Here, parts[0] == for/if/else/...
        if (stmt == null)
        {                    // Not a real statement.
            emitSectionText(stmtStr, funcText);
            return;
        }
        if (stmt.delta < 0)
        {
            if (state.stack.length <= 0)
                throw new etc.ParseError(tmplName, state.line, "close tag does not match any previous statement: " + stmtStr);
            state.stack.pop();
        }
        if (stmt.delta > 0)
            state.stack.push(stmtStr);

        if (stmt.paramMin != null &&
            stmt.paramMin >= parts.length)
            throw new etc.ParseError(tmplName, state.line, "statement needs more parameters: " + stmtStr);
        if (stmt.prefixFunc != null)
        {
            funcText.push(stmt.prefixFunc(parts, state, tmplName, etc));
        } else
            funcText.push(stmt.prefix);
        if (stmt.suffix != null)
        {
            if (parts.length <= 1)
            {
                if (stmt.paramDefault != null)
                    funcText.push(stmt.paramDefault);
            } else
            {
                for (var i = 1; i < parts.length; i++)
                {
                    if (i > 1)
                        funcText.push(' ');
                    funcText.push(parts[i]);
                }
            }
            funcText.push(stmt.suffix);
        }
    }


    var emitSectionText = function(text, funcText)
    {
        if (text.length <= 0)
            return;

        var nlPrefix = 0;               // Index to first non-newline in prefix.
        var nlSuffix = text.length - 1; // Index to first non-space/tab in suffix.

        while (nlPrefix < text.length && (text.charAt(nlPrefix) == '\n'))
            nlPrefix++;
        while (nlSuffix >= 0 && (text.charAt(nlSuffix) == ' ' || text.charAt(nlSuffix) == '\t'))
            nlSuffix--;
        if (nlSuffix < nlPrefix)
            nlSuffix = nlPrefix;
        if (nlPrefix > 0)
        {
            funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("');

            var s = text.substring(0, nlPrefix).replace('\n', '\\n'); // A macro IE fix from BJessen.
            if (s.charAt(s.length - 1) == '\n')
                s = s.substring(0, s.length - 1);
            funcText.push(s);
            funcText.push('");');
        }
        var lines = text.substring(nlPrefix, nlSuffix + 1).split('\n');
        for (var i = 0; i < lines.length; i++)
        {
            emitSectionTextLine(lines[i], funcText);
            if (i < lines.length - 1)
                funcText.push('_OUT.write("\\n");\n');
        }

        if (nlSuffix + 1 < text.length)
        {
            funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("');
            var s = text.substring(nlSuffix + 1).replace('\n', '\\n');
            if (s.charAt(s.length - 1) == '\n')
                s = s.substring(0, s.length - 1);
            funcText.push(s);
            funcText.push('");');
        }
    }

    var emitSectionTextLine = function(line, funcText)
    {

        var endMarkPrev = '}';
        var endExprPrev = -1;
        while (endExprPrev + endMarkPrev.length < line.length)
        {


            var begMark = "${", endMark = "}";
            var begExpr = line.indexOf(begMark, endExprPrev + endMarkPrev.length); // In "a${b}c", begExpr == 1
            if (begExpr < 0)
                break;
            if (line.charAt(begExpr + 2) == '%')
            {
                begMark = "${%";
                endMark = "%}";
            }
            var endExpr = line.indexOf(endMark, begExpr + begMark.length);         // In "a${b}c", endExpr == 4;
            if (endExpr < 0)
                break;



            emitText(line.substring(endExprPrev + endMarkPrev.length, begExpr), funcText);

            // Example: exprs == 'firstName|default:"John Doe"|capitalize'.split('|')
            var exprArr = line.substring(begExpr + begMark.length, endExpr).replace(/\|\|/g, "#@@#").split('|');
            for (var k in exprArr)
            {
                if (exprArr[k].replace) // IE 5.x fix from Igor Poteryaev.
                    exprArr[k] = exprArr[k].replace(/#@@#/g, '||');
            }
            funcText.push('_OUT.write(');

            emitExpression(exprArr, exprArr.length - 1, funcText);
            funcText.push(');');
            endExprPrev = endExpr;
            endMarkPrev = endMark;
        }
        emitText(line.substring(endExprPrev + endMarkPrev.length), funcText);
    }

    var emitText = function(text, funcText)
    {

        if (text == null ||
            text.length <= 0)
            return;
        text = text.replace(/\\/g, '\\\\');
        text = text.replace(/\n/g, '\\n');
        text = text.replace(/"/g, '\\"');
        funcText.push('_OUT.write("');

        funcText.push(text);

        funcText.push('");');
    }

    var emitExpression = function(exprArr, index, funcText)
    {
        // Ex: foo|a:x|b:y1,y2|c:z1,z2 is emitted as c(b(a(foo,x),y1,y2),z1,z2)
        var expr = exprArr[index]; // Ex: exprArr == [firstName,capitalize,default:"John Doe"]

        if (index <= 0)
        {          // Ex: expr    == 'default:"John Doe"'
            funcText.push(expr);
            return;
        }
        var parts = expr.split(':');
        funcText.push('_MODIFIERS["');
        funcText.push(parts[0]); // The parts[0] is a modifier function name, like capitalize.
        funcText.push('"](');
        emitExpression(exprArr, index - 1, funcText);
        if (parts.length > 1)
        {
            funcText.push(',');
            funcText.push(parts[1]);
        }
        funcText.push(')');
    }


    var cleanWhiteSpace = function(result)
    {
        result = result + "";
        result = result.replace(/\t/g, "    ");
        result = result.replace(/\r\n/g, "\n");
        result = result.replace(/\r/g, "\n");
        result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev.
        return result;
    }

    var scrubWhiteSpace = function(result)
    {
        result = result + "";
        result = result.replace(/^\s+/g, "");
        result = result.replace(/\s+$/g, "");
        result = result.replace(/\s+/g, " ");
        result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev.
        return result;
    }






    // The DOM helper functions depend on DOM/DHTML, so they only work in a browser.
    // However, these are not considered core to the engine.
    //
    TrimPath.parseDOMTemplate = function(elementId, optDocument, optEtc)
    {
        if (optDocument == null)
            optDocument = document;
        var element = optDocument.getElementById(elementId);
        var content = element.value;     // Like textarea.value.
        if (content == null)
            content = element.innerHTML; // Like textarea.innerHTML.
        content = content.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
        return TrimPath.parseTemplate(content, elementId, optEtc);
    }

    TrimPath.processDOMTemplate = function(elementId, context, optFlags, optDocument, optEtc)
    {
        return TrimPath.parseDOMTemplate(elementId, optDocument, optEtc).process(context, optFlags);
    };




    /* added by Wei Zijun */
    /*

    load the template from XML File.

    var str= TrimPath.parseXMLTemplate("template.xml","nodeName").process(data);

    ( the node named nodeName is must has only one child node and the child is CDATA node. )


    example:

    <nodeName>
    <![CDATA[
    <span> hello, ${somebody}. </span>
    ]]>
    </nodeName>

    the fireFox does not supports the selectSingleNode method.
    please seek the help from google.

    */

    TrimPath.xmlDoc = null;
    TrimPath.xmlDocFileName = null;

    TrimPath.parseXMLTemplate = function(xmlFileName, nodeName, optEtc)
    {
        try
        {

            if (xmlFileName != TrimPath.xmlDocFileName)
            {
                TrimPath.xmlDoc = createDOMDocument();
                TrimPath.xmlDoc.load(xmlFileName);
                TrimPath.xmlDocFileName = xmlFileName;
            }
            var cdataNode = TrimPath.xmlDoc.selectSingleNode("//" + nodeName).childNodes[0];
            if (cdataNode.nodeType != 4)
            {
                /* for firefox */
                cdataNode = TrimPath.xmlDoc.selectSingleNode("//" + nodeName).childNodes[1];
            }
            var content = cdataNode.nodeValue;
        } catch (e)
        {
            throw "the template xml file is wrong";
        }
        return TrimPath.parseTemplate(content, nodeName, optEtc);
    };



    var createDOMDocument = function()
    {
        var xDoc = null;
        if (typeof ActiveXObject != "undefined")
        {
            var msXmlAx = null;
            try
            {
                msXmlAx = new ActiveXObject("Msxml2.DOMDocument.4.0");
            } catch (e)
            {
                try
                {
                    msXmlAx = new ActiveXObject("Msxml2.DOMDocument");
                } catch (e)
                {
                    msXmlAx = new ActiveXObject("Msxml.DOMDocument");
                }
            }
            xDoc = msXmlAx;
        } else if (document.implementation && document.implementation.createDocument)
        {
            xDoc = document.implementation.createDocument("text/xml", "", null);
        }
        if (xDoc == null || typeof xDoc.load == "undefined")
        {
            xDoc = null;
        } else
        {
            xDoc.async = false;
        }
        return xDoc;
    }



})();