/*
LTemplate.js
Copyright (C) 2007 NAKAMURA Satoru

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/**
 * LTemplate
 * 
 * Light weight template engine for JavaScript.
 * 
 * @link http://clonedoppelganger.net/javascript/LTemplate.html
 * @author NAKAMURA Satoru <clonedoppelganger at gmail.com>
 * @version 0.0.3 <2007/12/08>
 */
var LTemplate = function(template) {
  this.initialize(template);
}

LTemplate.prototype = {

  /** @param {String} template */
  initialize: function(template) {
    this.template = template;
    this.source = "var applied = function(context) {" + "\n"  + this.parse()
      + "\n"  + "return __out.join('');" + "\n"  + "}(context);";
  },

  /**
   * @param {Object} context
   * @return The output from processing the template.
   */
  applyTemplate: function(context) {
    if (!context) context = {};
    try {
      eval(this.source);
    } catch (e) {
      alert(
        "Template Parse Error (LTemplate)\n"
        + "Type: " + e.name + "\n"
        + "Message:" + e.message
      );
    }
    return applied;
  },

  parse: function() {
    var template = this.template;
    var output = ["try { var __cnt = 0; var __out = []; "
      + "__out[__cnt++] = unescape('"];
    var outlen = 1;
    var isCode = false;
    var isPrint = false;
    var printStr = "";
    for (var i = 0, n = template.length; i < n; i++) {
      var one = template.charAt(i);
      var two = (i + 1 < n) ? one + template.charAt(i + 1) : null;
      var three = (i + 2 < n) ? two + template.charAt(i + 2) : null;
      // escape sequence
      if (three == "[\\%" || three == "\\%]") {
        var c = "";
        if (isCode || isPrint) {
          c = three.replace("\\", "");
        } else {
          c = escape(three.replace("\\", ""));
        }
        if (isPrint) {
          printStr += c;
        } else {
          output[outlen++] = c;
        }
        i += 2;
      // start tag
      } else if (two == "[%" && three != "[%=") {
        isCode = true;
        output[outlen++] = "');\n";
        i += 1;
      // start print tag
      } else if (three == "[%=") {
        isPrint = true;
        output[outlen++] = "');\n" + "__out[__cnt++] = ";
        i += 2;
      // end tag
      } else if (two == "%]") {
        if (isPrint) {
          output[outlen++] = this.modify(printStr) + ";";
          printStr = "";
          isPrint = false;
        }
        isCode = false;
        output[outlen++] = "\n" + "__out[__cnt++] = unescape('";
        i += 1;
      // set output string
      } else {
        var c = "";
        if (isCode || isPrint) {
          c = one;
        } else {
          c = escape(one);
        }
        if (isPrint) {
          printStr += c;
        } else {
          output[outlen++] = c;
        }
      }
    }
    return output.join("") + "');" + "\n" +  "} catch(e) {" + "\n"
      + "alert('Template Parse Error (LTemplate)\\n"
      + "Type: ' + e.name + '\\nMessage: ' + e.message);}";
  },

  modify: function(term) {
    var terms = LTemplate.splitTerm(term);
    var result = terms[0];
    for (var i = 1; i < terms.length; i++) {
      var val = LTemplate.modifiers.trim(terms[i]);
      var idx = val.indexOf(":");
      var name = (idx == -1) ? val : val.substr(0, idx);
      var option = (idx == -1) ? "" : val.substr(idx + 1);
      if (LTemplate.modifiers[name] != null) {
        result = "LTemplate.modifiers['" + name + "'](" + result + ",'" + option + "')";
      }
    }
    return result;
  }

}

LTemplate.splitTerm = function(term) {
  var innerDq = false;
  var res = [];
  var reslen = 0;
  var buf = [];
  var buflen = 0;
  for (var i = 0, n = term.length; i < n; i++) {
    var one = term.charAt(i);
    var next = (i + 2 < n) ? term.charAt(i + 1) : null;
    if (one == "\\") {
      buf[buflen++] = "\\\\";
    } else if (one == "'") {
      buf[buflen++] = "\\" + "'";
    } else if (one == '"') {
      innerDq = innerDq ? false : true;
      buf[buflen++] = one;
    } else if (one == "|") {
      if (innerDq) {
        buf[buflen++] = one;
      } else {
        res[reslen++] = LTemplate.modifiers["trim"](buf.join(""));
        buf = [];
        burlen = 0;
      }
    } else {
      buf[buflen++] = one;
    }
  }
  res[reslen++] = LTemplate.modifiers["trim"](buf.join(""));
  return res;
}

LTemplate.splitOption = function(option) {
  var idx = option.indexOf(":");
  if (option == null || option == "") {
    return false;
  } else if (idx == -1) {
    return [eval(option)];
  }
  return [eval(option.substr(0, idx)), eval(option.substr(idx + 1))];
}

LTemplate.modifiers = {

  "escape": function(value) {
    return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\"/g, '&quot;').replace(/\'/g, '&#039;');
  },

  "encuri": function(value) {
    return encodeURIComponent(value);
  },

  "trim": function(value) {
    return value.replace(/^\s+|\s+$/g, "");
  },

  "nl2br": function(value) {
    return value.replace(/\n/g, "<br />");
  },

  "strip_tags": function(value) {
    return value.replace(/<(.|\n)+?>/g, "");
  },

  "upper": function(value) {
    return value.toUpperCase();
  },

  "lower": function(value) {
    return value.toLowerCase();
  },

  "default": function(value, option) {
    return value != "" ? value : eval(option);
  },

  "cat": function(value, option) {
    return value + eval(option);
  },

  "truncate": function(value, option) {
    var count = 80;
    var character = "...";
    var splitedOption = LTemplate.splitOption(option);
    if (splitedOption) {
      count = splitedOption[0];
      if (splitedOption.length == 2) {
        character = splitedOption[1];
      }
    }
    if (value.length > count) {
      return value.substr(0, count) + character;
    } else {
      return value;
    }
  },

  "date_format": function(value, option) {
    var format = eval(option);
    if (typeof DateFormatter == "object" && typeof format == "string") {
      return DateFormatter.format(value, format);
    } else {
      return value;
    }
  },

  "replace": function(value, option) {
    var search = "";
    var replace = "";
    var splitedOption = LTemplate.splitOption(option);
    if (splitedOption) {
      search = splitedOption[0];
      if (splitedOption.length == 2) {
        replace = splitedOption[1];
      }
    }
    return value.replace(search, replace);
  }

}
