1 if (wysihtml5.browser.supported()) {
3 module("wysihtml5.dom.parse", {
4 sanitize: function(html, rules, context, cleanUp, uneditableClass) {
5 return wysihtml5.dom.parse(html, {
9 "uneditableClass": uneditableClass
13 equal: function(actual, expected, message) {
14 return QUnit.assert.htmlEqual(actual, expected, message);
18 test("Simple tests using plain tags only", function() {
28 this.sanitize("<i id=\"foo\">bar</i>", rules),
30 "Unknown tag gets renamed to span"
34 this.sanitize("<p>foo</p>", rules),
36 "Known tag gets renamed to it's corresponding conversion"
40 this.sanitize("<script>window;</script>", rules),
42 "Forbidden tag gets correctly removed"
46 this.sanitize("foobar", rules),
52 this.sanitize("<table><tbody><tr><td>I'm a table!</td></tr></tbody></table>"),
53 "<span><span><span><span>I'm a table!</span></span></span></span>",
54 "Passing no conversion renames all into <span> elements"
58 this.sanitize("<p>foobar<br></p>", { tags: { p: true, br: true } }),
60 "Didn't rewrite the HTML"
64 this.sanitize("<div><!-- COMMENT -->foo</div>"),
66 "Stripped out comments"
70 this.sanitize("<article>foo</article>", { tags: { article: true } }),
71 "<article>foo</article>",
76 this.sanitize("<!DOCTYPE html><p>html5 doctype</p>", { tags: { p: true } }),
77 "<p>html5 doctype</p>",
78 "Stripped out doctype"
83 test("Advanced tests using tags and attributes", function() {
87 set_attributes: { alt: "foo", border: "1" },
88 check_attributes: { src: "url", width: "numbers", height: "numbers", border: "numbers" }
92 set_attributes: { title: "" }
95 h1: { rename_tag: "h2" },
103 '<h1 id="main-headline" >take this you snorty little sanitizer</h1>' +
104 '<h2>yes, you!</h2>' +
105 '<h3>i\'m old and ready to die</h3>' +
106 '<div><video src="pr0n.avi">foobar</video><img src="http://foo.gif" height="10" width="10"><img src="/foo.gif"></div>' +
107 '<div><a href="http://www.google.de"></a></div>',
110 '<h2>take this you snorty little sanitizer</h2>' +
111 '<h2>yes, you!</h2>' +
112 '<span><img alt="foo" border="1" src="http://foo.gif" height="10" width="10"><img alt="foo" border="1"></span>' +
113 '<span><i title=""></i></span>'
116 test("Attribute check of 'url' cleans up", function() {
120 check_attributes: { src: "url" }
127 '<img src="http://url.gif">' +
128 '<img src="/path/to/absolute%20href.gif">' +
129 '<img src="mango time">',
132 '<img src="http://url.gif"><img><img>'
136 test("Attribute check of 'src' cleans up", function() {
140 check_attributes: { src: "src" }
147 '<img src="HTTP://url.gif">' +
148 '<img src="/path/to/absolute%20href.gif">' +
149 '<img src="mailto:christopher@foobar.com">' +
150 '<img src="mango time">',
153 '<img src="http://url.gif">' +
154 '<img src="/path/to/absolute%20href.gif">' +
160 test("Attribute check of 'href' cleans up", function() {
164 check_attributes: { href: "href" }
171 '<a href="/foobar"></a>' +
172 '<a href="HTTPS://google.com"></a>' +
173 '<a href="http://google.com"></a>' +
174 '<a href="MAILTO:christopher@foobar.com"></a>' +
175 '<a href="mango time"></a>' +
176 '<a href="ftp://google.com"></a>',
179 '<a href="/foobar"></a>' +
180 '<a href="https://google.com"></a>' +
181 '<a href="http://google.com"></a>' +
182 '<a href="mailto:christopher@foobar.com"></a>' +
188 test("Bug in IE8 where invalid html causes duplicated content", function() {
190 tags: { p: true, span: true, div: true }
193 var result = this.sanitize('<SPAN><P><SPAN><div>FOO</div>', rules);
194 ok(result.indexOf("FOO") === result.lastIndexOf("FOO"));
198 test("Bug in IE8 where elements are duplicated when multiple parsed", function() {
200 tags: { p: true, span: true, div: true }
203 var firstResult = this.sanitize('<SPAN><P><SPAN>foo<P></P>', rules);
204 var secondResult = this.sanitize(firstResult, rules);
206 ok(secondResult.indexOf("foo") !== -1);
207 this.equal(firstResult, secondResult);
209 firstResult = this.sanitize('<SPAN><DIV><SPAN>foo<DIV></DIV>', rules);
210 secondResult = this.sanitize(firstResult, rules);
212 ok(secondResult.indexOf("foo") !== -1);
213 this.equal(firstResult, secondResult);
216 test("Test cleanup mode", function() {
218 classes: { a: 1, c: 1 },
219 tags: { span: true, div: true }
223 this.sanitize("<div><span>foo</span></div>", rules, null, true),
228 this.sanitize("<span><p>foo</p></span>", rules, null, true),
233 this.sanitize('<span class="a"></span><span class="a">foo</span>', rules, null, true),
234 '<span class="a">foo</span>',
235 "Empty 'span' is correctly removed"
239 this.sanitize('<span><span class="a">1</span> <span class="b">2</span> <span class="c">3</span></span>', rules, null, true),
240 '<span class="a">1</span> 2 <span class="c">3</span>',
241 "Senseless 'span' is correctly removed"
246 test("Advanced tests for 'img' elements", function() {
249 "wysiwyg-float-right": 1,
250 "wysiwyg-float-left": 1
269 '<img src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" alt="Christopher Blum" width="57" height="75" class="wysiwyg-float-right">',
272 '<img alt="Christopher Blum" class="wysiwyg-float-right" height="75" src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57">'
277 '<img src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" alt="Christopher Blum" width="57" height="75" ALIGN="RIGHT">',
280 '<img alt="Christopher Blum" class="wysiwyg-float-right" height="75" src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57">'
285 '<img src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" alt="Christopher Blum" width="57" height="75" align="left">',
288 '<img alt="Christopher Blum" class="wysiwyg-float-left" height="75" src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57">'
293 '<img src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" alt="Christopher Blum" width="57" height="75" align="">',
296 '<img alt="Christopher Blum" height="75" src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57">'
301 '<img src="/img/users/1/2/7/f98db1f73.6149675_s4.jpg" alt="Christopher Blum" width="57" height="75">',
304 '<img alt="Christopher Blum" height="75" width="57">'
309 '<img src="file://foobar.jpg" alt="Christopher Blum" width="57" height="75">',
312 '<img alt="Christopher Blum" height="75" width="57">'
317 '<img src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57" height="75">',
320 '<img alt="" height="75" src="https://www.xing.com/img/users/1/2/7/f98db1f73.6149675_s4.jpg" width="57">'
333 test("Advanced tests for 'br' elements", function() {
336 "wysiwyg-clear-both": 1,
337 "wysiwyg-clear-left": 1,
338 "wysiwyg-clear-right": 1
352 '<div>foo<br clear="both">bar</div>',
355 '<div>foo<br class="wysiwyg-clear-both">bar</div>'
360 '<div>foo<br clear="all">bar</div>',
363 '<div>foo<br class="wysiwyg-clear-both">bar</div>'
368 '<div>foo<br clear="left" id="foo">bar</div>',
371 '<div>foo<br class="wysiwyg-clear-left">bar</div>'
376 '<br clear="right">',
379 '<br class="wysiwyg-clear-right">'
395 '<br class="wysiwyg-clear-left">'
400 '<br class="wysiwyg-clear-left">',
403 '<br class="wysiwyg-clear-left">'
408 '<br clear="left" class="wysiwyg-clear-left">',
411 '<br class="wysiwyg-clear-left">'
416 '<br clear="left" class="wysiwyg-clear-left wysiwyg-clear-right">',
419 '<br class="wysiwyg-clear-left wysiwyg-clear-right">'
424 '<br clear="left" class="wysiwyg-clear-right">',
427 '<br class="wysiwyg-clear-left wysiwyg-clear-right">'
432 test("Advanced tests for 'font' elements", function() {
435 "wysiwyg-font-size-xx-small": 1,
436 "wysiwyg-font-size-small": 1,
437 "wysiwyg-font-size-medium": 1,
438 "wysiwyg-font-size-large": 1,
439 "wysiwyg-font-size-x-large": 1,
440 "wysiwyg-font-size-xx-large": 1,
441 "wysiwyg-font-size-smaller": 1,
442 "wysiwyg-font-size-larger": 1
446 add_class: { size: "size_font" },
454 '<font size="1">foo</font>',
457 '<span class="wysiwyg-font-size-xx-small">foo</span>'
462 '<font size="2">foo</font>',
465 '<span class="wysiwyg-font-size-small">foo</span>'
470 '<font size="3">foo</font>',
473 '<span class="wysiwyg-font-size-medium">foo</span>'
478 '<font size="4">foo</font>',
481 '<span class="wysiwyg-font-size-large">foo</span>'
486 '<font size="5">foo</font>',
489 '<span class="wysiwyg-font-size-x-large">foo</span>'
494 '<font size="6">foo</font>',
497 '<span class="wysiwyg-font-size-xx-large">foo</span>'
502 '<font size="7">foo</font>',
505 '<span class="wysiwyg-font-size-xx-large">foo</span>'
510 '<font size="+1">foo</font>',
513 '<span class="wysiwyg-font-size-larger">foo</span>'
518 '<font size="-1">foo</font>',
521 '<span class="wysiwyg-font-size-smaller">foo</span>'
526 test("Check whether namespaces are handled correctly", function() {
534 this.sanitize("<o:p>foo</o:p>", rules),
536 "Unknown tag with namespace gets renamed to span"
541 test("Check whether classes are correctly treated", function() {
553 this.sanitize('<header class="a b c">foo</header>', rules),
554 '<span class="a c">foo</span>',
555 "Allowed classes 'a' and 'c' are correctly kept and unknown class 'b' is correctly removed."
559 this.sanitize('<footer class="ab c d" class="a">foo</footer>', rules),
560 '<div class="c">foo</div>',
561 "Allowed classes 'c' is correctly kept and unknown class 'b' is correctly removed."
565 test("Check mailto links", function() {
578 this.sanitize('<a href="mailto:foo@bar.com">foo</a>', rules),
579 '<a href="mailto:foo@bar.com">foo</a>',
580 "'mailto:' urls are not stripped"
584 test("Check anchor links", function() {
596 this.sanitize('<a href="#anchor">foo</a>', rules),
597 '<a href="#anchor">foo</a>',
598 "'#'-starting anchor urls are not stripped"
602 test("Check custom data attributes", function() {
607 "data-max-width": "numbers"
615 this.sanitize('<span data-max-width="24px" data-min-width="25">foo</span>', rules),
616 '<span data-max-width="24">foo</span>',
617 "custom data attributes are not stripped"
621 test("Check Firefox misbehavior with tilde characters in urls", function() {
636 // See https://bugzilla.mozilla.org/show_bug.cgi?id=664398
639 // var d = document.createElement("div");
640 // d.innerHTML ='<a href="~"></a>';
643 // <a href="%7E"></a>
646 this.sanitize('<a href="http://google.com/~foo"></a>', rules).indexOf("~") !== -1
650 test("Check concatenation of text nodes", function() {
652 tags: { span: 1, div: 1 }
655 var tree = document.createElement("div");
656 tree.appendChild(document.createTextNode("foo "));
657 tree.appendChild(document.createTextNode("bar baz "));
658 tree.appendChild(document.createTextNode("bam! "));
660 var span = document.createElement("span");
661 span.innerHTML = "boobs! hihihi ...";
662 tree.appendChild(span);
664 var result = this.sanitize(tree, rules);
665 equal(result.childNodes.length, 2);
666 equal(result.innerHTML, "foo bar baz bam! <span>boobs! hihihi ...</span>");
669 test("Check element unwrapping", function() {
680 input = "<div>Hi,<span> there<span></span>!</span></div>",
681 output = "Hi, there!<br>";
682 this.equal(this.sanitize(input, rules), output);
685 test("Check spacing when unwrapping elements", function() {
708 input_list = "<ul><li>This</li><li>is</li><li>a</li><li>list</li></ul>",
709 output_list = "This is a list<br>",
710 input_table = "<table><tbody><tr><td>This</td><td>is</td><td>a</td><td>table</td></tr></tbody></table>",
711 output_table = "This is a table<br>";
713 this.equal(this.sanitize(input_list, rules), output_list, "List unwrapping working ok");
714 this.equal(this.sanitize(input_table, rules), output_table, "Table unwrapping working ok");
717 test("Test valid type check by attributes", function() {
719 "type_definitions": {
731 "check_attributes": {
740 input = '<img src="data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt="alt" />',
741 input_valid = '<img alt="" src="http://www.asd/a.jpg">',
742 input_valid_2 = '<img src="http://reykjavik.edicy.co/photos/photo02.jpg" alt="" height="243" width="710">';
744 this.equal(this.sanitize(input, rules), "", "Image with data src gets removed");
745 this.equal(this.sanitize(input_valid, rules), input_valid, "Valid image is kept");
746 this.equal(this.sanitize(input_valid_2, rules), input_valid_2, "Valid image is kept2");
749 test("Test valid type check and remove_action", function() {
751 "type_definitions": {
763 "check_attributes": {
766 "remove_action": "rename",
767 "remove_action_rename_to": "span"
770 "check_attributes": {
776 input = '<div class="testclass">Test</div>',
777 input2 = '<div class="otherclass">Test</div>',
778 output2 = '<span class="otherclass">Test</span>';
780 this.equal(this.sanitize(input, rules), input, "Div is kept as having valid class");
781 this.equal(this.sanitize(input2, rules), output2, "Div is renamed to span when type declaration is not met");
784 test("Test valid type definition visible_content_object ", function() {
786 "type_definitions": {
787 "visible_content_object": {
789 "has_visible_contet": 1
796 "visible_content_object": 1
798 "remove_action": "unwrap",
799 "check_attributes": {
804 "check_attributes": {
811 input1 = '<div></div>',
812 input2 = '<div> <span> </span> </div>',
813 input3 = '<div><img src="pic.jpg"/></div>',
814 input4 = '<div>test</div>',
815 input5 = '<div style="width: 10px; height: 10px;"> <span> </span> </div>',
816 tester = document.createElement('div');
818 this.equal(this.sanitize(input1, rules), "<br>", "Empty DIV gets removed");
819 this.equal(this.sanitize(input2, rules), " <span> </span> <br>", "DIV with no textual content gets unwrapped");
821 this.equal(this.sanitize(input3, rules), input3, "DIV with img inside is kept");
822 this.equal(this.sanitize(input4, rules), input4, "DIV with textual content is kept");
824 document.body.appendChild(tester);
826 tester.innerHTML = input2;
827 this.equal(this.sanitize(tester, rules).innerHTML, " <span> </span> <br>", "DIV with no dimesions and in dom gets unwrapped");
829 tester.innerHTML = input5;
830 this.equal(this.sanitize(tester, rules).innerHTML, input5 , "DIV with dimensions and in dom is kept");
834 test("Test keeping comments ", function() {
838 input = 'Test <!-- some comment -->';
839 this.equal(this.sanitize(input, rules), input, "Comments are kept if configured to keep");
842 test("Test global valid attributes for all elements ", function() {
851 "check_attributes": {
866 input1 = '<div id="test">Test</div>',
867 input2 = '<div class="test">Test</div>',
868 output2 = '<div>Test</div>',
869 input3 = '<div data-name="test" data-value="test">Test</div>',
870 input4 = '<span data-name="test" data-value="1234">Test</span>',
871 output4 = '<span data-value="1234">Test</span>';
873 this.equal(this.sanitize(input1, rules), input1, "Global attribute allows id for all elements and div is allowewd tag and kept");
874 this.equal(this.sanitize(input1, rules2), input1, "Global attribute allows id for all elements and div is allowewd tag and kept even if no check_attributes");
875 this.equal(this.sanitize(input2, rules), output2, "Div is kept but attribute 'class' is not allowed locally and globally, so it is removed.");
876 this.equal(this.sanitize(input3, rules), input3, "Global eattribute configuration allows all attributes beginning with 'data-'.");
877 this.equal(this.sanitize(input4, rules), output4, "Local check_attributes overwrites global attributes");
881 test("Test classes blacklist ", function() {
884 "classes_blacklist": {
885 "Apple-interchange-newline": 1
892 input1 = '<span class="Apple-interchange-newline">Test</span',
893 output1 = '<span>Test</span>',
894 input2 = '<span class="SomeClass">Test</span>',
895 input3 = '<span class="SomeClass Apple-interchange-newline">Test</span>';
897 this.equal(this.sanitize(input1, rules), output1, "Blacklist class removed");
898 this.equal(this.sanitize(input2, rules), input2, "Other class kept");
899 this.equal(this.sanitize(input3, rules), input2, "Other class kept, but blacklisted class removed");