wysihtml5_with_ps 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Makefile +98 -0
- data/Rakefile +38 -0
- data/lib/base/base.js +139 -0
- data/lib/rangy/rangy-core.js +3211 -0
- data/lib/wysihtml5_with_ps/version.rb +3 -0
- data/test/assert/html_equal_test.js +32 -0
- data/test/browser_test.js +85 -0
- data/test/dom/auto_link_test.js +105 -0
- data/test/dom/contains_test.js +18 -0
- data/test/dom/convert_to_list_test.js +101 -0
- data/test/dom/copy_attributes_test.js +51 -0
- data/test/dom/copy_styles_test.js +110 -0
- data/test/dom/delegate_test.js +62 -0
- data/test/dom/get_as_dom_test.js +55 -0
- data/test/dom/get_parent_element_test.js +161 -0
- data/test/dom/get_style_test.js +54 -0
- data/test/dom/has_element_with_class_name_test.js +29 -0
- data/test/dom/has_element_with_tag_name_test.js +25 -0
- data/test/dom/insert_css_test.js +31 -0
- data/test/dom/observe_test.js +83 -0
- data/test/dom/parse_test.js +614 -0
- data/test/dom/rename_element_test.js +28 -0
- data/test/dom/resolve_list_test.js +46 -0
- data/test/dom/sandbox_test.js +184 -0
- data/test/dom/set_attributes_test.js +15 -0
- data/test/dom/set_styles_test.js +19 -0
- data/test/editor_test.js +547 -0
- data/test/incompatible_test.js +60 -0
- data/test/index.html +126 -0
- data/test/lang/array_test.js +22 -0
- data/test/lang/object_test.js +22 -0
- data/test/lang/string_test.js +19 -0
- data/test/quirks/clean_pasted_html_test.js +11 -0
- data/test/undo_manager_test.js +94 -0
- metadata +116 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module("wysihtml5.dom.renameElement", {
|
2
|
+
equal: function(actual, expected, message) {
|
3
|
+
return wysihtml5.assert.htmlEqual(actual, expected, message);
|
4
|
+
},
|
5
|
+
|
6
|
+
renameElement: function(html, newNodeName) {
|
7
|
+
var container = wysihtml5.dom.getAsDom(html);
|
8
|
+
wysihtml5.dom.renameElement(container.firstChild, newNodeName);
|
9
|
+
return container.innerHTML;
|
10
|
+
}
|
11
|
+
});
|
12
|
+
|
13
|
+
test("Basic tests", function() {
|
14
|
+
this.equal(
|
15
|
+
this.renameElement("<p>foo</p>", "div"),
|
16
|
+
"<div>foo</div>"
|
17
|
+
);
|
18
|
+
|
19
|
+
this.equal(
|
20
|
+
this.renameElement("<ul><li>foo</li></ul>", "ol"),
|
21
|
+
"<ol><li>foo</li></ol>"
|
22
|
+
);
|
23
|
+
|
24
|
+
this.equal(
|
25
|
+
this.renameElement('<p align="left" class="foo"></p>', "h2"),
|
26
|
+
'<h2 align="left" class="foo"></h2>'
|
27
|
+
);
|
28
|
+
});
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module("wysihtml5.dom.resolveList", {
|
2
|
+
equal: function(actual, expected, message) {
|
3
|
+
return wysihtml5.assert.htmlEqual(actual, expected, message);
|
4
|
+
},
|
5
|
+
|
6
|
+
resolveList: function(html) {
|
7
|
+
var container = wysihtml5.dom.getAsDom(html);
|
8
|
+
document.body.appendChild(container);
|
9
|
+
wysihtml5.dom.resolveList(container.firstChild);
|
10
|
+
var innerHTML = container.innerHTML;
|
11
|
+
container.parentNode.removeChild(container);
|
12
|
+
return innerHTML;
|
13
|
+
}
|
14
|
+
});
|
15
|
+
|
16
|
+
test("Basic tests", function() {
|
17
|
+
this.equal(
|
18
|
+
this.resolveList("<ul><li>foo</li></ul>"),
|
19
|
+
"foo<br>"
|
20
|
+
);
|
21
|
+
|
22
|
+
this.equal(
|
23
|
+
this.resolveList("<ul><li>foo</li><li>bar</li></ul>"),
|
24
|
+
"foo<br>bar<br>"
|
25
|
+
);
|
26
|
+
|
27
|
+
this.equal(
|
28
|
+
this.resolveList("<ol><li>foo</li><li>bar</li></ol>"),
|
29
|
+
"foo<br>bar<br>"
|
30
|
+
);
|
31
|
+
|
32
|
+
this.equal(
|
33
|
+
this.resolveList("<ol><li></li><li>bar</li></ol>"),
|
34
|
+
"bar<br>"
|
35
|
+
);
|
36
|
+
|
37
|
+
this.equal(
|
38
|
+
this.resolveList("<ol><li>foo<br></li><li>bar</li></ol>"),
|
39
|
+
"foo<br>bar<br>"
|
40
|
+
);
|
41
|
+
|
42
|
+
this.equal(
|
43
|
+
this.resolveList("<ul><li><h1>foo</h1></li><li><div>bar</div></li><li>baz</li></ul>"),
|
44
|
+
"<h1>foo</h1><div>bar</div>baz<br>"
|
45
|
+
);
|
46
|
+
});
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module("wysihtml5.dom.Sandbox", {
|
2
|
+
teardown: function() {
|
3
|
+
var iframe;
|
4
|
+
while (iframe = document.querySelector("iframe.wysihtml5-sandbox")) {
|
5
|
+
iframe.parentNode.removeChild(iframe);
|
6
|
+
}
|
7
|
+
},
|
8
|
+
|
9
|
+
getCharset: function(doc) {
|
10
|
+
var charset = doc.characterSet || doc.charset;
|
11
|
+
if (/unicode|utf-8/.test(charset)) {
|
12
|
+
return "utf-8";
|
13
|
+
}
|
14
|
+
return charset;
|
15
|
+
},
|
16
|
+
|
17
|
+
eval: function(iframeWindow, code) {
|
18
|
+
try {
|
19
|
+
return iframeWindow.execScript ? iframeWindow.execScript(code) : iframeWindow.eval(code);
|
20
|
+
} catch(e) {
|
21
|
+
return null;
|
22
|
+
}
|
23
|
+
},
|
24
|
+
|
25
|
+
isUnset: function(evalCode, iframeWindow) {
|
26
|
+
var value = this.eval(iframeWindow, evalCode);
|
27
|
+
return !value || value == wysihtml5.EMPTY_FUNCTION;
|
28
|
+
}
|
29
|
+
});
|
30
|
+
|
31
|
+
|
32
|
+
asyncTest("Basic Test", function() {
|
33
|
+
expect(8);
|
34
|
+
|
35
|
+
var sandbox = new wysihtml5.dom.Sandbox(function(param) {
|
36
|
+
equal(param, sandbox, "The parameter passed into the readyCallback is the sandbox instance");
|
37
|
+
|
38
|
+
var iframes = document.querySelectorAll("iframe.wysihtml5-sandbox");
|
39
|
+
equal(iframes.length, 1, "iFrame sandbox inserted into dom tree");
|
40
|
+
|
41
|
+
var iframe = iframes[iframes.length - 1],
|
42
|
+
isIframeInvisible = iframe.width == 0 && iframe.height == 0 && iframe.frameBorder == 0;
|
43
|
+
ok(isIframeInvisible, "iframe is not visible");
|
44
|
+
|
45
|
+
var isSandboxed = iframe.getAttribute("security") == "restricted";
|
46
|
+
ok(isSandboxed, "iFrame is sandboxed");
|
47
|
+
|
48
|
+
var isWindowObject = sandbox.getWindow().setInterval && sandbox.getWindow().clearInterval;
|
49
|
+
ok(isWindowObject, "wysihtml5.Sandbox.prototype.getWindow() works properly");
|
50
|
+
|
51
|
+
var isDocumentObject = sandbox.getDocument().appendChild && sandbox.getDocument().body;
|
52
|
+
ok(isDocumentObject, "wysihtml5.Sandbox.prototype.getDocument() works properly");
|
53
|
+
|
54
|
+
equal(sandbox.getIframe(), iframe, "wysihtml5.Sandbox.prototype.getIframe() returns the iframe correctly");
|
55
|
+
equal(typeof(sandbox.getWindow().onerror), "function", "window.onerror is set");
|
56
|
+
|
57
|
+
start();
|
58
|
+
});
|
59
|
+
|
60
|
+
sandbox.insertInto(document.body);
|
61
|
+
});
|
62
|
+
|
63
|
+
|
64
|
+
asyncTest("Security test #1", function() {
|
65
|
+
expect(14);
|
66
|
+
|
67
|
+
var that = this;
|
68
|
+
|
69
|
+
var sandbox = new wysihtml5.dom.Sandbox(function() {
|
70
|
+
var iframeWindow = sandbox.getWindow();
|
71
|
+
|
72
|
+
var isSafari = wysihtml5.browser.USER_AGENT.indexOf("Safari") !== -1 && wysihtml5.browser.USER_AGENT.indexOf("Chrome") === 1;
|
73
|
+
|
74
|
+
if (isSafari) {
|
75
|
+
// This test fails in Safari 5, as it's impossible to unset a cookie there
|
76
|
+
ok(true, "Cookie is NOT unset (but that's expected in Safari)");
|
77
|
+
} else {
|
78
|
+
ok(that.isUnset("document.cookie", iframeWindow), "Cookie is unset");
|
79
|
+
}
|
80
|
+
|
81
|
+
ok(that.isUnset("document.open", iframeWindow), "document.open is unset");
|
82
|
+
ok(that.isUnset("document.write", iframeWindow), "document.write is unset");
|
83
|
+
ok(that.isUnset("window.parent", iframeWindow), "window.parent is unset");
|
84
|
+
ok(that.isUnset("window.opener", iframeWindow), "window.opener is unset");
|
85
|
+
ok(that.isUnset("window.localStorage", iframeWindow), "localStorage is unset");
|
86
|
+
ok(that.isUnset("window.globalStorage", iframeWindow), "globalStorage is unset");
|
87
|
+
ok(that.isUnset("window.XMLHttpRequest", iframeWindow), "XMLHttpRequest is an empty function");
|
88
|
+
ok(that.isUnset("window.XDomainRequest", iframeWindow), "XDomainRequest is an empty function");
|
89
|
+
ok(that.isUnset("window.alert", iframeWindow), "alert is an empty function");
|
90
|
+
ok(that.isUnset("window.prompt", iframeWindow), "prompt is an empty function");
|
91
|
+
ok(that.isUnset("window.openDatabase", iframeWindow), "window.openDatabase is unset");
|
92
|
+
ok(that.isUnset("window.indexedDB", iframeWindow), "window.indexedDB is unset");
|
93
|
+
ok(that.isUnset("window.postMessage", iframeWindow), "window.openDatabase is unset");
|
94
|
+
|
95
|
+
start();
|
96
|
+
});
|
97
|
+
|
98
|
+
sandbox.insertInto(document.body);
|
99
|
+
});
|
100
|
+
|
101
|
+
|
102
|
+
asyncTest("Security test #2", function() {
|
103
|
+
expect(2);
|
104
|
+
|
105
|
+
var sandbox = new wysihtml5.dom.Sandbox(function() {
|
106
|
+
var html = '<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" onerror="#{script}" onload="try { window.parent._hackedCookie=document.cookie; } catch(e){}; try { window.parent._hackedVariable=1; } catch(e) {}">';
|
107
|
+
sandbox.getDocument().body.innerHTML = html;
|
108
|
+
|
109
|
+
setTimeout(function() {
|
110
|
+
equal(window._hackedCookie || "", "", "Cookie can't be easily stolen");
|
111
|
+
equal(window._hackedVariable || 0, 0, "iFrame has no access to parent");
|
112
|
+
|
113
|
+
start();
|
114
|
+
}, 2000);
|
115
|
+
});
|
116
|
+
|
117
|
+
sandbox.insertInto(document.body);
|
118
|
+
});
|
119
|
+
|
120
|
+
|
121
|
+
asyncTest("Check charset & doctype", function() {
|
122
|
+
expect(3);
|
123
|
+
|
124
|
+
var that = this;
|
125
|
+
|
126
|
+
var sandbox = new wysihtml5.dom.Sandbox(function() {
|
127
|
+
var iframeDocument = sandbox.getDocument(),
|
128
|
+
isQuirksMode = iframeDocument.compatMode == "BackCompat";
|
129
|
+
|
130
|
+
ok(!isQuirksMode, "iFrame isn't in quirks mode");
|
131
|
+
equal(that.getCharset(iframeDocument), that.getCharset(document), "Charset correctly inherited by iframe");
|
132
|
+
|
133
|
+
iframeDocument.body.innerHTML = '<meta charset="iso-8859-1">ü';
|
134
|
+
|
135
|
+
setTimeout(function() {
|
136
|
+
equal(that.getCharset(iframeDocument), that.getCharset(document), "Charset isn't overwritten");
|
137
|
+
start();
|
138
|
+
}, 500);
|
139
|
+
});
|
140
|
+
|
141
|
+
sandbox.insertInto(document.body);
|
142
|
+
});
|
143
|
+
|
144
|
+
|
145
|
+
asyncTest("Check insertion of single stylesheet", function() {
|
146
|
+
expect(1);
|
147
|
+
|
148
|
+
new wysihtml5.dom.Sandbox(function(sandbox) {
|
149
|
+
var doc = sandbox.getDocument();
|
150
|
+
equal(doc.getElementsByTagName("link").length, 1, "Correct amount of stylesheets inserted into the dom tree");
|
151
|
+
start();
|
152
|
+
}, {
|
153
|
+
stylesheets: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/blitzer/jquery-ui.css"
|
154
|
+
}).insertInto(document.body);
|
155
|
+
});
|
156
|
+
|
157
|
+
|
158
|
+
asyncTest("Check insertion of multiple stylesheets", function() {
|
159
|
+
expect(1);
|
160
|
+
|
161
|
+
new wysihtml5.dom.Sandbox(function(sandbox) {
|
162
|
+
var doc = sandbox.getDocument();
|
163
|
+
equal(doc.getElementsByTagName("link").length, 2, "Correct amount of stylesheets inserted into the dom tree");
|
164
|
+
start();
|
165
|
+
}, {
|
166
|
+
stylesheets: [
|
167
|
+
"https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/blitzer/jquery-ui.css",
|
168
|
+
"https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/excite-bike/jquery-ui.css"
|
169
|
+
]
|
170
|
+
}).insertInto(document.body);
|
171
|
+
});
|
172
|
+
|
173
|
+
|
174
|
+
asyncTest("Check X-UA-Compatible", function() {
|
175
|
+
expect(1);
|
176
|
+
|
177
|
+
new wysihtml5.dom.Sandbox(function(sandbox) {
|
178
|
+
var doc = sandbox.getDocument(),
|
179
|
+
docMode = doc.documentMode;
|
180
|
+
|
181
|
+
ok(doc.documentMode === document.documentMode, "iFrame is in in the same document mode as the parent site");
|
182
|
+
start();
|
183
|
+
}).insertInto(document.body);
|
184
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module("wysihtml5.dom.setAttributes", {
|
2
|
+
setup: function() {
|
3
|
+
this.element = document.createElement("div");
|
4
|
+
}
|
5
|
+
});
|
6
|
+
|
7
|
+
test("Basic test", function() {
|
8
|
+
wysihtml5.dom.setAttributes({
|
9
|
+
id: "foo",
|
10
|
+
"class": "bar"
|
11
|
+
}).on(this.element);
|
12
|
+
|
13
|
+
equal(this.element.id, "foo");
|
14
|
+
equal(this.element.className, "bar");
|
15
|
+
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module("wysihtml5.dom.setStyles", {
|
2
|
+
setup: function() {
|
3
|
+
this.element = document.createElement("div");
|
4
|
+
document.body.appendChild(this.element);
|
5
|
+
},
|
6
|
+
|
7
|
+
teardown: function() {
|
8
|
+
this.element.parentNode.removeChild(this.element);
|
9
|
+
}
|
10
|
+
});
|
11
|
+
|
12
|
+
test("Basic test", function() {
|
13
|
+
wysihtml5.dom.setStyles("text-align: right; float: left").on(this.element);
|
14
|
+
equal(wysihtml5.dom.getStyle("text-align").from(this.element), "right");
|
15
|
+
equal(wysihtml5.dom.getStyle("float").from(this.element), "left");
|
16
|
+
|
17
|
+
wysihtml5.dom.setStyles({ "float": "right" }).on(this.element);
|
18
|
+
equal(wysihtml5.dom.getStyle("float").from(this.element), "right");
|
19
|
+
});
|
data/test/editor_test.js
ADDED
@@ -0,0 +1,547 @@
|
|
1
|
+
if (wysihtml5.browser.supported()) {
|
2
|
+
module("wysihtml5.Editor", {
|
3
|
+
setup: function() {
|
4
|
+
wysihtml5.dom.insertCSS([
|
5
|
+
"#wysihtml5-test-textarea { width: 50%; height: 100px; margin-top: 5px; font-style: italic; border: 2px solid red; border-radius: 2px; }",
|
6
|
+
"#wysihtml5-test-textarea:focus { margin-top: 10px; }",
|
7
|
+
"#wysihtml5-test-textarea:disabled { margin-top: 20px; }"
|
8
|
+
]).into(document);
|
9
|
+
|
10
|
+
this.textareaElement = document.createElement("textarea");
|
11
|
+
this.textareaElement.id = "wysihtml5-test-textarea";
|
12
|
+
this.textareaElement.title = "Please enter your foo";
|
13
|
+
this.textareaElement.value = "hey tiff, what's up?";
|
14
|
+
|
15
|
+
this.form = document.createElement("form");
|
16
|
+
this.form.onsubmit = function() { return false; };
|
17
|
+
this.form.appendChild(this.textareaElement);
|
18
|
+
|
19
|
+
this.originalBodyClassName = document.body.className;
|
20
|
+
|
21
|
+
document.body.appendChild(this.form);
|
22
|
+
},
|
23
|
+
|
24
|
+
teardown: function() {
|
25
|
+
var leftover;
|
26
|
+
while (leftover = document.querySelector("iframe.wysihtml5-sandbox, input[name='_wysihtml5_mode']")) {
|
27
|
+
leftover.parentNode.removeChild(leftover);
|
28
|
+
}
|
29
|
+
this.form.parentNode.removeChild(this.form);
|
30
|
+
document.body.className = this.originalBodyClassName;
|
31
|
+
},
|
32
|
+
|
33
|
+
getComposerElement: function() {
|
34
|
+
return this.getIframeElement().contentWindow.document.body;
|
35
|
+
},
|
36
|
+
|
37
|
+
getIframeElement: function() {
|
38
|
+
var iframes = document.querySelectorAll("iframe.wysihtml5-sandbox");
|
39
|
+
return iframes[iframes.length - 1];
|
40
|
+
}
|
41
|
+
});
|
42
|
+
|
43
|
+
asyncTest("Basic test", function() {
|
44
|
+
expect(18);
|
45
|
+
|
46
|
+
var that = this;
|
47
|
+
|
48
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
49
|
+
editor.observe("load", function() {
|
50
|
+
var iframeElement = that.getIframeElement(),
|
51
|
+
composerElement = that.getComposerElement(),
|
52
|
+
textareaElement = that.textareaElement;
|
53
|
+
ok(true, "Load callback triggered");
|
54
|
+
ok(wysihtml5.dom.hasClass(document.body, "wysihtml5-supported"), "<body> received correct class name");
|
55
|
+
equal(textareaElement.style.display, "none", "Textarea not visible");
|
56
|
+
ok(iframeElement.style.display, "", "Editor iFrame is visible");
|
57
|
+
equal(editor.currentView.name, "composer", "Current view is 'composer'");
|
58
|
+
|
59
|
+
// Make textarea visible for a short amount of time, in order to calculate dimensions properly
|
60
|
+
textareaElement.style.display = "block";
|
61
|
+
deepEqual(
|
62
|
+
[iframeElement.offsetHeight, iframeElement.offsetWidth],
|
63
|
+
[textareaElement.offsetHeight, textareaElement.offsetWidth],
|
64
|
+
"Editor has the same dimensions as the original textarea"
|
65
|
+
);
|
66
|
+
textareaElement.style.display = "none";
|
67
|
+
|
68
|
+
var hiddenField = textareaElement.nextSibling;
|
69
|
+
equal(hiddenField.name, "_wysihtml5_mode", "Hidden field has correct name");
|
70
|
+
equal(hiddenField.value, "1", "Hidden field has correct value");
|
71
|
+
equal(hiddenField.type, "hidden", "Hidden field is actually hidden");
|
72
|
+
equal(textareaElement.nextSibling.nextSibling, iframeElement, "Editor iframe is inserted after the textarea");
|
73
|
+
equal(composerElement.getAttribute("contentEditable"), "true", "Body element in iframe is editable");
|
74
|
+
equal(editor.textarea.element, textareaElement, "Textarea correctly available on editor instance");
|
75
|
+
equal(editor.composer.element, composerElement, "contentEditable element available on editor instance");
|
76
|
+
equal(wysihtml5.dom.getStyle("font-style").from(composerElement), "italic", "Correct font-style applied to editor element");
|
77
|
+
equal(wysihtml5.dom.getStyle("width").from(iframeElement), "50%", "Correct width applied to iframe")
|
78
|
+
equal(wysihtml5.dom.getStyle("height").from(iframeElement), "100px", "Correct height applied to iframe")
|
79
|
+
|
80
|
+
if ("borderRadius" in document.createElement("div").style) {
|
81
|
+
expect(19);
|
82
|
+
ok(wysihtml5.dom.getStyle("border-top-right-radius").from(iframeElement).indexOf("2px") !== -1, "border-radius correctly copied");
|
83
|
+
}
|
84
|
+
|
85
|
+
equal(composerElement.innerHTML.toLowerCase(), "hey tiff, what's up?", "Copied the initial textarea value to the editor");
|
86
|
+
ok(wysihtml5.dom.hasClass(composerElement, "wysihtml5-editor"), "Editor element has correct class name");
|
87
|
+
|
88
|
+
start();
|
89
|
+
});
|
90
|
+
});
|
91
|
+
|
92
|
+
|
93
|
+
asyncTest("Check setting of name as class name on iframe and iframe's body", function() {
|
94
|
+
expect(4);
|
95
|
+
|
96
|
+
this.textareaElement.className = "death-star";
|
97
|
+
|
98
|
+
var that = this,
|
99
|
+
name = "star-wars-input",
|
100
|
+
editor = new wysihtml5.Editor(this.textareaElement, { name: "star-wars-input" });
|
101
|
+
|
102
|
+
editor.observe("load", function() {
|
103
|
+
var iframeElement = that.getIframeElement(),
|
104
|
+
composerElement = that.getComposerElement(),
|
105
|
+
textareaElement = that.textareaElement;
|
106
|
+
ok(wysihtml5.dom.hasClass(iframeElement, name), "iFrame has adopted name as className");
|
107
|
+
ok(wysihtml5.dom.hasClass(composerElement, name), "iFrame's body has adopted name as className");
|
108
|
+
ok(wysihtml5.dom.hasClass(composerElement, "death-star"), "iFrame's body has adopted the textarea className");
|
109
|
+
ok(!wysihtml5.dom.hasClass(textareaElement, name), "Textarea has not adopted name as className");
|
110
|
+
start();
|
111
|
+
});
|
112
|
+
});
|
113
|
+
|
114
|
+
|
115
|
+
asyncTest("Check textarea with box-sizing: border-box;", function() {
|
116
|
+
expect(1);
|
117
|
+
|
118
|
+
var that = this;
|
119
|
+
|
120
|
+
wysihtml5.dom.setStyles({
|
121
|
+
MozBoxSizing: "border-box",
|
122
|
+
WebkitBoxSizing: "border-box",
|
123
|
+
MsBoxSizing: "border-box",
|
124
|
+
boxSizing: "border-box"
|
125
|
+
}).on(this.textareaElement);
|
126
|
+
|
127
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
128
|
+
editor.observe("load", function() {
|
129
|
+
// Make textarea visible for a short amount of time, in order to calculate dimensions properly
|
130
|
+
that.textareaElement.style.display = "block";
|
131
|
+
deepEqual(
|
132
|
+
[that.getIframeElement().offsetWidth, that.getIframeElement().offsetHeight],
|
133
|
+
[that.textareaElement.offsetWidth, that.textareaElement.offsetHeight],
|
134
|
+
"Editor has the same dimensions as the original textarea"
|
135
|
+
);
|
136
|
+
that.textareaElement.style.display = "none";
|
137
|
+
|
138
|
+
start();
|
139
|
+
});
|
140
|
+
});
|
141
|
+
|
142
|
+
|
143
|
+
asyncTest("Check whether attributes are copied", function() {
|
144
|
+
expect(1);
|
145
|
+
|
146
|
+
var that = this;
|
147
|
+
|
148
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
149
|
+
editor.observe("load", function() {
|
150
|
+
equal(that.getComposerElement().title, that.textareaElement.title, "Editor got attributes copied over from textarea");
|
151
|
+
start();
|
152
|
+
});
|
153
|
+
});
|
154
|
+
|
155
|
+
|
156
|
+
asyncTest("Check events", function() {
|
157
|
+
expect(8);
|
158
|
+
|
159
|
+
var that = this;
|
160
|
+
|
161
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
162
|
+
|
163
|
+
editor.observe("beforeload", function() {
|
164
|
+
ok(true, "'beforeload' event correctly fired");
|
165
|
+
});
|
166
|
+
|
167
|
+
editor.observe("load", function() {
|
168
|
+
var composerElement = that.getComposerElement(),
|
169
|
+
iframeElement = that.getIframeElement();
|
170
|
+
|
171
|
+
editor.observe("focus", function() {
|
172
|
+
ok(true, "'focus' event correctly fired");
|
173
|
+
});
|
174
|
+
|
175
|
+
editor.observe("blur", function() {
|
176
|
+
ok(true, "'blur' event correctly fired");
|
177
|
+
});
|
178
|
+
|
179
|
+
editor.observe("change", function() {
|
180
|
+
ok(true, "'change' event correctly fired");
|
181
|
+
});
|
182
|
+
|
183
|
+
editor.observe("paste", function() {
|
184
|
+
ok(true, "'paste' event correctly fired");
|
185
|
+
});
|
186
|
+
|
187
|
+
editor.observe("drop", function() {
|
188
|
+
ok(true, "'drop' event correctly fired");
|
189
|
+
});
|
190
|
+
|
191
|
+
editor.observe("custom_event", function() {
|
192
|
+
ok(true, "'custom_event' correctly fired");
|
193
|
+
});
|
194
|
+
|
195
|
+
QUnit.triggerEvent(composerElement, "focus");
|
196
|
+
editor.stopObserving("focus");
|
197
|
+
|
198
|
+
// Modify innerHTML in order to force 'change' event to trigger onblur
|
199
|
+
composerElement.innerHTML = "foobar";
|
200
|
+
QUnit.triggerEvent(composerElement, "blur");
|
201
|
+
QUnit.triggerEvent(composerElement, "focusout");
|
202
|
+
equal(wysihtml5.dom.getStyle("margin-top").from(iframeElement), "5px", ":focus styles are correctly unset");
|
203
|
+
QUnit.triggerEvent(composerElement, "paste");
|
204
|
+
QUnit.triggerEvent(composerElement, "drop");
|
205
|
+
|
206
|
+
editor.fire("custom_event");
|
207
|
+
|
208
|
+
// Delay teardown in order to avoid unwanted js errors caused by a too early removed sandbox iframe
|
209
|
+
// which then causes js errors in Safari 5
|
210
|
+
setTimeout(function() { start(); }, 100);
|
211
|
+
});
|
212
|
+
});
|
213
|
+
|
214
|
+
|
215
|
+
asyncTest("Check sync (basic)", function() {
|
216
|
+
expect(1);
|
217
|
+
|
218
|
+
var that = this;
|
219
|
+
|
220
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
221
|
+
editor.observe("load", function() {
|
222
|
+
var html = "<p>hello foobar, what up?</p>";
|
223
|
+
that.getComposerElement().innerHTML = html;
|
224
|
+
|
225
|
+
setTimeout(function() {
|
226
|
+
equal(that.textareaElement.value.toLowerCase(), html.toLowerCase(), "Editor content got correctly copied over to original textarea");
|
227
|
+
start();
|
228
|
+
}, 500);
|
229
|
+
});
|
230
|
+
});
|
231
|
+
|
232
|
+
|
233
|
+
asyncTest("Check sync (advanced)", function() {
|
234
|
+
expect(5);
|
235
|
+
|
236
|
+
var that = this;
|
237
|
+
|
238
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
239
|
+
parserRules: { tags: { "strong": true } }
|
240
|
+
});
|
241
|
+
|
242
|
+
editor.observe("load", function() {
|
243
|
+
var html = "<strong>timmay!</strong>",
|
244
|
+
composerElement = that.getComposerElement();
|
245
|
+
composerElement.innerHTML = html;
|
246
|
+
|
247
|
+
setTimeout(function() {
|
248
|
+
equal(that.textareaElement.value.toLowerCase(), html.toLowerCase(), "Editor content got correctly copied over to original textarea");
|
249
|
+
|
250
|
+
composerElement.innerHTML = "<font color=\"red\">hey </font><strong>helen!</strong>";
|
251
|
+
editor.fire("change_view", "textarea");
|
252
|
+
equal(that.textareaElement.value.toLowerCase(), "hey <strong>helen!</strong>", "Editor got immediately copied over to textarea after switching the view");
|
253
|
+
|
254
|
+
that.textareaElement.value = "<i>hey </i><strong>richard!</strong>";
|
255
|
+
editor.fire("change_view", "composer");
|
256
|
+
equal(composerElement.innerHTML.toLowerCase(), "hey <strong>richard!</strong>", "Textarea sanitized and copied over it's value to the editor after switch");
|
257
|
+
|
258
|
+
composerElement.innerHTML = "<i>hey </i><strong>timmay!</strong>";
|
259
|
+
QUnit.triggerEvent(that.form, "submit");
|
260
|
+
equal(that.textareaElement.value.toLowerCase(), "hey <strong>timmay!</strong>", "Textarea gets the sanitized content of the editor onsubmit");
|
261
|
+
|
262
|
+
setTimeout(function() {
|
263
|
+
that.form.reset();
|
264
|
+
|
265
|
+
// Timeout needed since reset() isn't executed synchronously
|
266
|
+
setTimeout(function() {
|
267
|
+
equal(wysihtml5.dom.getTextContent(composerElement), "", "Editor is empty after reset");
|
268
|
+
start();
|
269
|
+
}, 100);
|
270
|
+
|
271
|
+
}, 500);
|
272
|
+
|
273
|
+
}, 500);
|
274
|
+
|
275
|
+
});
|
276
|
+
});
|
277
|
+
|
278
|
+
|
279
|
+
asyncTest("Check placeholder", function() {
|
280
|
+
expect(13);
|
281
|
+
|
282
|
+
var that = this;
|
283
|
+
|
284
|
+
var placeholderText = "enter text ...";
|
285
|
+
this.textareaElement.value = "";
|
286
|
+
this.textareaElement.setAttribute("placeholder", "enter text ...");
|
287
|
+
|
288
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
289
|
+
editor.observe("load", function() {
|
290
|
+
var composerElement = that.getComposerElement();
|
291
|
+
equal(wysihtml5.dom.getTextContent(composerElement), placeholderText, "Placeholder text correctly copied into textarea");
|
292
|
+
ok(wysihtml5.dom.hasClass(composerElement, "placeholder"), "Editor got 'placeholder' css class");
|
293
|
+
ok(editor.hasPlaceholderSet(), "'hasPlaceholderSet' returns correct value when placeholder is actually set");
|
294
|
+
editor.fire("focus:composer");
|
295
|
+
equal(wysihtml5.dom.getTextContent(composerElement), "", "Editor is empty after focus");
|
296
|
+
ok(!wysihtml5.dom.hasClass(composerElement, "placeholder"), "Editor hasn't got 'placeholder' css class");
|
297
|
+
ok(!editor.hasPlaceholderSet(), "'hasPlaceholderSet' returns correct value when placeholder isn't actually set");
|
298
|
+
editor.fire("blur:composer");
|
299
|
+
equal(wysihtml5.dom.getTextContent(composerElement), placeholderText, "Editor restored placeholder text after unfocus");
|
300
|
+
editor.fire("focus:composer");
|
301
|
+
equal(wysihtml5.dom.getTextContent(composerElement), "");
|
302
|
+
composerElement.innerHTML = "some content";
|
303
|
+
editor.fire("blur:composer");
|
304
|
+
equal(wysihtml5.dom.getTextContent(composerElement), "some content");
|
305
|
+
ok(!wysihtml5.dom.hasClass(composerElement, "placeholder"), "Editor hasn't got 'placeholder' css class");
|
306
|
+
editor.fire("focus:composer");
|
307
|
+
// Following html causes innerText and textContent to report an empty string
|
308
|
+
var html = '<img>';
|
309
|
+
composerElement.innerHTML = html;
|
310
|
+
editor.fire("blur:composer");
|
311
|
+
equal(composerElement.innerHTML.toLowerCase(), html, "HTML hasn't been cleared even though the innerText and textContent properties indicate empty content.");
|
312
|
+
ok(!wysihtml5.dom.hasClass(composerElement, "placeholder"), "Editor hasn't got 'placeholder' css class");
|
313
|
+
|
314
|
+
setTimeout(function() {
|
315
|
+
that.form.reset();
|
316
|
+
|
317
|
+
// Timeout needed since reset() isn't executed synchronously
|
318
|
+
setTimeout(function() {
|
319
|
+
equal(wysihtml5.dom.getTextContent(composerElement), placeholderText, "After form reset the editor has the placeholder as content");
|
320
|
+
start();
|
321
|
+
}, 100);
|
322
|
+
|
323
|
+
}, 500);
|
324
|
+
});
|
325
|
+
});
|
326
|
+
|
327
|
+
|
328
|
+
asyncTest("Check public api", function() {
|
329
|
+
expect(13);
|
330
|
+
|
331
|
+
var that = this;
|
332
|
+
|
333
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
334
|
+
parserRules: { tags: { p: { rename_tag: "div" } } },
|
335
|
+
bodyClassName: "editor-is-supported",
|
336
|
+
composerClassName: "editor"
|
337
|
+
});
|
338
|
+
|
339
|
+
editor.observe("load", function() {
|
340
|
+
ok(editor.isCompatible(), "isCompatible() returns correct value");
|
341
|
+
ok(wysihtml5.dom.hasClass(document.body, "editor-is-supported"), "<body> received correct class name");
|
342
|
+
|
343
|
+
var composerElement = that.getComposerElement();
|
344
|
+
editor.clear();
|
345
|
+
equal(wysihtml5.dom.getTextContent(composerElement), "", "Editor empty after calling 'clear'");
|
346
|
+
ok(wysihtml5.dom.hasClass(composerElement, "editor"), "Composer element has correct class name");
|
347
|
+
|
348
|
+
var html = "hello <strong>foo</strong>!";
|
349
|
+
editor.setValue(html);
|
350
|
+
equal(composerElement.innerHTML.toLowerCase(), html, "Editor content correctly set after calling 'setValue'");
|
351
|
+
ok(!editor.isEmpty(), "'isEmpty' returns correct value when the composer element isn't actually empty");
|
352
|
+
|
353
|
+
var value = editor.getValue();
|
354
|
+
equal(value.toLowerCase(), html, "Editor content correctly returned after calling 'getValue'");
|
355
|
+
|
356
|
+
editor.clear();
|
357
|
+
value = editor.getValue();
|
358
|
+
equal(value, "");
|
359
|
+
ok(editor.isEmpty(), "'isEmpty' returns correct value when the composer element is actually empty");
|
360
|
+
|
361
|
+
equal(editor.parse("<p>foo</p>").toLowerCase(), "<div>foo</div>", "'parse' returns correct value");
|
362
|
+
|
363
|
+
// Check disable/enable
|
364
|
+
editor.disable();
|
365
|
+
ok(!composerElement.getAttribute("contentEditable"), "When disabled the composer hasn't the contentEditable attribute");
|
366
|
+
|
367
|
+
editor.enable();
|
368
|
+
equal(composerElement.getAttribute("contentEditable"), "true", "After enabling the editor the contentEditable property is true");
|
369
|
+
ok(!composerElement.getAttribute("disabled"), "After enabling the disabled attribute is unset");
|
370
|
+
|
371
|
+
start();
|
372
|
+
});
|
373
|
+
});
|
374
|
+
|
375
|
+
|
376
|
+
asyncTest("Parser (default parser method with parserRules as object", function() {
|
377
|
+
expect(2);
|
378
|
+
|
379
|
+
var parserRules = {
|
380
|
+
tags: {
|
381
|
+
div: true,
|
382
|
+
p: { rename_tag: "div" },
|
383
|
+
span: true,
|
384
|
+
script: undefined
|
385
|
+
}
|
386
|
+
};
|
387
|
+
|
388
|
+
var input = "<p>foobar</p>",
|
389
|
+
output = "<div>foobar</div>";
|
390
|
+
|
391
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
392
|
+
parserRules: parserRules
|
393
|
+
});
|
394
|
+
|
395
|
+
editor.observe("load", function() {
|
396
|
+
equal(editor.config.parserRules, parserRules, "Parser rules correctly set on config object");
|
397
|
+
// Invoke parsing via second parameter of setValue()
|
398
|
+
editor.setValue(input, true);
|
399
|
+
equal(editor.getValue().toLowerCase(), output, "HTML got correctly parsed within setValue()");
|
400
|
+
start();
|
401
|
+
});
|
402
|
+
});
|
403
|
+
|
404
|
+
|
405
|
+
asyncTest("Parser (custom parser method with parserRules as object", function() {
|
406
|
+
expect(7);
|
407
|
+
|
408
|
+
var that = this,
|
409
|
+
parserRules = { script: undefined },
|
410
|
+
input = this.textareaElement.value,
|
411
|
+
output = input;
|
412
|
+
|
413
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
414
|
+
parserRules: parserRules,
|
415
|
+
parser: function(html, rules, context) {
|
416
|
+
equal(html.toLowerCase(), input, "HTML passed into parser is equal to the one which just got inserted");
|
417
|
+
equal(rules, parserRules, "Rules passed into parser are equal to those given to the editor");
|
418
|
+
equal(context, that.getIframeElement().contentWindow.document, "Context passed into parser is equal the document object of the editor's iframe");
|
419
|
+
return html.replace(/\<script\>.*?\<\/script\>/gi, "");
|
420
|
+
}
|
421
|
+
});
|
422
|
+
|
423
|
+
editor.observe("load", function() {
|
424
|
+
input = "<p>foobar</p><script>alert(1);</script>";
|
425
|
+
output = "<p>foobar</p>";
|
426
|
+
|
427
|
+
// Invoke parsing via second parameter of setValue()
|
428
|
+
editor.setValue(input, true);
|
429
|
+
equal(editor.getValue().toLowerCase(), output, "HTML got correctly parsed within setValue()");
|
430
|
+
|
431
|
+
start();
|
432
|
+
});
|
433
|
+
});
|
434
|
+
|
435
|
+
|
436
|
+
asyncTest("Inserting an element which causes the textContent/innerText of the contentEditable element to be empty works correctly", function() {
|
437
|
+
expect(2);
|
438
|
+
|
439
|
+
var that = this;
|
440
|
+
|
441
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
442
|
+
editor.observe("load", function() {
|
443
|
+
var html = '<img>',
|
444
|
+
composerElement = that.getComposerElement(),
|
445
|
+
textareaElement = that.textareaElement;
|
446
|
+
composerElement.innerHTML = html;
|
447
|
+
|
448
|
+
// Fire events that could cause a change in the composer
|
449
|
+
QUnit.triggerEvent(composerElement, "keypress");
|
450
|
+
QUnit.triggerEvent(composerElement, "keyup");
|
451
|
+
QUnit.triggerEvent(composerElement, "cut");
|
452
|
+
QUnit.triggerEvent(composerElement, "blur");
|
453
|
+
|
454
|
+
setTimeout(function() {
|
455
|
+
equal(composerElement.innerHTML.toLowerCase(), html, "Composer still has correct content");
|
456
|
+
equal(textareaElement.value.toLowerCase(), html, "Textarea got correct value");
|
457
|
+
start();
|
458
|
+
}, 500);
|
459
|
+
});
|
460
|
+
});
|
461
|
+
|
462
|
+
|
463
|
+
asyncTest("Check for stylesheets", function() {
|
464
|
+
expect(5);
|
465
|
+
|
466
|
+
var that = this;
|
467
|
+
|
468
|
+
var stylesheetUrls = [
|
469
|
+
"http://yui.yahooapis.com/2.8.2r1/build/reset/reset-min.css",
|
470
|
+
"http://yui.yahooapis.com/2.8.0/build/reset/reset-min.css"
|
471
|
+
];
|
472
|
+
|
473
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
474
|
+
stylesheets: stylesheetUrls
|
475
|
+
});
|
476
|
+
|
477
|
+
editor.observe("load", function() {
|
478
|
+
var iframeElement = that.getIframeElement(),
|
479
|
+
iframeDoc = iframeElement.contentWindow.document,
|
480
|
+
linkElements = iframeDoc.getElementsByTagName("link");
|
481
|
+
equal(linkElements.length, 2, "Correct amount of stylesheets inserted into the dom tree");
|
482
|
+
equal(linkElements[0].getAttribute("href"), stylesheetUrls[0]);
|
483
|
+
equal(linkElements[0].getAttribute("rel"), "stylesheet");
|
484
|
+
equal(linkElements[1].getAttribute("href"), stylesheetUrls[1]);
|
485
|
+
equal(linkElements[1].getAttribute("rel"), "stylesheet");
|
486
|
+
start();
|
487
|
+
});
|
488
|
+
});
|
489
|
+
|
490
|
+
|
491
|
+
asyncTest("Check config.supportTouchDevices = false", function() {
|
492
|
+
expect(2);
|
493
|
+
|
494
|
+
var that = this;
|
495
|
+
|
496
|
+
var originalIsTouchDevice = wysihtml5.browser.isTouchDevice;
|
497
|
+
wysihtml5.browser.isTouchDevice = function() { return true; };
|
498
|
+
|
499
|
+
var editor = new wysihtml5.Editor(this.textareaElement, {
|
500
|
+
supportTouchDevices: false
|
501
|
+
});
|
502
|
+
|
503
|
+
editor.observe("load", function() {
|
504
|
+
ok(!that.getIframeElement(), "No editor iframe has been inserted");
|
505
|
+
equal(that.textareaElement.style.display, "", "Textarea is visible");
|
506
|
+
|
507
|
+
wysihtml5.browser.isTouchDevice = originalIsTouchDevice;
|
508
|
+
|
509
|
+
start();
|
510
|
+
});
|
511
|
+
});
|
512
|
+
|
513
|
+
asyncTest("Check whether everything works when the textarea is not within a form", function() {
|
514
|
+
expect(3);
|
515
|
+
|
516
|
+
var textareaElement = document.createElement("textarea");
|
517
|
+
document.body.appendChild(textareaElement);
|
518
|
+
var editor = new wysihtml5.Editor(textareaElement);
|
519
|
+
|
520
|
+
var that = this;
|
521
|
+
editor.observe("load", function() {
|
522
|
+
ok(!document.querySelector("input[name='_wysihtml5_mode']"), "No hidden _wysihtml5_mode input has been created");
|
523
|
+
ok(that.getIframeElement(), "Editor's iframe has been created");
|
524
|
+
equal(textareaElement.style.display, "none", "Textarea is not visible");
|
525
|
+
textareaElement.parentNode.removeChild(textareaElement);
|
526
|
+
|
527
|
+
start();
|
528
|
+
});
|
529
|
+
});
|
530
|
+
|
531
|
+
asyncTest("Test disabled textarea", function() {
|
532
|
+
expect(2);
|
533
|
+
|
534
|
+
this.textareaElement.disabled = true;
|
535
|
+
var that = this;
|
536
|
+
|
537
|
+
var editor = new wysihtml5.Editor(this.textareaElement);
|
538
|
+
|
539
|
+
editor.on("load", function() {
|
540
|
+
var iframeElement = that.getIframeElement(),
|
541
|
+
composerElement = that.getComposerElement();
|
542
|
+
equal(wysihtml5.dom.getStyle("margin-top").from(iframeElement), "20px", "Correct :disabled styles applied");
|
543
|
+
ok(!composerElement.hasAttribute("contentEditable"), "Editor is unfocusable");
|
544
|
+
start();
|
545
|
+
});
|
546
|
+
});
|
547
|
+
}
|