我怎样才能有一个prototype.js模板编码HTML实体?

时间:2012-07-19 19:31:38

标签: javascript prototypejs html-entities

如果我有一个prototype.js Template从JavaScript对象生成HTML代码,我怎样才能让它自动转义JavaScript对象数据中的HTML实体,以便HTML不会中断?

示例JavaScript代码:

var data = {
    name: 'Josh',
    url: 'http://josh.gitlin.name/',
    statement: 'I\'m a JavaScript and PHP developer. Find me on Stack Overflow and say "hi"!'
}

var template = new Template(
    '<div><label>Name: <input name=\"name\" value=\"#{name}\"></label></div>'+
    '<div><label>URL: <input name=\"url\" value=\"#{url}\"></label></div>'+
    '<div><label>Personal Statement: <input name=\"statement\" value=\"#{statement}\"></label></div>'
);

$('test').update(template.evaluate(data));

JSFiddle code for the above

上述代码会生成格式错误的<input>代码,因为data.statement包含引号"。如何在不修改Template对象或克隆data对象的情况下让data自动转义上述示例中的HTML实体?

2 个答案:

答案 0 :(得分:1)

为了做到这一点,需要做两件事:

  1. escape类的Template方法必须知道转义HTML实体
  2. 必须扩展escapeHTML method which Prototype.js adds to the String object以将引号编码为&quot;个实体。 (可能还有更多。现在方式太简单了......)
  3. 这两个都可以使用以下代码实现:

    String.prototype.escapeHTML = String.prototype.escapeHTML.wrap(function(proceed){
        return proceed().replace(/"/g,'&quot;');
    });
    
    Template.addMethods({
        evaluateEscapeHTML: function(object) {
            if (object && Object.isFunction(object.toTemplateReplacements))
              object = object.toTemplateReplacements();
    
            return this.template.gsub(this.pattern, function(match) {
              if (object == null) return (match[1] + '');
    
              var before = match[1] || '';
              if (before == '\\') return match[2];
    
              var ctx = object, expr = match[3],
                  pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
    
              match = pattern.exec(expr);
              if (match == null) return before;
    
              while (match != null) {
                var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
                ctx = ctx[comp];
                if (null == ctx || '' == match[3]) break;
                expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
                match = pattern.exec(expr);
              }
    
              return before + String.interpret(ctx).escapeHTML();
            });
        }
    });
    

    此代码将扩展Prototype.js并向模板添加新的evaluateEscapeHTML方法,因此此代码应该运行一次,从那时起,任何Template现在都有一个新的evaluateEscapeHTML方法:

    var template = new Template(
        '<div><label>Name: <input name=\"name\" value=\"#{name}\"></label></div>'+
        '<div><label>URL: <input name=\"url\" value=\"#{url}\"></label></div>'+
        '<div><label>Personal Statement: <input name=\"statement\" value=\"#{statement}\"></label></div>'
    );
    
    $('test').update(template.evaluate(data));
    

    JSFiddle to test this

    请注意,我必须从prototype.js 1.7版的evaluate复制 整个 Template方法。这有一个主要的缺点,如果evaluate方法的未来版本得到改进,我的代码也需要改进。但是,我无法找到更好的方法。

    我对evaluate方法的具体修改是更改此行:

    return before + String.interpret(ctx);
    

    为:

    return before + String.interpret(ctx).escapeHTML();
    

    这就是全部,这就是为什么我不能以某种方式extendwrap代码的其余部分......

答案 1 :(得分:1)

阅读源代码,模板中没有任何内容可以逃脱html。此外,提供的String#escapeHTML非常糟糕,因为它尝试使用正则表达式(wtf2)剥离标记(wtf)并且甚至不会转义引号。

您可以使用以下方法手动转义字符串:

function escapeHTML(text) {
    var map = {
        "&" : "amp",
        "'": "#39",
        '"': "quot",
        "<": "lt",
        ">": "gt"

    };

    return text.replace( /[&'"<>]/g, function(m) {
        return "&" + map[m] + ";";
    });
}