locomotive-aloha-rails 0.23.2.1 → 0.23.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -3
- data/lib/aloha/rails/engine.rb +1 -1
- data/lib/aloha/rails/version.rb +2 -2
- data/lib/tasks/aloha-assets.rake +1 -1
- data/vendor/assets/javascripts/aloha/css/aloha-sidebar.css +41 -17
- data/vendor/assets/javascripts/aloha/css/aloha.css +2 -2
- data/vendor/assets/javascripts/aloha/lib/aloha/contenthandlermanager.js +19 -18
- data/vendor/assets/javascripts/aloha/lib/aloha/engine.js +168 -9
- data/vendor/assets/javascripts/aloha/lib/aloha/ephemera.js +8 -1
- data/vendor/assets/javascripts/aloha/lib/aloha/markup.js +3 -2
- data/vendor/assets/javascripts/aloha/lib/aloha/pluginmanager.js +8 -0
- data/vendor/assets/javascripts/aloha/lib/aloha/sidebar.js +1 -1
- data/vendor/assets/javascripts/aloha/lib/aloha/state-override.js +59 -37
- data/vendor/assets/javascripts/aloha/lib/util/arrays.js +53 -6
- data/vendor/assets/javascripts/aloha/lib/util/boundary-markers.js +86 -0
- data/vendor/assets/javascripts/aloha/lib/util/dom2.js +178 -19
- data/vendor/assets/javascripts/aloha/lib/util/html.js +77 -10
- data/vendor/assets/javascripts/aloha/lib/util/maps.js +23 -1
- data/vendor/assets/javascripts/aloha/lib/util/range-context.js +429 -181
- data/vendor/assets/javascripts/aloha/lib/util/strings.js +9 -1
- data/vendor/assets/javascripts/aloha/plugins/common/align/nls/de/i18n.js +4 -1
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/blockmanager.js +2 -5
- data/vendor/assets/javascripts/aloha/plugins/common/characterpicker/css/characterpicker.css +6 -3
- data/vendor/assets/javascripts/aloha/plugins/common/characterpicker/lib/characterpicker-plugin.js +29 -32
- data/vendor/assets/javascripts/aloha/plugins/common/contenthandler/lib/blockelementcontenthandler.js +41 -70
- data/vendor/assets/javascripts/aloha/plugins/common/dom-to-xhtml/lib/dom-to-xhtml.js +1 -1
- data/vendor/assets/javascripts/aloha/plugins/common/format/lib/format-plugin.js +22 -10
- data/vendor/assets/javascripts/aloha/plugins/common/link/css/link.css +2 -8
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-cell.js +7 -0
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-plugin.js +149 -131
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table.js +77 -47
- data/vendor/assets/javascripts/aloha/plugins/common/ui/lib/port-helper-attribute-field.js +4 -8
- data/vendor/assets/javascripts/aloha/plugins/common/ui/nls/de/i18n.js +1 -0
- data/vendor/assets/javascripts/aloha/plugins/extra/cite/css/cite.css +0 -10
- data/vendor/assets/javascripts/aloha/plugins/extra/cite/lib/cite-plugin.js +4 -4
- data/vendor/assets/javascripts/aloha/plugins/extra/headerids/lib/headerids-plugin.js +84 -32
- data/vendor/assets/javascripts/aloha/plugins/extra/numerated-headers/nls/de/i18n.js +2 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/numerated-headers/nls/i18n.js +2 -1
- metadata +15 -14
@@ -50,12 +50,37 @@ define([
|
|
50
50
|
'U': true
|
51
51
|
};
|
52
52
|
|
53
|
-
//
|
54
|
-
|
53
|
+
// NB: "block-level" is not technically defined for elements that are new in
|
54
|
+
// HTML5.
|
55
|
+
var BLOCKLEVEL_ELEMENTS = [
|
56
|
+
'address',
|
57
|
+
'article', // HTML5
|
58
|
+
'aside', // HTML5
|
59
|
+
'audio', // HTML5
|
55
60
|
'blockquote',
|
61
|
+
'canvas', // HTML5
|
62
|
+
'dd',
|
63
|
+
'div',
|
64
|
+
'dl',
|
65
|
+
'fieldset',
|
66
|
+
'figcaption',
|
67
|
+
'figure',
|
68
|
+
'footer',
|
69
|
+
'form',
|
56
70
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
71
|
+
'header',
|
72
|
+
'hgroup',
|
73
|
+
'hr',
|
74
|
+
'noscript',
|
75
|
+
'ol',
|
76
|
+
'output',
|
57
77
|
'p',
|
58
|
-
'pre'
|
78
|
+
'pre',
|
79
|
+
'section', // HTML5
|
80
|
+
'table',
|
81
|
+
'tfoot',
|
82
|
+
'ul',
|
83
|
+
'video' // HTML5
|
59
84
|
];
|
60
85
|
|
61
86
|
/**
|
@@ -65,17 +90,20 @@ define([
|
|
65
90
|
* @type {object<string, boolean>}
|
66
91
|
*/
|
67
92
|
var blocksTagnameMap = {};
|
68
|
-
Maps.fillKeys(blocksTagnameMap,
|
69
|
-
Maps.fillKeys(
|
70
|
-
|
71
|
-
|
93
|
+
Maps.fillKeys(blocksTagnameMap, BLOCKLEVEL_ELEMENTS, true);
|
94
|
+
Maps.fillKeys(
|
95
|
+
blocksTagnameMap,
|
96
|
+
Arrays.map(BLOCKLEVEL_ELEMENTS, function (str) {
|
97
|
+
return str.toUpperCase();
|
98
|
+
}),
|
99
|
+
true
|
100
|
+
);
|
72
101
|
|
73
102
|
function isBlock(node) {
|
74
103
|
return blocksTagnameMap[node.nodeName];
|
75
104
|
}
|
76
105
|
|
77
106
|
function isIgnorableWhitespace(node) {
|
78
|
-
// TODO
|
79
107
|
return 3 === node.nodeType && !node.length;
|
80
108
|
}
|
81
109
|
|
@@ -111,11 +139,50 @@ define([
|
|
111
139
|
return found;
|
112
140
|
}
|
113
141
|
|
142
|
+
function isEditingHost(node) {
|
143
|
+
return 1 === node.nodeType && "true" === node.contentEditable;
|
144
|
+
}
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Starting from the given node, and working backwards through the siblings,
|
148
|
+
* find the node that satisfies the given condition.
|
149
|
+
*
|
150
|
+
* @param {HTMLElement} node The node at which to start the search.
|
151
|
+
* @param {function(HTMLElement):boolean} condition A predicate the receives
|
152
|
+
* one of children of `node`.
|
153
|
+
*
|
154
|
+
* @return {HTMLElement} The first node that meets the given condition.
|
155
|
+
*/
|
156
|
+
function findNodeRight(node, condition) {
|
157
|
+
while (node && !condition(node)) {
|
158
|
+
node = node.previousSibling;
|
159
|
+
}
|
160
|
+
return node;
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Checks if the given editable is a valid container for paragraphs.
|
165
|
+
*
|
166
|
+
* @param {Aloha.Editable} editable The editable to be checked
|
167
|
+
*
|
168
|
+
* @return {boolean} False if the editable may not contain paragraphs
|
169
|
+
*/
|
170
|
+
function allowNestedParagraph(editable) {
|
171
|
+
if (editable.obj.prop("tagName") === "SPAN" ||
|
172
|
+
editable.obj.prop("tagName") === "P") {
|
173
|
+
return false;
|
174
|
+
}
|
175
|
+
return true;
|
176
|
+
}
|
177
|
+
|
114
178
|
return {
|
115
|
-
|
179
|
+
BLOCKLEVEL_ELEMENTS: BLOCKLEVEL_ELEMENTS,
|
116
180
|
isBlock: isBlock,
|
117
181
|
isIgnorableWhitespace: isIgnorableWhitespace,
|
118
182
|
isInlineFormattable: isInlineFormattable,
|
119
|
-
isProppedBlock: isProppedBlock
|
183
|
+
isProppedBlock: isProppedBlock,
|
184
|
+
isEditingHost: isEditingHost,
|
185
|
+
findNodeRight: findNodeRight,
|
186
|
+
allowNestedParagraph: allowNestedParagraph
|
120
187
|
};
|
121
188
|
});
|
@@ -88,6 +88,9 @@ define([], function () {
|
|
88
88
|
return map;
|
89
89
|
}
|
90
90
|
|
91
|
+
/**
|
92
|
+
* Returns an array of the map's keys.
|
93
|
+
*/
|
91
94
|
function keys(map) {
|
92
95
|
var ks = [],
|
93
96
|
k;
|
@@ -99,10 +102,29 @@ define([], function () {
|
|
99
102
|
return ks;
|
100
103
|
}
|
101
104
|
|
105
|
+
/**
|
106
|
+
* For each mapping, call cb(value, key, map).
|
107
|
+
*
|
108
|
+
* Emulates ECMAScript edition 5 Array.forEach.
|
109
|
+
*
|
110
|
+
* Contrary to "for (key in map)" iterates only over the
|
111
|
+
* "hasOwnProperty" properties of the map, which is usually what you
|
112
|
+
* want.
|
113
|
+
*/
|
114
|
+
function forEach(map, cb) {
|
115
|
+
var key;
|
116
|
+
for (key in map) {
|
117
|
+
if (map.hasOwnProperty(key)) {
|
118
|
+
cb(map[key], key, map);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
102
123
|
return {
|
103
124
|
isEmpty: isEmpty,
|
104
125
|
fillTuples: fillTuples,
|
105
126
|
fillKeys: fillKeys,
|
106
|
-
keys: keys
|
127
|
+
keys: keys,
|
128
|
+
forEach: forEach
|
107
129
|
};
|
108
130
|
});
|
@@ -24,16 +24,31 @@
|
|
24
24
|
* provided you include this license notice and a URL through which
|
25
25
|
* recipients can access the Corresponding Source.
|
26
26
|
*/
|
27
|
+
/**
|
28
|
+
* TODO improve restacking and joining algorithm
|
29
|
+
* TODO what do do about insignificant whitespace when pushing down or setting a context?
|
30
|
+
* TODO check contained-in rules when when pushing down or setting a context
|
31
|
+
* TODO formatStyle: in the following case the outer "font-family: arial" span should be removed.
|
32
|
+
* Can be done similar to how findReusableAncestor() works.
|
33
|
+
* <span style="font-family: arial">
|
34
|
+
* <span style="font-family: times">one</span>
|
35
|
+
* <span style="font-family: helvetica">two<span>
|
36
|
+
* </span>
|
37
|
+
*/
|
27
38
|
define([
|
39
|
+
'jquery',
|
28
40
|
'util/dom2',
|
29
41
|
'util/arrays',
|
30
42
|
'util/trees',
|
43
|
+
'util/strings',
|
31
44
|
'util/functions',
|
32
45
|
'util/html'
|
33
46
|
], function (
|
47
|
+
$,
|
34
48
|
Dom,
|
35
49
|
Arrays,
|
36
50
|
Trees,
|
51
|
+
Strings,
|
37
52
|
Fn,
|
38
53
|
Html
|
39
54
|
) {
|
@@ -48,10 +63,10 @@ define([
|
|
48
63
|
var fn = before;
|
49
64
|
Dom.walk(parent.firstChild, function (child) {
|
50
65
|
if (child !== beforeAfterChild) {
|
51
|
-
|
66
|
+
fn(child, arg);
|
52
67
|
} else {
|
53
68
|
fn = after;
|
54
|
-
|
69
|
+
at(child, arg);
|
55
70
|
}
|
56
71
|
});
|
57
72
|
}
|
@@ -93,6 +108,20 @@ define([
|
|
93
108
|
}
|
94
109
|
}
|
95
110
|
|
111
|
+
function makePointNodeStep(pointNode, atEnd, stepOutsideInside, stepPartial) {
|
112
|
+
// Because the start node is inside the range, the end node is
|
113
|
+
// outside, and all ancestors of start and end are partially
|
114
|
+
// inside/outside (for startEnd/endEnd positions the nodes are
|
115
|
+
// also ancestors of the position).
|
116
|
+
return function (node, arg) {
|
117
|
+
if (node === pointNode && !atEnd) {
|
118
|
+
stepOutsideInside(node, arg);
|
119
|
+
} else {
|
120
|
+
stepPartial(node, arg);
|
121
|
+
}
|
122
|
+
};
|
123
|
+
}
|
124
|
+
|
96
125
|
/**
|
97
126
|
* Walks the boundary of the range.
|
98
127
|
*
|
@@ -118,34 +147,23 @@ define([
|
|
118
147
|
var end = Dom.nodeAtOffset(ec, eo);
|
119
148
|
var startEnd = Dom.isAtEnd(sc, so);
|
120
149
|
var endEnd = Dom.isAtEnd(ec, eo);
|
121
|
-
var
|
122
|
-
var
|
123
|
-
var
|
124
|
-
var
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
// also ancestors of the position).
|
130
|
-
function stepAtStart(node, arg) {
|
131
|
-
return node === start && !startEnd
|
132
|
-
? stepInside(node, arg)
|
133
|
-
: stepPartial(node, arg);
|
134
|
-
}
|
135
|
-
function stepAtEnd(node, arg) {
|
136
|
-
return node === end && !endEnd
|
137
|
-
? stepOutside(node, arg)
|
138
|
-
: stepPartial(node, arg);
|
139
|
-
}
|
140
|
-
ascendWalkSiblings(uptoCacChildStart, startEnd, carryDown, stepOutside, stepAtStart, stepInside, arg);
|
141
|
-
ascendWalkSiblings(uptoCacChildEnd, endEnd, carryDown, stepInside, stepAtEnd, stepOutside, arg);
|
150
|
+
var ascStart = Dom.childAndParentsUntilNode(start, cac);
|
151
|
+
var ascEnd = Dom.childAndParentsUntilNode(end, cac);
|
152
|
+
var stepAtStart = makePointNodeStep(start, startEnd, stepInside, stepPartial);
|
153
|
+
var stepAtEnd = makePointNodeStep(end, endEnd, stepOutside, stepPartial);
|
154
|
+
ascendWalkSiblings(ascStart, startEnd, carryDown, stepOutside, stepAtStart, stepInside, arg);
|
155
|
+
ascendWalkSiblings(ascEnd, endEnd, carryDown, stepInside, stepAtEnd, stepOutside, arg);
|
156
|
+
var cacChildStart = Arrays.last(ascStart);
|
157
|
+
var cacChildEnd = Arrays.last(ascEnd);
|
142
158
|
if (cacChildStart && cacChildStart !== cacChildEnd) {
|
143
159
|
var next;
|
144
160
|
Dom.walkUntilNode(cac.firstChild, stepOutside, cacChildStart, arg);
|
145
|
-
next =
|
161
|
+
next = cacChildStart.nextSibling;
|
162
|
+
stepAtStart(cacChildStart, arg);
|
146
163
|
Dom.walkUntilNode(next, stepInside, cacChildEnd, arg);
|
147
164
|
if (cacChildEnd) {
|
148
|
-
next =
|
165
|
+
next = cacChildEnd.nextSibling;
|
166
|
+
stepAtEnd(cacChildEnd, arg);
|
149
167
|
Dom.walk(next, stepOutside, arg);
|
150
168
|
}
|
151
169
|
}
|
@@ -217,41 +235,45 @@ define([
|
|
217
235
|
* @param liveRange range's boundary points should be between nodes
|
218
236
|
* (Dom.splitTextContainers).
|
219
237
|
*
|
220
|
-
* @param
|
221
|
-
*
|
238
|
+
* @param formatter a map with the following properties
|
239
|
+
* isUpperBoundary(node) - identifies exclusive upper
|
240
|
+
* boundary element, only elements below which will be modified.
|
222
241
|
*
|
223
|
-
*
|
224
|
-
*
|
225
|
-
*
|
226
|
-
*
|
227
|
-
*
|
242
|
+
* getOverride(node) - returns a node's override, or null/undefined
|
243
|
+
* if the node does not provide an override. The topmost node for
|
244
|
+
* which getOverride returns a non-null value is the topmost
|
245
|
+
* override. If there is a topmost override, and it is below the
|
246
|
+
* upper boundary element, it will be cleared and pushed down.
|
247
|
+
* A node with an override must not also provide the context:
|
248
|
+
* !(null != getOverride(node) && hasContext(node))
|
228
249
|
*
|
229
|
-
*
|
230
|
-
*
|
231
|
-
*
|
232
|
-
*
|
233
|
-
*
|
250
|
+
* clearOverride(node) - should clear the given node of an
|
251
|
+
* override. The given node may or may not have an override
|
252
|
+
* set. Will be invoked shallowly for all ancestors of start and end
|
253
|
+
* containers (up to isUpperBoundary or hasContext). May perform
|
254
|
+
* mutations as explained above.
|
234
255
|
*
|
235
|
-
*
|
236
|
-
*
|
256
|
+
* clearOverrideRec(node) - like clearOverride but should clear
|
257
|
+
* the override recursively. If not provided, clearOverride will
|
258
|
+
* be applied recursively.
|
237
259
|
*
|
238
|
-
*
|
239
|
-
*
|
240
|
-
*
|
241
|
-
*
|
242
|
-
*
|
260
|
+
* pushDownOverride(node, override) - applies the given
|
261
|
+
* override to node. Should check whether the given node doesn't
|
262
|
+
* already provide its own override, in which case the given
|
263
|
+
* override should not be applied. May perform mutations as
|
264
|
+
* explained above.
|
243
265
|
*
|
244
|
-
*
|
245
|
-
*
|
266
|
+
* hasContext(node) - returns true if the given node
|
267
|
+
* already provides the context to set.
|
246
268
|
*
|
247
|
-
*
|
248
|
-
*
|
249
|
-
*
|
250
|
-
*
|
251
|
-
*
|
252
|
-
*
|
269
|
+
* setContext(node, hasOverrideAncestor) - applies the context
|
270
|
+
* to the given node. Should clear overrides recursively. Should
|
271
|
+
* also clear context recursively to avoid unnecessarily nested
|
272
|
+
* contexts. hasOverrideAncestor is true if an override is in effect
|
273
|
+
* above the given node (see explanation above). May perform
|
274
|
+
* mutations as explained above.
|
253
275
|
*/
|
254
|
-
function mutate(liveRange,
|
276
|
+
function mutate(liveRange, formatter, rootHasImpliedContext) {
|
255
277
|
if (liveRange.collapsed) {
|
256
278
|
return;
|
257
279
|
}
|
@@ -262,27 +284,90 @@ define([
|
|
262
284
|
var bottommostOverrideNode = null;
|
263
285
|
var isNonClearableOverride = false;
|
264
286
|
var upperBoundaryAndBeyond = false;
|
265
|
-
var fromCacToContext = Dom.childAndParentsUntilIncl(cac,
|
287
|
+
var fromCacToContext = Dom.childAndParentsUntilIncl(cac, function (node) {
|
288
|
+
// Because we shouldn't expect hasContext to handle the
|
289
|
+
// document element (which has nodeType 9).
|
290
|
+
return !node.parentNode || 9 === node.parentNode.nodeType || formatter.hasContext(node);
|
291
|
+
});
|
266
292
|
Arrays.forEach(fromCacToContext, function (node) {
|
267
|
-
upperBoundaryAndBeyond = upperBoundaryAndBeyond || isUpperBoundary(node);
|
268
|
-
if (getOverride(node)) {
|
293
|
+
upperBoundaryAndBeyond = upperBoundaryAndBeyond || formatter.isUpperBoundary(node);
|
294
|
+
if (null != formatter.getOverride(node)) {
|
269
295
|
topmostOverrideNode = node;
|
270
296
|
isNonClearableOverride = upperBoundaryAndBeyond;
|
271
297
|
bottommostOverrideNode = bottommostOverrideNode || node;
|
272
298
|
}
|
273
299
|
});
|
274
|
-
if ((
|
300
|
+
if ((rootHasImpliedContext || formatter.hasContext(Arrays.last(fromCacToContext)))
|
275
301
|
&& !isNonClearableOverride) {
|
276
302
|
var pushDownFrom = topmostOverrideNode || cac;
|
277
|
-
var cacOverride = getOverride(bottommostOverrideNode || cac);
|
278
|
-
|
303
|
+
var cacOverride = formatter.getOverride(bottommostOverrideNode || cac);
|
304
|
+
var clearOverrideRec = formatter.clearOverrideRec || function (node) {
|
305
|
+
Dom.walkRec(node, formatter.clearOverride);
|
306
|
+
};
|
307
|
+
pushDownContext(
|
308
|
+
liveRange,
|
309
|
+
pushDownFrom,
|
310
|
+
cacOverride,
|
311
|
+
formatter.getOverride,
|
312
|
+
formatter.clearOverride,
|
313
|
+
clearOverrideRec,
|
314
|
+
formatter.pushDownOverride
|
315
|
+
);
|
279
316
|
} else {
|
280
|
-
|
281
|
-
|
282
|
-
}
|
317
|
+
var setContext = function (node) {
|
318
|
+
formatter.setContext(node, isNonClearableOverride);
|
319
|
+
};
|
320
|
+
walkBoundary(
|
321
|
+
liveRange,
|
322
|
+
formatter.getOverride,
|
323
|
+
formatter.pushDownOverride,
|
324
|
+
formatter.clearOverride,
|
325
|
+
setContext
|
326
|
+
);
|
283
327
|
}
|
284
328
|
}
|
285
329
|
|
330
|
+
function fixupRange(liveRange, mutate) {
|
331
|
+
// Because we are mutating the range several times and don't
|
332
|
+
// want the caller to see the in-between updates, and because we
|
333
|
+
// are using trimRange() below to adjust the range's boundary
|
334
|
+
// points, which we don't want the browser to re-adjust (which
|
335
|
+
// some browsers do).
|
336
|
+
var range = Dom.stableRange(liveRange);
|
337
|
+
|
338
|
+
// Because we should avoid splitTextContainers() if this call is a noop.
|
339
|
+
if (range.collapsed) {
|
340
|
+
return;
|
341
|
+
}
|
342
|
+
|
343
|
+
// Because trimRangeClosingOpening(), mutate() and
|
344
|
+
// adjustPointMoveBackWithinRange() require boundary points to
|
345
|
+
// be between nodes.
|
346
|
+
Dom.splitTextContainers(range);
|
347
|
+
|
348
|
+
// Because we want unbolding
|
349
|
+
// <b>one<i>two{</i>three}</b>
|
350
|
+
// to result in
|
351
|
+
// <b>one<i>two</i></b>three
|
352
|
+
// and not in
|
353
|
+
// <b>one</b><i><b>two</b></i>three
|
354
|
+
// and because adjustPointMoveBackWithinRange() requires the
|
355
|
+
// left boundary point to be next to a non-ignorable node.
|
356
|
+
Dom.trimRangeClosingOpening(range, Html.isIgnorableWhitespace);
|
357
|
+
|
358
|
+
// Because mutation needs to keep track and adjust boundary
|
359
|
+
// points.
|
360
|
+
var leftPoint = Dom.cursorFromBoundaryPoint(range.startContainer, range.startOffset);
|
361
|
+
var rightPoint = Dom.cursorFromBoundaryPoint(range.endContainer, range.endOffset);
|
362
|
+
|
363
|
+
mutate(range, leftPoint, rightPoint);
|
364
|
+
|
365
|
+
// Because we must reflect the adjusted boundary points in the
|
366
|
+
// given range.
|
367
|
+
Dom.setRangeStartFromCursor(liveRange, leftPoint);
|
368
|
+
Dom.setRangeEndFromCursor(liveRange, rightPoint);
|
369
|
+
}
|
370
|
+
|
286
371
|
function adjustPointShallowRemove(point, left, node) {
|
287
372
|
if (point.node === node) {
|
288
373
|
point.next();
|
@@ -314,15 +399,15 @@ define([
|
|
314
399
|
}
|
315
400
|
}
|
316
401
|
|
317
|
-
function
|
402
|
+
function removeShallowAdjust(node, leftPoint, rightPoint) {
|
318
403
|
adjustPointShallowRemove(leftPoint, true, node);
|
319
404
|
adjustPointShallowRemove(rightPoint, false, node);
|
320
|
-
Dom.
|
405
|
+
Dom.removeShallow(node);
|
321
406
|
}
|
322
407
|
|
323
408
|
function wrapAdjust(node, wrapper, leftPoint, rightPoint) {
|
324
409
|
if (wrapper.parentNode) {
|
325
|
-
|
410
|
+
removeShallowAdjust(wrapper, leftPoint, rightPoint);
|
326
411
|
}
|
327
412
|
adjustPointWrap(leftPoint, true, node, wrapper);
|
328
413
|
adjustPointWrap(rightPoint, false, node, wrapper);
|
@@ -335,21 +420,15 @@ define([
|
|
335
420
|
Dom.insert(node, ref, atEnd);
|
336
421
|
}
|
337
422
|
|
338
|
-
function nextSibling(node) {
|
339
|
-
return node.nextSibling;
|
340
|
-
}
|
341
|
-
|
342
|
-
// TODO when restacking the <b> that wraps "z" in
|
343
|
-
// <u><b>x</b><s><b>z</b></s></u>, join with the <b> that wraps "x".
|
344
423
|
function restackRec(node, hasContext, notIgnoreHorizontal, notIgnoreVertical) {
|
345
424
|
if (1 !== node.nodeType || notIgnoreVertical(node)) {
|
346
425
|
return null;
|
347
426
|
}
|
348
|
-
var maybeContext = Dom.
|
427
|
+
var maybeContext = Dom.next(node.firstChild, notIgnoreHorizontal);
|
349
428
|
if (!maybeContext) {
|
350
429
|
return null;
|
351
430
|
}
|
352
|
-
var notIgnorable = Dom.
|
431
|
+
var notIgnorable = Dom.next(maybeContext.nextSibling, notIgnoreHorizontal);
|
353
432
|
if (notIgnorable) {
|
354
433
|
return null;
|
355
434
|
}
|
@@ -375,154 +454,323 @@ define([
|
|
375
454
|
return true;
|
376
455
|
}
|
377
456
|
|
378
|
-
function
|
379
|
-
|
380
|
-
|
457
|
+
function ensureWrapper(node, nodeName, hasWrapper, leftPoint, rightPoint) {
|
458
|
+
if (node.previousSibling && !hasWrapper(node.previousSibling)) {
|
459
|
+
// Because restacking here solves two problems: one the
|
460
|
+
// case where the context was unnecessarily pushed down
|
461
|
+
// on the left of the range, and two to join with a
|
462
|
+
// context node that already exists to the left of the
|
463
|
+
// range.
|
464
|
+
restack(node.previousSibling,
|
465
|
+
hasWrapper,
|
466
|
+
Html.isIgnorableWhitespace,
|
467
|
+
Html.isInlineFormattable,
|
468
|
+
leftPoint,
|
469
|
+
rightPoint);
|
470
|
+
}
|
471
|
+
if (node.previousSibling && hasWrapper(node.previousSibling)) {
|
472
|
+
insertAdjust(node, node.previousSibling, true, leftPoint, rightPoint);
|
473
|
+
return true;
|
474
|
+
}
|
475
|
+
if (!hasWrapper(node)) {
|
476
|
+
var wrapper = document.createElement(nodeName);
|
477
|
+
wrapAdjust(node, wrapper, leftPoint, rightPoint);
|
478
|
+
return true;
|
479
|
+
}
|
480
|
+
return false;
|
481
|
+
}
|
482
|
+
|
483
|
+
function isUpperBoundary_default(node) {
|
484
|
+
// Because the body element is an obvious upper boundary, and
|
485
|
+
// because, when we are inside an editable, we shouldn't make
|
486
|
+
// modifications outside the editable (if we are not inside
|
487
|
+
// an editable, we don't care).
|
488
|
+
return 'BODY' === node.nodeName || Html.isEditingHost(node);
|
489
|
+
}
|
381
490
|
|
491
|
+
function makeNodeFormatter(nodeName, leftPoint, rightPoint) {
|
382
492
|
function hasContext(node) {
|
383
|
-
if (unformat) {
|
384
|
-
// Because we pass rootHasContext=true to mutate.
|
385
|
-
return false;
|
386
|
-
}
|
387
493
|
return nodeName === node.nodeName;
|
388
494
|
}
|
389
495
|
|
390
|
-
function
|
391
|
-
if (
|
392
|
-
|
496
|
+
function clearContext(node) {
|
497
|
+
if (nodeName === node.nodeName) {
|
498
|
+
removeShallowAdjust(node, leftPoint, rightPoint);
|
393
499
|
}
|
394
|
-
return nodeName === node.nodeName;
|
395
500
|
}
|
396
501
|
|
397
|
-
function
|
398
|
-
|
502
|
+
function clearContextRec(node) {
|
503
|
+
Dom.walkRec(node, clearContext);
|
399
504
|
}
|
400
505
|
|
401
|
-
function
|
402
|
-
|
403
|
-
|
404
|
-
|
506
|
+
function setContext(node) {
|
507
|
+
if (ensureWrapper(node, nodeName, hasContext, leftPoint, rightPoint)) {
|
508
|
+
// Because the node was wrapped with a context, and if
|
509
|
+
// the node itself has the context, it should be cleared
|
510
|
+
// to avoid nested contexts.
|
511
|
+
clearContextRec(node);
|
512
|
+
} else {
|
513
|
+
// Because the node itself has the context and was not
|
514
|
+
// wrapped, we must only clear its children.
|
515
|
+
Dom.walk(node.firstChild, clearContextRec);
|
405
516
|
}
|
406
|
-
return next;
|
407
517
|
}
|
408
518
|
|
409
|
-
|
410
|
-
|
411
|
-
|
519
|
+
return {
|
520
|
+
hasContext: hasContext,
|
521
|
+
getOverride: Fn.noop,
|
522
|
+
clearOverride: Fn.noop,
|
523
|
+
pushDownOverride: Fn.noop,
|
524
|
+
setContext: setContext,
|
525
|
+
isUpperBoundary: isUpperBoundary_default
|
526
|
+
};
|
527
|
+
}
|
412
528
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
}
|
418
|
-
return next;
|
529
|
+
function makeNodeUnformatter(nodeName, leftPoint, rightPoint) {
|
530
|
+
|
531
|
+
function getOverride(node) {
|
532
|
+
return nodeName === node.nodeName ? true : null;
|
419
533
|
}
|
420
534
|
|
421
|
-
function
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
function ensureWrapper(node, hasWrapper) {
|
426
|
-
if (node.previousSibling && !hasWrapper(node.previousSibling)) {
|
427
|
-
// Because restacking here solves two problems: one the
|
428
|
-
// case where the context was unnecessarily pushed down
|
429
|
-
// on the left of the range, and two to join with a
|
430
|
-
// context node that already exists to the left of the
|
431
|
-
// range.
|
432
|
-
restack(node.previousSibling,
|
433
|
-
hasWrapper,
|
434
|
-
Html.isIgnorableWhitespace,
|
435
|
-
Html.isInlineFormattable,
|
436
|
-
leftPoint, rightPoint);
|
437
|
-
}
|
438
|
-
if (node.previousSibling && hasWrapper(node.previousSibling)) {
|
439
|
-
insertAdjust(node, node.previousSibling, true, leftPoint, rightPoint);
|
440
|
-
return true;
|
441
|
-
} else if (!hasWrapper(node)) {
|
442
|
-
var wrapper = document.createElement(nodeName);
|
443
|
-
wrapAdjust(node, wrapper, leftPoint, rightPoint);
|
444
|
-
return true;
|
535
|
+
function clearOverride(node) {
|
536
|
+
if (nodeName === node.nodeName) {
|
537
|
+
removeShallowAdjust(node, leftPoint, rightPoint);
|
445
538
|
}
|
446
|
-
return false;
|
447
539
|
}
|
448
540
|
|
449
541
|
function pushDownOverride(node, override) {
|
450
542
|
if (!override) {
|
451
|
-
return
|
543
|
+
return;
|
452
544
|
}
|
453
|
-
|
454
|
-
|
545
|
+
ensureWrapper(node, nodeName, getOverride, leftPoint, rightPoint);
|
546
|
+
}
|
547
|
+
|
548
|
+
return {
|
549
|
+
hasContext: Fn.returnFalse,
|
550
|
+
setContext: Fn.noop,
|
551
|
+
getOverride: getOverride,
|
552
|
+
clearOverride: clearOverride,
|
553
|
+
pushDownOverride: pushDownOverride,
|
554
|
+
isUpperBoundary: isUpperBoundary_default
|
555
|
+
};
|
556
|
+
}
|
557
|
+
|
558
|
+
function createStyleWrapper_default() {
|
559
|
+
return document.createElement('SPAN');
|
560
|
+
}
|
561
|
+
|
562
|
+
function isStyleEq_default(styleValueA, styleValueB) {
|
563
|
+
return styleValueA === styleValueB;
|
564
|
+
}
|
565
|
+
|
566
|
+
function isStyleWrapperReusable_default(node) {
|
567
|
+
return 'SPAN' === node.nodeName;
|
568
|
+
}
|
569
|
+
|
570
|
+
function isStyleWrapperPrunable_default(node) {
|
571
|
+
return ('SPAN' === node.nodeName
|
572
|
+
&& Arrays.every(Arrays.map(Dom.attrs(node), Arrays.second),
|
573
|
+
Strings.empty));
|
574
|
+
}
|
575
|
+
|
576
|
+
function makeStyleFormatter(styleName, styleValue, createWrapper, isStyleEq, isReusable, isPrunable, leftPoint, rightPoint) {
|
577
|
+
|
578
|
+
function removeStyle(node, styleName) {
|
579
|
+
if (Strings.empty(Dom.getStyle(node, styleName))) {
|
580
|
+
return;
|
581
|
+
}
|
582
|
+
Dom.setStyle(node, styleName, null);
|
583
|
+
if (isPrunable(node)) {
|
584
|
+
removeShallowAdjust(node, leftPoint, rightPoint);
|
455
585
|
}
|
456
|
-
var next = node.nextSibling;
|
457
|
-
ensureWrapper(node, hasOverride);
|
458
|
-
return next;
|
459
586
|
}
|
460
587
|
|
461
|
-
function
|
462
|
-
if (
|
463
|
-
|
588
|
+
function setStyle(node, styleName, styleValue, prevWrapper) {
|
589
|
+
if (prevWrapper && prevWrapper === node.previousSibling) {
|
590
|
+
insertAdjust(node, prevWrapper, true, leftPoint, rightPoint);
|
591
|
+
removeStyle(node, styleName);
|
592
|
+
return prevWrapper;
|
464
593
|
}
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
// the node itself has the context, it should be cleared
|
469
|
-
// to avoid nested contexts.
|
470
|
-
clearContextRec(node);
|
471
|
-
} else {
|
472
|
-
// Because the node itself has the context and was not
|
473
|
-
// wrapped, we must only clear its children.
|
474
|
-
Dom.walk(node.firstChild, clearContextRec);
|
594
|
+
if (isReusable(node)) {
|
595
|
+
Dom.setStyle(node, styleName, styleValue);
|
596
|
+
return prevWrapper;
|
475
597
|
}
|
476
|
-
|
477
|
-
|
598
|
+
var wrapper = createWrapper();
|
599
|
+
Dom.setStyle(wrapper, styleName, styleValue);
|
600
|
+
wrapAdjust(node, wrapper, leftPoint, rightPoint);
|
601
|
+
removeStyle(node, styleName);
|
602
|
+
return wrapper;
|
478
603
|
}
|
479
604
|
|
480
|
-
function
|
481
|
-
return
|
605
|
+
function hasContext(node) {
|
606
|
+
return isStyleEq(Dom.getStyle(node, styleName), styleValue);
|
482
607
|
}
|
483
608
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
609
|
+
function getOverride(node) {
|
610
|
+
var override = Dom.getStyle(node, styleName);
|
611
|
+
return (Strings.empty(override) || isStyleEq(override, styleValue)
|
612
|
+
? null
|
613
|
+
: override);
|
614
|
+
}
|
490
615
|
|
491
|
-
|
492
|
-
|
493
|
-
return;
|
616
|
+
function clearOverride(node) {
|
617
|
+
removeStyle(node, styleName);
|
494
618
|
}
|
495
619
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
Dom.splitTextContainers(range);
|
620
|
+
function clearOverrideRec(node) {
|
621
|
+
Dom.walkRec(node, clearOverride);
|
622
|
+
}
|
500
623
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
// left boundary point to be next to a non-ignorable node.
|
509
|
-
Dom.trimRangeClosingOpening(range, Html.isIgnorableWhitespace);
|
624
|
+
var overrideWrapper = null;
|
625
|
+
function pushDownOverride(node, override) {
|
626
|
+
if (Strings.empty(override) || !Strings.empty(Dom.getStyle(node, styleName))) {
|
627
|
+
return;
|
628
|
+
}
|
629
|
+
overrideWrapper = setStyle(node, styleName, override, overrideWrapper);
|
630
|
+
}
|
510
631
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
632
|
+
var contextWrapper = null;
|
633
|
+
function setContext(node) {
|
634
|
+
Dom.walk(node.firstChild, clearOverrideRec);
|
635
|
+
contextWrapper = setStyle(node, styleName, styleValue, contextWrapper);
|
636
|
+
}
|
515
637
|
|
516
|
-
|
638
|
+
return {
|
639
|
+
hasContext: hasContext,
|
640
|
+
getOverride: getOverride,
|
641
|
+
clearOverride: clearOverride,
|
642
|
+
pushDownOverride: pushDownOverride,
|
643
|
+
setContext: setContext,
|
644
|
+
isUpperBoundary: isUpperBoundary_default
|
645
|
+
};
|
646
|
+
}
|
517
647
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
648
|
+
function format(liveRange, nodeName) {
|
649
|
+
fixupRange(liveRange, function (range, leftPoint, rightPoint) {
|
650
|
+
mutate(range, makeNodeFormatter(nodeName, leftPoint, rightPoint));
|
651
|
+
});
|
652
|
+
}
|
653
|
+
|
654
|
+
function unformat(liveRange, nodeName) {
|
655
|
+
fixupRange(liveRange, function (range, leftPoint, rightPoint) {
|
656
|
+
mutate(range, makeNodeUnformatter(nodeName, leftPoint, rightPoint), true);
|
657
|
+
});
|
658
|
+
}
|
659
|
+
|
660
|
+
function findReusableAncestor(range, hasContext, getOverride, isUpperBoundary, isReusable) {
|
661
|
+
var obstruction = null;
|
662
|
+
function untilIncl(node) {
|
663
|
+
return (null != getOverride(node)
|
664
|
+
|| hasContext(node)
|
665
|
+
|| isReusable(node)
|
666
|
+
|| isUpperBoundary(node));
|
667
|
+
}
|
668
|
+
function beforeAfter(node) {
|
669
|
+
obstruction = obstruction || !Html.isIgnorableWhitespace(node);
|
670
|
+
}
|
671
|
+
var start = Dom.nodeAtOffset(range.startContainer, range.startOffset);
|
672
|
+
var end = Dom.nodeAtOffset(range.endContainer, range.endOffset);
|
673
|
+
var startEnd = Dom.isAtEnd(range.startContainer, range.startOffset);
|
674
|
+
var endEnd = Dom.isAtEnd(range.endContainer, range.endOffset);
|
675
|
+
var ascStart = Dom.childAndParentsUntilIncl(start, untilIncl);
|
676
|
+
var ascEnd = Dom.childAndParentsUntilIncl(end, untilIncl);
|
677
|
+
var reusable = Arrays.last(ascStart);
|
678
|
+
function at(node) {
|
679
|
+
// Because the start node is inside the range.
|
680
|
+
if (node === start && !startEnd) {
|
681
|
+
return;
|
682
|
+
}
|
683
|
+
// Because the end node is outside the range.
|
684
|
+
if (node === end && !endEnd) {
|
685
|
+
beforeAfter(node);
|
686
|
+
return;
|
687
|
+
}
|
688
|
+
obstruction = obstruction || !Html.isInlineFormattable(node);
|
689
|
+
}
|
690
|
+
if (!reusable || !isReusable(reusable) || reusable !== Arrays.last(ascEnd)) {
|
691
|
+
return null;
|
692
|
+
}
|
693
|
+
ascendWalkSiblings(ascStart, startEnd, Fn.noop, beforeAfter, at, Fn.noop);
|
694
|
+
if (obstruction) {
|
695
|
+
return null;
|
696
|
+
}
|
697
|
+
ascendWalkSiblings(ascEnd, endEnd, Fn.noop, Fn.noop, at, beforeAfter);
|
698
|
+
if (obstruction) {
|
699
|
+
return null;
|
700
|
+
}
|
701
|
+
return reusable;
|
702
|
+
}
|
703
|
+
|
704
|
+
function formatStyle(liveRange, styleName, styleValue, createWrapper, isStyleEq, isReusable, isPrunable) {
|
705
|
+
createWrapper = createWrapper || createStyleWrapper_default;
|
706
|
+
isStyleEq = isStyleEq || isStyleEq_default;
|
707
|
+
isReusable = isReusable || isStyleWrapperReusable_default;
|
708
|
+
isPrunable = isPrunable || isStyleWrapperPrunable_default;
|
709
|
+
fixupRange(liveRange, function (range, leftPoint, rightPoint) {
|
710
|
+
var formatter = makeStyleFormatter(
|
711
|
+
styleName,
|
712
|
+
styleValue,
|
713
|
+
createWrapper,
|
714
|
+
isStyleEq,
|
715
|
+
isReusable,
|
716
|
+
isPrunable,
|
717
|
+
leftPoint,
|
718
|
+
rightPoint
|
719
|
+
);
|
720
|
+
var reusableAncestor = findReusableAncestor(
|
721
|
+
range,
|
722
|
+
formatter.hasContext,
|
723
|
+
formatter.getOverride,
|
724
|
+
formatter.isUpperBoundary,
|
725
|
+
isReusable
|
726
|
+
);
|
727
|
+
if (reusableAncestor) {
|
728
|
+
formatter.setContext(reusableAncestor);
|
729
|
+
} else {
|
730
|
+
mutate(range, formatter, false);
|
731
|
+
}
|
732
|
+
});
|
733
|
+
}
|
734
|
+
|
735
|
+
function splitBoundary(liveRange, pred, clone) {
|
736
|
+
clone = clone || Dom.cloneShallow;
|
737
|
+
fixupRange(liveRange, function (range, leftPoint, rightPoint) {
|
738
|
+
|
739
|
+
var wrapper = null;
|
740
|
+
|
741
|
+
function carryDown(elem, stop) {
|
742
|
+
return stop || !pred(elem);
|
743
|
+
}
|
744
|
+
|
745
|
+
function pushDown(node, stop) {
|
746
|
+
if (stop) {
|
747
|
+
return;
|
748
|
+
}
|
749
|
+
if (!wrapper || node.parentNode.previousSibling !== wrapper) {
|
750
|
+
wrapper = clone(node.parentNode);
|
751
|
+
insertAdjust(wrapper, node.parentNode, false, leftPoint, rightPoint);
|
752
|
+
}
|
753
|
+
insertAdjust(node, wrapper, true, leftPoint, rightPoint);
|
754
|
+
}
|
755
|
+
|
756
|
+
var sc = range.startContainer;
|
757
|
+
var so = range.startOffset;
|
758
|
+
var ec = range.endContainer;
|
759
|
+
var eo = range.endOffset;
|
760
|
+
var cac = range.commonAncestorContainer;
|
761
|
+
var startEnd = Dom.isAtEnd(sc, so);
|
762
|
+
var endEnd = Dom.isAtEnd(ec, eo);
|
763
|
+
var ascStart = Dom.childAndParentsUntilNode(Dom.nodeAtOffset(sc, so), cac);
|
764
|
+
var ascEnd = Dom.childAndParentsUntilNode(Dom.nodeAtOffset(ec, eo), cac);
|
765
|
+
ascendWalkSiblings(ascStart, startEnd, carryDown, pushDown, Fn.noop, Fn.noop, null);
|
766
|
+
ascendWalkSiblings(ascEnd, endEnd, carryDown, pushDown, Fn.noop, Fn.noop, null);
|
767
|
+
});
|
522
768
|
}
|
523
769
|
|
524
770
|
return {
|
525
|
-
|
526
|
-
|
771
|
+
format: format,
|
772
|
+
unformat: unformat,
|
773
|
+
formatStyle: formatStyle,
|
774
|
+
splitBoundary: splitBoundary
|
527
775
|
};
|
528
776
|
});
|