rangy-rails 1.3alpha.804.0 → 1.3.1.pre.dev
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.
- 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);
|