public
Description: Prototype JavaScript framework
Homepage: http://prototypejs.org/
Clone URL: git://github.com/sstephenson/prototype.git
Fix String#escapeHTML and String#unescapeHTML (to ensure no memory leakage and 
more consistent output). [#47:resolved]
jdalton (author)
Fri Apr 25 12:18:09 -0700 2008
savetheclocktower (committer)
Mon May 19 15:09:56 -0700 2008
commit  57a901febacfd831bdb6c9b5b95753f4d256a65b
tree    1b60e48b5c8e0548f3c15f130e0cb281721420e5
parent  7a1ee73953690cc420d4887b2214bb07534f2ca3
...
83
84
85
86
 
87
88
89
90
91
 
 
 
92
93
 
94
95
96
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
229
230
231
232
 
233
234
235
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
238
239
...
83
84
85
 
86
87
88
89
90
 
91
92
93
94
 
95
96
97
98
...
213
214
215
 
 
 
 
 
 
 
 
 
216
217
218
...
222
223
224
 
225
226
227
228
 
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
0
@@ -83,14 +83,16 @@ Object.extend(String.prototype, {
0
   escapeHTML: function() {
0
     var self = arguments.callee;
0
     self.text.data = this;
0
-    return self.div.innerHTML;
0
+    return self.container.innerHTML;
0
   },
0
 
0
   unescapeHTML: function() {
0
     var div = new Element('div');
0
-    div.innerHTML = this.stripTags();
0
+    // Safari requires the text nested inside another element to render correctly
0
+    div.innerHTML = '<pre>' + this.stripTags() + '</pre>';
0
+    div = div.firstChild;
0
     return div.childNodes[0] ? (div.childNodes.length > 1 ? 
0
-      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : 
0
+      $A(div.childNodes).inject('', function(memo, node) { return memo + node.nodeValue }) : 
0
       div.childNodes[0].nodeValue) : '';
0
   },
0
   
0
@@ -211,15 +213,6 @@ Object.extend(String.prototype, {
0
   }
0
 });
0
 
0
-if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
0
-  escapeHTML: function() {
0
-    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
0
-  },
0
-  unescapeHTML: function() {
0
-    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
0
-  }
0
-});
0
-
0
 String.prototype.gsub.prepareReplacement = function(replacement) {
0
   if (Object.isFunction(replacement)) return replacement;
0
   var template = new Template(replacement);
0
@@ -229,11 +222,39 @@ String.prototype.gsub.prepareReplacement = function(replacement) {
0
 String.prototype.parseQuery = String.prototype.toQueryParams;
0
 
0
 Object.extend(String.prototype.escapeHTML, {
0
-  div:  document.createElement('div'),
0
+  container: document.createElement('pre'),
0
   text: document.createTextNode('')
0
 });
0
 
0
-String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
0
+String.prototype.escapeHTML.container.appendChild(String.prototype.escapeHTML.text);
0
+
0
+if (Prototype.Browser.IE)
0
+  // IE converts all newlines to carriage returns so we swap them back
0
+  String.prototype.unescapeHTML = String.prototype.unescapeHTML.wrap(function(proceed) {
0
+    return proceed().replace(/\r/g, '\n')
0
+  });
0
+
0
+if (Prototype.Browser.WebKit && Prototype.BrowserFeatures.SelectorsAPI)
0
+  // Safari 3.x has issues with escaping the ">" character
0
+  (function() {
0
+    var escapeHTML = String.prototype.escapeHTML;
0
+    Object.extend(
0
+      String.prototype.escapeHTML = escapeHTML.wrap(function(proceed) {
0
+        return proceed().replace(/>/g, "&gt;")
0
+      }), {
0
+      container: escapeHTML.container,
0
+      text: escapeHTML.text
0
+    })
0
+  })();
0
+  
0
+if ('&'.escapeHTML() !== '&amp;') {
0
+  // Safari 2.x has issues with escaping html inside a "pre" element so we use the deprecated "xmp" element instead
0
+  Object.extend(String.prototype.escapeHTML, {
0
+    container: document.createElement('xmp'),
0
+    text: document.createTextNode('')
0
+  });
0
+  String.prototype.escapeHTML.container.appendChild(String.prototype.escapeHTML.text);
0
+}
0
 
0
 var Template = Class.create({
0
   initialize: function(template, pattern) {
...
255
256
257
 
258
259
260
 
 
 
261
262
263
...
255
256
257
258
259
260
261
262
263
264
265
266
267
0
@@ -255,9 +255,13 @@ new Test.Unit.Runner({
0
     
0
     this.assertEqual(largeTextUnescaped, largeTextEscaped.unescapeHTML());
0
     
0
+    this.assertEqual('test \xfa', 'test &uacute;'.unescapeHTML());
0
     this.assertEqual('1\n2', '1\n2'.unescapeHTML());
0
     this.assertEqual('Pride & Prejudice', '<h1>Pride &amp; Prejudice</h1>'.unescapeHTML());
0
     
0
+    var escapedTest = '"&lt;" means "<" in HTML';
0
+    this.assertEqual(escapedTest, escapedTest.escapeHTML().unescapeHTML());
0
+    
0
     this.benchmark(function() { largeTextEscaped.unescapeHTML() }, 1000);
0
     
0
   },

Comments