This repository has been archived by the owner on Mar 8, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 998
/
formatBlock.js
222 lines (197 loc) · 7.41 KB
/
formatBlock.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
(function(wysihtml5) {
var dom = wysihtml5.dom,
// Following elements are grouped
// when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
// instead of creating a H4 within a H1 which would result in semantically invalid html
BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "BLOCKQUOTE", "DIV"];
/**
* Remove similiar classes (based on classRegExp)
* and add the desired class name
*/
function _addClass(element, className, classRegExp) {
if (element.className) {
_removeClass(element, classRegExp);
element.className += " " + className;
} else {
element.className = className;
}
}
function _removeClass(element, classRegExp) {
element.className = element.className.replace(classRegExp, "");
}
/**
* Check whether given node is a text node and whether it's empty
*/
function _isBlankTextNode(node) {
return node.nodeType === wysihtml5.TEXT_NODE && !wysihtml5.lang.string(node.data).trim();
}
/**
* Returns previous sibling node that is not a blank text node
*/
function _getPreviousSiblingThatIsNotBlank(node) {
var previousSibling = node.previousSibling;
while (previousSibling && _isBlankTextNode(previousSibling)) {
previousSibling = previousSibling.previousSibling;
}
return previousSibling;
}
/**
* Returns next sibling node that is not a blank text node
*/
function _getNextSiblingThatIsNotBlank(node) {
var nextSibling = node.nextSibling;
while (nextSibling && _isBlankTextNode(nextSibling)) {
nextSibling = nextSibling.nextSibling;
}
return nextSibling;
}
/**
* Adds line breaks before and after the given node if the previous and next siblings
* aren't already causing a visual line break (block element or <br>)
*/
function _addLineBreakBeforeAndAfter(node) {
var doc = node.ownerDocument,
nextSibling = _getNextSiblingThatIsNotBlank(node),
previousSibling = _getPreviousSiblingThatIsNotBlank(node);
if (nextSibling && !_isLineBreakOrBlockElement(nextSibling)) {
node.parentNode.insertBefore(doc.createElement("br"), nextSibling);
}
if (previousSibling && !_isLineBreakOrBlockElement(previousSibling)) {
node.parentNode.insertBefore(doc.createElement("br"), node);
}
}
/**
* Removes line breaks before and after the given node
*/
function _removeLineBreakBeforeAndAfter(node) {
var nextSibling = _getNextSiblingThatIsNotBlank(node),
previousSibling = _getPreviousSiblingThatIsNotBlank(node);
if (nextSibling && _isLineBreak(nextSibling)) {
nextSibling.parentNode.removeChild(nextSibling);
}
if (previousSibling && _isLineBreak(previousSibling)) {
previousSibling.parentNode.removeChild(previousSibling);
}
}
function _removeLastChildIfLineBreak(node) {
var lastChild = node.lastChild;
if (lastChild && _isLineBreak(lastChild)) {
lastChild.parentNode.removeChild(lastChild);
}
}
function _isLineBreak(node) {
return node.nodeName === "BR";
}
/**
* Checks whether the elment causes a visual line break
* (<br> or block elements)
*/
function _isLineBreakOrBlockElement(element) {
if (_isLineBreak(element)) {
return true;
}
if (dom.getStyle("display").from(element) === "block") {
return true;
}
return false;
}
/**
* Execute native query command
* and if necessary modify the inserted node's className
*/
function _execCommand(doc, command, nodeName, className) {
if (className) {
var eventListener = dom.observe(doc, "DOMNodeInserted", function(event) {
var target = event.target,
displayStyle;
if (target.nodeType !== wysihtml5.ELEMENT_NODE) {
return;
}
displayStyle = dom.getStyle("display").from(target);
if (displayStyle.substr(0, 6) !== "inline") {
// Make sure that only block elements receive the given class
target.className += " " + className;
}
});
}
doc.execCommand(command, false, nodeName);
if (eventListener) {
eventListener.stop();
}
}
function _selectLineAndWrap(composer, element) {
composer.selection.selectLine();
composer.selection.surround(element);
_removeLineBreakBeforeAndAfter(element);
_removeLastChildIfLineBreak(element);
composer.selection.selectNode(element, wysihtml5.browser.displaysCaretInEmptyContentEditableCorrectly());
}
function _hasClasses(element) {
return !!wysihtml5.lang.string(element.className).trim();
}
wysihtml5.commands.formatBlock = {
exec: function(composer, command, nodeName, className, classRegExp) {
var doc = composer.doc,
blockElement = this.state(composer, command, nodeName, className, classRegExp),
useLineBreaks = composer.config.useLineBreaks,
defaultNodeName = useLineBreaks ? "DIV" : "P",
selectedNode;
nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
if (blockElement) {
composer.selection.executeAndRestoreSimple(function() {
if (classRegExp) {
_removeClass(blockElement, classRegExp);
}
var hasClasses = _hasClasses(blockElement);
if (!hasClasses && (useLineBreaks || nodeName === "P")) {
// Insert a line break afterwards and beforewards when there are siblings
// that are not of type line break or block element
_addLineBreakBeforeAndAfter(blockElement);
dom.replaceWithChildNodes(blockElement);
} else {
// Make sure that styling is kept by renaming the element to a <div> or <p> and copying over the class name
dom.renameElement(blockElement, nodeName === "P" ? "DIV" : defaultNodeName);
}
});
return;
}
// Find similiar block element and rename it (<h2 class="foo"></h2> => <h1 class="foo"></h1>)
if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
selectedNode = composer.selection.getSelectedNode();
blockElement = dom.getParentElement(selectedNode, {
nodeName: BLOCK_ELEMENTS_GROUP
});
if (blockElement) {
composer.selection.executeAndRestore(function() {
// Rename current block element to new block element and add class
if (nodeName) {
blockElement = dom.renameElement(blockElement, nodeName);
}
if (className) {
_addClass(blockElement, className, classRegExp);
}
});
return;
}
}
if (composer.commands.support(command)) {
_execCommand(doc, command, nodeName || defaultNodeName, className);
return;
}
blockElement = doc.createElement(nodeName || defaultNodeName);
if (className) {
blockElement.className = className;
}
_selectLineAndWrap(composer, blockElement);
},
state: function(composer, command, nodeName, className, classRegExp) {
nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
var selectedNode = composer.selection.getSelectedNode();
return dom.getParentElement(selectedNode, {
nodeName: nodeName,
className: className,
classRegExp: classRegExp
});
}
};
})(wysihtml5);