locomotive-aloha-rails 0.23.2.1 → 0.23.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
});
|