/**
 * laytpl 轻量模板引擎
 */

layui.define(function(exports){
  "use strict";

  // 默认属性
  var config = {
    open: '{{', // 标签符前缀
    close: '}}' // 标签符后缀
  };

  // 模板工具
  var tool = {
    escape: function(html){
      var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
      if(html === undefined || html === null) return '';

      html += '';
      if(!exp.test(html)) return html;

      return html.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
      .replace(/</g, '&lt;').replace(/>/g, '&gt;')
      .replace(/'/g, '&#39;').replace(/"/g, '&quot;');
    }
  };

  // 内部方法
  var inner = {
    exp: function(str){
      return new RegExp(str, 'g');
    },
    // 错误提示
    error: function(e, source){
      var error = 'Laytpl Error: ';
      typeof console === 'object' && console.error(error + e + '\n'+ (source || ''));
      return error + e;
    }
  };

  // constructor
  var Class = function(template, options){
    var that = this;
    that.config = that.config || {};
    that.template = template;

    // 简单属性合并
    var extend = function(obj){
      for(var i in obj){
        that.config[i] = obj[i];
      }
    };

    extend(config);
    extend(options);
  };

  // 标签正则
  Class.prototype.tagExp = function(type, _, __){
    var options = this.config;
    var types = [
      '#([\\s\\S])+?',   // js 语句
      '([^{#}])*?' // 普通字段
    ][type || 0];

    return inner.exp((_||'') + options.open + types + options.close + (__||''));
  };

  // 模版解析
  Class.prototype.parse = function(template, data){
    var that = this;
    var options = that.config;
    var source = template;
    var jss = inner.exp('^'+ options.open +'#', '');
    var jsse = inner.exp(options.close +'$', '');

    // 模板必须为 string 类型
    if(typeof template !== 'string') return template;

    // 正则解析
    template = template.replace(/\s+|\r|\t|\n/g, ' ')
    .replace(inner.exp(options.open +'#'), options.open +'# ')
    .replace(inner.exp(options.close +'}'), '} '+ options.close).replace(/\\/g, '\\\\')

    // 不匹配指定区域的内容
    .replace(inner.exp(options.open + '!(.+?)!' + options.close), function(str){
      str = str.replace(inner.exp('^'+ options.open + '!'), '')
      .replace(inner.exp('!'+ options.close), '')
      .replace(inner.exp(options.open + '|' + options.close), function(tag){
        return tag.replace(/(.)/g, '\\$1')
      });
      return str
    })

    // 匹配 JS 语法
    .replace(/(?="|')/g, '\\').replace(that.tagExp(), function(str){
      str = str.replace(jss, '').replace(jsse, '');
      return '";' + str.replace(/\\(.)/g, '$1') + ';view+="';
    })

    // 匹配普通输出语句
    .replace(that.tagExp(1), function(str){
      var start = '"+laytpl.escape(';
      if(str.replace(/\s/g, '') === options.open + options.close){
        return '';
      }
      str = str.replace(inner.exp(options.open + '|' + options.close), '');
      if(/^=/.test(str)){
        str = str.replace(/^=/, '');
      } else if(/^-/.test(str)){
        str = str.replace(/^-/, '');
        start = '"+(';
      }
      return start + str.replace(/\\(.)/g, '$1') + ')+"';
    });

    template = '"use strict";var view = "' + template + '";return view;';

    try {
      /**
       * 请注意: 开发者在使用模板语法时，需确保模板中的 JS 语句不来自于页面用户输入。
       * 即模板中的 JS 语句必须在页面开发者自身的可控范围内，否则请避免使用该模板解析。
       */
      that.cache = template = new Function('d, laytpl', template);
      return template(data, tool);
    } catch(e) {
      delete that.cache;
      return inner.error(e, source);
    }
  };

  // 数据渲染
  Class.prototype.render = function(data, callback){
    data = data || {};

    var that = this;
    var result = that.cache ? that.cache(data, tool) : that.parse(that.template, data);

    // 返回渲染结果
    typeof callback === 'function' && callback(result);
    return result;
  };

  // 创建实例
  var laytpl = function(template, options){
    return new Class(template, options);
  };

  // 配置全局属性
  laytpl.config = function(options){
    options = options || {};
    for(var i in options){
      config[i] = options[i];
    }
  };

  laytpl.v = '2.0.0';

  // export
  exports('laytpl', laytpl);
});
