rangy-rails 1.3alpha.772.0 → 1.3alpha.780.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,927 +9,7 @@
9
9
  *
10
10
  * Copyright 2013, Tim Down
11
11
  * Licensed under the MIT license.
12
- * Version: 1.3alpha.772
13
- * Build date: 26 February 2013
12
+ * Version: 1.3alpha.780M
13
+ * Build date: 17 May 2013
14
14
  */
15
- rangy.createModule("CssClassApplier", function(api, module) {
16
- api.requireModules( ["WrappedSelection", "WrappedRange"] );
17
-
18
- var dom = api.dom;
19
- var DomPosition = dom.DomPosition;
20
- var contains = dom.arrayContains;
21
-
22
-
23
- var defaultTagName = "span";
24
-
25
- function trim(str) {
26
- return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
27
- }
28
-
29
- function hasClass(el, cssClass) {
30
- return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
31
- }
32
-
33
- function addClass(el, cssClass) {
34
- if (el.className) {
35
- if (!hasClass(el, cssClass)) {
36
- el.className += " " + cssClass;
37
- }
38
- } else {
39
- el.className = cssClass;
40
- }
41
- }
42
-
43
- var removeClass = (function() {
44
- function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
45
- return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
46
- }
47
-
48
- return function(el, cssClass) {
49
- if (el.className) {
50
- el.className = el.className.replace(new RegExp("(^|\\s)" + cssClass + "(\\s|$)"), replacer);
51
- }
52
- };
53
- })();
54
-
55
- function sortClassName(className) {
56
- return className.split(/\s+/).sort().join(" ");
57
- }
58
-
59
- function getSortedClassName(el) {
60
- return sortClassName(el.className);
61
- }
62
-
63
- function haveSameClasses(el1, el2) {
64
- return getSortedClassName(el1) == getSortedClassName(el2);
65
- }
66
-
67
- function compareRanges(r1, r2) {
68
- return r1.compareBoundaryPoints(r2.START_TO_START, r2);
69
- }
70
-
71
- function movePosition(position, oldParent, oldIndex, newParent, newIndex) {
72
- var node = position.node, offset = position.offset;
73
-
74
- var newNode = node, newOffset = offset;
75
-
76
- if (node == newParent && offset > newIndex) {
77
- newOffset++;
78
- }
79
-
80
- if (node == oldParent && (offset == oldIndex || offset == oldIndex + 1)) {
81
- newNode = newParent;
82
- newOffset += newIndex - oldIndex;
83
- }
84
-
85
- if (node == oldParent && offset > oldIndex + 1) {
86
- newOffset--;
87
- }
88
-
89
- position.node = newNode;
90
- position.offset = newOffset;
91
- }
92
-
93
- function movePreservingPositions(node, newParent, newIndex, positionsToPreserve) {
94
- // For convenience, allow newIndex to be -1 to mean "insert at the end".
95
- if (newIndex == -1) {
96
- newIndex = newParent.childNodes.length;
97
- }
98
-
99
- var oldParent = node.parentNode;
100
- var oldIndex = dom.getNodeIndex(node);
101
-
102
- for (var i = 0, position; position = positionsToPreserve[i++]; ) {
103
- movePosition(position, oldParent, oldIndex, newParent, newIndex);
104
- }
105
-
106
- // Now actually move the node.
107
- if (newParent.childNodes.length == newIndex) {
108
- newParent.appendChild(node);
109
- } else {
110
- newParent.insertBefore(node, newParent.childNodes[newIndex]);
111
- }
112
- }
113
-
114
- function moveChildrenPreservingPositions(node, newParent, newIndex, removeNode, positionsToPreserve) {
115
- var child, children = [];
116
- while ( (child = node.firstChild) ) {
117
- movePreservingPositions(child, newParent, newIndex++, positionsToPreserve);
118
- children.push(child);
119
- }
120
- if (removeNode) {
121
- node.parentNode.removeChild(node);
122
- }
123
- return children;
124
- }
125
-
126
- function replaceWithOwnChildrenPreservingPositions(element, positionsToPreserve) {
127
- return moveChildrenPreservingPositions(element, element.parentNode, dom.getNodeIndex(element), true, positionsToPreserve);
128
- }
129
-
130
- function rangeSelectsAnyText(range, textNode) {
131
- var textNodeRange = range.cloneRange();
132
- textNodeRange.selectNodeContents(textNode);
133
-
134
- var intersectionRange = textNodeRange.intersection(range);
135
- var text = intersectionRange ? intersectionRange.toString() : "";
136
- textNodeRange.detach();
137
-
138
- return text != "";
139
- }
140
-
141
- function getEffectiveTextNodes(range) {
142
- var nodes = range.getNodes([3]);
143
-
144
- // Optimization as per issue 145
145
-
146
- // Remove non-intersecting text nodes from the start of the range
147
- var start = 0, node;
148
- while ( (node = nodes[start]) && !rangeSelectsAnyText(range, node) ) {
149
- ++start;
150
- }
151
-
152
- // Remove non-intersecting text nodes from the start of the range
153
- var end = nodes.length - 1;
154
- while ( (node = nodes[end]) && !rangeSelectsAnyText(range, node) ) {
155
- --end;
156
- }
157
-
158
- return nodes.slice(start, end + 1);
159
- }
160
-
161
- function elementsHaveSameNonClassAttributes(el1, el2) {
162
- if (el1.attributes.length != el2.attributes.length) return false;
163
- for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
164
- attr1 = el1.attributes[i];
165
- name = attr1.name;
166
- if (name != "class") {
167
- attr2 = el2.attributes.getNamedItem(name);
168
- if (attr1.specified != attr2.specified) return false;
169
- if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
170
- }
171
- }
172
- return true;
173
- }
174
-
175
- function elementHasNonClassAttributes(el, exceptions) {
176
- for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
177
- attrName = el.attributes[i].name;
178
- if ( !(exceptions && contains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
179
- return true;
180
- }
181
- }
182
- return false;
183
- }
184
-
185
- function elementHasProps(el, props) {
186
- var propValue;
187
- for (var p in props) {
188
- if (props.hasOwnProperty(p)) {
189
- propValue = props[p];
190
- if (typeof propValue == "object") {
191
- if (!elementHasProps(el[p], propValue)) {
192
- return false;
193
- }
194
- } else if (el[p] !== propValue) {
195
- return false;
196
- }
197
- }
198
- }
199
- return true;
200
- }
201
-
202
- var getComputedStyleProperty = dom.getComputedStyleProperty;
203
- var isEditableElement;
204
-
205
- (function() {
206
- var testEl = document.createElement("div");
207
- if (typeof testEl.isContentEditable == "boolean") {
208
- isEditableElement = function(node) {
209
- return node && node.nodeType == 1 && node.isContentEditable;
210
- };
211
- } else {
212
- isEditableElement = function(node) {
213
- if (!node || node.nodeType != 1 || node.contentEditable == "false") {
214
- return false;
215
- }
216
- return node.contentEditable == "true" || isEditableElement(node.parentNode);
217
- };
218
- }
219
- })();
220
-
221
- function isEditingHost(node) {
222
- var parent;
223
- return node && node.nodeType == 1
224
- && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
225
- || (isEditableElement(node) && !isEditableElement(node.parentNode)));
226
- }
227
-
228
- function isEditable(node) {
229
- return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
230
- }
231
-
232
- var inlineDisplayRegex = /^inline(-block|-table)?$/i;
233
-
234
- function isNonInlineElement(node) {
235
- return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
236
- }
237
-
238
- // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
239
- var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
240
-
241
- function isUnrenderedWhiteSpaceNode(node) {
242
- if (node.data.length == 0) {
243
- return true;
244
- }
245
- if (htmlNonWhiteSpaceRegex.test(node.data)) {
246
- return false;
247
- }
248
- var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
249
- switch (cssWhiteSpace) {
250
- case "pre":
251
- case "pre-wrap":
252
- case "-moz-pre-wrap":
253
- return false;
254
- case "pre-line":
255
- if (/[\r\n]/.test(node.data)) {
256
- return false;
257
- }
258
- }
259
-
260
- // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
261
- // non-inline element, it will not be rendered. This seems to be a good enough definition.
262
- return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
263
- }
264
-
265
- function getRangeBoundaries(ranges) {
266
- var positions = [], i, range;
267
- for (i = 0; range = ranges[i++]; ) {
268
- positions.push(
269
- new DomPosition(range.startContainer, range.startOffset),
270
- new DomPosition(range.endContainer, range.endOffset)
271
- );
272
- }
273
- return positions;
274
- }
275
-
276
- function updateRangesFromBoundaries(ranges, positions) {
277
- for (var i = 0, range, start, end, len = ranges.length; i < len; ++i) {
278
- range = ranges[i];
279
- start = positions[i * 2];
280
- end = positions[i * 2 + 1];
281
- range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
282
- }
283
- }
284
-
285
- function isSplitPoint(node, offset) {
286
- if (dom.isCharacterDataNode(node)) {
287
- if (offset == 0) {
288
- return !!node.previousSibling;
289
- } else if (offset == node.length) {
290
- return !!node.nextSibling;
291
- } else {
292
- return true;
293
- }
294
- }
295
-
296
- return offset > 0 && offset < node.childNodes.length;
297
- }
298
-
299
- function splitNodeAt(node, descendantNode, descendantOffset, positionsToPreserve) {
300
- var newNode, parentNode;
301
- var splitAtStart = (descendantOffset == 0);
302
-
303
- if (dom.isAncestorOf(descendantNode, node)) {
304
- return node;
305
- }
306
-
307
- if (dom.isCharacterDataNode(descendantNode)) {
308
- var descendantIndex = dom.getNodeIndex(descendantNode);
309
- if (descendantOffset == 0) {
310
- descendantOffset = descendantIndex;
311
- } else if (descendantOffset == descendantNode.length) {
312
- descendantOffset = descendantIndex + 1;
313
- } else {
314
- throw module.createError("splitNodeAt() should not be called with offset in the middle of a data node ("
315
- + descendantOffset + " in " + descendantNode.data);
316
- }
317
- descendantNode = descendantNode.parentNode;
318
- }
319
-
320
- if (isSplitPoint(descendantNode, descendantOffset)) {
321
- // descendantNode is now guaranteed not to be a text or other character node
322
- newNode = descendantNode.cloneNode(false);
323
- parentNode = descendantNode.parentNode;
324
- if (newNode.id) {
325
- newNode.removeAttribute("id");
326
- }
327
- var child, newChildIndex = 0;
328
-
329
- while ( (child = descendantNode.childNodes[descendantOffset]) ) {
330
- movePreservingPositions(child, newNode, newChildIndex++, positionsToPreserve);
331
- //newNode.appendChild(child);
332
- }
333
- movePreservingPositions(newNode, parentNode, dom.getNodeIndex(descendantNode) + 1, positionsToPreserve);
334
- //dom.insertAfter(newNode, descendantNode);
335
- return (descendantNode == node) ? newNode : splitNodeAt(node, parentNode, dom.getNodeIndex(newNode), positionsToPreserve);
336
- } else if (node != descendantNode) {
337
- newNode = descendantNode.parentNode;
338
-
339
- // Work out a new split point in the parent node
340
- var newNodeIndex = dom.getNodeIndex(descendantNode);
341
-
342
- if (!splitAtStart) {
343
- newNodeIndex++;
344
- }
345
- return splitNodeAt(node, newNode, newNodeIndex, positionsToPreserve);
346
- }
347
- return node;
348
- }
349
-
350
- function areElementsMergeable(el1, el2) {
351
- return el1.tagName == el2.tagName
352
- && haveSameClasses(el1, el2)
353
- && elementsHaveSameNonClassAttributes(el1, el2)
354
- && getComputedStyleProperty(el1, "display") == "inline"
355
- && getComputedStyleProperty(el2, "display") == "inline";
356
- }
357
-
358
- function createAdjacentMergeableTextNodeGetter(forward) {
359
- var propName = forward ? "nextSibling" : "previousSibling";
360
-
361
- return function(textNode, checkParentElement) {
362
- var el = textNode.parentNode;
363
- var adjacentNode = textNode[propName];
364
- if (adjacentNode) {
365
- // Can merge if the node's previous/next sibling is a text node
366
- if (adjacentNode && adjacentNode.nodeType == 3) {
367
- return adjacentNode;
368
- }
369
- } else if (checkParentElement) {
370
- // Compare text node parent element with its sibling
371
- adjacentNode = el[propName];
372
- if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)/* && adjacentNode.hasChildNodes()*/) {
373
- return adjacentNode[forward ? "firstChild" : "lastChild"];
374
- }
375
- }
376
- return null;
377
- };
378
- }
379
-
380
- var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
381
- getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
382
-
383
-
384
- function Merge(firstNode) {
385
- this.isElementMerge = (firstNode.nodeType == 1);
386
- this.textNodes = [];
387
- var firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
388
- if (firstTextNode) {
389
- this.textNodes[0] = firstTextNode;
390
- }
391
- }
392
-
393
- Merge.prototype = {
394
- doMerge: function(positionsToPreserve) {
395
- var textNodes = this.textNodes;
396
- var firstTextNode = textNodes[0];
397
- if (textNodes.length > 1) {
398
- var textParts = [], combinedTextLength = 0, textNode, parent;
399
- for (var i = 0, len = textNodes.length, j, position; i < len; ++i) {
400
- textNode = textNodes[i];
401
- parent = textNode.parentNode;
402
- if (i > 0) {
403
- parent.removeChild(textNode);
404
- if (!parent.hasChildNodes()) {
405
- parent.parentNode.removeChild(parent);
406
- }
407
- if (positionsToPreserve) {
408
- for (j = 0; position = positionsToPreserve[j++]; ) {
409
- // Handle case where position is inside the text node being merged into a preceding node
410
- if (position.node == textNode) {
411
- position.node = firstTextNode;
412
- position.offset += combinedTextLength;
413
- }
414
- }
415
- }
416
- }
417
- textParts[i] = textNode.data;
418
- combinedTextLength += textNode.data.length;
419
- }
420
- firstTextNode.data = textParts.join("");
421
- }
422
- return firstTextNode.data;
423
- },
424
-
425
- getLength: function() {
426
- var i = this.textNodes.length, len = 0;
427
- while (i--) {
428
- len += this.textNodes[i].length;
429
- }
430
- return len;
431
- },
432
-
433
- toString: function() {
434
- var textBits = [];
435
- for (var i = 0, len = this.textNodes.length; i < len; ++i) {
436
- textBits[i] = "'" + this.textNodes[i].data + "'";
437
- }
438
- return "[Merge(" + textBits.join(",") + ")]";
439
- }
440
- };
441
-
442
- var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly", "useExistingElements",
443
- "removeEmptyElements"];
444
-
445
- // TODO: Populate this with every attribute name that corresponds to a property with a different name
446
- var attrNamesForProperties = {};
447
-
448
- function ClassApplier(cssClass, options, tagNames) {
449
- this.cssClass = cssClass;
450
- var normalize, i, len, propName;
451
-
452
- var elementPropertiesFromOptions = null;
453
-
454
- // Initialize from options object
455
- if (typeof options == "object" && options !== null) {
456
- tagNames = options.tagNames;
457
- elementPropertiesFromOptions = options.elementProperties;
458
-
459
- for (i = 0; propName = optionProperties[i++]; ) {
460
- if (options.hasOwnProperty(propName)) {
461
- this[propName] = options[propName];
462
- }
463
- }
464
- normalize = options.normalize;
465
- } else {
466
- normalize = options;
467
- }
468
-
469
- // Backward compatibility: the second parameter can also be a Boolean indicating to normalize after unapplying
470
- this.normalize = (typeof normalize == "undefined") ? true : normalize;
471
-
472
- // Initialize element properties and attribute exceptions
473
- this.attrExceptions = [];
474
- var el = document.createElement(this.elementTagName);
475
- this.elementProperties = this.copyPropertiesToElement(elementPropertiesFromOptions, el, true);
476
-
477
- this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ?
478
- this.elementProperties.className : cssClass;
479
-
480
- // Initialize tag names
481
- this.applyToAnyTagName = false;
482
- var type = typeof tagNames;
483
- if (type == "string") {
484
- if (tagNames == "*") {
485
- this.applyToAnyTagName = true;
486
- } else {
487
- this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
488
- }
489
- } else if (type == "object" && typeof tagNames.length == "number") {
490
- this.tagNames = [];
491
- for (i = 0, len = tagNames.length; i < len; ++i) {
492
- if (tagNames[i] == "*") {
493
- this.applyToAnyTagName = true;
494
- } else {
495
- this.tagNames.push(tagNames[i].toLowerCase());
496
- }
497
- }
498
- } else {
499
- this.tagNames = [this.elementTagName];
500
- }
501
- }
502
-
503
- ClassApplier.prototype = {
504
- elementTagName: defaultTagName,
505
- elementProperties: {},
506
- ignoreWhiteSpace: true,
507
- applyToEditableOnly: false,
508
- useExistingElements: true,
509
- removeEmptyElements: true,
510
-
511
- copyPropertiesToElement: function(props, el, createCopy) {
512
- var s, elStyle, elProps = {}, elPropsStyle, propValue, elPropValue, attrName;
513
-
514
- for (var p in props) {
515
- if (props.hasOwnProperty(p)) {
516
- propValue = props[p];
517
- elPropValue = el[p];
518
-
519
- // Special case for class. The copied properties object has the applier's CSS class as well as its
520
- // own to simplify checks when removing styling elements
521
- if (p == "className") {
522
- addClass(el, propValue);
523
- addClass(el, this.cssClass);
524
- el[p] = sortClassName(el[p]);
525
- if (createCopy) {
526
- elProps[p] = el[p];
527
- }
528
- }
529
-
530
- // Special case for style
531
- else if (p == "style") {
532
- elStyle = elPropValue;
533
- if (createCopy) {
534
- elProps[p] = elPropsStyle = {};
535
- }
536
- for (s in props[p]) {
537
- elStyle[s] = propValue[s];
538
- if (createCopy) {
539
- elPropsStyle[s] = elStyle[s];
540
- }
541
- }
542
- this.attrExceptions.push(p);
543
- } else {
544
- el[p] = propValue;
545
- // Copy the property back from the dummy element so that later comparisons to check whether
546
- // elements may be removed are checking against the right value. For example, the href property
547
- // of an element returns a fully qualified URL even if it was previously assigned a relative
548
- // URL.
549
- if (createCopy) {
550
- elProps[p] = el[p];
551
-
552
- // Not all properties map to identically named attributes
553
- attrName = attrNamesForProperties.hasOwnProperty(p) ? attrNamesForProperties[p] : p;
554
- this.attrExceptions.push(attrName);
555
- }
556
- }
557
- }
558
- }
559
-
560
- return createCopy ? elProps : "";
561
- },
562
-
563
- hasClass: function(node) {
564
- return node.nodeType == 1 &&
565
- contains(this.tagNames, node.tagName.toLowerCase()) &&
566
- hasClass(node, this.cssClass);
567
- },
568
-
569
- getSelfOrAncestorWithClass: function(node) {
570
- while (node) {
571
- if (this.hasClass(node)) {
572
- return node;
573
- }
574
- node = node.parentNode;
575
- }
576
- return null;
577
- },
578
-
579
- isModifiable: function(node) {
580
- return !this.applyToEditableOnly || isEditable(node);
581
- },
582
-
583
- // White space adjacent to an unwrappable node can be ignored for wrapping
584
- isIgnorableWhiteSpaceNode: function(node) {
585
- return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
586
- },
587
-
588
- // Normalizes nodes after applying a CSS class to a Range.
589
- postApply: function(textNodes, range, positionsToPreserve, isUndo) {
590
- var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
591
-
592
- var merges = [], currentMerge;
593
-
594
- var rangeStartNode = firstNode, rangeEndNode = lastNode;
595
- var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
596
-
597
- var textNode, precedingTextNode;
598
-
599
- // Check for every required merge and create a Merge object for each
600
- for (var i = 0, len = textNodes.length; i < len; ++i) {
601
- textNode = textNodes[i];
602
- precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
603
- if (precedingTextNode) {
604
- if (!currentMerge) {
605
- currentMerge = new Merge(precedingTextNode);
606
- merges.push(currentMerge);
607
- }
608
- currentMerge.textNodes.push(textNode);
609
- if (textNode === firstNode) {
610
- rangeStartNode = currentMerge.textNodes[0];
611
- rangeStartOffset = rangeStartNode.length;
612
- }
613
- if (textNode === lastNode) {
614
- rangeEndNode = currentMerge.textNodes[0];
615
- rangeEndOffset = currentMerge.getLength();
616
- }
617
- } else {
618
- currentMerge = null;
619
- }
620
- }
621
-
622
- // Test whether the first node after the range needs merging
623
- var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
624
-
625
- if (nextTextNode) {
626
- if (!currentMerge) {
627
- currentMerge = new Merge(lastNode);
628
- merges.push(currentMerge);
629
- }
630
- currentMerge.textNodes.push(nextTextNode);
631
- }
632
-
633
- // Apply the merges
634
- if (merges.length) {
635
- for (i = 0, len = merges.length; i < len; ++i) {
636
- merges[i].doMerge(positionsToPreserve);
637
- }
638
-
639
- // Set the range boundaries
640
- range.setStartAndEnd(rangeStartNode, rangeStartOffset, rangeEndNode, rangeEndOffset);
641
- }
642
- },
643
-
644
- createContainer: function(doc) {
645
- var el = doc.createElement(this.elementTagName);
646
- this.copyPropertiesToElement(this.elementProperties, el, false);
647
- addClass(el, this.cssClass);
648
- return el;
649
- },
650
-
651
- applyToTextNode: function(textNode, positionsToPreserve) {
652
- var parent = textNode.parentNode;
653
- if (parent.childNodes.length == 1 &&
654
- this.useExistingElements &&
655
- contains(this.tagNames, parent.tagName.toLowerCase()) &&
656
- elementHasProps(parent, this.elementProperties)) {
657
-
658
- addClass(parent, this.cssClass);
659
- } else {
660
- var el = this.createContainer(dom.getDocument(textNode));
661
- textNode.parentNode.insertBefore(el, textNode);
662
- el.appendChild(textNode);
663
- }
664
- },
665
-
666
- isRemovable: function(el) {
667
- return el.tagName.toLowerCase() == this.elementTagName
668
- && getSortedClassName(el) == this.elementSortedClassName
669
- && elementHasProps(el, this.elementProperties)
670
- && !elementHasNonClassAttributes(el, this.attrExceptions)
671
- && this.isModifiable(el);
672
- },
673
-
674
- isEmptyContainer: function(el) {
675
- var childNodeCount = el.childNodes.length;
676
- return el.nodeType == 1
677
- && this.isRemovable(el)
678
- && (childNodeCount == 0 || (childNodeCount == 1 && this.isEmptyContainer(el.firstChild)));
679
- },
680
-
681
- removeEmptyContainers: function(range) {
682
- var applier = this;
683
- var nodesToRemove = range.getNodes([1], function(el) {
684
- return applier.isEmptyContainer(el);
685
- });
686
-
687
- for (var i = 0, node; node = nodesToRemove[i++]; ) {
688
- node.parentNode.removeChild(node);
689
- }
690
- },
691
-
692
- undoToTextNode: function(textNode, range, ancestorWithClass, positionsToPreserve) {
693
- if (!range.containsNode(ancestorWithClass)) {
694
- // Split out the portion of the ancestor from which we can remove the CSS class
695
- //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
696
- var ancestorRange = range.cloneRange();
697
- ancestorRange.selectNode(ancestorWithClass);
698
- if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)) {
699
- splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, positionsToPreserve);
700
- range.setEndAfter(ancestorWithClass);
701
- }
702
- if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)) {
703
- ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, positionsToPreserve);
704
- }
705
- }
706
- if (this.isRemovable(ancestorWithClass)) {
707
- replaceWithOwnChildrenPreservingPositions(ancestorWithClass, positionsToPreserve);
708
- } else {
709
- removeClass(ancestorWithClass, this.cssClass);
710
- }
711
- },
712
-
713
- applyToRange: function(range, rangesToPreserve) {
714
- rangesToPreserve = rangesToPreserve || [];
715
-
716
- // Create an array of range boundaries to preserve
717
- var positionsToPreserve = getRangeBoundaries(rangesToPreserve || []);
718
-
719
- range.splitBoundariesPreservingPositions(positionsToPreserve);
720
-
721
- // Tidy up the DOM by removing empty containers
722
- if (this.removeEmptyElements) {
723
- this.removeEmptyContainers(range);
724
- }
725
-
726
- var textNodes = getEffectiveTextNodes(range);
727
-
728
- if (textNodes.length) {
729
- for (var i = 0, textNode; textNode = textNodes[i++]; ) {
730
- if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode)
731
- && this.isModifiable(textNode)) {
732
- this.applyToTextNode(textNode, positionsToPreserve);
733
- }
734
- }
735
- textNode = textNodes[textNodes.length - 1];
736
- range.setStartAndEnd(textNodes[0], 0, textNode, textNode.length);
737
- if (this.normalize) {
738
- this.postApply(textNodes, range, positionsToPreserve, false);
739
- }
740
-
741
- // Update the ranges from the preserved boundary positions
742
- updateRangesFromBoundaries(rangesToPreserve, positionsToPreserve);
743
- }
744
- },
745
-
746
- applyToRanges: function(ranges) {
747
-
748
- var i = ranges.length;
749
- while (i--) {
750
- this.applyToRange(ranges[i], ranges);
751
- }
752
-
753
-
754
- return ranges;
755
- },
756
-
757
- applyToSelection: function(win) {
758
- var sel = api.getSelection(win);
759
- sel.setRanges( this.applyToRanges(sel.getAllRanges()) );
760
- },
761
-
762
- undoToRange: function(range, rangesToPreserve) {
763
- // Create an array of range boundaries to preserve
764
- rangesToPreserve = rangesToPreserve || [];
765
- var positionsToPreserve = getRangeBoundaries(rangesToPreserve);
766
-
767
-
768
- range.splitBoundariesPreservingPositions(positionsToPreserve);
769
-
770
- // Tidy up the DOM by removing empty containers
771
- if (this.removeEmptyElements) {
772
- this.removeEmptyContainers(range, positionsToPreserve);
773
- }
774
-
775
- var textNodes = getEffectiveTextNodes(range);
776
- var textNode, ancestorWithClass;
777
- var lastTextNode = textNodes[textNodes.length - 1];
778
-
779
- if (textNodes.length) {
780
- for (var i = 0, len = textNodes.length; i < len; ++i) {
781
- textNode = textNodes[i];
782
- ancestorWithClass = this.getSelfOrAncestorWithClass(textNode);
783
- if (ancestorWithClass && this.isModifiable(textNode)) {
784
- this.undoToTextNode(textNode, range, ancestorWithClass, positionsToPreserve);
785
- }
786
-
787
- // Ensure the range is still valid
788
- range.setStartAndEnd(textNodes[0], 0, lastTextNode, lastTextNode.length);
789
- }
790
-
791
-
792
- if (this.normalize) {
793
- this.postApply(textNodes, range, positionsToPreserve, true);
794
- }
795
-
796
- // Update the ranges from the preserved boundary positions
797
- updateRangesFromBoundaries(rangesToPreserve, positionsToPreserve);
798
- }
799
- },
800
-
801
- undoToRanges: function(ranges) {
802
- // Get ranges returned in document order
803
- var i = ranges.length;
804
-
805
- while (i--) {
806
- this.undoToRange(ranges[i], ranges);
807
- }
808
-
809
- return ranges;
810
- },
811
-
812
- undoToSelection: function(win) {
813
- var sel = api.getSelection(win);
814
- var ranges = api.getSelection(win).getAllRanges();
815
- this.undoToRanges(ranges);
816
- sel.setRanges(ranges);
817
- },
818
-
819
- getTextSelectedByRange: function(textNode, range) {
820
- var textRange = range.cloneRange();
821
- textRange.selectNodeContents(textNode);
822
-
823
- var intersectionRange = textRange.intersection(range);
824
- var text = intersectionRange ? intersectionRange.toString() : "";
825
- textRange.detach();
826
-
827
- return text;
828
- },
829
-
830
- isAppliedToRange: function(range) {
831
- if (range.collapsed || range.toString() == "") {
832
- return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
833
- } else {
834
- var textNodes = range.getNodes( [3] );
835
- if (textNodes.length)
836
- for (var i = 0, textNode; textNode = textNodes[i++]; ) {
837
- if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode)
838
- && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
839
- return false;
840
- }
841
- }
842
- return true;
843
- }
844
- },
845
-
846
- isAppliedToRanges: function(ranges) {
847
- var i = ranges.length;
848
- while (i--) {
849
- if (!this.isAppliedToRange(ranges[i])) {
850
- return false;
851
- }
852
- }
853
- return true;
854
- },
855
-
856
- isAppliedToSelection: function(win) {
857
- var sel = api.getSelection(win);
858
- return this.isAppliedToRanges(sel.getAllRanges());
859
- },
860
-
861
- toggleRange: function(range) {
862
- if (this.isAppliedToRange(range)) {
863
- this.undoToRange(range);
864
- } else {
865
- this.applyToRange(range);
866
- }
867
- },
868
-
869
- toggleRanges: function(ranges) {
870
- if (this.isAppliedToRanges(ranges)) {
871
- this.undoToRanges(ranges);
872
- } else {
873
- this.applyToRanges(ranges);
874
- }
875
- },
876
-
877
- toggleSelection: function(win) {
878
- if (this.isAppliedToSelection(win)) {
879
- this.undoToSelection(win);
880
- } else {
881
- this.applyToSelection(win);
882
- }
883
- },
884
-
885
- getElementsWithClassIntersectingRange: function(range) {
886
- var elements = [];
887
- var applier = this;
888
- range.getNodes([3], function(textNode) {
889
- var el = applier.getSelfOrAncestorWithClass(textNode);
890
- if (el && !contains(elements, el)) {
891
- elements.push(el);
892
- }
893
- });
894
- return elements;
895
- },
896
-
897
- getElementsWithClassIntersectingSelection: function(win) {
898
- var sel = api.getSelection(win);
899
- var elements = [];
900
- var applier = this;
901
- sel.eachRange(function(range) {
902
- var rangeElements = applier.getElementsWithClassIntersectingRange(range);
903
- for (var i = 0, el; el = rangeElements[i++]; ) {
904
- if (!contains(elements, el)) {
905
- elements.push(el);
906
- }
907
- }
908
- });
909
- return elements;
910
- },
911
-
912
- detach: function() {}
913
- };
914
-
915
- function createClassApplier(cssClass, options, tagNames) {
916
- return new ClassApplier(cssClass, options, tagNames);
917
- }
918
-
919
- ClassApplier.util = {
920
- hasClass: hasClass,
921
- addClass: addClass,
922
- removeClass: removeClass,
923
- hasSameClasses: haveSameClasses,
924
- replaceWithOwnChildren: replaceWithOwnChildrenPreservingPositions,
925
- elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
926
- elementHasNonClassAttributes: elementHasNonClassAttributes,
927
- splitNodeAt: splitNodeAt,
928
- isEditableElement: isEditableElement,
929
- isEditingHost: isEditingHost,
930
- isEditable: isEditable
931
- };
932
-
933
- api.CssClassApplier = api.ClassApplier = ClassApplier;
934
- api.createCssClassApplier = api.createClassApplier = createClassApplier;
935
- });
15
+ rangy.createModule("ClassApplier",["WrappedSelection"],function(a,b){function g(a){return a.replace(/^\s\s*/,"").replace(/\s\s*$/,"")}function h(a,b){return a.className&&(new RegExp("(?:^|\\s)"+b+"(?:\\s|$)")).test(a.className)}function i(a,b){a.className?h(a,b)||(a.className+=" "+b):a.className=b}function k(a){return a.split(/\s+/).sort().join(" ")}function l(a){return k(a.className)}function m(a,b){return l(a)==l(b)}function n(a,b){return a.compareBoundaryPoints(b.START_TO_START,b)}function o(a,b,c,d,e){var f=a.node,g=a.offset,h=f,i=g;f==d&&g>e&&i++,f==b&&(g==c||g==c+1)&&(h=d,i+=e-c),f==b&&g>c+1&&i--,a.node=h,a.offset=i}function p(a,b,d,e){d==-1&&(d=b.childNodes.length);var f=a.parentNode,g=c.getNodeIndex(a);for(var h=0,i;i=e[h++];)o(i,f,g,b,d);b.childNodes.length==d?b.appendChild(a):b.insertBefore(a,b.childNodes[d])}function q(a,b,c,d,e){var f,g=[];while(f=a.firstChild)p(f,b,c++,e),g.push(f);return d&&a.parentNode.removeChild(a),g}function r(a,b){return q(a,a.parentNode,c.getNodeIndex(a),!0,b)}function s(a,b){var c=a.cloneRange();c.selectNodeContents(b);var d=c.intersection(a),e=d?d.toString():"";return c.detach(),e!=""}function t(a){var b=a.getNodes([3]),c=0,d;while((d=b[c])&&!s(a,d))++c;var e=b.length-1;while((d=b[e])&&!s(a,d))--e;return b.slice(c,e+1)}function u(a,b){if(a.attributes.length!=b.attributes.length)return!1;for(var c=0,d=a.attributes.length,e,f,g;c<d;++c){e=a.attributes[c],g=e.name;if(g!="class"){f=b.attributes.getNamedItem(g);if(e.specified!=f.specified)return!1;if(e.specified&&e.nodeValue!==f.nodeValue)return!1}}return!0}function v(a,b){for(var c=0,d=a.attributes.length,f;c<d;++c){f=a.attributes[c].name;if((!b||!e(b,f))&&a.attributes[c].specified&&f!="class")return!0}return!1}function w(a,b){var c;for(var d in b)if(b.hasOwnProperty(d)){c=b[d];if(typeof c=="object"){if(!w(a[d],c))return!1}else if(a[d]!==c)return!1}return!0}function z(a){var b;return a&&a.nodeType==1&&((b=a.parentNode)&&b.nodeType==9&&b.designMode=="on"||y(a)&&!y(a.parentNode))}function A(a){return(y(a)||a.nodeType!=1&&y(a.parentNode))&&!z(a)}function C(a){return a&&a.nodeType==1&&!B.test(x(a,"display"))}function E(a){if(a.data.length==0)return!0;if(D.test(a.data))return!1;var b=x(a.parentNode,"whiteSpace");switch(b){case"pre":case"pre-wrap":case"-moz-pre-wrap":return!1;case"pre-line":if(/[\r\n]/.test(a.data))return!1}return C(a.previousSibling)||C(a.nextSibling)}function F(a){var b=[],c,e;for(c=0;e=a[c++];)b.push(new d(e.startContainer,e.startOffset),new d(e.endContainer,e.endOffset));return b}function G(a,b){for(var c=0,d,e,f,g=a.length;c<g;++c)d=a[c],e=b[c*2],f=b[c*2+1],d.setStartAndEnd(e.node,e.offset,f.node,f.offset)}function H(a,b){return c.isCharacterDataNode(a)?b==0?!!a.previousSibling:b==a.length?!!a.nextSibling:!0:b>0&&b<a.childNodes.length}function I(a,d,e,f){var g,h,i=e==0;if(c.isAncestorOf(d,a))return a;if(c.isCharacterDataNode(d)){var j=c.getNodeIndex(d);if(e==0)e=j;else{if(e!=d.length)throw b.createError("splitNodeAt() should not be called with offset in the middle of a data node ("+e+" in "+d.data);e=j+1}d=d.parentNode}if(H(d,e)){g=d.cloneNode(!1),h=d.parentNode,g.id&&g.removeAttribute("id");var k,l=0;while(k=d.childNodes[e])p(k,g,l++,f);return p(g,h,c.getNodeIndex(d)+1,f),d==a?g:I(a,h,c.getNodeIndex(g),f)}if(a!=d){g=d.parentNode;var m=c.getNodeIndex(d);return i||m++,I(a,g,m,f)}return a}function J(a,b){return a.tagName==b.tagName&&m(a,b)&&u(a,b)&&x(a,"display")=="inline"&&x(b,"display")=="inline"}function K(a){var b=a?"nextSibling":"previousSibling";return function(c,d){var e=c.parentNode,f=c[b];if(f){if(f&&f.nodeType==3)return f}else if(d){f=e[b];if(f&&f.nodeType==1&&J(e,f))return f[a?"firstChild":"lastChild"]}return null}}function N(a){this.isElementMerge=a.nodeType==1,this.textNodes=[];var b=this.isElementMerge?a.lastChild:a;b&&(this.textNodes[0]=b)}function Q(a,b,c){this.cssClass=a;var d,e,f,h,i=null;if(typeof b=="object"&&b!==null){c=b.tagNames,i=b.elementProperties;for(e=0;h=O[e++];)b.hasOwnProperty(h)&&(this[h]=b[h]);d=b.normalize}else d=b;this.normalize=typeof d=="undefined"?!0:d,this.attrExceptions=[];var j=document.createElement(this.elementTagName);this.elementProperties=this.copyPropertiesToElement(i,j,!0),this.elementSortedClassName=this.elementProperties.hasOwnProperty("className")?this.elementProperties.className:a,this.applyToAnyTagName=!1;var k=typeof c;if(k=="string")c=="*"?this.applyToAnyTagName=!0:this.tagNames=g(c.toLowerCase()).split(/\s*,\s*/);else if(k=="object"&&typeof c.length=="number"){this.tagNames=[];for(e=0,f=c.length;e<f;++e)c[e]=="*"?this.applyToAnyTagName=!0:this.tagNames.push(c[e].toLowerCase())}else this.tagNames=[this.elementTagName]}function R(a,b,c){return new Q(a,b,c)}var c=a.dom,d=c.DomPosition,e=c.arrayContains,f="span",j=function(){function a(a,b,c){return b&&c?" ":""}return function(b,c){b.className&&(b.className=b.className.replace(new RegExp("(^|\\s)"+c+"(\\s|$)"),a))}}(),x=c.getComputedStyleProperty,y;(function(){var a=document.createElement("div");typeof a.isContentEditable=="boolean"?y=function(a){return a&&a.nodeType==1&&a.isContentEditable}:y=function(a){return!a||a.nodeType!=1||a.contentEditable=="false"?!1:a.contentEditable=="true"||y(a.parentNode)}})();var B=/^inline(-block|-table)?$/i,D=/[^\r\n\t\f \u200B]/,L=K(!1),M=K(!0);N.prototype={doMerge:function(a){var b=this.textNodes,c=b[0];if(b.length>1){var d=[],e=0,f,g;for(var h=0,i=b.length,j,k;h<i;++h){f=b[h],g=f.parentNode;if(h>0){g.removeChild(f),g.hasChildNodes()||g.parentNode.removeChild(g);if(a)for(j=0;k=a[j++];)k.node==f&&(k.node=c,k.offset+=e)}d[h]=f.data,e+=f.data.length}c.data=d.join("")}return c.data},getLength:function(){var a=this.textNodes.length,b=0;while(a--)b+=this.textNodes[a].length;return b},toString:function(){var a=[];for(var b=0,c=this.textNodes.length;b<c;++b)a[b]="'"+this.textNodes[b].data+"'";return"[Merge("+a.join(",")+")]"}};var O=["elementTagName","ignoreWhiteSpace","applyToEditableOnly","useExistingElements","removeEmptyElements"],P={};Q.prototype={elementTagName:f,elementProperties:{},ignoreWhiteSpace:!0,applyToEditableOnly:!1,useExistingElements:!0,removeEmptyElements:!0,copyPropertiesToElement:function(a,b,c){var d,e,f={},g,h,j,l;for(var m in a)if(a.hasOwnProperty(m)){h=a[m],j=b[m];if(m=="className")i(b,h),i(b,this.cssClass),b[m]=k(b[m]),c&&(f[m]=b[m]);else if(m=="style"){e=j,c&&(f[m]=g={});for(d in a[m])e[d]=h[d],c&&(g[d]=e[d]);this.attrExceptions.push(m)}else b[m]=h,c&&(f[m]=b[m],l=P.hasOwnProperty(m)?P[m]:m,this.attrExceptions.push(l))}return c?f:""},hasClass:function(a){return a.nodeType==1&&e(this.tagNames,a.tagName.toLowerCase())&&h(a,this.cssClass)},getSelfOrAncestorWithClass:function(a){while(a){if(this.hasClass(a))return a;a=a.parentNode}return null},isModifiable:function(a){return!this.applyToEditableOnly||A(a)},isIgnorableWhiteSpaceNode:function(a){return this.ignoreWhiteSpace&&a&&a.nodeType==3&&E(a)},postApply:function(a,b,c,d){var e=a[0],f=a[a.length-1],g=[],h,i=e,j=f,k=0,l=f.length,m,n;for(var o=0,p=a.length;o<p;++o)m=a[o],n=L(m,!d),n?(h||(h=new N(n),g.push(h)),h.textNodes.push(m),m===e&&(i=h.textNodes[0],k=i.length),m===f&&(j=h.textNodes[0],l=h.getLength())):h=null;var q=M(f,!d);q&&(h||(h=new N(f),g.push(h)),h.textNodes.push(q));if(g.length){for(o=0,p=g.length;o<p;++o)g[o].doMerge(c);b.setStartAndEnd(i,k,j,l)}},createContainer:function(a){var b=a.createElement(this.elementTagName);return this.copyPropertiesToElement(this.elementProperties,b,!1),i(b,this.cssClass),b},applyToTextNode:function(a,b){var d=a.parentNode;if(d.childNodes.length==1&&this.useExistingElements&&e(this.tagNames,d.tagName.toLowerCase())&&w(d,this.elementProperties))i(d,this.cssClass);else{var f=this.createContainer(c.getDocument(a));a.parentNode.insertBefore(f,a),f.appendChild(a)}},isRemovable:function(a){return a.tagName.toLowerCase()==this.elementTagName&&l(a)==this.elementSortedClassName&&w(a,this.elementProperties)&&!v(a,this.attrExceptions)&&this.isModifiable(a)},isEmptyContainer:function(a){var b=a.childNodes.length;return a.nodeType==1&&this.isRemovable(a)&&(b==0||b==1&&this.isEmptyContainer(a.firstChild))},removeEmptyContainers:function(a){var b=this,c=a.getNodes([1],function(a){return b.isEmptyContainer(a)});for(var d=0,e;e=c[d++];)e.parentNode.removeChild(e)},undoToTextNode:function(a,b,c,d){if(!b.containsNode(c)){var e=b.cloneRange();e.selectNode(c),e.isPointInRange(b.endContainer,b.endOffset)&&(I(c,b.endContainer,b.endOffset,d),b.setEndAfter(c)),e.isPointInRange(b.startContainer,b.startOffset)&&(c=I(c,b.startContainer,b.startOffset,d))}this.isRemovable(c)?r(c,d):j(c,this.cssClass)},applyToRange:function(a,b){b=b||[];var c=F(b||[]);a.splitBoundariesPreservingPositions(c),this.removeEmptyElements&&this.removeEmptyContainers(a);var d=t(a);if(d.length){for(var e=0,f;f=d[e++];)!this.isIgnorableWhiteSpaceNode(f)&&!this.getSelfOrAncestorWithClass(f)&&this.isModifiable(f)&&this.applyToTextNode(f,c);f=d[d.length-1],a.setStartAndEnd(d[0],0,f,f.length),this.normalize&&this.postApply(d,a,c,!1),G(b,c)}},applyToRanges:function(a){var b=a.length;while(b--)this.applyToRange(a[b],a);return a},applyToSelection:function(b){var c=a.getSelection(b);c.setRanges(this.applyToRanges(c.getAllRanges()))},undoToRange:function(a,b){b=b||[];var c=F(b);a.splitBoundariesPreservingPositions(c),this.removeEmptyElements&&this.removeEmptyContainers(a,c);var d=t(a),e,f,g=d[d.length-1];if(d.length){for(var h=0,i=d.length;h<i;++h)e=d[h],f=this.getSelfOrAncestorWithClass(e),f&&this.isModifiable(e)&&this.undoToTextNode(e,a,f,c),a.setStartAndEnd(d[0],0,g,g.length);this.normalize&&this.postApply(d,a,c,!0),G(b,c)}},undoToRanges:function(a){var b=a.length;while(b--)this.undoToRange(a[b],a);return a},undoToSelection:function(b){var c=a.getSelection(b),d=a.getSelection(b).getAllRanges();this.undoToRanges(d),c.setRanges(d)},getTextSelectedByRange:function(a,b){var c=b.cloneRange();c.selectNodeContents(a);var d=c.intersection(b),e=d?d.toString():"";return c.detach(),e},isAppliedToRange:function(a){if(a.collapsed||a.toString()=="")return!!this.getSelfOrAncestorWithClass(a.commonAncestorContainer);var b=a.getNodes([3]);if(b.length)for(var c=0,d;d=b[c++];)if(!this.isIgnorableWhiteSpaceNode(d)&&s(a,d)&&this.isModifiable(d)&&!this.getSelfOrAncestorWithClass(d))return!1;return!0},isAppliedToRanges:function(a){var b=a.length;if(b==0)return!1;while(b--)if(!this.isAppliedToRange(a[b]))return!1;return!0},isAppliedToSelection:function(b){var c=a.getSelection(b);return this.isAppliedToRanges(c.getAllRanges())},toggleRange:function(a){this.isAppliedToRange(a)?this.undoToRange(a):this.applyToRange(a)},toggleRanges:function(a){this.isAppliedToRanges(a)?this.undoToRanges(a):this.applyToRanges(a)},toggleSelection:function(a){this.isAppliedToSelection(a)?this.undoToSelection(a):this.applyToSelection(a)},getElementsWithClassIntersectingRange:function(a){var b=[],c=this;return a.getNodes([3],function(a){var d=c.getSelfOrAncestorWithClass(a);d&&!e(b,d)&&b.push(d)}),b},getElementsWithClassIntersectingSelection:function(b){var c=a.getSelection(b),d=[],f=this;return c.eachRange(function(a){var b=f.getElementsWithClassIntersectingRange(a);for(var c=0,g;g=b[c++];)e(d,g)||d.push(g)}),d},detach:function(){}},Q.util={hasClass:h,addClass:i,removeClass:j,hasSameClasses:m,replaceWithOwnChildren:r,elementsHaveSameNonClassAttributes:u,elementHasNonClassAttributes:v,splitNodeAt:I,isEditableElement:y,isEditingHost:z,isEditable:A},a.CssClassApplier=a.ClassApplier=Q,a.createCssClassApplier=a.createClassApplier=R})