rangy-rails 1.3alpha.804.0 → 1.3.1.pre.dev
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/lib/rangy-rails/version.rb +1 -1
- data/vendor/assets/javascripts/rangy-core.js +3157 -3050
- data/vendor/assets/javascripts/rangy-cssclassapplier.js +908 -790
- data/vendor/assets/javascripts/rangy-highlighter.js +550 -429
- data/vendor/assets/javascripts/rangy-selectionsaverestore.js +199 -182
- data/vendor/assets/javascripts/rangy-serializer.js +255 -236
- data/vendor/assets/javascripts/rangy-textrange.js +1565 -1547
- metadata +3 -5
- data/vendor/assets/javascripts/rangy-position.js +0 -524
@@ -3,983 +3,1101 @@
|
|
3
3
|
* Adds, removes and toggles classes on Ranges and Selections
|
4
4
|
*
|
5
5
|
* Part of Rangy, a cross-browser JavaScript range and selection library
|
6
|
-
*
|
6
|
+
* https://github.com/timdown/rangy
|
7
7
|
*
|
8
8
|
* Depends on Rangy core.
|
9
9
|
*
|
10
|
-
* Copyright
|
10
|
+
* Copyright 2015, Tim Down
|
11
11
|
* Licensed under the MIT license.
|
12
|
-
* Version: 1.
|
13
|
-
* Build date:
|
12
|
+
* Version: 1.3.1-dev
|
13
|
+
* Build date: 20 May 2015
|
14
14
|
*/
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
(function(factory, root) {
|
16
|
+
if (typeof define == "function" && define.amd) {
|
17
|
+
// AMD. Register as an anonymous module with a dependency on Rangy.
|
18
|
+
define(["./rangy-core"], factory);
|
19
|
+
} else if (typeof module != "undefined" && typeof exports == "object") {
|
20
|
+
// Node/CommonJS style
|
21
|
+
module.exports = factory( require("rangy") );
|
22
|
+
} else {
|
23
|
+
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
|
24
|
+
factory(root.rangy);
|
25
|
+
}
|
26
|
+
})(function(rangy) {
|
27
|
+
rangy.createModule("ClassApplier", ["WrappedSelection"], function(api, module) {
|
28
|
+
var dom = api.dom;
|
29
|
+
var DomPosition = dom.DomPosition;
|
30
|
+
var contains = dom.arrayContains;
|
31
|
+
var util = api.util;
|
32
|
+
var forEach = util.forEach;
|
33
|
+
|
34
|
+
|
35
|
+
var defaultTagName = "span";
|
36
|
+
var createElementNSSupported = util.isHostMethod(document, "createElementNS");
|
37
|
+
|
38
|
+
function each(obj, func) {
|
39
|
+
for (var i in obj) {
|
40
|
+
if (obj.hasOwnProperty(i)) {
|
41
|
+
if (func(i, obj[i]) === false) {
|
42
|
+
return false;
|
43
|
+
}
|
28
44
|
}
|
29
45
|
}
|
46
|
+
return true;
|
30
47
|
}
|
31
|
-
return true;
|
32
|
-
}
|
33
|
-
|
34
|
-
function trim(str) {
|
35
|
-
return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
|
36
|
-
}
|
37
|
-
|
38
|
-
function hasClass(el, cssClass) {
|
39
|
-
return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
|
40
|
-
}
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
if (!hasClass(el, cssClass)) {
|
45
|
-
el.className += " " + cssClass;
|
46
|
-
}
|
47
|
-
} else {
|
48
|
-
el.className = cssClass;
|
49
|
+
function trim(str) {
|
50
|
+
return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
|
49
51
|
}
|
50
|
-
}
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
|
53
|
+
function classNameContainsClass(fullClassName, className) {
|
54
|
+
return !!fullClassName && new RegExp("(?:^|\\s)" + className + "(?:\\s|$)").test(fullClassName);
|
55
55
|
}
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
// Inefficient, inelegant nonsense for IE's svg element, which has no classList and non-HTML className implementation
|
58
|
+
function hasClass(el, className) {
|
59
|
+
if (typeof el.classList == "object") {
|
60
|
+
return el.classList.contains(className);
|
61
|
+
} else {
|
62
|
+
var classNameSupported = (typeof el.className == "string");
|
63
|
+
var elClass = classNameSupported ? el.className : el.getAttribute("class");
|
64
|
+
return classNameContainsClass(elClass, className);
|
60
65
|
}
|
61
|
-
}
|
62
|
-
})();
|
63
|
-
|
64
|
-
function sortClassName(className) {
|
65
|
-
return className.split(/\s+/).sort().join(" ");
|
66
|
-
}
|
66
|
+
}
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
function addClass(el, className) {
|
69
|
+
if (typeof el.classList == "object") {
|
70
|
+
el.classList.add(className);
|
71
|
+
} else {
|
72
|
+
var classNameSupported = (typeof el.className == "string");
|
73
|
+
var elClass = classNameSupported ? el.className : el.getAttribute("class");
|
74
|
+
if (elClass) {
|
75
|
+
if (!classNameContainsClass(elClass, className)) {
|
76
|
+
elClass += " " + className;
|
77
|
+
}
|
78
|
+
} else {
|
79
|
+
elClass = className;
|
80
|
+
}
|
81
|
+
if (classNameSupported) {
|
82
|
+
el.className = elClass;
|
83
|
+
} else {
|
84
|
+
el.setAttribute("class", elClass);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
71
88
|
|
72
|
-
|
73
|
-
|
74
|
-
|
89
|
+
var removeClass = (function() {
|
90
|
+
function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
|
91
|
+
return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
|
92
|
+
}
|
75
93
|
|
76
|
-
|
77
|
-
|
78
|
-
|
94
|
+
return function(el, className) {
|
95
|
+
if (typeof el.classList == "object") {
|
96
|
+
el.classList.remove(className);
|
97
|
+
} else {
|
98
|
+
var classNameSupported = (typeof el.className == "string");
|
99
|
+
var elClass = classNameSupported ? el.className : el.getAttribute("class");
|
100
|
+
elClass = elClass.replace(new RegExp("(^|\\s)" + className + "(\\s|$)"), replacer);
|
101
|
+
if (classNameSupported) {
|
102
|
+
el.className = elClass;
|
103
|
+
} else {
|
104
|
+
el.setAttribute("class", elClass);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
};
|
108
|
+
})();
|
79
109
|
|
80
|
-
|
81
|
-
|
110
|
+
function getClass(el) {
|
111
|
+
var classNameSupported = (typeof el.className == "string");
|
112
|
+
return classNameSupported ? el.className : el.getAttribute("class");
|
82
113
|
}
|
83
114
|
|
84
|
-
|
85
|
-
|
86
|
-
newOffset += newIndex - oldIndex;
|
115
|
+
function sortClassName(className) {
|
116
|
+
return className && className.split(/\s+/).sort().join(" ");
|
87
117
|
}
|
88
118
|
|
89
|
-
|
90
|
-
|
119
|
+
function getSortedClassName(el) {
|
120
|
+
return sortClassName( getClass(el) );
|
91
121
|
}
|
92
122
|
|
93
|
-
|
94
|
-
|
95
|
-
}
|
96
|
-
|
97
|
-
function movePositionWhenRemovingNode(position, parentNode, index) {
|
98
|
-
if (position.node == parentNode && position.offset > index) {
|
99
|
-
--position.offset;
|
123
|
+
function haveSameClasses(el1, el2) {
|
124
|
+
return getSortedClassName(el1) == getSortedClassName(el2);
|
100
125
|
}
|
101
|
-
}
|
102
126
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
127
|
+
function hasAllClasses(el, className) {
|
128
|
+
var classes = className.split(/\s+/);
|
129
|
+
for (var i = 0, len = classes.length; i < len; ++i) {
|
130
|
+
if (!hasClass(el, trim(classes[i]))) {
|
131
|
+
return false;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
return true;
|
107
135
|
}
|
108
136
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
for (var i = 0, position; position = positionsToPreserve[i++]; ) {
|
113
|
-
movePosition(position, oldParent, oldIndex, newParent, newIndex);
|
137
|
+
function canTextBeStyled(textNode) {
|
138
|
+
var parent = textNode.parentNode;
|
139
|
+
return (parent && parent.nodeType == 1 && !/^(textarea|style|script|select|iframe)$/i.test(parent.nodeName));
|
114
140
|
}
|
115
141
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
} else {
|
120
|
-
newParent.insertBefore(node, newParent.childNodes[newIndex]);
|
121
|
-
}
|
122
|
-
}
|
123
|
-
|
124
|
-
function removePreservingPositions(node, positionsToPreserve) {
|
142
|
+
function movePosition(position, oldParent, oldIndex, newParent, newIndex) {
|
143
|
+
var posNode = position.node, posOffset = position.offset;
|
144
|
+
var newNode = posNode, newOffset = posOffset;
|
125
145
|
|
126
|
-
|
127
|
-
|
146
|
+
if (posNode == newParent && posOffset > newIndex) {
|
147
|
+
++newOffset;
|
148
|
+
}
|
128
149
|
|
129
|
-
|
130
|
-
|
131
|
-
|
150
|
+
if (posNode == oldParent && (posOffset == oldIndex || posOffset == oldIndex + 1)) {
|
151
|
+
newNode = newParent;
|
152
|
+
newOffset += newIndex - oldIndex;
|
153
|
+
}
|
132
154
|
|
133
|
-
|
134
|
-
|
155
|
+
if (posNode == oldParent && posOffset > oldIndex + 1) {
|
156
|
+
--newOffset;
|
157
|
+
}
|
135
158
|
|
136
|
-
|
137
|
-
|
138
|
-
while ( (child = node.firstChild) ) {
|
139
|
-
movePreservingPositions(child, newParent, newIndex++, positionsToPreserve);
|
140
|
-
children.push(child);
|
159
|
+
position.node = newNode;
|
160
|
+
position.offset = newOffset;
|
141
161
|
}
|
142
|
-
if (removeNode) {
|
143
|
-
node.parentNode.removeChild(node);
|
144
|
-
}
|
145
|
-
return children;
|
146
|
-
}
|
147
162
|
|
148
|
-
|
149
|
-
|
150
|
-
|
163
|
+
function movePositionWhenRemovingNode(position, parentNode, index) {
|
164
|
+
if (position.node == parentNode && position.offset > index) {
|
165
|
+
--position.offset;
|
166
|
+
}
|
167
|
+
}
|
151
168
|
|
152
|
-
|
153
|
-
|
154
|
-
|
169
|
+
function movePreservingPositions(node, newParent, newIndex, positionsToPreserve) {
|
170
|
+
// For convenience, allow newIndex to be -1 to mean "insert at the end".
|
171
|
+
if (newIndex == -1) {
|
172
|
+
newIndex = newParent.childNodes.length;
|
173
|
+
}
|
155
174
|
|
156
|
-
|
157
|
-
|
158
|
-
textNodeRange.detach();
|
175
|
+
var oldParent = node.parentNode;
|
176
|
+
var oldIndex = dom.getNodeIndex(node);
|
159
177
|
|
160
|
-
|
161
|
-
|
178
|
+
forEach(positionsToPreserve, function(position) {
|
179
|
+
movePosition(position, oldParent, oldIndex, newParent, newIndex);
|
180
|
+
});
|
162
181
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
var start = 0, node;
|
170
|
-
while ( (node = nodes[start]) && !rangeSelectsAnyText(range, node) ) {
|
171
|
-
++start;
|
182
|
+
// Now actually move the node.
|
183
|
+
if (newParent.childNodes.length == newIndex) {
|
184
|
+
newParent.appendChild(node);
|
185
|
+
} else {
|
186
|
+
newParent.insertBefore(node, newParent.childNodes[newIndex]);
|
187
|
+
}
|
172
188
|
}
|
173
189
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
190
|
+
function removePreservingPositions(node, positionsToPreserve) {
|
191
|
+
|
192
|
+
var oldParent = node.parentNode;
|
193
|
+
var oldIndex = dom.getNodeIndex(node);
|
194
|
+
|
195
|
+
forEach(positionsToPreserve, function(position) {
|
196
|
+
movePositionWhenRemovingNode(position, oldParent, oldIndex);
|
197
|
+
});
|
198
|
+
|
199
|
+
dom.removeNode(node);
|
178
200
|
}
|
179
|
-
|
180
|
-
return nodes.slice(start, end + 1);
|
181
|
-
}
|
182
201
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
if (name != "class") {
|
189
|
-
attr2 = el2.attributes.getNamedItem(name);
|
190
|
-
if ( (attr1 === null) != (attr2 === null) ) return false;
|
191
|
-
if (attr1.specified != attr2.specified) return false;
|
192
|
-
if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
|
202
|
+
function moveChildrenPreservingPositions(node, newParent, newIndex, removeNode, positionsToPreserve) {
|
203
|
+
var child, children = [];
|
204
|
+
while ( (child = node.firstChild) ) {
|
205
|
+
movePreservingPositions(child, newParent, newIndex++, positionsToPreserve);
|
206
|
+
children.push(child);
|
193
207
|
}
|
208
|
+
if (removeNode) {
|
209
|
+
removePreservingPositions(node, positionsToPreserve);
|
210
|
+
}
|
211
|
+
return children;
|
194
212
|
}
|
195
|
-
return true;
|
196
|
-
}
|
197
213
|
|
198
|
-
|
199
|
-
|
200
|
-
attrName = el.attributes[i].name;
|
201
|
-
if ( !(exceptions && contains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
|
202
|
-
return true;
|
203
|
-
}
|
214
|
+
function replaceWithOwnChildrenPreservingPositions(element, positionsToPreserve) {
|
215
|
+
return moveChildrenPreservingPositions(element, element.parentNode, dom.getNodeIndex(element), true, positionsToPreserve);
|
204
216
|
}
|
205
|
-
return false;
|
206
|
-
}
|
207
217
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
if (!elementHasProperties(el[p], propValue)) {
|
212
|
-
return false;
|
213
|
-
}
|
214
|
-
} else if (el[p] !== propValue) {
|
215
|
-
return false;
|
216
|
-
}
|
217
|
-
});
|
218
|
-
return true;
|
219
|
-
}
|
218
|
+
function rangeSelectsAnyText(range, textNode) {
|
219
|
+
var textNodeRange = range.cloneRange();
|
220
|
+
textNodeRange.selectNodeContents(textNode);
|
220
221
|
|
221
|
-
|
222
|
-
|
223
|
-
var testEl = document.createElement("div");
|
224
|
-
return typeof testEl.isContentEditable == "boolean" ?
|
225
|
-
function (node) {
|
226
|
-
return node && node.nodeType == 1 && node.isContentEditable;
|
227
|
-
} :
|
228
|
-
function (node) {
|
229
|
-
if (!node || node.nodeType != 1 || node.contentEditable == "false") {
|
230
|
-
return false;
|
231
|
-
}
|
232
|
-
return node.contentEditable == "true" || isEditableElement(node.parentNode);
|
233
|
-
};
|
234
|
-
})();
|
222
|
+
var intersectionRange = textNodeRange.intersection(range);
|
223
|
+
var text = intersectionRange ? intersectionRange.toString() : "";
|
235
224
|
|
236
|
-
|
237
|
-
|
238
|
-
return node && node.nodeType == 1
|
239
|
-
&& (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
|
240
|
-
|| (isEditableElement(node) && !isEditableElement(node.parentNode)));
|
241
|
-
}
|
225
|
+
return text != "";
|
226
|
+
}
|
242
227
|
|
243
|
-
|
244
|
-
|
245
|
-
}
|
228
|
+
function getEffectiveTextNodes(range) {
|
229
|
+
var nodes = range.getNodes([3]);
|
246
230
|
|
247
|
-
|
231
|
+
// Optimization as per issue 145
|
248
232
|
|
249
|
-
|
250
|
-
|
251
|
-
|
233
|
+
// Remove non-intersecting text nodes from the start of the range
|
234
|
+
var start = 0, node;
|
235
|
+
while ( (node = nodes[start]) && !rangeSelectsAnyText(range, node) ) {
|
236
|
+
++start;
|
237
|
+
}
|
252
238
|
|
253
|
-
|
254
|
-
|
239
|
+
// Remove non-intersecting text nodes from the start of the range
|
240
|
+
var end = nodes.length - 1;
|
241
|
+
while ( (node = nodes[end]) && !rangeSelectsAnyText(range, node) ) {
|
242
|
+
--end;
|
243
|
+
}
|
255
244
|
|
256
|
-
|
257
|
-
if (node.data.length == 0) {
|
258
|
-
return true;
|
245
|
+
return nodes.slice(start, end + 1);
|
259
246
|
}
|
260
|
-
|
261
|
-
|
247
|
+
|
248
|
+
function elementsHaveSameNonClassAttributes(el1, el2) {
|
249
|
+
if (el1.attributes.length != el2.attributes.length) return false;
|
250
|
+
for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
|
251
|
+
attr1 = el1.attributes[i];
|
252
|
+
name = attr1.name;
|
253
|
+
if (name != "class") {
|
254
|
+
attr2 = el2.attributes.getNamedItem(name);
|
255
|
+
if ( (attr1 === null) != (attr2 === null) ) return false;
|
256
|
+
if (attr1.specified != attr2.specified) return false;
|
257
|
+
if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
return true;
|
262
261
|
}
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
case "pre-line":
|
270
|
-
if (/[\r\n]/.test(node.data)) {
|
271
|
-
return false;
|
262
|
+
|
263
|
+
function elementHasNonClassAttributes(el, exceptions) {
|
264
|
+
for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
|
265
|
+
attrName = el.attributes[i].name;
|
266
|
+
if ( !(exceptions && contains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
|
267
|
+
return true;
|
272
268
|
}
|
269
|
+
}
|
270
|
+
return false;
|
273
271
|
}
|
274
272
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
273
|
+
var getComputedStyleProperty = dom.getComputedStyleProperty;
|
274
|
+
var isEditableElement = (function() {
|
275
|
+
var testEl = document.createElement("div");
|
276
|
+
return typeof testEl.isContentEditable == "boolean" ?
|
277
|
+
function (node) {
|
278
|
+
return node && node.nodeType == 1 && node.isContentEditable;
|
279
|
+
} :
|
280
|
+
function (node) {
|
281
|
+
if (!node || node.nodeType != 1 || node.contentEditable == "false") {
|
282
|
+
return false;
|
283
|
+
}
|
284
|
+
return node.contentEditable == "true" || isEditableElement(node.parentNode);
|
285
|
+
};
|
286
|
+
})();
|
287
|
+
|
288
|
+
function isEditingHost(node) {
|
289
|
+
var parent;
|
290
|
+
return node && node.nodeType == 1 &&
|
291
|
+
(( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on") ||
|
292
|
+
(isEditableElement(node) && !isEditableElement(node.parentNode)));
|
293
|
+
}
|
279
294
|
|
280
|
-
|
281
|
-
|
282
|
-
for (i = 0; range = ranges[i++]; ) {
|
283
|
-
positions.push(
|
284
|
-
new DomPosition(range.startContainer, range.startOffset),
|
285
|
-
new DomPosition(range.endContainer, range.endOffset)
|
286
|
-
);
|
295
|
+
function isEditable(node) {
|
296
|
+
return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
|
287
297
|
}
|
288
|
-
return positions;
|
289
|
-
}
|
290
298
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
end = positions[i * 2 + 1];
|
296
|
-
range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
|
299
|
+
var inlineDisplayRegex = /^inline(-block|-table)?$/i;
|
300
|
+
|
301
|
+
function isNonInlineElement(node) {
|
302
|
+
return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
|
297
303
|
}
|
298
|
-
}
|
299
304
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
return !!node.nextSibling;
|
306
|
-
} else {
|
305
|
+
// White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
|
306
|
+
var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
|
307
|
+
|
308
|
+
function isUnrenderedWhiteSpaceNode(node) {
|
309
|
+
if (node.data.length == 0) {
|
307
310
|
return true;
|
308
311
|
}
|
309
|
-
|
312
|
+
if (htmlNonWhiteSpaceRegex.test(node.data)) {
|
313
|
+
return false;
|
314
|
+
}
|
315
|
+
var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
|
316
|
+
switch (cssWhiteSpace) {
|
317
|
+
case "pre":
|
318
|
+
case "pre-wrap":
|
319
|
+
case "-moz-pre-wrap":
|
320
|
+
return false;
|
321
|
+
case "pre-line":
|
322
|
+
if (/[\r\n]/.test(node.data)) {
|
323
|
+
return false;
|
324
|
+
}
|
325
|
+
}
|
310
326
|
|
311
|
-
|
312
|
-
|
327
|
+
// We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
|
328
|
+
// non-inline element, it will not be rendered. This seems to be a good enough definition.
|
329
|
+
return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
|
330
|
+
}
|
313
331
|
|
314
|
-
|
315
|
-
|
316
|
-
|
332
|
+
function getRangeBoundaries(ranges) {
|
333
|
+
var positions = [], i, range;
|
334
|
+
for (i = 0; range = ranges[i++]; ) {
|
335
|
+
positions.push(
|
336
|
+
new DomPosition(range.startContainer, range.startOffset),
|
337
|
+
new DomPosition(range.endContainer, range.endOffset)
|
338
|
+
);
|
339
|
+
}
|
340
|
+
return positions;
|
341
|
+
}
|
317
342
|
|
318
|
-
|
319
|
-
|
343
|
+
function updateRangesFromBoundaries(ranges, positions) {
|
344
|
+
for (var i = 0, range, start, end, len = ranges.length; i < len; ++i) {
|
345
|
+
range = ranges[i];
|
346
|
+
start = positions[i * 2];
|
347
|
+
end = positions[i * 2 + 1];
|
348
|
+
range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
|
349
|
+
}
|
320
350
|
}
|
321
351
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
352
|
+
function isSplitPoint(node, offset) {
|
353
|
+
if (dom.isCharacterDataNode(node)) {
|
354
|
+
if (offset == 0) {
|
355
|
+
return !!node.previousSibling;
|
356
|
+
} else if (offset == node.length) {
|
357
|
+
return !!node.nextSibling;
|
358
|
+
} else {
|
359
|
+
return true;
|
360
|
+
}
|
331
361
|
}
|
332
|
-
|
362
|
+
|
363
|
+
return offset > 0 && offset < node.childNodes.length;
|
333
364
|
}
|
334
365
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
if (
|
340
|
-
|
366
|
+
function splitNodeAt(node, descendantNode, descendantOffset, positionsToPreserve) {
|
367
|
+
var newNode, parentNode;
|
368
|
+
var splitAtStart = (descendantOffset == 0);
|
369
|
+
|
370
|
+
if (dom.isAncestorOf(descendantNode, node)) {
|
371
|
+
return node;
|
341
372
|
}
|
342
|
-
var child, newChildIndex = 0;
|
343
373
|
|
344
|
-
|
345
|
-
|
374
|
+
if (dom.isCharacterDataNode(descendantNode)) {
|
375
|
+
var descendantIndex = dom.getNodeIndex(descendantNode);
|
376
|
+
if (descendantOffset == 0) {
|
377
|
+
descendantOffset = descendantIndex;
|
378
|
+
} else if (descendantOffset == descendantNode.length) {
|
379
|
+
descendantOffset = descendantIndex + 1;
|
380
|
+
} else {
|
381
|
+
throw module.createError("splitNodeAt() should not be called with offset in the middle of a data node (" +
|
382
|
+
descendantOffset + " in " + descendantNode.data);
|
383
|
+
}
|
384
|
+
descendantNode = descendantNode.parentNode;
|
346
385
|
}
|
347
|
-
movePreservingPositions(newNode, parentNode, dom.getNodeIndex(descendantNode) + 1, positionsToPreserve);
|
348
|
-
return (descendantNode == node) ? newNode : splitNodeAt(node, parentNode, dom.getNodeIndex(newNode), positionsToPreserve);
|
349
|
-
} else if (node != descendantNode) {
|
350
|
-
newNode = descendantNode.parentNode;
|
351
386
|
|
352
|
-
|
353
|
-
|
387
|
+
if (isSplitPoint(descendantNode, descendantOffset)) {
|
388
|
+
// descendantNode is now guaranteed not to be a text or other character node
|
389
|
+
newNode = descendantNode.cloneNode(false);
|
390
|
+
parentNode = descendantNode.parentNode;
|
391
|
+
if (newNode.id) {
|
392
|
+
newNode.removeAttribute("id");
|
393
|
+
}
|
394
|
+
var child, newChildIndex = 0;
|
395
|
+
|
396
|
+
while ( (child = descendantNode.childNodes[descendantOffset]) ) {
|
397
|
+
movePreservingPositions(child, newNode, newChildIndex++, positionsToPreserve);
|
398
|
+
}
|
399
|
+
movePreservingPositions(newNode, parentNode, dom.getNodeIndex(descendantNode) + 1, positionsToPreserve);
|
400
|
+
return (descendantNode == node) ? newNode : splitNodeAt(node, parentNode, dom.getNodeIndex(newNode), positionsToPreserve);
|
401
|
+
} else if (node != descendantNode) {
|
402
|
+
newNode = descendantNode.parentNode;
|
403
|
+
|
404
|
+
// Work out a new split point in the parent node
|
405
|
+
var newNodeIndex = dom.getNodeIndex(descendantNode);
|
354
406
|
|
355
|
-
|
356
|
-
|
407
|
+
if (!splitAtStart) {
|
408
|
+
newNodeIndex++;
|
409
|
+
}
|
410
|
+
return splitNodeAt(node, newNode, newNodeIndex, positionsToPreserve);
|
357
411
|
}
|
358
|
-
return
|
412
|
+
return node;
|
359
413
|
}
|
360
|
-
return node;
|
361
|
-
}
|
362
414
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
415
|
+
function areElementsMergeable(el1, el2) {
|
416
|
+
return el1.namespaceURI == el2.namespaceURI &&
|
417
|
+
el1.tagName.toLowerCase() == el2.tagName.toLowerCase() &&
|
418
|
+
haveSameClasses(el1, el2) &&
|
419
|
+
elementsHaveSameNonClassAttributes(el1, el2) &&
|
420
|
+
getComputedStyleProperty(el1, "display") == "inline" &&
|
421
|
+
getComputedStyleProperty(el2, "display") == "inline";
|
422
|
+
}
|
423
|
+
|
424
|
+
function createAdjacentMergeableTextNodeGetter(forward) {
|
425
|
+
var siblingPropName = forward ? "nextSibling" : "previousSibling";
|
370
426
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
if (
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
if (adjacentNodeChild && adjacentNodeChild.nodeType == 3) {
|
388
|
-
return adjacentNodeChild;
|
427
|
+
return function(textNode, checkParentElement) {
|
428
|
+
var el = textNode.parentNode;
|
429
|
+
var adjacentNode = textNode[siblingPropName];
|
430
|
+
if (adjacentNode) {
|
431
|
+
// Can merge if the node's previous/next sibling is a text node
|
432
|
+
if (adjacentNode && adjacentNode.nodeType == 3) {
|
433
|
+
return adjacentNode;
|
434
|
+
}
|
435
|
+
} else if (checkParentElement) {
|
436
|
+
// Compare text node parent element with its sibling
|
437
|
+
adjacentNode = el[siblingPropName];
|
438
|
+
if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) {
|
439
|
+
var adjacentNodeChild = adjacentNode[forward ? "firstChild" : "lastChild"];
|
440
|
+
if (adjacentNodeChild && adjacentNodeChild.nodeType == 3) {
|
441
|
+
return adjacentNodeChild;
|
442
|
+
}
|
389
443
|
}
|
390
444
|
}
|
391
|
-
|
392
|
-
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
|
397
|
-
getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
|
445
|
+
return null;
|
446
|
+
};
|
447
|
+
}
|
398
448
|
|
449
|
+
var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
|
450
|
+
getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
|
399
451
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
452
|
+
|
453
|
+
function Merge(firstNode) {
|
454
|
+
this.isElementMerge = (firstNode.nodeType == 1);
|
455
|
+
this.textNodes = [];
|
456
|
+
var firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
|
457
|
+
if (firstTextNode) {
|
458
|
+
this.textNodes[0] = firstTextNode;
|
459
|
+
}
|
406
460
|
}
|
407
|
-
}
|
408
461
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
textNode
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
462
|
+
Merge.prototype = {
|
463
|
+
doMerge: function(positionsToPreserve) {
|
464
|
+
var textNodes = this.textNodes;
|
465
|
+
var firstTextNode = textNodes[0];
|
466
|
+
if (textNodes.length > 1) {
|
467
|
+
var firstTextNodeIndex = dom.getNodeIndex(firstTextNode);
|
468
|
+
var textParts = [], combinedTextLength = 0, textNode, parent;
|
469
|
+
forEach(textNodes, function(textNode, i) {
|
470
|
+
parent = textNode.parentNode;
|
471
|
+
if (i > 0) {
|
472
|
+
parent.removeChild(textNode);
|
473
|
+
if (!parent.hasChildNodes()) {
|
474
|
+
dom.removeNode(parent);
|
475
|
+
}
|
476
|
+
if (positionsToPreserve) {
|
477
|
+
forEach(positionsToPreserve, function(position) {
|
478
|
+
// Handle case where position is inside the text node being merged into a preceding node
|
479
|
+
if (position.node == textNode) {
|
480
|
+
position.node = firstTextNode;
|
481
|
+
position.offset += combinedTextLength;
|
482
|
+
}
|
483
|
+
// Handle case where both text nodes precede the position within the same parent node
|
484
|
+
if (position.node == parent && position.offset > firstTextNodeIndex) {
|
485
|
+
--position.offset;
|
486
|
+
if (position.offset == firstTextNodeIndex + 1 && i < len - 1) {
|
487
|
+
position.node = firstTextNode;
|
488
|
+
position.offset = combinedTextLength;
|
489
|
+
}
|
490
|
+
}
|
491
|
+
});
|
430
492
|
}
|
431
493
|
}
|
432
|
-
|
433
|
-
|
434
|
-
|
494
|
+
textParts[i] = textNode.data;
|
495
|
+
combinedTextLength += textNode.data.length;
|
496
|
+
});
|
497
|
+
firstTextNode.data = textParts.join("");
|
435
498
|
}
|
436
|
-
firstTextNode.data
|
437
|
-
}
|
438
|
-
return firstTextNode.data;
|
439
|
-
},
|
499
|
+
return firstTextNode.data;
|
500
|
+
},
|
440
501
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
502
|
+
getLength: function() {
|
503
|
+
var i = this.textNodes.length, len = 0;
|
504
|
+
while (i--) {
|
505
|
+
len += this.textNodes[i].length;
|
506
|
+
}
|
507
|
+
return len;
|
508
|
+
},
|
509
|
+
|
510
|
+
toString: function() {
|
511
|
+
var textParts = [];
|
512
|
+
forEach(this.textNodes, function(textNode, i) {
|
513
|
+
textParts[i] = "'" + textNode.data + "'";
|
514
|
+
});
|
515
|
+
return "[Merge(" + textParts.join(",") + ")]";
|
453
516
|
}
|
454
|
-
|
455
|
-
}
|
456
|
-
};
|
517
|
+
};
|
457
518
|
|
458
|
-
|
459
|
-
|
519
|
+
var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly", "useExistingElements",
|
520
|
+
"removeEmptyElements", "onElementCreate"];
|
460
521
|
|
461
|
-
|
462
|
-
|
522
|
+
// TODO: Populate this with every attribute name that corresponds to a property with a different name. Really??
|
523
|
+
var attrNamesForProperties = {};
|
463
524
|
|
464
|
-
|
465
|
-
|
466
|
-
|
525
|
+
function ClassApplier(className, options, tagNames) {
|
526
|
+
var normalize, i, len, propName, applier = this;
|
527
|
+
applier.cssClass = applier.className = className; // cssClass property is for backward compatibility
|
467
528
|
|
468
|
-
|
529
|
+
var elementPropertiesFromOptions = null, elementAttributes = {};
|
469
530
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
531
|
+
// Initialize from options object
|
532
|
+
if (typeof options == "object" && options !== null) {
|
533
|
+
if (typeof options.elementTagName !== "undefined") {
|
534
|
+
options.elementTagName = options.elementTagName.toLowerCase();
|
535
|
+
}
|
536
|
+
tagNames = options.tagNames;
|
537
|
+
elementPropertiesFromOptions = options.elementProperties;
|
538
|
+
elementAttributes = options.elementAttributes;
|
475
539
|
|
476
|
-
|
477
|
-
|
478
|
-
|
540
|
+
for (i = 0; propName = optionProperties[i++]; ) {
|
541
|
+
if (options.hasOwnProperty(propName)) {
|
542
|
+
applier[propName] = options[propName];
|
543
|
+
}
|
479
544
|
}
|
480
|
-
|
481
|
-
normalize = options.normalize;
|
482
|
-
} else {
|
483
|
-
normalize = options;
|
484
|
-
}
|
485
|
-
|
486
|
-
// Backward compatibility: the second parameter can also be a Boolean indicating to normalize after unapplying
|
487
|
-
applier.normalize = (typeof normalize == "undefined") ? true : normalize;
|
488
|
-
|
489
|
-
// Initialize element properties and attribute exceptions
|
490
|
-
applier.attrExceptions = [];
|
491
|
-
var el = document.createElement(applier.elementTagName);
|
492
|
-
applier.elementProperties = applier.copyPropertiesToElement(elementPropertiesFromOptions, el, true);
|
493
|
-
each(elementAttributes, function(attrName) {
|
494
|
-
applier.attrExceptions.push(attrName);
|
495
|
-
});
|
496
|
-
applier.elementAttributes = elementAttributes;
|
497
|
-
|
498
|
-
applier.elementSortedClassName = applier.elementProperties.hasOwnProperty("className") ?
|
499
|
-
applier.elementProperties.className : cssClass;
|
500
|
-
|
501
|
-
// Initialize tag names
|
502
|
-
applier.applyToAnyTagName = false;
|
503
|
-
var type = typeof tagNames;
|
504
|
-
if (type == "string") {
|
505
|
-
if (tagNames == "*") {
|
506
|
-
applier.applyToAnyTagName = true;
|
545
|
+
normalize = options.normalize;
|
507
546
|
} else {
|
508
|
-
|
547
|
+
normalize = options;
|
509
548
|
}
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
549
|
+
|
550
|
+
// Backward compatibility: the second parameter can also be a Boolean indicating to normalize after unapplying
|
551
|
+
applier.normalize = (typeof normalize == "undefined") ? true : normalize;
|
552
|
+
|
553
|
+
// Initialize element properties and attribute exceptions
|
554
|
+
applier.attrExceptions = [];
|
555
|
+
var el = document.createElement(applier.elementTagName);
|
556
|
+
applier.elementProperties = applier.copyPropertiesToElement(elementPropertiesFromOptions, el, true);
|
557
|
+
each(elementAttributes, function(attrName, attrValue) {
|
558
|
+
applier.attrExceptions.push(attrName);
|
559
|
+
// Ensure each attribute value is a string
|
560
|
+
elementAttributes[attrName] = "" + attrValue;
|
561
|
+
});
|
562
|
+
applier.elementAttributes = elementAttributes;
|
563
|
+
|
564
|
+
applier.elementSortedClassName = applier.elementProperties.hasOwnProperty("className") ?
|
565
|
+
sortClassName(applier.elementProperties.className + " " + className) : className;
|
566
|
+
|
567
|
+
// Initialize tag names
|
568
|
+
applier.applyToAnyTagName = false;
|
569
|
+
var type = typeof tagNames;
|
570
|
+
if (type == "string") {
|
571
|
+
if (tagNames == "*") {
|
514
572
|
applier.applyToAnyTagName = true;
|
515
573
|
} else {
|
516
|
-
applier.tagNames
|
574
|
+
applier.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
|
575
|
+
}
|
576
|
+
} else if (type == "object" && typeof tagNames.length == "number") {
|
577
|
+
applier.tagNames = [];
|
578
|
+
for (i = 0, len = tagNames.length; i < len; ++i) {
|
579
|
+
if (tagNames[i] == "*") {
|
580
|
+
applier.applyToAnyTagName = true;
|
581
|
+
} else {
|
582
|
+
applier.tagNames.push(tagNames[i].toLowerCase());
|
583
|
+
}
|
517
584
|
}
|
585
|
+
} else {
|
586
|
+
applier.tagNames = [applier.elementTagName];
|
518
587
|
}
|
519
|
-
} else {
|
520
|
-
applier.tagNames = [applier.elementTagName];
|
521
588
|
}
|
522
|
-
}
|
523
589
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
590
|
+
ClassApplier.prototype = {
|
591
|
+
elementTagName: defaultTagName,
|
592
|
+
elementProperties: {},
|
593
|
+
elementAttributes: {},
|
594
|
+
ignoreWhiteSpace: true,
|
595
|
+
applyToEditableOnly: false,
|
596
|
+
useExistingElements: true,
|
597
|
+
removeEmptyElements: true,
|
598
|
+
onElementCreate: null,
|
599
|
+
|
600
|
+
copyPropertiesToElement: function(props, el, createCopy) {
|
601
|
+
var s, elStyle, elProps = {}, elPropsStyle, propValue, elPropValue, attrName;
|
602
|
+
|
603
|
+
for (var p in props) {
|
604
|
+
if (props.hasOwnProperty(p)) {
|
605
|
+
propValue = props[p];
|
606
|
+
elPropValue = el[p];
|
607
|
+
|
608
|
+
// Special case for class. The copied properties object has the applier's class as well as its own
|
609
|
+
// to simplify checks when removing styling elements
|
610
|
+
if (p == "className") {
|
611
|
+
addClass(el, propValue);
|
612
|
+
addClass(el, this.className);
|
613
|
+
el[p] = sortClassName(el[p]);
|
614
|
+
if (createCopy) {
|
615
|
+
elProps[p] = propValue;
|
616
|
+
}
|
550
617
|
}
|
551
|
-
}
|
552
618
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
if (createCopy) {
|
557
|
-
elProps[p] = elPropsStyle = {};
|
558
|
-
}
|
559
|
-
for (s in props[p]) {
|
560
|
-
elStyle[s] = propValue[s];
|
619
|
+
// Special case for style
|
620
|
+
else if (p == "style") {
|
621
|
+
elStyle = elPropValue;
|
561
622
|
if (createCopy) {
|
562
|
-
|
623
|
+
elProps[p] = elPropsStyle = {};
|
624
|
+
}
|
625
|
+
for (s in props[p]) {
|
626
|
+
if (props[p].hasOwnProperty(s)) {
|
627
|
+
elStyle[s] = propValue[s];
|
628
|
+
if (createCopy) {
|
629
|
+
elPropsStyle[s] = elStyle[s];
|
630
|
+
}
|
631
|
+
}
|
632
|
+
}
|
633
|
+
this.attrExceptions.push(p);
|
634
|
+
} else {
|
635
|
+
el[p] = propValue;
|
636
|
+
// Copy the property back from the dummy element so that later comparisons to check whether
|
637
|
+
// elements may be removed are checking against the right value. For example, the href property
|
638
|
+
// of an element returns a fully qualified URL even if it was previously assigned a relative
|
639
|
+
// URL.
|
640
|
+
if (createCopy) {
|
641
|
+
elProps[p] = el[p];
|
642
|
+
|
643
|
+
// Not all properties map to identically-named attributes
|
644
|
+
attrName = attrNamesForProperties.hasOwnProperty(p) ? attrNamesForProperties[p] : p;
|
645
|
+
this.attrExceptions.push(attrName);
|
563
646
|
}
|
564
|
-
}
|
565
|
-
this.attrExceptions.push(p);
|
566
|
-
} else {
|
567
|
-
el[p] = propValue;
|
568
|
-
// Copy the property back from the dummy element so that later comparisons to check whether
|
569
|
-
// elements may be removed are checking against the right value. For example, the href property
|
570
|
-
// of an element returns a fully qualified URL even if it was previously assigned a relative
|
571
|
-
// URL.
|
572
|
-
if (createCopy) {
|
573
|
-
elProps[p] = el[p];
|
574
|
-
|
575
|
-
// Not all properties map to identically-named attributes
|
576
|
-
attrName = attrNamesForProperties.hasOwnProperty(p) ? attrNamesForProperties[p] : p;
|
577
|
-
this.attrExceptions.push(attrName);
|
578
647
|
}
|
579
648
|
}
|
580
649
|
}
|
581
|
-
}
|
582
650
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
651
|
+
return createCopy ? elProps : "";
|
652
|
+
},
|
653
|
+
|
654
|
+
copyAttributesToElement: function(attrs, el) {
|
655
|
+
for (var attrName in attrs) {
|
656
|
+
if (attrs.hasOwnProperty(attrName) && !/^class(?:Name)?$/i.test(attrName)) {
|
657
|
+
el.setAttribute(attrName, attrs[attrName]);
|
658
|
+
}
|
590
659
|
}
|
591
|
-
}
|
592
|
-
|
660
|
+
},
|
661
|
+
|
662
|
+
appliesToElement: function(el) {
|
663
|
+
return contains(this.tagNames, el.tagName.toLowerCase());
|
664
|
+
},
|
665
|
+
|
666
|
+
getEmptyElements: function(range) {
|
667
|
+
var applier = this;
|
668
|
+
return range.getNodes([1], function(el) {
|
669
|
+
return applier.appliesToElement(el) && !el.hasChildNodes();
|
670
|
+
});
|
671
|
+
},
|
672
|
+
|
673
|
+
hasClass: function(node) {
|
674
|
+
return node.nodeType == 1 &&
|
675
|
+
(this.applyToAnyTagName || this.appliesToElement(node)) &&
|
676
|
+
hasClass(node, this.className);
|
677
|
+
},
|
678
|
+
|
679
|
+
getSelfOrAncestorWithClass: function(node) {
|
680
|
+
while (node) {
|
681
|
+
if (this.hasClass(node)) {
|
682
|
+
return node;
|
683
|
+
}
|
684
|
+
node = node.parentNode;
|
685
|
+
}
|
686
|
+
return null;
|
687
|
+
},
|
593
688
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
hasClass(node, this.cssClass);
|
598
|
-
},
|
689
|
+
isModifiable: function(node) {
|
690
|
+
return !this.applyToEditableOnly || isEditable(node);
|
691
|
+
},
|
599
692
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
}
|
605
|
-
node = node.parentNode;
|
606
|
-
}
|
607
|
-
return null;
|
608
|
-
},
|
693
|
+
// White space adjacent to an unwrappable node can be ignored for wrapping
|
694
|
+
isIgnorableWhiteSpaceNode: function(node) {
|
695
|
+
return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
|
696
|
+
},
|
609
697
|
|
610
|
-
|
611
|
-
|
612
|
-
|
698
|
+
// Normalizes nodes after applying a class to a Range.
|
699
|
+
postApply: function(textNodes, range, positionsToPreserve, isUndo) {
|
700
|
+
var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
|
613
701
|
|
614
|
-
|
615
|
-
isIgnorableWhiteSpaceNode: function(node) {
|
616
|
-
return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
|
617
|
-
},
|
702
|
+
var merges = [], currentMerge;
|
618
703
|
|
619
|
-
|
620
|
-
|
621
|
-
var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
|
704
|
+
var rangeStartNode = firstNode, rangeEndNode = lastNode;
|
705
|
+
var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
|
622
706
|
|
623
|
-
|
707
|
+
var textNode, precedingTextNode;
|
624
708
|
|
625
|
-
|
626
|
-
|
709
|
+
// Check for every required merge and create a Merge object for each
|
710
|
+
forEach(textNodes, function(textNode) {
|
711
|
+
precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
|
712
|
+
if (precedingTextNode) {
|
713
|
+
if (!currentMerge) {
|
714
|
+
currentMerge = new Merge(precedingTextNode);
|
715
|
+
merges.push(currentMerge);
|
716
|
+
}
|
717
|
+
currentMerge.textNodes.push(textNode);
|
718
|
+
if (textNode === firstNode) {
|
719
|
+
rangeStartNode = currentMerge.textNodes[0];
|
720
|
+
rangeStartOffset = rangeStartNode.length;
|
721
|
+
}
|
722
|
+
if (textNode === lastNode) {
|
723
|
+
rangeEndNode = currentMerge.textNodes[0];
|
724
|
+
rangeEndOffset = currentMerge.getLength();
|
725
|
+
}
|
726
|
+
} else {
|
727
|
+
currentMerge = null;
|
728
|
+
}
|
729
|
+
});
|
627
730
|
|
628
|
-
|
731
|
+
// Test whether the first node after the range needs merging
|
732
|
+
var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
|
629
733
|
|
630
|
-
|
631
|
-
for (var i = 0, len = textNodes.length; i < len; ++i) {
|
632
|
-
textNode = textNodes[i];
|
633
|
-
precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
|
634
|
-
if (precedingTextNode) {
|
734
|
+
if (nextTextNode) {
|
635
735
|
if (!currentMerge) {
|
636
|
-
currentMerge = new Merge(
|
736
|
+
currentMerge = new Merge(lastNode);
|
637
737
|
merges.push(currentMerge);
|
638
738
|
}
|
639
|
-
currentMerge.textNodes.push(
|
640
|
-
if (textNode === firstNode) {
|
641
|
-
rangeStartNode = currentMerge.textNodes[0];
|
642
|
-
rangeStartOffset = rangeStartNode.length;
|
643
|
-
}
|
644
|
-
if (textNode === lastNode) {
|
645
|
-
rangeEndNode = currentMerge.textNodes[0];
|
646
|
-
rangeEndOffset = currentMerge.getLength();
|
647
|
-
}
|
648
|
-
} else {
|
649
|
-
currentMerge = null;
|
739
|
+
currentMerge.textNodes.push(nextTextNode);
|
650
740
|
}
|
651
|
-
}
|
652
741
|
|
653
|
-
|
654
|
-
|
742
|
+
// Apply the merges
|
743
|
+
if (merges.length) {
|
744
|
+
for (i = 0, len = merges.length; i < len; ++i) {
|
745
|
+
merges[i].doMerge(positionsToPreserve);
|
746
|
+
}
|
655
747
|
|
656
|
-
|
657
|
-
|
658
|
-
currentMerge = new Merge(lastNode);
|
659
|
-
merges.push(currentMerge);
|
748
|
+
// Set the range boundaries
|
749
|
+
range.setStartAndEnd(rangeStartNode, rangeStartOffset, rangeEndNode, rangeEndOffset);
|
660
750
|
}
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
751
|
+
},
|
752
|
+
|
753
|
+
createContainer: function(parentNode) {
|
754
|
+
var doc = dom.getDocument(parentNode);
|
755
|
+
var namespace;
|
756
|
+
var el = createElementNSSupported && !dom.isHtmlNamespace(parentNode) && (namespace = parentNode.namespaceURI) ?
|
757
|
+
doc.createElementNS(parentNode.namespaceURI, this.elementTagName) :
|
758
|
+
doc.createElement(this.elementTagName);
|
759
|
+
|
760
|
+
this.copyPropertiesToElement(this.elementProperties, el, false);
|
761
|
+
this.copyAttributesToElement(this.elementAttributes, el);
|
762
|
+
addClass(el, this.className);
|
763
|
+
if (this.onElementCreate) {
|
764
|
+
this.onElementCreate(el, this);
|
668
765
|
}
|
766
|
+
return el;
|
767
|
+
},
|
669
768
|
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
769
|
+
elementHasProperties: function(el, props) {
|
770
|
+
var applier = this;
|
771
|
+
return each(props, function(p, propValue) {
|
772
|
+
if (p == "className") {
|
773
|
+
// For checking whether we should reuse an existing element, we just want to check that the element
|
774
|
+
// has all the classes specified in the className property. When deciding whether the element is
|
775
|
+
// removable when unapplying a class, there is separate special handling to check whether the
|
776
|
+
// element has extra classes so the same simple check will do.
|
777
|
+
return hasAllClasses(el, propValue);
|
778
|
+
} else if (typeof propValue == "object") {
|
779
|
+
if (!applier.elementHasProperties(el[p], propValue)) {
|
780
|
+
return false;
|
781
|
+
}
|
782
|
+
} else if (el[p] !== propValue) {
|
783
|
+
return false;
|
784
|
+
}
|
785
|
+
});
|
786
|
+
},
|
685
787
|
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
788
|
+
elementHasAttributes: function(el, attrs) {
|
789
|
+
return each(attrs, function(name, value) {
|
790
|
+
if (el.getAttribute(name) !== value) {
|
791
|
+
return false;
|
792
|
+
}
|
793
|
+
});
|
794
|
+
},
|
795
|
+
|
796
|
+
applyToTextNode: function(textNode, positionsToPreserve) {
|
797
|
+
|
798
|
+
// Check whether the text node can be styled. Text within a <style> or <script> element, for example,
|
799
|
+
// should not be styled. See issue 283.
|
800
|
+
if (canTextBeStyled(textNode)) {
|
801
|
+
var parent = textNode.parentNode;
|
802
|
+
if (parent.childNodes.length == 1 &&
|
803
|
+
this.useExistingElements &&
|
804
|
+
this.appliesToElement(parent) &&
|
805
|
+
this.elementHasProperties(parent, this.elementProperties) &&
|
806
|
+
this.elementHasAttributes(parent, this.elementAttributes)) {
|
807
|
+
|
808
|
+
addClass(parent, this.className);
|
809
|
+
} else {
|
810
|
+
var textNodeParent = textNode.parentNode;
|
811
|
+
var el = this.createContainer(textNodeParent);
|
812
|
+
textNodeParent.insertBefore(el, textNode);
|
813
|
+
el.appendChild(textNode);
|
814
|
+
}
|
815
|
+
}
|
692
816
|
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
817
|
+
},
|
818
|
+
|
819
|
+
isRemovable: function(el) {
|
820
|
+
return el.tagName.toLowerCase() == this.elementTagName &&
|
821
|
+
getSortedClassName(el) == this.elementSortedClassName &&
|
822
|
+
this.elementHasProperties(el, this.elementProperties) &&
|
823
|
+
!elementHasNonClassAttributes(el, this.attrExceptions) &&
|
824
|
+
this.elementHasAttributes(el, this.elementAttributes) &&
|
825
|
+
this.isModifiable(el);
|
826
|
+
},
|
827
|
+
|
828
|
+
isEmptyContainer: function(el) {
|
829
|
+
var childNodeCount = el.childNodes.length;
|
830
|
+
return el.nodeType == 1 &&
|
831
|
+
this.isRemovable(el) &&
|
832
|
+
(childNodeCount == 0 || (childNodeCount == 1 && this.isEmptyContainer(el.firstChild)));
|
833
|
+
},
|
834
|
+
|
835
|
+
removeEmptyContainers: function(range) {
|
836
|
+
var applier = this;
|
837
|
+
var nodesToRemove = range.getNodes([1], function(el) {
|
838
|
+
return applier.isEmptyContainer(el);
|
839
|
+
});
|
840
|
+
|
841
|
+
var rangesToPreserve = [range];
|
842
|
+
var positionsToPreserve = getRangeBoundaries(rangesToPreserve);
|
843
|
+
|
844
|
+
forEach(nodesToRemove, function(node) {
|
845
|
+
removePreservingPositions(node, positionsToPreserve);
|
846
|
+
});
|
847
|
+
|
848
|
+
// Update the range from the preserved boundary positions
|
849
|
+
updateRangesFromBoundaries(rangesToPreserve, positionsToPreserve);
|
850
|
+
},
|
851
|
+
|
852
|
+
undoToTextNode: function(textNode, range, ancestorWithClass, positionsToPreserve) {
|
853
|
+
if (!range.containsNode(ancestorWithClass)) {
|
854
|
+
// Split out the portion of the ancestor from which we can remove the class
|
855
|
+
//var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
|
856
|
+
var ancestorRange = range.cloneRange();
|
857
|
+
ancestorRange.selectNode(ancestorWithClass);
|
858
|
+
if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)) {
|
859
|
+
splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, positionsToPreserve);
|
860
|
+
range.setEndAfter(ancestorWithClass);
|
861
|
+
}
|
862
|
+
if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)) {
|
863
|
+
ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, positionsToPreserve);
|
864
|
+
}
|
865
|
+
}
|
728
866
|
|
729
|
-
|
730
|
-
|
731
|
-
|
867
|
+
if (this.isRemovable(ancestorWithClass)) {
|
868
|
+
replaceWithOwnChildrenPreservingPositions(ancestorWithClass, positionsToPreserve);
|
869
|
+
} else {
|
870
|
+
removeClass(ancestorWithClass, this.className);
|
871
|
+
}
|
872
|
+
},
|
732
873
|
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
var ancestorRange = range.cloneRange();
|
738
|
-
ancestorRange.selectNode(ancestorWithClass);
|
739
|
-
if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)) {
|
740
|
-
splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, positionsToPreserve);
|
741
|
-
range.setEndAfter(ancestorWithClass);
|
874
|
+
splitAncestorWithClass: function(container, offset, positionsToPreserve) {
|
875
|
+
var ancestorWithClass = this.getSelfOrAncestorWithClass(container);
|
876
|
+
if (ancestorWithClass) {
|
877
|
+
splitNodeAt(ancestorWithClass, container, offset, positionsToPreserve);
|
742
878
|
}
|
743
|
-
|
744
|
-
|
879
|
+
},
|
880
|
+
|
881
|
+
undoToAncestor: function(ancestorWithClass, positionsToPreserve) {
|
882
|
+
if (this.isRemovable(ancestorWithClass)) {
|
883
|
+
replaceWithOwnChildrenPreservingPositions(ancestorWithClass, positionsToPreserve);
|
884
|
+
} else {
|
885
|
+
removeClass(ancestorWithClass, this.className);
|
745
886
|
}
|
746
|
-
}
|
747
|
-
if (this.isRemovable(ancestorWithClass)) {
|
748
|
-
replaceWithOwnChildrenPreservingPositions(ancestorWithClass, positionsToPreserve);
|
749
|
-
} else {
|
750
|
-
removeClass(ancestorWithClass, this.cssClass);
|
751
|
-
}
|
752
|
-
},
|
887
|
+
},
|
753
888
|
|
754
|
-
|
755
|
-
|
889
|
+
applyToRange: function(range, rangesToPreserve) {
|
890
|
+
var applier = this;
|
891
|
+
rangesToPreserve = rangesToPreserve || [];
|
756
892
|
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
range.splitBoundariesPreservingPositions(positionsToPreserve);
|
893
|
+
// Create an array of range boundaries to preserve
|
894
|
+
var positionsToPreserve = getRangeBoundaries(rangesToPreserve || []);
|
761
895
|
|
762
|
-
|
763
|
-
if (this.removeEmptyElements) {
|
764
|
-
this.removeEmptyContainers(range);
|
765
|
-
}
|
896
|
+
range.splitBoundariesPreservingPositions(positionsToPreserve);
|
766
897
|
|
767
|
-
|
898
|
+
// Tidy up the DOM by removing empty containers
|
899
|
+
if (applier.removeEmptyElements) {
|
900
|
+
applier.removeEmptyContainers(range);
|
901
|
+
}
|
768
902
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
903
|
+
var textNodes = getEffectiveTextNodes(range);
|
904
|
+
|
905
|
+
if (textNodes.length) {
|
906
|
+
forEach(textNodes, function(textNode) {
|
907
|
+
if (!applier.isIgnorableWhiteSpaceNode(textNode) && !applier.getSelfOrAncestorWithClass(textNode) &&
|
908
|
+
applier.isModifiable(textNode)) {
|
909
|
+
applier.applyToTextNode(textNode, positionsToPreserve);
|
910
|
+
}
|
911
|
+
});
|
912
|
+
var lastTextNode = textNodes[textNodes.length - 1];
|
913
|
+
range.setStartAndEnd(textNodes[0], 0, lastTextNode, lastTextNode.length);
|
914
|
+
if (applier.normalize) {
|
915
|
+
applier.postApply(textNodes, range, positionsToPreserve, false);
|
774
916
|
}
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
if (this.normalize) {
|
779
|
-
this.postApply(textNodes, range, positionsToPreserve, false);
|
917
|
+
|
918
|
+
// Update the ranges from the preserved boundary positions
|
919
|
+
updateRangesFromBoundaries(rangesToPreserve, positionsToPreserve);
|
780
920
|
}
|
781
921
|
|
782
|
-
//
|
783
|
-
|
784
|
-
}
|
785
|
-
},
|
922
|
+
// Apply classes to any appropriate empty elements
|
923
|
+
var emptyElements = applier.getEmptyElements(range);
|
786
924
|
|
787
|
-
|
925
|
+
forEach(emptyElements, function(el) {
|
926
|
+
addClass(el, applier.className);
|
927
|
+
});
|
928
|
+
},
|
788
929
|
|
789
|
-
|
790
|
-
while (i--) {
|
791
|
-
this.applyToRange(ranges[i], ranges);
|
792
|
-
}
|
930
|
+
applyToRanges: function(ranges) {
|
793
931
|
|
932
|
+
var i = ranges.length;
|
933
|
+
while (i--) {
|
934
|
+
this.applyToRange(ranges[i], ranges);
|
935
|
+
}
|
794
936
|
|
795
|
-
return ranges;
|
796
|
-
},
|
797
937
|
|
798
|
-
|
799
|
-
|
800
|
-
sel.setRanges( this.applyToRanges(sel.getAllRanges()) );
|
801
|
-
},
|
938
|
+
return ranges;
|
939
|
+
},
|
802
940
|
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
941
|
+
applyToSelection: function(win) {
|
942
|
+
var sel = api.getSelection(win);
|
943
|
+
sel.setRanges( this.applyToRanges(sel.getAllRanges()) );
|
944
|
+
},
|
807
945
|
|
946
|
+
undoToRange: function(range, rangesToPreserve) {
|
947
|
+
var applier = this;
|
948
|
+
// Create an array of range boundaries to preserve
|
949
|
+
rangesToPreserve = rangesToPreserve || [];
|
950
|
+
var positionsToPreserve = getRangeBoundaries(rangesToPreserve);
|
808
951
|
|
809
|
-
range.splitBoundariesPreservingPositions(positionsToPreserve);
|
810
952
|
|
811
|
-
|
812
|
-
if (this.removeEmptyElements) {
|
813
|
-
this.removeEmptyContainers(range, positionsToPreserve);
|
814
|
-
}
|
953
|
+
range.splitBoundariesPreservingPositions(positionsToPreserve);
|
815
954
|
|
816
|
-
|
817
|
-
|
818
|
-
|
955
|
+
// Tidy up the DOM by removing empty containers
|
956
|
+
if (applier.removeEmptyElements) {
|
957
|
+
applier.removeEmptyContainers(range, positionsToPreserve);
|
958
|
+
}
|
819
959
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
960
|
+
var textNodes = getEffectiveTextNodes(range);
|
961
|
+
var textNode, ancestorWithClass;
|
962
|
+
var lastTextNode = textNodes[textNodes.length - 1];
|
963
|
+
|
964
|
+
if (textNodes.length) {
|
965
|
+
applier.splitAncestorWithClass(range.endContainer, range.endOffset, positionsToPreserve);
|
966
|
+
applier.splitAncestorWithClass(range.startContainer, range.startOffset, positionsToPreserve);
|
967
|
+
for (var i = 0, len = textNodes.length; i < len; ++i) {
|
968
|
+
textNode = textNodes[i];
|
969
|
+
ancestorWithClass = applier.getSelfOrAncestorWithClass(textNode);
|
970
|
+
if (ancestorWithClass && applier.isModifiable(textNode)) {
|
971
|
+
applier.undoToAncestor(ancestorWithClass, positionsToPreserve);
|
972
|
+
}
|
826
973
|
}
|
827
|
-
|
828
974
|
// Ensure the range is still valid
|
829
975
|
range.setStartAndEnd(textNodes[0], 0, lastTextNode, lastTextNode.length);
|
830
|
-
}
|
831
976
|
|
832
977
|
|
833
|
-
|
834
|
-
|
835
|
-
|
978
|
+
if (applier.normalize) {
|
979
|
+
applier.postApply(textNodes, range, positionsToPreserve, true);
|
980
|
+
}
|
836
981
|
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
},
|
982
|
+
// Update the ranges from the preserved boundary positions
|
983
|
+
updateRangesFromBoundaries(rangesToPreserve, positionsToPreserve);
|
984
|
+
}
|
841
985
|
|
842
|
-
|
843
|
-
|
844
|
-
var i = ranges.length;
|
986
|
+
// Remove class from any appropriate empty elements
|
987
|
+
var emptyElements = applier.getEmptyElements(range);
|
845
988
|
|
846
|
-
|
847
|
-
|
848
|
-
|
989
|
+
forEach(emptyElements, function(el) {
|
990
|
+
removeClass(el, applier.className);
|
991
|
+
});
|
992
|
+
},
|
849
993
|
|
850
|
-
|
851
|
-
|
994
|
+
undoToRanges: function(ranges) {
|
995
|
+
// Get ranges returned in document order
|
996
|
+
var i = ranges.length;
|
852
997
|
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
this.undoToRanges(ranges);
|
857
|
-
sel.setRanges(ranges);
|
858
|
-
},
|
998
|
+
while (i--) {
|
999
|
+
this.undoToRange(ranges[i], ranges);
|
1000
|
+
}
|
859
1001
|
|
860
|
-
|
861
|
-
|
862
|
-
var textRange = range.cloneRange();
|
863
|
-
textRange.selectNodeContents(textNode);
|
1002
|
+
return ranges;
|
1003
|
+
},
|
864
1004
|
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
*/
|
1005
|
+
undoToSelection: function(win) {
|
1006
|
+
var sel = api.getSelection(win);
|
1007
|
+
var ranges = api.getSelection(win).getAllRanges();
|
1008
|
+
this.undoToRanges(ranges);
|
1009
|
+
sel.setRanges(ranges);
|
1010
|
+
},
|
872
1011
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
1012
|
+
isAppliedToRange: function(range) {
|
1013
|
+
if (range.collapsed || range.toString() == "") {
|
1014
|
+
return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
|
1015
|
+
} else {
|
1016
|
+
var textNodes = range.getNodes( [3] );
|
1017
|
+
if (textNodes.length)
|
1018
|
+
for (var i = 0, textNode; textNode = textNodes[i++]; ) {
|
1019
|
+
if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode) &&
|
1020
|
+
this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
|
1021
|
+
return false;
|
1022
|
+
}
|
883
1023
|
}
|
1024
|
+
return true;
|
884
1025
|
}
|
885
|
-
|
886
|
-
}
|
887
|
-
},
|
1026
|
+
},
|
888
1027
|
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
return false;
|
893
|
-
}
|
894
|
-
while (i--) {
|
895
|
-
if (!this.isAppliedToRange(ranges[i])) {
|
1028
|
+
isAppliedToRanges: function(ranges) {
|
1029
|
+
var i = ranges.length;
|
1030
|
+
if (i == 0) {
|
896
1031
|
return false;
|
897
1032
|
}
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
},
|
1033
|
+
while (i--) {
|
1034
|
+
if (!this.isAppliedToRange(ranges[i])) {
|
1035
|
+
return false;
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
return true;
|
1039
|
+
},
|
906
1040
|
|
907
|
-
|
908
|
-
|
909
|
-
this.
|
910
|
-
}
|
911
|
-
this.applyToRange(range);
|
912
|
-
}
|
913
|
-
},
|
1041
|
+
isAppliedToSelection: function(win) {
|
1042
|
+
var sel = api.getSelection(win);
|
1043
|
+
return this.isAppliedToRanges(sel.getAllRanges());
|
1044
|
+
},
|
914
1045
|
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
}
|
922
|
-
},
|
923
|
-
*/
|
1046
|
+
toggleRange: function(range) {
|
1047
|
+
if (this.isAppliedToRange(range)) {
|
1048
|
+
this.undoToRange(range);
|
1049
|
+
} else {
|
1050
|
+
this.applyToRange(range);
|
1051
|
+
}
|
1052
|
+
},
|
924
1053
|
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
}
|
931
|
-
},
|
932
|
-
|
933
|
-
getElementsWithClassIntersectingRange: function(range) {
|
934
|
-
var elements = [];
|
935
|
-
var applier = this;
|
936
|
-
range.getNodes([3], function(textNode) {
|
937
|
-
var el = applier.getSelfOrAncestorWithClass(textNode);
|
938
|
-
if (el && !contains(elements, el)) {
|
939
|
-
elements.push(el);
|
1054
|
+
toggleSelection: function(win) {
|
1055
|
+
if (this.isAppliedToSelection(win)) {
|
1056
|
+
this.undoToSelection(win);
|
1057
|
+
} else {
|
1058
|
+
this.applyToSelection(win);
|
940
1059
|
}
|
941
|
-
}
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
var applier = this;
|
950
|
-
sel.eachRange(function(range) {
|
951
|
-
var rangeElements = applier.getElementsWithClassIntersectingRange(range);
|
952
|
-
for (var i = 0, el; el = rangeElements[i++]; ) {
|
953
|
-
if (!contains(elements, el)) {
|
1060
|
+
},
|
1061
|
+
|
1062
|
+
getElementsWithClassIntersectingRange: function(range) {
|
1063
|
+
var elements = [];
|
1064
|
+
var applier = this;
|
1065
|
+
range.getNodes([3], function(textNode) {
|
1066
|
+
var el = applier.getSelfOrAncestorWithClass(textNode);
|
1067
|
+
if (el && !contains(elements, el)) {
|
954
1068
|
elements.push(el);
|
955
1069
|
}
|
956
|
-
}
|
957
|
-
|
958
|
-
|
959
|
-
},
|
960
|
-
*/
|
1070
|
+
});
|
1071
|
+
return elements;
|
1072
|
+
},
|
961
1073
|
|
962
|
-
|
963
|
-
|
1074
|
+
detach: function() {}
|
1075
|
+
};
|
964
1076
|
|
965
|
-
|
966
|
-
|
967
|
-
|
1077
|
+
function createClassApplier(className, options, tagNames) {
|
1078
|
+
return new ClassApplier(className, options, tagNames);
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
ClassApplier.util = {
|
1082
|
+
hasClass: hasClass,
|
1083
|
+
addClass: addClass,
|
1084
|
+
removeClass: removeClass,
|
1085
|
+
getClass: getClass,
|
1086
|
+
hasSameClasses: haveSameClasses,
|
1087
|
+
hasAllClasses: hasAllClasses,
|
1088
|
+
replaceWithOwnChildren: replaceWithOwnChildrenPreservingPositions,
|
1089
|
+
elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
|
1090
|
+
elementHasNonClassAttributes: elementHasNonClassAttributes,
|
1091
|
+
splitNodeAt: splitNodeAt,
|
1092
|
+
isEditableElement: isEditableElement,
|
1093
|
+
isEditingHost: isEditingHost,
|
1094
|
+
isEditable: isEditable
|
1095
|
+
};
|
968
1096
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
elementHasNonClassAttributes: elementHasNonClassAttributes,
|
977
|
-
splitNodeAt: splitNodeAt,
|
978
|
-
isEditableElement: isEditableElement,
|
979
|
-
isEditingHost: isEditingHost,
|
980
|
-
isEditable: isEditable
|
981
|
-
};
|
982
|
-
|
983
|
-
api.CssClassApplier = api.ClassApplier = ClassApplier;
|
984
|
-
api.createCssClassApplier = api.createClassApplier = createClassApplier;
|
985
|
-
});
|
1097
|
+
api.CssClassApplier = api.ClassApplier = ClassApplier;
|
1098
|
+
api.createClassApplier = createClassApplier;
|
1099
|
+
util.createAliasForDeprecatedMethod(api, "createCssClassApplier", "createClassApplier", module);
|
1100
|
+
});
|
1101
|
+
|
1102
|
+
return rangy;
|
1103
|
+
}, this);
|