//------------------------------------------------------------------------------ // Template-Engine //------------------------------------------------------------------------------ // depends-on: // inheritance //------------------------------------------------------------------------------ // String-Methoden ergänzen APPCO = {}; APPCO.apply = function(o, c, defaults){ // no "this" reference for friendly out of scope calls if (defaults) { APPCO.apply(o, defaults); } if (o && c && typeof c == 'object') { for (var p in c) { o[p] = c[p]; } } return o; }; // quick and dirty! Manche Autoren lehnen solche Erweiterungen ab APPCO.apply(String.prototype, { include: function (pattern) { return this.indexOf(pattern) > -1; }, startsWith: function (pattern) { return this.lastIndexOf(pattern, 0) === 0; }, endsWith: function (pattern) { var d = this.length - pattern.length; return d >= 0 && this.indexOf(pattern, d) === d; } }); APPCO.apply(String, { interpret: function(value) { return value == null ? '' : String(value); } }); // Namensraum var TELIB = {}; TELIB.Generator_cl = Class.create({ initialize: function () { this.reset_px(); }, reset_px: function () { this.code_a = ['var result_a = [];\n']; }, write_px: function (text_spl) { // Escape-Zeichen bei Strings ergänzen var text_s = text_spl.replace(/"/g, '\\"'); text_s = text_s.replace(/'/g, "\\'"); this.code_a.push('result_a.push("' + text_s + '");\n'); }, code_px: function (code_spl) { if (code_spl.startsWith('if')) { this.code_a.push('if (' + code_spl.substr(2) + ') {\n'); } else if (code_spl.startsWith('else')) { this.code_a.push('} else {\n'); } else if (code_spl.startsWith('endif')) { this.code_a.push('}\n'); } else if (code_spl.startsWith('for')) { this.code_a.push('for (' + code_spl.substr(3) + ') {\n'); } else if (code_spl.startsWith('endfor')) { this.code_a.push('}\n'); } else { this.code_a.push(code_spl + '\n'); } }, substitute_px: function (subst_spl) { this.code_a.push('result_a.push(' + String.interpret(subst_spl) + ');\n'); }, generate_px: function () { var result_s = this.code_a.join('') + ' return result_a.join("");'; var f_o = new Function ('context', result_s); return f_o; } }); TELIB.TemplateCompiler_cl = Class.create({ initialize: function () { this.gen_o = new TELIB.Generator_cl(); this.reset_px(); }, reset_px: function () { this.gen_o.reset_px(); this.preservePreWS_b = false; this.preservePostWS_b = false; }, setPreWS_px: function (on_bpl) { if ((on_bpl == undefined) || (on_bpl == false)) { this.preservePreWS_b = false; } else { this.preservePreWS_b = true; } }, setPostWS_px: function (on_bpl) { if ((on_bpl == undefined) || (on_bpl == false)) { this.preservePostWS_b = false; } else { this.preservePostWS_b = true; } }, compile_px: function (source_spl) { var state_i = 0; var pos_i = 0; var len_i = source_spl.length; var text_s = ''; var code_s = ''; var subst_s = ''; this.gen_o.reset_px(); var doubletest_f = function (char_spl) { var status_b = false; if ((pos_i + 1) < len_i) { if ((source_spl[pos_i] == char_spl) && (source_spl[pos_i+1] == char_spl)) { status_b = true; } } return status_b; } while (pos_i < len_i) { switch (state_i) { case 0: // outside if (source_spl[pos_i] == '@') { // ECMA 5! if (doubletest_f('@') == false) { state_i = 2; code_s = ''; } else { // als normales Zeichen verarbeiten, ein Zeichen überlesen state_i = 1; text_s = '@'; pos_i++; } } else if (source_spl[pos_i] == '#') { if (doubletest_f('#') == false) { state_i = 3; subst_s = ''; } else { // als normales Zeichen verarbeiten, ein Zeichen überlesen state_i = 1; text_s = '#'; pos_i++; } } else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) { state_i = 4; text_s = ''; pos_i--; // Zeichen erneut verarbeiten } else { state_i = 1; text_s = ''; pos_i--; // Zeichen erneut verarbeiten } break; case 1: // inText if (source_spl[pos_i] == '@') { if (doubletest_f('@') == false) { // Textende, neuer Code state_i = 0; this.gen_o.write_px(text_s); pos_i--; // Zeichen erneut verarbeiten } else { // als normales Zeichen verarbeiten, ein Zeichen überlesen text_s += '@'; pos_i++; } } else if (source_spl[pos_i] == '#') { if (doubletest_f('#') == false) { // Textende, neue Substitution state_i = 0; this.gen_o.write_px(text_s); pos_i--; // Zeichen erneut verarbeiten // Textende, neuer Code } else { // als normales Zeichen verarbeiten, ein Zeichen überlesen text_s += '#'; pos_i++; } } else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) { // Textende state_i = 0; this.gen_o.write_px(text_s); pos_i--; // Zeichen erneut verarbeiten } else { // sammeln if ((source_spl[pos_i] != '\n') && (source_spl[pos_i] != '\r')) { text_s += source_spl[pos_i]; } else if (source_spl[pos_i] == '\n') { text_s += '\\n'; } else { text_s += '\\r'; } } break; case 2: // inCode if (source_spl[pos_i] == '@') { if (doubletest_f('@') == false) { // zu Ende, erzeugen this.gen_o.code_px(code_s); state_i = 5; //0 } else { // als normales Zeichen verarbeiten, ein Zeichen überlesen code_s += '@'; pos_i++; } } else { // sammeln code_s += source_spl[pos_i]; } break; case 3: // inSubst // Verdopplung # hier nicht zulässig! if (source_spl[pos_i] == '#') { // zu Ende, erzeugen this.gen_o.substitute_px(subst_s); state_i = 0; } else { // sammeln subst_s += source_spl[pos_i]; } break; case 4: // pre-code-whitespace switch (source_spl[pos_i]) { case ' ': case '\t': // sammeln text_s += source_spl[pos_i]; break; default: state_i = 0; if (source_spl[pos_i] != '@') { this.gen_o.write_px(text_s); } else { if (doubletest_f('@') == false) { // Whitespace vor Code-Beginn erkannt if (this.preservePreWS_b == true) { // trotzdem ausgeben this.gen_o.write_px(text_s); } } // ansonsten wie normales Zeichen behandeln } pos_i--; // Zeichen erneut verarbeiten } break; case 5: // post-code-whitespace switch (source_spl[pos_i]) { case '\n': text_s += '\\n'; state_i = 0; break; case '\r': text_s += '\\r'; state_i = 0; break; case ' ': case '\t': // ggf. sammeln text_s += source_spl[pos_i]; break; default: // Whitespace nach Code erkannt if (this.preservePostWS_b == true) { // trotzdem ausgeben this.gen_o.write_px(text_s); } state_i = 0; pos_i--; // Zeichen erneut verarbeiten } break; } pos_i++; } // welcher Zustand liegt abschließend vor? if (state_i == 1) { this.gen_o.write_px(text_s); } else if (state_i == 2) { this.gen_o.code_px(code_s); } else if (state_i == 3) { this.gen_o.substitute_px(subst_s); } else if (state_i == 4) { if (this.preservePreWS_b == true) { this.gen_o.write_px(text_s); } } else if (state_i == 5) { if (this.preservePostWS_b == true) { this.gen_o.write_px(text_s); } } return this.gen_o.generate_px(); } }); // EOF