2012-07-19 35 views
2

如果我有一個從JavaScript對象生成HTML代碼的prototype.js Template,如何讓它自動轉義JavaScript對象數據中的HTML實體,以便HTML不會中斷?我怎樣纔能有一個prototype.js模板編碼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包含一個引號"。如何在上面的示例中自動轉義HTML實體Template而不修改data對象,也不克隆data對象?

回答

1

爲了做到這一點,有兩兩件事需要發生:

  1. Template類的escape方法必須知道逃跑HTML實體
  2. escapeHTML method which Prototype.js adds to the String object必須擴展到編碼引號成&quot;實體。 (甚至更多它現在方式太簡單了......。)

二者皆可以用下面的代碼來實現:

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

注意,我不得不複製Template整個evaluate方法從的prototype.js 1.7版本。這有一個主要的缺點,如果未來版本的evaluate方法得到改進,我的代碼也需要改進。然而,我找不到更好的方法來做到這一點。

的具體修改的evaluate方法,我做是爲了改變這一行:

return before + String.interpret(ctx); 

到:

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

這一切,這就是爲什麼它是這樣一個恥辱,我不能不知何故extendwrap其餘的代碼...

1

閱讀源代碼,在模板中沒有什麼會轉義html。此外,提供的String#escapeHTML是非常可怕的,因爲它試圖剝離標籤(wtf)與正則表達式(wtf2),甚至不會引號。

您可以手動與此逃脫你的字符串:

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

    }; 

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

我只是寫我自己的代碼,並在escapeHTML'多麼可怕'是很驚訝。你有更好的建議嗎?如果不是,我喜歡你提供的代碼。 (也歡迎我的解決方案的想法) – Josh 2012-07-19 19:58:46

+0

@Josh爲了轉義html,我認爲這個函數應該很好地完成這項工作。我不太熟悉原型足以將其注入到模板的核心功能中。所以我不能說這些。我只是使用一個真正的模板引擎,它不像它們缺乏。 :P – Esailija 2012-07-19 20:00:28

+0

我很熟悉原型,足以將其注入到Template的核心功能中,而且我也這麼做了。 ':-)'感謝您的功能,我會很好地使用它。 – Josh 2012-07-19 20:02:26