wysihtml-rails 0.5.0.beta13 → 0.5.0.beta14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/wysihtml/rails/version.rb +1 -1
- data/vendor/assets/javascripts/wysihtml-toolbar.js +2280 -193
- data/vendor/assets/javascripts/wysihtml.js +2266 -186
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79b9d17d508350d2026a68b4290a18f50cf80f86
|
4
|
+
data.tar.gz: 258e583602cb8db2d76294344b1d4e30360e2cf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 079c292c5b9ac8de4dd6cb8f93f4e52d8d341bbdd7f5c58cb438f38293fb47398b46472372e241bcf56fd70f9777603a4ac48b8b7cfa9e07bd940ac254be6ad1
|
7
|
+
data.tar.gz: 1c4fff75b28fdfa298e9d37b8e657bb908f0bc088df289673bd43ec1890d717b4b987f23ddd61d685c00cf356393e5b4cc2b4b841809c33f24f4cd6c74a9fdd8
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license wysihtml v0.5.0-
|
2
|
+
* @license wysihtml v0.5.0-beta14
|
3
3
|
* https://github.com/Voog/wysihtml
|
4
4
|
*
|
5
5
|
* Author: Christopher Blum (https://github.com/tiff)
|
@@ -10,7 +10,7 @@
|
|
10
10
|
*
|
11
11
|
*/
|
12
12
|
var wysihtml5 = {
|
13
|
-
version: "0.5.0-
|
13
|
+
version: "0.5.0-beta14",
|
14
14
|
|
15
15
|
// namespaces
|
16
16
|
commands: {},
|
@@ -422,6 +422,18 @@ var wysihtml5 = {
|
|
422
422
|
prevTxt = texts.shift(),
|
423
423
|
curText = prevTxt ? texts.shift() : null;
|
424
424
|
|
425
|
+
if (felement && felement.nodeType === 3) {
|
426
|
+
fnode = felement;
|
427
|
+
foffset = felement.nodeValue.length;
|
428
|
+
felement = undefined;
|
429
|
+
}
|
430
|
+
|
431
|
+
if (aelement && aelement.nodeType === 3) {
|
432
|
+
anode = aelement;
|
433
|
+
aoffset = 0;
|
434
|
+
aelement = undefined;
|
435
|
+
}
|
436
|
+
|
425
437
|
if ((anode === fnode && foffset < aoffset) || (anode !== fnode && (anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_PRECEDING) && !(anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_CONTAINS))) {
|
426
438
|
fnode = [anode, anode = fnode][0];
|
427
439
|
foffset = [aoffset, aoffset = foffset][0];
|
@@ -463,9 +475,18 @@ var wysihtml5 = {
|
|
463
475
|
};
|
464
476
|
Node.prototype.normalize = nf;
|
465
477
|
};
|
466
|
-
|
467
|
-
|
468
|
-
|
478
|
+
|
479
|
+
var F = function() {
|
480
|
+
window.removeEventListener("load", F);
|
481
|
+
if ("Node" in window && "normalize" in Node.prototype && normalizeHasCaretError()) {
|
482
|
+
normalizeFix();
|
483
|
+
}
|
484
|
+
};
|
485
|
+
|
486
|
+
if (doc.readyState !== "complete") {
|
487
|
+
window.addEventListener("load", F);
|
488
|
+
} else {
|
489
|
+
F();
|
469
490
|
}
|
470
491
|
};
|
471
492
|
|
@@ -4139,181 +4160,2110 @@ wysihtml5.polyfills(window, document);
|
|
4139
4160
|
range = api.createRange(this.win.document);
|
4140
4161
|
range.setStartAndEnd(node, offset);
|
4141
4162
|
}
|
4142
|
-
this.setSingleRange(range, this.isBackward());
|
4143
|
-
};
|
4144
|
-
}
|
4163
|
+
this.setSingleRange(range, this.isBackward());
|
4164
|
+
};
|
4165
|
+
}
|
4166
|
+
|
4167
|
+
selProto.setStart = createStartOrEndSetter(true);
|
4168
|
+
selProto.setEnd = createStartOrEndSetter(false);
|
4169
|
+
|
4170
|
+
// Add select() method to Range prototype. Any existing selection will be removed.
|
4171
|
+
api.rangePrototype.select = function(direction) {
|
4172
|
+
getSelection( this.getDocument() ).setSingleRange(this, direction);
|
4173
|
+
};
|
4174
|
+
|
4175
|
+
selProto.changeEachRange = function(func) {
|
4176
|
+
var ranges = [];
|
4177
|
+
var backward = this.isBackward();
|
4178
|
+
|
4179
|
+
this.eachRange(function(range) {
|
4180
|
+
func(range);
|
4181
|
+
ranges.push(range);
|
4182
|
+
});
|
4183
|
+
|
4184
|
+
this.removeAllRanges();
|
4185
|
+
if (backward && ranges.length == 1) {
|
4186
|
+
this.addRange(ranges[0], "backward");
|
4187
|
+
} else {
|
4188
|
+
this.setRanges(ranges);
|
4189
|
+
}
|
4190
|
+
};
|
4191
|
+
|
4192
|
+
selProto.containsNode = function(node, allowPartial) {
|
4193
|
+
return this.eachRange( function(range) {
|
4194
|
+
return range.containsNode(node, allowPartial);
|
4195
|
+
}, true ) || false;
|
4196
|
+
};
|
4197
|
+
|
4198
|
+
selProto.getBookmark = function(containerNode) {
|
4199
|
+
return {
|
4200
|
+
backward: this.isBackward(),
|
4201
|
+
rangeBookmarks: this.callMethodOnEachRange("getBookmark", [containerNode])
|
4202
|
+
};
|
4203
|
+
};
|
4204
|
+
|
4205
|
+
selProto.moveToBookmark = function(bookmark) {
|
4206
|
+
var selRanges = [];
|
4207
|
+
for (var i = 0, rangeBookmark, range; rangeBookmark = bookmark.rangeBookmarks[i++]; ) {
|
4208
|
+
range = api.createRange(this.win);
|
4209
|
+
range.moveToBookmark(rangeBookmark);
|
4210
|
+
selRanges.push(range);
|
4211
|
+
}
|
4212
|
+
if (bookmark.backward) {
|
4213
|
+
this.setSingleRange(selRanges[0], "backward");
|
4214
|
+
} else {
|
4215
|
+
this.setRanges(selRanges);
|
4216
|
+
}
|
4217
|
+
};
|
4218
|
+
|
4219
|
+
selProto.saveRanges = function() {
|
4220
|
+
return {
|
4221
|
+
backward: this.isBackward(),
|
4222
|
+
ranges: this.callMethodOnEachRange("cloneRange")
|
4223
|
+
};
|
4224
|
+
};
|
4225
|
+
|
4226
|
+
selProto.restoreRanges = function(selRanges) {
|
4227
|
+
this.removeAllRanges();
|
4228
|
+
for (var i = 0, range; range = selRanges.ranges[i]; ++i) {
|
4229
|
+
this.addRange(range, (selRanges.backward && i == 0));
|
4230
|
+
}
|
4231
|
+
};
|
4232
|
+
|
4233
|
+
selProto.toHtml = function() {
|
4234
|
+
var rangeHtmls = [];
|
4235
|
+
this.eachRange(function(range) {
|
4236
|
+
rangeHtmls.push( DomRange.toHtml(range) );
|
4237
|
+
});
|
4238
|
+
return rangeHtmls.join("");
|
4239
|
+
};
|
4240
|
+
|
4241
|
+
if (features.implementsTextRange) {
|
4242
|
+
selProto.getNativeTextRange = function() {
|
4243
|
+
var sel, textRange;
|
4244
|
+
if ( (sel = this.docSelection) ) {
|
4245
|
+
var range = sel.createRange();
|
4246
|
+
if (isTextRange(range)) {
|
4247
|
+
return range;
|
4248
|
+
} else {
|
4249
|
+
throw module.createError("getNativeTextRange: selection is a control selection");
|
4250
|
+
}
|
4251
|
+
} else if (this.rangeCount > 0) {
|
4252
|
+
return api.WrappedTextRange.rangeToTextRange( this.getRangeAt(0) );
|
4253
|
+
} else {
|
4254
|
+
throw module.createError("getNativeTextRange: selection contains no range");
|
4255
|
+
}
|
4256
|
+
};
|
4257
|
+
}
|
4258
|
+
|
4259
|
+
function inspect(sel) {
|
4260
|
+
var rangeInspects = [];
|
4261
|
+
var anchor = new DomPosition(sel.anchorNode, sel.anchorOffset);
|
4262
|
+
var focus = new DomPosition(sel.focusNode, sel.focusOffset);
|
4263
|
+
var name = (typeof sel.getName == "function") ? sel.getName() : "Selection";
|
4264
|
+
|
4265
|
+
if (typeof sel.rangeCount != "undefined") {
|
4266
|
+
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
|
4267
|
+
rangeInspects[i] = DomRange.inspect(sel.getRangeAt(i));
|
4268
|
+
}
|
4269
|
+
}
|
4270
|
+
return "[" + name + "(Ranges: " + rangeInspects.join(", ") +
|
4271
|
+
")(anchor: " + anchor.inspect() + ", focus: " + focus.inspect() + "]";
|
4272
|
+
}
|
4273
|
+
|
4274
|
+
selProto.getName = function() {
|
4275
|
+
return "WrappedSelection";
|
4276
|
+
};
|
4277
|
+
|
4278
|
+
selProto.inspect = function() {
|
4279
|
+
return inspect(this);
|
4280
|
+
};
|
4281
|
+
|
4282
|
+
selProto.detach = function() {
|
4283
|
+
actOnCachedSelection(this.win, "delete");
|
4284
|
+
deleteProperties(this);
|
4285
|
+
};
|
4286
|
+
|
4287
|
+
WrappedSelection.detachAll = function() {
|
4288
|
+
actOnCachedSelection(null, "deleteAll");
|
4289
|
+
};
|
4290
|
+
|
4291
|
+
WrappedSelection.inspect = inspect;
|
4292
|
+
WrappedSelection.isDirectionBackward = isDirectionBackward;
|
4293
|
+
|
4294
|
+
api.Selection = WrappedSelection;
|
4295
|
+
|
4296
|
+
api.selectionPrototype = selProto;
|
4297
|
+
|
4298
|
+
api.addShimListener(function(win) {
|
4299
|
+
if (typeof win.getSelection == "undefined") {
|
4300
|
+
win.getSelection = function() {
|
4301
|
+
return getSelection(win);
|
4302
|
+
};
|
4303
|
+
}
|
4304
|
+
win = null;
|
4305
|
+
});
|
4306
|
+
});
|
4307
|
+
|
4308
|
+
|
4309
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4310
|
+
|
4311
|
+
// Wait for document to load before initializing
|
4312
|
+
var docReady = false;
|
4313
|
+
|
4314
|
+
var loadHandler = function(e) {
|
4315
|
+
if (!docReady) {
|
4316
|
+
docReady = true;
|
4317
|
+
if (!api.initialized && api.config.autoInitialize) {
|
4318
|
+
init();
|
4319
|
+
}
|
4320
|
+
}
|
4321
|
+
};
|
4322
|
+
|
4323
|
+
if (isBrowser) {
|
4324
|
+
// Test whether the document has already been loaded and initialize immediately if so
|
4325
|
+
if (document.readyState == "complete") {
|
4326
|
+
loadHandler();
|
4327
|
+
} else {
|
4328
|
+
if (isHostMethod(document, "addEventListener")) {
|
4329
|
+
document.addEventListener("DOMContentLoaded", loadHandler, false);
|
4330
|
+
}
|
4331
|
+
|
4332
|
+
// Add a fallback in case the DOMContentLoaded event isn't supported
|
4333
|
+
addListener(window, "load", loadHandler);
|
4334
|
+
}
|
4335
|
+
}
|
4336
|
+
|
4337
|
+
return api;
|
4338
|
+
}, this);;/**
|
4339
|
+
* Text range module for Rangy.
|
4340
|
+
* Text-based manipulation and searching of ranges and selections.
|
4341
|
+
*
|
4342
|
+
* Features
|
4343
|
+
*
|
4344
|
+
* - Ability to move range boundaries by character or word offsets
|
4345
|
+
* - Customizable word tokenizer
|
4346
|
+
* - Ignores text nodes inside <script> or <style> elements or those hidden by CSS display and visibility properties
|
4347
|
+
* - Range findText method to search for text or regex within the page or within a range. Flags for whole words and case
|
4348
|
+
* sensitivity
|
4349
|
+
* - Selection and range save/restore as text offsets within a node
|
4350
|
+
* - Methods to return visible text within a range or selection
|
4351
|
+
* - innerText method for elements
|
4352
|
+
*
|
4353
|
+
* References
|
4354
|
+
*
|
4355
|
+
* https://www.w3.org/Bugs/Public/show_bug.cgi?id=13145
|
4356
|
+
* http://aryeh.name/spec/innertext/innertext.html
|
4357
|
+
* http://dvcs.w3.org/hg/editing/raw-file/tip/editing.html
|
4358
|
+
*
|
4359
|
+
* Part of Rangy, a cross-browser JavaScript range and selection library
|
4360
|
+
* https://github.com/timdown/rangy
|
4361
|
+
*
|
4362
|
+
* Depends on Rangy core.
|
4363
|
+
*
|
4364
|
+
* Copyright 2015, Tim Down
|
4365
|
+
* Licensed under the MIT license.
|
4366
|
+
* Version: 1.3.0
|
4367
|
+
* Build date: 10 May 2015
|
4368
|
+
*/
|
4369
|
+
|
4370
|
+
/**
|
4371
|
+
* Problem: handling of trailing spaces before line breaks is handled inconsistently between browsers.
|
4372
|
+
*
|
4373
|
+
* First, a <br>: this is relatively simple. For the following HTML:
|
4374
|
+
*
|
4375
|
+
* 1 <br>2
|
4376
|
+
*
|
4377
|
+
* - IE and WebKit render the space, include it in the selection (i.e. when the content is selected and pasted into a
|
4378
|
+
* textarea, the space is present) and allow the caret to be placed after it.
|
4379
|
+
* - Firefox does not acknowledge the space in the selection but it is possible to place the caret after it.
|
4380
|
+
* - Opera does not render the space but has two separate caret positions on either side of the space (left and right
|
4381
|
+
* arrow keys show this) and includes the space in the selection.
|
4382
|
+
*
|
4383
|
+
* The other case is the line break or breaks implied by block elements. For the following HTML:
|
4384
|
+
*
|
4385
|
+
* <p>1 </p><p>2<p>
|
4386
|
+
*
|
4387
|
+
* - WebKit does not acknowledge the space in any way
|
4388
|
+
* - Firefox, IE and Opera as per <br>
|
4389
|
+
*
|
4390
|
+
* One more case is trailing spaces before line breaks in elements with white-space: pre-line. For the following HTML:
|
4391
|
+
*
|
4392
|
+
* <p style="white-space: pre-line">1
|
4393
|
+
* 2</p>
|
4394
|
+
*
|
4395
|
+
* - Firefox and WebKit include the space in caret positions
|
4396
|
+
* - IE does not support pre-line up to and including version 9
|
4397
|
+
* - Opera ignores the space
|
4398
|
+
* - Trailing space only renders if there is a non-collapsed character in the line
|
4399
|
+
*
|
4400
|
+
* Problem is whether Rangy should ever acknowledge the space and if so, when. Another problem is whether this can be
|
4401
|
+
* feature-tested
|
4402
|
+
*/
|
4403
|
+
(function(factory, root) {
|
4404
|
+
if (typeof define == "function" && define.amd) {
|
4405
|
+
// AMD. Register as an anonymous module with a dependency on Rangy.
|
4406
|
+
define(["./rangy-core"], factory);
|
4407
|
+
} else if (typeof module != "undefined" && typeof exports == "object") {
|
4408
|
+
// Node/CommonJS style
|
4409
|
+
module.exports = factory( require("rangy") );
|
4410
|
+
} else {
|
4411
|
+
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
|
4412
|
+
factory(root.rangy);
|
4413
|
+
}
|
4414
|
+
})(function(rangy) {
|
4415
|
+
rangy.createModule("TextRange", ["WrappedSelection"], function(api, module) {
|
4416
|
+
var UNDEF = "undefined";
|
4417
|
+
var CHARACTER = "character", WORD = "word";
|
4418
|
+
var dom = api.dom, util = api.util;
|
4419
|
+
var extend = util.extend;
|
4420
|
+
var createOptions = util.createOptions;
|
4421
|
+
var getBody = dom.getBody;
|
4422
|
+
|
4423
|
+
|
4424
|
+
var spacesRegex = /^[ \t\f\r\n]+$/;
|
4425
|
+
var spacesMinusLineBreaksRegex = /^[ \t\f\r]+$/;
|
4426
|
+
var allWhiteSpaceRegex = /^[\t-\r \u0085\u00A0\u1680\u180E\u2000-\u200B\u2028\u2029\u202F\u205F\u3000]+$/;
|
4427
|
+
var nonLineBreakWhiteSpaceRegex = /^[\t \u00A0\u1680\u180E\u2000-\u200B\u202F\u205F\u3000]+$/;
|
4428
|
+
var lineBreakRegex = /^[\n-\r\u0085\u2028\u2029]$/;
|
4429
|
+
|
4430
|
+
var defaultLanguage = "en";
|
4431
|
+
|
4432
|
+
var isDirectionBackward = api.Selection.isDirectionBackward;
|
4433
|
+
|
4434
|
+
// Properties representing whether trailing spaces inside blocks are completely collapsed (as they are in WebKit,
|
4435
|
+
// but not other browsers). Also test whether trailing spaces before <br> elements are collapsed.
|
4436
|
+
var trailingSpaceInBlockCollapses = false;
|
4437
|
+
var trailingSpaceBeforeBrCollapses = false;
|
4438
|
+
var trailingSpaceBeforeBlockCollapses = false;
|
4439
|
+
var trailingSpaceBeforeLineBreakInPreLineCollapses = true;
|
4440
|
+
|
4441
|
+
(function() {
|
4442
|
+
var el = dom.createTestElement(document, "<p>1 </p><p></p>", true);
|
4443
|
+
var p = el.firstChild;
|
4444
|
+
var sel = api.getSelection();
|
4445
|
+
sel.collapse(p.lastChild, 2);
|
4446
|
+
sel.setStart(p.firstChild, 0);
|
4447
|
+
trailingSpaceInBlockCollapses = ("" + sel).length == 1;
|
4448
|
+
|
4449
|
+
el.innerHTML = "1 <br />";
|
4450
|
+
sel.collapse(el, 2);
|
4451
|
+
sel.setStart(el.firstChild, 0);
|
4452
|
+
trailingSpaceBeforeBrCollapses = ("" + sel).length == 1;
|
4453
|
+
|
4454
|
+
el.innerHTML = "1 <p>1</p>";
|
4455
|
+
sel.collapse(el, 2);
|
4456
|
+
sel.setStart(el.firstChild, 0);
|
4457
|
+
trailingSpaceBeforeBlockCollapses = ("" + sel).length == 1;
|
4458
|
+
|
4459
|
+
dom.removeNode(el);
|
4460
|
+
sel.removeAllRanges();
|
4461
|
+
})();
|
4462
|
+
|
4463
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4464
|
+
|
4465
|
+
// This function must create word and non-word tokens for the whole of the text supplied to it
|
4466
|
+
function defaultTokenizer(chars, wordOptions) {
|
4467
|
+
var word = chars.join(""), result, tokenRanges = [];
|
4468
|
+
|
4469
|
+
function createTokenRange(start, end, isWord) {
|
4470
|
+
tokenRanges.push( { start: start, end: end, isWord: isWord } );
|
4471
|
+
}
|
4472
|
+
|
4473
|
+
// Match words and mark characters
|
4474
|
+
var lastWordEnd = 0, wordStart, wordEnd;
|
4475
|
+
while ( (result = wordOptions.wordRegex.exec(word)) ) {
|
4476
|
+
wordStart = result.index;
|
4477
|
+
wordEnd = wordStart + result[0].length;
|
4478
|
+
|
4479
|
+
// Create token for non-word characters preceding this word
|
4480
|
+
if (wordStart > lastWordEnd) {
|
4481
|
+
createTokenRange(lastWordEnd, wordStart, false);
|
4482
|
+
}
|
4483
|
+
|
4484
|
+
// Get trailing space characters for word
|
4485
|
+
if (wordOptions.includeTrailingSpace) {
|
4486
|
+
while ( nonLineBreakWhiteSpaceRegex.test(chars[wordEnd]) ) {
|
4487
|
+
++wordEnd;
|
4488
|
+
}
|
4489
|
+
}
|
4490
|
+
createTokenRange(wordStart, wordEnd, true);
|
4491
|
+
lastWordEnd = wordEnd;
|
4492
|
+
}
|
4493
|
+
|
4494
|
+
// Create token for trailing non-word characters, if any exist
|
4495
|
+
if (lastWordEnd < chars.length) {
|
4496
|
+
createTokenRange(lastWordEnd, chars.length, false);
|
4497
|
+
}
|
4498
|
+
|
4499
|
+
return tokenRanges;
|
4500
|
+
}
|
4501
|
+
|
4502
|
+
function convertCharRangeToToken(chars, tokenRange) {
|
4503
|
+
var tokenChars = chars.slice(tokenRange.start, tokenRange.end);
|
4504
|
+
var token = {
|
4505
|
+
isWord: tokenRange.isWord,
|
4506
|
+
chars: tokenChars,
|
4507
|
+
toString: function() {
|
4508
|
+
return tokenChars.join("");
|
4509
|
+
}
|
4510
|
+
};
|
4511
|
+
for (var i = 0, len = tokenChars.length; i < len; ++i) {
|
4512
|
+
tokenChars[i].token = token;
|
4513
|
+
}
|
4514
|
+
return token;
|
4515
|
+
}
|
4516
|
+
|
4517
|
+
function tokenize(chars, wordOptions, tokenizer) {
|
4518
|
+
var tokenRanges = tokenizer(chars, wordOptions);
|
4519
|
+
var tokens = [];
|
4520
|
+
for (var i = 0, tokenRange; tokenRange = tokenRanges[i++]; ) {
|
4521
|
+
tokens.push( convertCharRangeToToken(chars, tokenRange) );
|
4522
|
+
}
|
4523
|
+
return tokens;
|
4524
|
+
}
|
4525
|
+
|
4526
|
+
var defaultCharacterOptions = {
|
4527
|
+
includeBlockContentTrailingSpace: true,
|
4528
|
+
includeSpaceBeforeBr: true,
|
4529
|
+
includeSpaceBeforeBlock: true,
|
4530
|
+
includePreLineTrailingSpace: true,
|
4531
|
+
ignoreCharacters: ""
|
4532
|
+
};
|
4533
|
+
|
4534
|
+
function normalizeIgnoredCharacters(ignoredCharacters) {
|
4535
|
+
// Check if character is ignored
|
4536
|
+
var ignoredChars = ignoredCharacters || "";
|
4537
|
+
|
4538
|
+
// Normalize ignored characters into a string consisting of characters in ascending order of character code
|
4539
|
+
var ignoredCharsArray = (typeof ignoredChars == "string") ? ignoredChars.split("") : ignoredChars;
|
4540
|
+
ignoredCharsArray.sort(function(char1, char2) {
|
4541
|
+
return char1.charCodeAt(0) - char2.charCodeAt(0);
|
4542
|
+
});
|
4543
|
+
|
4544
|
+
/// Convert back to a string and remove duplicates
|
4545
|
+
return ignoredCharsArray.join("").replace(/(.)\1+/g, "$1");
|
4546
|
+
}
|
4547
|
+
|
4548
|
+
var defaultCaretCharacterOptions = {
|
4549
|
+
includeBlockContentTrailingSpace: !trailingSpaceBeforeLineBreakInPreLineCollapses,
|
4550
|
+
includeSpaceBeforeBr: !trailingSpaceBeforeBrCollapses,
|
4551
|
+
includeSpaceBeforeBlock: !trailingSpaceBeforeBlockCollapses,
|
4552
|
+
includePreLineTrailingSpace: true
|
4553
|
+
};
|
4554
|
+
|
4555
|
+
var defaultWordOptions = {
|
4556
|
+
"en": {
|
4557
|
+
wordRegex: /[a-z0-9]+('[a-z0-9]+)*/gi,
|
4558
|
+
includeTrailingSpace: false,
|
4559
|
+
tokenizer: defaultTokenizer
|
4560
|
+
}
|
4561
|
+
};
|
4562
|
+
|
4563
|
+
var defaultFindOptions = {
|
4564
|
+
caseSensitive: false,
|
4565
|
+
withinRange: null,
|
4566
|
+
wholeWordsOnly: false,
|
4567
|
+
wrap: false,
|
4568
|
+
direction: "forward",
|
4569
|
+
wordOptions: null,
|
4570
|
+
characterOptions: null
|
4571
|
+
};
|
4572
|
+
|
4573
|
+
var defaultMoveOptions = {
|
4574
|
+
wordOptions: null,
|
4575
|
+
characterOptions: null
|
4576
|
+
};
|
4577
|
+
|
4578
|
+
var defaultExpandOptions = {
|
4579
|
+
wordOptions: null,
|
4580
|
+
characterOptions: null,
|
4581
|
+
trim: false,
|
4582
|
+
trimStart: true,
|
4583
|
+
trimEnd: true
|
4584
|
+
};
|
4585
|
+
|
4586
|
+
var defaultWordIteratorOptions = {
|
4587
|
+
wordOptions: null,
|
4588
|
+
characterOptions: null,
|
4589
|
+
direction: "forward"
|
4590
|
+
};
|
4591
|
+
|
4592
|
+
function createWordOptions(options) {
|
4593
|
+
var lang, defaults;
|
4594
|
+
if (!options) {
|
4595
|
+
return defaultWordOptions[defaultLanguage];
|
4596
|
+
} else {
|
4597
|
+
lang = options.language || defaultLanguage;
|
4598
|
+
defaults = {};
|
4599
|
+
extend(defaults, defaultWordOptions[lang] || defaultWordOptions[defaultLanguage]);
|
4600
|
+
extend(defaults, options);
|
4601
|
+
return defaults;
|
4602
|
+
}
|
4603
|
+
}
|
4604
|
+
|
4605
|
+
function createNestedOptions(optionsParam, defaults) {
|
4606
|
+
var options = createOptions(optionsParam, defaults);
|
4607
|
+
if (defaults.hasOwnProperty("wordOptions")) {
|
4608
|
+
options.wordOptions = createWordOptions(options.wordOptions);
|
4609
|
+
}
|
4610
|
+
if (defaults.hasOwnProperty("characterOptions")) {
|
4611
|
+
options.characterOptions = createOptions(options.characterOptions, defaultCharacterOptions);
|
4612
|
+
}
|
4613
|
+
return options;
|
4614
|
+
}
|
4615
|
+
|
4616
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4617
|
+
|
4618
|
+
/* DOM utility functions */
|
4619
|
+
var getComputedStyleProperty = dom.getComputedStyleProperty;
|
4620
|
+
|
4621
|
+
// Create cachable versions of DOM functions
|
4622
|
+
|
4623
|
+
// Test for old IE's incorrect display properties
|
4624
|
+
var tableCssDisplayBlock;
|
4625
|
+
(function() {
|
4626
|
+
var table = document.createElement("table");
|
4627
|
+
var body = getBody(document);
|
4628
|
+
body.appendChild(table);
|
4629
|
+
tableCssDisplayBlock = (getComputedStyleProperty(table, "display") == "block");
|
4630
|
+
body.removeChild(table);
|
4631
|
+
})();
|
4632
|
+
|
4633
|
+
var defaultDisplayValueForTag = {
|
4634
|
+
table: "table",
|
4635
|
+
caption: "table-caption",
|
4636
|
+
colgroup: "table-column-group",
|
4637
|
+
col: "table-column",
|
4638
|
+
thead: "table-header-group",
|
4639
|
+
tbody: "table-row-group",
|
4640
|
+
tfoot: "table-footer-group",
|
4641
|
+
tr: "table-row",
|
4642
|
+
td: "table-cell",
|
4643
|
+
th: "table-cell"
|
4644
|
+
};
|
4645
|
+
|
4646
|
+
// Corrects IE's "block" value for table-related elements
|
4647
|
+
function getComputedDisplay(el, win) {
|
4648
|
+
var display = getComputedStyleProperty(el, "display", win);
|
4649
|
+
var tagName = el.tagName.toLowerCase();
|
4650
|
+
return (display == "block" &&
|
4651
|
+
tableCssDisplayBlock &&
|
4652
|
+
defaultDisplayValueForTag.hasOwnProperty(tagName)) ?
|
4653
|
+
defaultDisplayValueForTag[tagName] : display;
|
4654
|
+
}
|
4655
|
+
|
4656
|
+
function isHidden(node) {
|
4657
|
+
var ancestors = getAncestorsAndSelf(node);
|
4658
|
+
for (var i = 0, len = ancestors.length; i < len; ++i) {
|
4659
|
+
if (ancestors[i].nodeType == 1 && getComputedDisplay(ancestors[i]) == "none") {
|
4660
|
+
return true;
|
4661
|
+
}
|
4662
|
+
}
|
4663
|
+
|
4664
|
+
return false;
|
4665
|
+
}
|
4666
|
+
|
4667
|
+
function isVisibilityHiddenTextNode(textNode) {
|
4668
|
+
var el;
|
4669
|
+
return textNode.nodeType == 3 &&
|
4670
|
+
(el = textNode.parentNode) &&
|
4671
|
+
getComputedStyleProperty(el, "visibility") == "hidden";
|
4672
|
+
}
|
4673
|
+
|
4674
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4675
|
+
|
4676
|
+
|
4677
|
+
// "A block node is either an Element whose "display" property does not have
|
4678
|
+
// resolved value "inline" or "inline-block" or "inline-table" or "none", or a
|
4679
|
+
// Document, or a DocumentFragment."
|
4680
|
+
function isBlockNode(node) {
|
4681
|
+
return node &&
|
4682
|
+
((node.nodeType == 1 && !/^(inline(-block|-table)?|none)$/.test(getComputedDisplay(node))) ||
|
4683
|
+
node.nodeType == 9 || node.nodeType == 11);
|
4684
|
+
}
|
4685
|
+
|
4686
|
+
function getLastDescendantOrSelf(node) {
|
4687
|
+
var lastChild = node.lastChild;
|
4688
|
+
return lastChild ? getLastDescendantOrSelf(lastChild) : node;
|
4689
|
+
}
|
4690
|
+
|
4691
|
+
function containsPositions(node) {
|
4692
|
+
return dom.isCharacterDataNode(node) ||
|
4693
|
+
!/^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i.test(node.nodeName);
|
4694
|
+
}
|
4695
|
+
|
4696
|
+
function getAncestors(node) {
|
4697
|
+
var ancestors = [];
|
4698
|
+
while (node.parentNode) {
|
4699
|
+
ancestors.unshift(node.parentNode);
|
4700
|
+
node = node.parentNode;
|
4701
|
+
}
|
4702
|
+
return ancestors;
|
4703
|
+
}
|
4704
|
+
|
4705
|
+
function getAncestorsAndSelf(node) {
|
4706
|
+
return getAncestors(node).concat([node]);
|
4707
|
+
}
|
4708
|
+
|
4709
|
+
function nextNodeDescendants(node) {
|
4710
|
+
while (node && !node.nextSibling) {
|
4711
|
+
node = node.parentNode;
|
4712
|
+
}
|
4713
|
+
if (!node) {
|
4714
|
+
return null;
|
4715
|
+
}
|
4716
|
+
return node.nextSibling;
|
4717
|
+
}
|
4718
|
+
|
4719
|
+
function nextNode(node, excludeChildren) {
|
4720
|
+
if (!excludeChildren && node.hasChildNodes()) {
|
4721
|
+
return node.firstChild;
|
4722
|
+
}
|
4723
|
+
return nextNodeDescendants(node);
|
4724
|
+
}
|
4725
|
+
|
4726
|
+
function previousNode(node) {
|
4727
|
+
var previous = node.previousSibling;
|
4728
|
+
if (previous) {
|
4729
|
+
node = previous;
|
4730
|
+
while (node.hasChildNodes()) {
|
4731
|
+
node = node.lastChild;
|
4732
|
+
}
|
4733
|
+
return node;
|
4734
|
+
}
|
4735
|
+
var parent = node.parentNode;
|
4736
|
+
if (parent && parent.nodeType == 1) {
|
4737
|
+
return parent;
|
4738
|
+
}
|
4739
|
+
return null;
|
4740
|
+
}
|
4741
|
+
|
4742
|
+
// Adpated from Aryeh's code.
|
4743
|
+
// "A whitespace node is either a Text node whose data is the empty string; or
|
4744
|
+
// a Text node whose data consists only of one or more tabs (0x0009), line
|
4745
|
+
// feeds (0x000A), carriage returns (0x000D), and/or spaces (0x0020), and whose
|
4746
|
+
// parent is an Element whose resolved value for "white-space" is "normal" or
|
4747
|
+
// "nowrap"; or a Text node whose data consists only of one or more tabs
|
4748
|
+
// (0x0009), carriage returns (0x000D), and/or spaces (0x0020), and whose
|
4749
|
+
// parent is an Element whose resolved value for "white-space" is "pre-line"."
|
4750
|
+
function isWhitespaceNode(node) {
|
4751
|
+
if (!node || node.nodeType != 3) {
|
4752
|
+
return false;
|
4753
|
+
}
|
4754
|
+
var text = node.data;
|
4755
|
+
if (text === "") {
|
4756
|
+
return true;
|
4757
|
+
}
|
4758
|
+
var parent = node.parentNode;
|
4759
|
+
if (!parent || parent.nodeType != 1) {
|
4760
|
+
return false;
|
4761
|
+
}
|
4762
|
+
var computedWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
|
4763
|
+
|
4764
|
+
return (/^[\t\n\r ]+$/.test(text) && /^(normal|nowrap)$/.test(computedWhiteSpace)) ||
|
4765
|
+
(/^[\t\r ]+$/.test(text) && computedWhiteSpace == "pre-line");
|
4766
|
+
}
|
4767
|
+
|
4768
|
+
// Adpated from Aryeh's code.
|
4769
|
+
// "node is a collapsed whitespace node if the following algorithm returns
|
4770
|
+
// true:"
|
4771
|
+
function isCollapsedWhitespaceNode(node) {
|
4772
|
+
// "If node's data is the empty string, return true."
|
4773
|
+
if (node.data === "") {
|
4774
|
+
return true;
|
4775
|
+
}
|
4776
|
+
|
4777
|
+
// "If node is not a whitespace node, return false."
|
4778
|
+
if (!isWhitespaceNode(node)) {
|
4779
|
+
return false;
|
4780
|
+
}
|
4781
|
+
|
4782
|
+
// "Let ancestor be node's parent."
|
4783
|
+
var ancestor = node.parentNode;
|
4784
|
+
|
4785
|
+
// "If ancestor is null, return true."
|
4786
|
+
if (!ancestor) {
|
4787
|
+
return true;
|
4788
|
+
}
|
4789
|
+
|
4790
|
+
// "If the "display" property of some ancestor of node has resolved value "none", return true."
|
4791
|
+
if (isHidden(node)) {
|
4792
|
+
return true;
|
4793
|
+
}
|
4794
|
+
|
4795
|
+
return false;
|
4796
|
+
}
|
4797
|
+
|
4798
|
+
function isCollapsedNode(node) {
|
4799
|
+
var type = node.nodeType;
|
4800
|
+
return type == 7 /* PROCESSING_INSTRUCTION */ ||
|
4801
|
+
type == 8 /* COMMENT */ ||
|
4802
|
+
isHidden(node) ||
|
4803
|
+
/^(script|style)$/i.test(node.nodeName) ||
|
4804
|
+
isVisibilityHiddenTextNode(node) ||
|
4805
|
+
isCollapsedWhitespaceNode(node);
|
4806
|
+
}
|
4807
|
+
|
4808
|
+
function isIgnoredNode(node, win) {
|
4809
|
+
var type = node.nodeType;
|
4810
|
+
return type == 7 /* PROCESSING_INSTRUCTION */ ||
|
4811
|
+
type == 8 /* COMMENT */ ||
|
4812
|
+
(type == 1 && getComputedDisplay(node, win) == "none");
|
4813
|
+
}
|
4814
|
+
|
4815
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4816
|
+
|
4817
|
+
// Possibly overengineered caching system to prevent repeated DOM calls slowing everything down
|
4818
|
+
|
4819
|
+
function Cache() {
|
4820
|
+
this.store = {};
|
4821
|
+
}
|
4822
|
+
|
4823
|
+
Cache.prototype = {
|
4824
|
+
get: function(key) {
|
4825
|
+
return this.store.hasOwnProperty(key) ? this.store[key] : null;
|
4826
|
+
},
|
4827
|
+
|
4828
|
+
set: function(key, value) {
|
4829
|
+
return this.store[key] = value;
|
4830
|
+
}
|
4831
|
+
};
|
4832
|
+
|
4833
|
+
var cachedCount = 0, uncachedCount = 0;
|
4834
|
+
|
4835
|
+
function createCachingGetter(methodName, func, objProperty) {
|
4836
|
+
return function(args) {
|
4837
|
+
var cache = this.cache;
|
4838
|
+
if (cache.hasOwnProperty(methodName)) {
|
4839
|
+
cachedCount++;
|
4840
|
+
return cache[methodName];
|
4841
|
+
} else {
|
4842
|
+
uncachedCount++;
|
4843
|
+
var value = func.call(this, objProperty ? this[objProperty] : this, args);
|
4844
|
+
cache[methodName] = value;
|
4845
|
+
return value;
|
4846
|
+
}
|
4847
|
+
};
|
4848
|
+
}
|
4849
|
+
|
4850
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4851
|
+
|
4852
|
+
function NodeWrapper(node, session) {
|
4853
|
+
this.node = node;
|
4854
|
+
this.session = session;
|
4855
|
+
this.cache = new Cache();
|
4856
|
+
this.positions = new Cache();
|
4857
|
+
}
|
4858
|
+
|
4859
|
+
var nodeProto = {
|
4860
|
+
getPosition: function(offset) {
|
4861
|
+
var positions = this.positions;
|
4862
|
+
return positions.get(offset) || positions.set(offset, new Position(this, offset));
|
4863
|
+
},
|
4864
|
+
|
4865
|
+
toString: function() {
|
4866
|
+
return "[NodeWrapper(" + dom.inspectNode(this.node) + ")]";
|
4867
|
+
}
|
4868
|
+
};
|
4869
|
+
|
4870
|
+
NodeWrapper.prototype = nodeProto;
|
4871
|
+
|
4872
|
+
var EMPTY = "EMPTY",
|
4873
|
+
NON_SPACE = "NON_SPACE",
|
4874
|
+
UNCOLLAPSIBLE_SPACE = "UNCOLLAPSIBLE_SPACE",
|
4875
|
+
COLLAPSIBLE_SPACE = "COLLAPSIBLE_SPACE",
|
4876
|
+
TRAILING_SPACE_BEFORE_BLOCK = "TRAILING_SPACE_BEFORE_BLOCK",
|
4877
|
+
TRAILING_SPACE_IN_BLOCK = "TRAILING_SPACE_IN_BLOCK",
|
4878
|
+
TRAILING_SPACE_BEFORE_BR = "TRAILING_SPACE_BEFORE_BR",
|
4879
|
+
PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK = "PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK",
|
4880
|
+
TRAILING_LINE_BREAK_AFTER_BR = "TRAILING_LINE_BREAK_AFTER_BR",
|
4881
|
+
INCLUDED_TRAILING_LINE_BREAK_AFTER_BR = "INCLUDED_TRAILING_LINE_BREAK_AFTER_BR";
|
4882
|
+
|
4883
|
+
extend(nodeProto, {
|
4884
|
+
isCharacterDataNode: createCachingGetter("isCharacterDataNode", dom.isCharacterDataNode, "node"),
|
4885
|
+
getNodeIndex: createCachingGetter("nodeIndex", dom.getNodeIndex, "node"),
|
4886
|
+
getLength: createCachingGetter("nodeLength", dom.getNodeLength, "node"),
|
4887
|
+
containsPositions: createCachingGetter("containsPositions", containsPositions, "node"),
|
4888
|
+
isWhitespace: createCachingGetter("isWhitespace", isWhitespaceNode, "node"),
|
4889
|
+
isCollapsedWhitespace: createCachingGetter("isCollapsedWhitespace", isCollapsedWhitespaceNode, "node"),
|
4890
|
+
getComputedDisplay: createCachingGetter("computedDisplay", getComputedDisplay, "node"),
|
4891
|
+
isCollapsed: createCachingGetter("collapsed", isCollapsedNode, "node"),
|
4892
|
+
isIgnored: createCachingGetter("ignored", isIgnoredNode, "node"),
|
4893
|
+
next: createCachingGetter("nextPos", nextNode, "node"),
|
4894
|
+
previous: createCachingGetter("previous", previousNode, "node"),
|
4895
|
+
|
4896
|
+
getTextNodeInfo: createCachingGetter("textNodeInfo", function(textNode) {
|
4897
|
+
var spaceRegex = null, collapseSpaces = false;
|
4898
|
+
var cssWhitespace = getComputedStyleProperty(textNode.parentNode, "whiteSpace");
|
4899
|
+
var preLine = (cssWhitespace == "pre-line");
|
4900
|
+
if (preLine) {
|
4901
|
+
spaceRegex = spacesMinusLineBreaksRegex;
|
4902
|
+
collapseSpaces = true;
|
4903
|
+
} else if (cssWhitespace == "normal" || cssWhitespace == "nowrap") {
|
4904
|
+
spaceRegex = spacesRegex;
|
4905
|
+
collapseSpaces = true;
|
4906
|
+
}
|
4907
|
+
|
4908
|
+
return {
|
4909
|
+
node: textNode,
|
4910
|
+
text: textNode.data,
|
4911
|
+
spaceRegex: spaceRegex,
|
4912
|
+
collapseSpaces: collapseSpaces,
|
4913
|
+
preLine: preLine
|
4914
|
+
};
|
4915
|
+
}, "node"),
|
4916
|
+
|
4917
|
+
hasInnerText: createCachingGetter("hasInnerText", function(el, backward) {
|
4918
|
+
var session = this.session;
|
4919
|
+
var posAfterEl = session.getPosition(el.parentNode, this.getNodeIndex() + 1);
|
4920
|
+
var firstPosInEl = session.getPosition(el, 0);
|
4921
|
+
|
4922
|
+
var pos = backward ? posAfterEl : firstPosInEl;
|
4923
|
+
var endPos = backward ? firstPosInEl : posAfterEl;
|
4924
|
+
|
4925
|
+
/*
|
4926
|
+
<body><p>X </p><p>Y</p></body>
|
4927
|
+
|
4928
|
+
Positions:
|
4929
|
+
|
4930
|
+
body:0:""
|
4931
|
+
p:0:""
|
4932
|
+
text:0:""
|
4933
|
+
text:1:"X"
|
4934
|
+
text:2:TRAILING_SPACE_IN_BLOCK
|
4935
|
+
text:3:COLLAPSED_SPACE
|
4936
|
+
p:1:""
|
4937
|
+
body:1:"\n"
|
4938
|
+
p:0:""
|
4939
|
+
text:0:""
|
4940
|
+
text:1:"Y"
|
4941
|
+
|
4942
|
+
A character is a TRAILING_SPACE_IN_BLOCK iff:
|
4943
|
+
|
4944
|
+
- There is no uncollapsed character after it within the visible containing block element
|
4945
|
+
|
4946
|
+
A character is a TRAILING_SPACE_BEFORE_BR iff:
|
4947
|
+
|
4948
|
+
- There is no uncollapsed character after it preceding a <br> element
|
4949
|
+
|
4950
|
+
An element has inner text iff
|
4951
|
+
|
4952
|
+
- It is not hidden
|
4953
|
+
- It contains an uncollapsed character
|
4954
|
+
|
4955
|
+
All trailing spaces (pre-line, before <br>, end of block) require definite non-empty characters to render.
|
4956
|
+
*/
|
4957
|
+
|
4958
|
+
while (pos !== endPos) {
|
4959
|
+
pos.prepopulateChar();
|
4960
|
+
if (pos.isDefinitelyNonEmpty()) {
|
4961
|
+
return true;
|
4962
|
+
}
|
4963
|
+
pos = backward ? pos.previousVisible() : pos.nextVisible();
|
4964
|
+
}
|
4965
|
+
|
4966
|
+
return false;
|
4967
|
+
}, "node"),
|
4968
|
+
|
4969
|
+
isRenderedBlock: createCachingGetter("isRenderedBlock", function(el) {
|
4970
|
+
// Ensure that a block element containing a <br> is considered to have inner text
|
4971
|
+
var brs = el.getElementsByTagName("br");
|
4972
|
+
for (var i = 0, len = brs.length; i < len; ++i) {
|
4973
|
+
if (!isCollapsedNode(brs[i])) {
|
4974
|
+
return true;
|
4975
|
+
}
|
4976
|
+
}
|
4977
|
+
return this.hasInnerText();
|
4978
|
+
}, "node"),
|
4979
|
+
|
4980
|
+
getTrailingSpace: createCachingGetter("trailingSpace", function(el) {
|
4981
|
+
if (el.tagName.toLowerCase() == "br") {
|
4982
|
+
return "";
|
4983
|
+
} else {
|
4984
|
+
switch (this.getComputedDisplay()) {
|
4985
|
+
case "inline":
|
4986
|
+
var child = el.lastChild;
|
4987
|
+
while (child) {
|
4988
|
+
if (!isIgnoredNode(child)) {
|
4989
|
+
return (child.nodeType == 1) ? this.session.getNodeWrapper(child).getTrailingSpace() : "";
|
4990
|
+
}
|
4991
|
+
child = child.previousSibling;
|
4992
|
+
}
|
4993
|
+
break;
|
4994
|
+
case "inline-block":
|
4995
|
+
case "inline-table":
|
4996
|
+
case "none":
|
4997
|
+
case "table-column":
|
4998
|
+
case "table-column-group":
|
4999
|
+
break;
|
5000
|
+
case "table-cell":
|
5001
|
+
return "\t";
|
5002
|
+
default:
|
5003
|
+
return this.isRenderedBlock(true) ? "\n" : "";
|
5004
|
+
}
|
5005
|
+
}
|
5006
|
+
return "";
|
5007
|
+
}, "node"),
|
5008
|
+
|
5009
|
+
getLeadingSpace: createCachingGetter("leadingSpace", function(el) {
|
5010
|
+
switch (this.getComputedDisplay()) {
|
5011
|
+
case "inline":
|
5012
|
+
case "inline-block":
|
5013
|
+
case "inline-table":
|
5014
|
+
case "none":
|
5015
|
+
case "table-column":
|
5016
|
+
case "table-column-group":
|
5017
|
+
case "table-cell":
|
5018
|
+
break;
|
5019
|
+
default:
|
5020
|
+
return this.isRenderedBlock(false) ? "\n" : "";
|
5021
|
+
}
|
5022
|
+
return "";
|
5023
|
+
}, "node")
|
5024
|
+
});
|
5025
|
+
|
5026
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5027
|
+
|
5028
|
+
function Position(nodeWrapper, offset) {
|
5029
|
+
this.offset = offset;
|
5030
|
+
this.nodeWrapper = nodeWrapper;
|
5031
|
+
this.node = nodeWrapper.node;
|
5032
|
+
this.session = nodeWrapper.session;
|
5033
|
+
this.cache = new Cache();
|
5034
|
+
}
|
5035
|
+
|
5036
|
+
function inspectPosition() {
|
5037
|
+
return "[Position(" + dom.inspectNode(this.node) + ":" + this.offset + ")]";
|
5038
|
+
}
|
5039
|
+
|
5040
|
+
var positionProto = {
|
5041
|
+
character: "",
|
5042
|
+
characterType: EMPTY,
|
5043
|
+
isBr: false,
|
5044
|
+
|
5045
|
+
/*
|
5046
|
+
This method:
|
5047
|
+
- Fully populates positions that have characters that can be determined independently of any other characters.
|
5048
|
+
- Populates most types of space positions with a provisional character. The character is finalized later.
|
5049
|
+
*/
|
5050
|
+
prepopulateChar: function() {
|
5051
|
+
var pos = this;
|
5052
|
+
if (!pos.prepopulatedChar) {
|
5053
|
+
var node = pos.node, offset = pos.offset;
|
5054
|
+
var visibleChar = "", charType = EMPTY;
|
5055
|
+
var finalizedChar = false;
|
5056
|
+
if (offset > 0) {
|
5057
|
+
if (node.nodeType == 3) {
|
5058
|
+
var text = node.data;
|
5059
|
+
var textChar = text.charAt(offset - 1);
|
5060
|
+
|
5061
|
+
var nodeInfo = pos.nodeWrapper.getTextNodeInfo();
|
5062
|
+
var spaceRegex = nodeInfo.spaceRegex;
|
5063
|
+
if (nodeInfo.collapseSpaces) {
|
5064
|
+
if (spaceRegex.test(textChar)) {
|
5065
|
+
// "If the character at position is from set, append a single space (U+0020) to newdata and advance
|
5066
|
+
// position until the character at position is not from set."
|
5067
|
+
|
5068
|
+
// We also need to check for the case where we're in a pre-line and we have a space preceding a
|
5069
|
+
// line break, because such spaces are collapsed in some browsers
|
5070
|
+
if (offset > 1 && spaceRegex.test(text.charAt(offset - 2))) {
|
5071
|
+
} else if (nodeInfo.preLine && text.charAt(offset) === "\n") {
|
5072
|
+
visibleChar = " ";
|
5073
|
+
charType = PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK;
|
5074
|
+
} else {
|
5075
|
+
visibleChar = " ";
|
5076
|
+
//pos.checkForFollowingLineBreak = true;
|
5077
|
+
charType = COLLAPSIBLE_SPACE;
|
5078
|
+
}
|
5079
|
+
} else {
|
5080
|
+
visibleChar = textChar;
|
5081
|
+
charType = NON_SPACE;
|
5082
|
+
finalizedChar = true;
|
5083
|
+
}
|
5084
|
+
} else {
|
5085
|
+
visibleChar = textChar;
|
5086
|
+
charType = UNCOLLAPSIBLE_SPACE;
|
5087
|
+
finalizedChar = true;
|
5088
|
+
}
|
5089
|
+
} else {
|
5090
|
+
var nodePassed = node.childNodes[offset - 1];
|
5091
|
+
if (nodePassed && nodePassed.nodeType == 1 && !isCollapsedNode(nodePassed)) {
|
5092
|
+
if (nodePassed.tagName.toLowerCase() == "br") {
|
5093
|
+
visibleChar = "\n";
|
5094
|
+
pos.isBr = true;
|
5095
|
+
charType = COLLAPSIBLE_SPACE;
|
5096
|
+
finalizedChar = false;
|
5097
|
+
} else {
|
5098
|
+
pos.checkForTrailingSpace = true;
|
5099
|
+
}
|
5100
|
+
}
|
5101
|
+
|
5102
|
+
// Check the leading space of the next node for the case when a block element follows an inline
|
5103
|
+
// element or text node. In that case, there is an implied line break between the two nodes.
|
5104
|
+
if (!visibleChar) {
|
5105
|
+
var nextNode = node.childNodes[offset];
|
5106
|
+
if (nextNode && nextNode.nodeType == 1 && !isCollapsedNode(nextNode)) {
|
5107
|
+
pos.checkForLeadingSpace = true;
|
5108
|
+
}
|
5109
|
+
}
|
5110
|
+
}
|
5111
|
+
}
|
5112
|
+
|
5113
|
+
pos.prepopulatedChar = true;
|
5114
|
+
pos.character = visibleChar;
|
5115
|
+
pos.characterType = charType;
|
5116
|
+
pos.isCharInvariant = finalizedChar;
|
5117
|
+
}
|
5118
|
+
},
|
5119
|
+
|
5120
|
+
isDefinitelyNonEmpty: function() {
|
5121
|
+
var charType = this.characterType;
|
5122
|
+
return charType == NON_SPACE || charType == UNCOLLAPSIBLE_SPACE;
|
5123
|
+
},
|
5124
|
+
|
5125
|
+
// Resolve leading and trailing spaces, which may involve prepopulating other positions
|
5126
|
+
resolveLeadingAndTrailingSpaces: function() {
|
5127
|
+
if (!this.prepopulatedChar) {
|
5128
|
+
this.prepopulateChar();
|
5129
|
+
}
|
5130
|
+
if (this.checkForTrailingSpace) {
|
5131
|
+
var trailingSpace = this.session.getNodeWrapper(this.node.childNodes[this.offset - 1]).getTrailingSpace();
|
5132
|
+
if (trailingSpace) {
|
5133
|
+
this.isTrailingSpace = true;
|
5134
|
+
this.character = trailingSpace;
|
5135
|
+
this.characterType = COLLAPSIBLE_SPACE;
|
5136
|
+
}
|
5137
|
+
this.checkForTrailingSpace = false;
|
5138
|
+
}
|
5139
|
+
if (this.checkForLeadingSpace) {
|
5140
|
+
var leadingSpace = this.session.getNodeWrapper(this.node.childNodes[this.offset]).getLeadingSpace();
|
5141
|
+
if (leadingSpace) {
|
5142
|
+
this.isLeadingSpace = true;
|
5143
|
+
this.character = leadingSpace;
|
5144
|
+
this.characterType = COLLAPSIBLE_SPACE;
|
5145
|
+
}
|
5146
|
+
this.checkForLeadingSpace = false;
|
5147
|
+
}
|
5148
|
+
},
|
5149
|
+
|
5150
|
+
getPrecedingUncollapsedPosition: function(characterOptions) {
|
5151
|
+
var pos = this, character;
|
5152
|
+
while ( (pos = pos.previousVisible()) ) {
|
5153
|
+
character = pos.getCharacter(characterOptions);
|
5154
|
+
if (character !== "") {
|
5155
|
+
return pos;
|
5156
|
+
}
|
5157
|
+
}
|
5158
|
+
|
5159
|
+
return null;
|
5160
|
+
},
|
5161
|
+
|
5162
|
+
getCharacter: function(characterOptions) {
|
5163
|
+
this.resolveLeadingAndTrailingSpaces();
|
5164
|
+
|
5165
|
+
var thisChar = this.character, returnChar;
|
5166
|
+
|
5167
|
+
// Check if character is ignored
|
5168
|
+
var ignoredChars = normalizeIgnoredCharacters(characterOptions.ignoreCharacters);
|
5169
|
+
var isIgnoredCharacter = (thisChar !== "" && ignoredChars.indexOf(thisChar) > -1);
|
5170
|
+
|
5171
|
+
// Check if this position's character is invariant (i.e. not dependent on character options) and return it
|
5172
|
+
// if so
|
5173
|
+
if (this.isCharInvariant) {
|
5174
|
+
returnChar = isIgnoredCharacter ? "" : thisChar;
|
5175
|
+
return returnChar;
|
5176
|
+
}
|
5177
|
+
|
5178
|
+
var cacheKey = ["character", characterOptions.includeSpaceBeforeBr, characterOptions.includeBlockContentTrailingSpace, characterOptions.includePreLineTrailingSpace, ignoredChars].join("_");
|
5179
|
+
var cachedChar = this.cache.get(cacheKey);
|
5180
|
+
if (cachedChar !== null) {
|
5181
|
+
return cachedChar;
|
5182
|
+
}
|
5183
|
+
|
5184
|
+
// We need to actually get the character now
|
5185
|
+
var character = "";
|
5186
|
+
var collapsible = (this.characterType == COLLAPSIBLE_SPACE);
|
5187
|
+
|
5188
|
+
var nextPos, previousPos;
|
5189
|
+
var gotPreviousPos = false;
|
5190
|
+
var pos = this;
|
5191
|
+
|
5192
|
+
function getPreviousPos() {
|
5193
|
+
if (!gotPreviousPos) {
|
5194
|
+
previousPos = pos.getPrecedingUncollapsedPosition(characterOptions);
|
5195
|
+
gotPreviousPos = true;
|
5196
|
+
}
|
5197
|
+
return previousPos;
|
5198
|
+
}
|
5199
|
+
|
5200
|
+
// Disallow a collapsible space that is followed by a line break or is the last character
|
5201
|
+
if (collapsible) {
|
5202
|
+
// Allow a trailing space that we've previously determined should be included
|
5203
|
+
if (this.type == INCLUDED_TRAILING_LINE_BREAK_AFTER_BR) {
|
5204
|
+
character = "\n";
|
5205
|
+
}
|
5206
|
+
// Disallow a collapsible space that follows a trailing space or line break, or is the first character,
|
5207
|
+
// or follows a collapsible included space
|
5208
|
+
else if (thisChar == " " &&
|
5209
|
+
(!getPreviousPos() || previousPos.isTrailingSpace || previousPos.character == "\n" || (previousPos.character == " " && previousPos.characterType == COLLAPSIBLE_SPACE))) {
|
5210
|
+
}
|
5211
|
+
// Allow a leading line break unless it follows a line break
|
5212
|
+
else if (thisChar == "\n" && this.isLeadingSpace) {
|
5213
|
+
if (getPreviousPos() && previousPos.character != "\n") {
|
5214
|
+
character = "\n";
|
5215
|
+
} else {
|
5216
|
+
}
|
5217
|
+
} else {
|
5218
|
+
nextPos = this.nextUncollapsed();
|
5219
|
+
if (nextPos) {
|
5220
|
+
if (nextPos.isBr) {
|
5221
|
+
this.type = TRAILING_SPACE_BEFORE_BR;
|
5222
|
+
} else if (nextPos.isTrailingSpace && nextPos.character == "\n") {
|
5223
|
+
this.type = TRAILING_SPACE_IN_BLOCK;
|
5224
|
+
} else if (nextPos.isLeadingSpace && nextPos.character == "\n") {
|
5225
|
+
this.type = TRAILING_SPACE_BEFORE_BLOCK;
|
5226
|
+
}
|
5227
|
+
|
5228
|
+
if (nextPos.character == "\n") {
|
5229
|
+
if (this.type == TRAILING_SPACE_BEFORE_BR && !characterOptions.includeSpaceBeforeBr) {
|
5230
|
+
} else if (this.type == TRAILING_SPACE_BEFORE_BLOCK && !characterOptions.includeSpaceBeforeBlock) {
|
5231
|
+
} else if (this.type == TRAILING_SPACE_IN_BLOCK && nextPos.isTrailingSpace && !characterOptions.includeBlockContentTrailingSpace) {
|
5232
|
+
} else if (this.type == PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK && nextPos.type == NON_SPACE && !characterOptions.includePreLineTrailingSpace) {
|
5233
|
+
} else if (thisChar == "\n") {
|
5234
|
+
if (nextPos.isTrailingSpace) {
|
5235
|
+
if (this.isTrailingSpace) {
|
5236
|
+
} else if (this.isBr) {
|
5237
|
+
nextPos.type = TRAILING_LINE_BREAK_AFTER_BR;
|
5238
|
+
|
5239
|
+
if (getPreviousPos() && previousPos.isLeadingSpace && !previousPos.isTrailingSpace && previousPos.character == "\n") {
|
5240
|
+
nextPos.character = "";
|
5241
|
+
} else {
|
5242
|
+
nextPos.type = INCLUDED_TRAILING_LINE_BREAK_AFTER_BR;
|
5243
|
+
}
|
5244
|
+
}
|
5245
|
+
} else {
|
5246
|
+
character = "\n";
|
5247
|
+
}
|
5248
|
+
} else if (thisChar == " ") {
|
5249
|
+
character = " ";
|
5250
|
+
} else {
|
5251
|
+
}
|
5252
|
+
} else {
|
5253
|
+
character = thisChar;
|
5254
|
+
}
|
5255
|
+
} else {
|
5256
|
+
}
|
5257
|
+
}
|
5258
|
+
}
|
5259
|
+
|
5260
|
+
if (ignoredChars.indexOf(character) > -1) {
|
5261
|
+
character = "";
|
5262
|
+
}
|
5263
|
+
|
5264
|
+
|
5265
|
+
this.cache.set(cacheKey, character);
|
5266
|
+
|
5267
|
+
return character;
|
5268
|
+
},
|
5269
|
+
|
5270
|
+
equals: function(pos) {
|
5271
|
+
return !!pos && this.node === pos.node && this.offset === pos.offset;
|
5272
|
+
},
|
5273
|
+
|
5274
|
+
inspect: inspectPosition,
|
5275
|
+
|
5276
|
+
toString: function() {
|
5277
|
+
return this.character;
|
5278
|
+
}
|
5279
|
+
};
|
5280
|
+
|
5281
|
+
Position.prototype = positionProto;
|
5282
|
+
|
5283
|
+
extend(positionProto, {
|
5284
|
+
next: createCachingGetter("nextPos", function(pos) {
|
5285
|
+
var nodeWrapper = pos.nodeWrapper, node = pos.node, offset = pos.offset, session = nodeWrapper.session;
|
5286
|
+
if (!node) {
|
5287
|
+
return null;
|
5288
|
+
}
|
5289
|
+
var nextNode, nextOffset, child;
|
5290
|
+
if (offset == nodeWrapper.getLength()) {
|
5291
|
+
// Move onto the next node
|
5292
|
+
nextNode = node.parentNode;
|
5293
|
+
nextOffset = nextNode ? nodeWrapper.getNodeIndex() + 1 : 0;
|
5294
|
+
} else {
|
5295
|
+
if (nodeWrapper.isCharacterDataNode()) {
|
5296
|
+
nextNode = node;
|
5297
|
+
nextOffset = offset + 1;
|
5298
|
+
} else {
|
5299
|
+
child = node.childNodes[offset];
|
5300
|
+
// Go into the children next, if children there are
|
5301
|
+
if (session.getNodeWrapper(child).containsPositions()) {
|
5302
|
+
nextNode = child;
|
5303
|
+
nextOffset = 0;
|
5304
|
+
} else {
|
5305
|
+
nextNode = node;
|
5306
|
+
nextOffset = offset + 1;
|
5307
|
+
}
|
5308
|
+
}
|
5309
|
+
}
|
5310
|
+
|
5311
|
+
return nextNode ? session.getPosition(nextNode, nextOffset) : null;
|
5312
|
+
}),
|
5313
|
+
|
5314
|
+
previous: createCachingGetter("previous", function(pos) {
|
5315
|
+
var nodeWrapper = pos.nodeWrapper, node = pos.node, offset = pos.offset, session = nodeWrapper.session;
|
5316
|
+
var previousNode, previousOffset, child;
|
5317
|
+
if (offset == 0) {
|
5318
|
+
previousNode = node.parentNode;
|
5319
|
+
previousOffset = previousNode ? nodeWrapper.getNodeIndex() : 0;
|
5320
|
+
} else {
|
5321
|
+
if (nodeWrapper.isCharacterDataNode()) {
|
5322
|
+
previousNode = node;
|
5323
|
+
previousOffset = offset - 1;
|
5324
|
+
} else {
|
5325
|
+
child = node.childNodes[offset - 1];
|
5326
|
+
// Go into the children next, if children there are
|
5327
|
+
if (session.getNodeWrapper(child).containsPositions()) {
|
5328
|
+
previousNode = child;
|
5329
|
+
previousOffset = dom.getNodeLength(child);
|
5330
|
+
} else {
|
5331
|
+
previousNode = node;
|
5332
|
+
previousOffset = offset - 1;
|
5333
|
+
}
|
5334
|
+
}
|
5335
|
+
}
|
5336
|
+
return previousNode ? session.getPosition(previousNode, previousOffset) : null;
|
5337
|
+
}),
|
5338
|
+
|
5339
|
+
/*
|
5340
|
+
Next and previous position moving functions that filter out
|
5341
|
+
|
5342
|
+
- Hidden (CSS visibility/display) elements
|
5343
|
+
- Script and style elements
|
5344
|
+
*/
|
5345
|
+
nextVisible: createCachingGetter("nextVisible", function(pos) {
|
5346
|
+
var next = pos.next();
|
5347
|
+
if (!next) {
|
5348
|
+
return null;
|
5349
|
+
}
|
5350
|
+
var nodeWrapper = next.nodeWrapper, node = next.node;
|
5351
|
+
var newPos = next;
|
5352
|
+
if (nodeWrapper.isCollapsed()) {
|
5353
|
+
// We're skipping this node and all its descendants
|
5354
|
+
newPos = nodeWrapper.session.getPosition(node.parentNode, nodeWrapper.getNodeIndex() + 1);
|
5355
|
+
}
|
5356
|
+
return newPos;
|
5357
|
+
}),
|
5358
|
+
|
5359
|
+
nextUncollapsed: createCachingGetter("nextUncollapsed", function(pos) {
|
5360
|
+
var nextPos = pos;
|
5361
|
+
while ( (nextPos = nextPos.nextVisible()) ) {
|
5362
|
+
nextPos.resolveLeadingAndTrailingSpaces();
|
5363
|
+
if (nextPos.character !== "") {
|
5364
|
+
return nextPos;
|
5365
|
+
}
|
5366
|
+
}
|
5367
|
+
return null;
|
5368
|
+
}),
|
5369
|
+
|
5370
|
+
previousVisible: createCachingGetter("previousVisible", function(pos) {
|
5371
|
+
var previous = pos.previous();
|
5372
|
+
if (!previous) {
|
5373
|
+
return null;
|
5374
|
+
}
|
5375
|
+
var nodeWrapper = previous.nodeWrapper, node = previous.node;
|
5376
|
+
var newPos = previous;
|
5377
|
+
if (nodeWrapper.isCollapsed()) {
|
5378
|
+
// We're skipping this node and all its descendants
|
5379
|
+
newPos = nodeWrapper.session.getPosition(node.parentNode, nodeWrapper.getNodeIndex());
|
5380
|
+
}
|
5381
|
+
return newPos;
|
5382
|
+
})
|
5383
|
+
});
|
5384
|
+
|
5385
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5386
|
+
|
5387
|
+
var currentSession = null;
|
5388
|
+
|
5389
|
+
var Session = (function() {
|
5390
|
+
function createWrapperCache(nodeProperty) {
|
5391
|
+
var cache = new Cache();
|
5392
|
+
|
5393
|
+
return {
|
5394
|
+
get: function(node) {
|
5395
|
+
var wrappersByProperty = cache.get(node[nodeProperty]);
|
5396
|
+
if (wrappersByProperty) {
|
5397
|
+
for (var i = 0, wrapper; wrapper = wrappersByProperty[i++]; ) {
|
5398
|
+
if (wrapper.node === node) {
|
5399
|
+
return wrapper;
|
5400
|
+
}
|
5401
|
+
}
|
5402
|
+
}
|
5403
|
+
return null;
|
5404
|
+
},
|
5405
|
+
|
5406
|
+
set: function(nodeWrapper) {
|
5407
|
+
var property = nodeWrapper.node[nodeProperty];
|
5408
|
+
var wrappersByProperty = cache.get(property) || cache.set(property, []);
|
5409
|
+
wrappersByProperty.push(nodeWrapper);
|
5410
|
+
}
|
5411
|
+
};
|
5412
|
+
}
|
5413
|
+
|
5414
|
+
var uniqueIDSupported = util.isHostProperty(document.documentElement, "uniqueID");
|
5415
|
+
|
5416
|
+
function Session() {
|
5417
|
+
this.initCaches();
|
5418
|
+
}
|
5419
|
+
|
5420
|
+
Session.prototype = {
|
5421
|
+
initCaches: function() {
|
5422
|
+
this.elementCache = uniqueIDSupported ? (function() {
|
5423
|
+
var elementsCache = new Cache();
|
5424
|
+
|
5425
|
+
return {
|
5426
|
+
get: function(el) {
|
5427
|
+
return elementsCache.get(el.uniqueID);
|
5428
|
+
},
|
5429
|
+
|
5430
|
+
set: function(elWrapper) {
|
5431
|
+
elementsCache.set(elWrapper.node.uniqueID, elWrapper);
|
5432
|
+
}
|
5433
|
+
};
|
5434
|
+
})() : createWrapperCache("tagName");
|
5435
|
+
|
5436
|
+
// Store text nodes keyed by data, although we may need to truncate this
|
5437
|
+
this.textNodeCache = createWrapperCache("data");
|
5438
|
+
this.otherNodeCache = createWrapperCache("nodeName");
|
5439
|
+
},
|
5440
|
+
|
5441
|
+
getNodeWrapper: function(node) {
|
5442
|
+
var wrapperCache;
|
5443
|
+
switch (node.nodeType) {
|
5444
|
+
case 1:
|
5445
|
+
wrapperCache = this.elementCache;
|
5446
|
+
break;
|
5447
|
+
case 3:
|
5448
|
+
wrapperCache = this.textNodeCache;
|
5449
|
+
break;
|
5450
|
+
default:
|
5451
|
+
wrapperCache = this.otherNodeCache;
|
5452
|
+
break;
|
5453
|
+
}
|
5454
|
+
|
5455
|
+
var wrapper = wrapperCache.get(node);
|
5456
|
+
if (!wrapper) {
|
5457
|
+
wrapper = new NodeWrapper(node, this);
|
5458
|
+
wrapperCache.set(wrapper);
|
5459
|
+
}
|
5460
|
+
return wrapper;
|
5461
|
+
},
|
5462
|
+
|
5463
|
+
getPosition: function(node, offset) {
|
5464
|
+
return this.getNodeWrapper(node).getPosition(offset);
|
5465
|
+
},
|
5466
|
+
|
5467
|
+
getRangeBoundaryPosition: function(range, isStart) {
|
5468
|
+
var prefix = isStart ? "start" : "end";
|
5469
|
+
return this.getPosition(range[prefix + "Container"], range[prefix + "Offset"]);
|
5470
|
+
},
|
5471
|
+
|
5472
|
+
detach: function() {
|
5473
|
+
this.elementCache = this.textNodeCache = this.otherNodeCache = null;
|
5474
|
+
}
|
5475
|
+
};
|
5476
|
+
|
5477
|
+
return Session;
|
5478
|
+
})();
|
5479
|
+
|
5480
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5481
|
+
|
5482
|
+
function startSession() {
|
5483
|
+
endSession();
|
5484
|
+
return (currentSession = new Session());
|
5485
|
+
}
|
5486
|
+
|
5487
|
+
function getSession() {
|
5488
|
+
return currentSession || startSession();
|
5489
|
+
}
|
5490
|
+
|
5491
|
+
function endSession() {
|
5492
|
+
if (currentSession) {
|
5493
|
+
currentSession.detach();
|
5494
|
+
}
|
5495
|
+
currentSession = null;
|
5496
|
+
}
|
5497
|
+
|
5498
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5499
|
+
|
5500
|
+
// Extensions to the rangy.dom utility object
|
5501
|
+
|
5502
|
+
extend(dom, {
|
5503
|
+
nextNode: nextNode,
|
5504
|
+
previousNode: previousNode
|
5505
|
+
});
|
5506
|
+
|
5507
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5508
|
+
|
5509
|
+
function createCharacterIterator(startPos, backward, endPos, characterOptions) {
|
5510
|
+
|
5511
|
+
// Adjust the end position to ensure that it is actually reached
|
5512
|
+
if (endPos) {
|
5513
|
+
if (backward) {
|
5514
|
+
if (isCollapsedNode(endPos.node)) {
|
5515
|
+
endPos = startPos.previousVisible();
|
5516
|
+
}
|
5517
|
+
} else {
|
5518
|
+
if (isCollapsedNode(endPos.node)) {
|
5519
|
+
endPos = endPos.nextVisible();
|
5520
|
+
}
|
5521
|
+
}
|
5522
|
+
}
|
5523
|
+
|
5524
|
+
var pos = startPos, finished = false;
|
5525
|
+
|
5526
|
+
function next() {
|
5527
|
+
var charPos = null;
|
5528
|
+
if (backward) {
|
5529
|
+
charPos = pos;
|
5530
|
+
if (!finished) {
|
5531
|
+
pos = pos.previousVisible();
|
5532
|
+
finished = !pos || (endPos && pos.equals(endPos));
|
5533
|
+
}
|
5534
|
+
} else {
|
5535
|
+
if (!finished) {
|
5536
|
+
charPos = pos = pos.nextVisible();
|
5537
|
+
finished = !pos || (endPos && pos.equals(endPos));
|
5538
|
+
}
|
5539
|
+
}
|
5540
|
+
if (finished) {
|
5541
|
+
pos = null;
|
5542
|
+
}
|
5543
|
+
return charPos;
|
5544
|
+
}
|
5545
|
+
|
5546
|
+
var previousTextPos, returnPreviousTextPos = false;
|
5547
|
+
|
5548
|
+
return {
|
5549
|
+
next: function() {
|
5550
|
+
if (returnPreviousTextPos) {
|
5551
|
+
returnPreviousTextPos = false;
|
5552
|
+
return previousTextPos;
|
5553
|
+
} else {
|
5554
|
+
var pos, character;
|
5555
|
+
while ( (pos = next()) ) {
|
5556
|
+
character = pos.getCharacter(characterOptions);
|
5557
|
+
if (character) {
|
5558
|
+
previousTextPos = pos;
|
5559
|
+
return pos;
|
5560
|
+
}
|
5561
|
+
}
|
5562
|
+
return null;
|
5563
|
+
}
|
5564
|
+
},
|
5565
|
+
|
5566
|
+
rewind: function() {
|
5567
|
+
if (previousTextPos) {
|
5568
|
+
returnPreviousTextPos = true;
|
5569
|
+
} else {
|
5570
|
+
throw module.createError("createCharacterIterator: cannot rewind. Only one position can be rewound.");
|
5571
|
+
}
|
5572
|
+
},
|
5573
|
+
|
5574
|
+
dispose: function() {
|
5575
|
+
startPos = endPos = null;
|
5576
|
+
}
|
5577
|
+
};
|
5578
|
+
}
|
5579
|
+
|
5580
|
+
var arrayIndexOf = Array.prototype.indexOf ?
|
5581
|
+
function(arr, val) {
|
5582
|
+
return arr.indexOf(val);
|
5583
|
+
} :
|
5584
|
+
function(arr, val) {
|
5585
|
+
for (var i = 0, len = arr.length; i < len; ++i) {
|
5586
|
+
if (arr[i] === val) {
|
5587
|
+
return i;
|
5588
|
+
}
|
5589
|
+
}
|
5590
|
+
return -1;
|
5591
|
+
};
|
5592
|
+
|
5593
|
+
// Provides a pair of iterators over text positions, tokenized. Transparently requests more text when next()
|
5594
|
+
// is called and there is no more tokenized text
|
5595
|
+
function createTokenizedTextProvider(pos, characterOptions, wordOptions) {
|
5596
|
+
var forwardIterator = createCharacterIterator(pos, false, null, characterOptions);
|
5597
|
+
var backwardIterator = createCharacterIterator(pos, true, null, characterOptions);
|
5598
|
+
var tokenizer = wordOptions.tokenizer;
|
5599
|
+
|
5600
|
+
// Consumes a word and the whitespace beyond it
|
5601
|
+
function consumeWord(forward) {
|
5602
|
+
var pos, textChar;
|
5603
|
+
var newChars = [], it = forward ? forwardIterator : backwardIterator;
|
5604
|
+
|
5605
|
+
var passedWordBoundary = false, insideWord = false;
|
5606
|
+
|
5607
|
+
while ( (pos = it.next()) ) {
|
5608
|
+
textChar = pos.character;
|
5609
|
+
|
5610
|
+
|
5611
|
+
if (allWhiteSpaceRegex.test(textChar)) {
|
5612
|
+
if (insideWord) {
|
5613
|
+
insideWord = false;
|
5614
|
+
passedWordBoundary = true;
|
5615
|
+
}
|
5616
|
+
} else {
|
5617
|
+
if (passedWordBoundary) {
|
5618
|
+
it.rewind();
|
5619
|
+
break;
|
5620
|
+
} else {
|
5621
|
+
insideWord = true;
|
5622
|
+
}
|
5623
|
+
}
|
5624
|
+
newChars.push(pos);
|
5625
|
+
}
|
5626
|
+
|
5627
|
+
|
5628
|
+
return newChars;
|
5629
|
+
}
|
5630
|
+
|
5631
|
+
// Get initial word surrounding initial position and tokenize it
|
5632
|
+
var forwardChars = consumeWord(true);
|
5633
|
+
var backwardChars = consumeWord(false).reverse();
|
5634
|
+
var tokens = tokenize(backwardChars.concat(forwardChars), wordOptions, tokenizer);
|
5635
|
+
|
5636
|
+
// Create initial token buffers
|
5637
|
+
var forwardTokensBuffer = forwardChars.length ?
|
5638
|
+
tokens.slice(arrayIndexOf(tokens, forwardChars[0].token)) : [];
|
5639
|
+
|
5640
|
+
var backwardTokensBuffer = backwardChars.length ?
|
5641
|
+
tokens.slice(0, arrayIndexOf(tokens, backwardChars.pop().token) + 1) : [];
|
5642
|
+
|
5643
|
+
function inspectBuffer(buffer) {
|
5644
|
+
var textPositions = ["[" + buffer.length + "]"];
|
5645
|
+
for (var i = 0; i < buffer.length; ++i) {
|
5646
|
+
textPositions.push("(word: " + buffer[i] + ", is word: " + buffer[i].isWord + ")");
|
5647
|
+
}
|
5648
|
+
return textPositions;
|
5649
|
+
}
|
5650
|
+
|
5651
|
+
|
5652
|
+
return {
|
5653
|
+
nextEndToken: function() {
|
5654
|
+
var lastToken, forwardChars;
|
5655
|
+
|
5656
|
+
// If we're down to the last token, consume character chunks until we have a word or run out of
|
5657
|
+
// characters to consume
|
5658
|
+
while ( forwardTokensBuffer.length == 1 &&
|
5659
|
+
!(lastToken = forwardTokensBuffer[0]).isWord &&
|
5660
|
+
(forwardChars = consumeWord(true)).length > 0) {
|
5661
|
+
|
5662
|
+
// Merge trailing non-word into next word and tokenize
|
5663
|
+
forwardTokensBuffer = tokenize(lastToken.chars.concat(forwardChars), wordOptions, tokenizer);
|
5664
|
+
}
|
5665
|
+
|
5666
|
+
return forwardTokensBuffer.shift();
|
5667
|
+
},
|
5668
|
+
|
5669
|
+
previousStartToken: function() {
|
5670
|
+
var lastToken, backwardChars;
|
5671
|
+
|
5672
|
+
// If we're down to the last token, consume character chunks until we have a word or run out of
|
5673
|
+
// characters to consume
|
5674
|
+
while ( backwardTokensBuffer.length == 1 &&
|
5675
|
+
!(lastToken = backwardTokensBuffer[0]).isWord &&
|
5676
|
+
(backwardChars = consumeWord(false)).length > 0) {
|
5677
|
+
|
5678
|
+
// Merge leading non-word into next word and tokenize
|
5679
|
+
backwardTokensBuffer = tokenize(backwardChars.reverse().concat(lastToken.chars), wordOptions, tokenizer);
|
5680
|
+
}
|
5681
|
+
|
5682
|
+
return backwardTokensBuffer.pop();
|
5683
|
+
},
|
5684
|
+
|
5685
|
+
dispose: function() {
|
5686
|
+
forwardIterator.dispose();
|
5687
|
+
backwardIterator.dispose();
|
5688
|
+
forwardTokensBuffer = backwardTokensBuffer = null;
|
5689
|
+
}
|
5690
|
+
};
|
5691
|
+
}
|
5692
|
+
|
5693
|
+
function movePositionBy(pos, unit, count, characterOptions, wordOptions) {
|
5694
|
+
var unitsMoved = 0, currentPos, newPos = pos, charIterator, nextPos, absCount = Math.abs(count), token;
|
5695
|
+
if (count !== 0) {
|
5696
|
+
var backward = (count < 0);
|
5697
|
+
|
5698
|
+
switch (unit) {
|
5699
|
+
case CHARACTER:
|
5700
|
+
charIterator = createCharacterIterator(pos, backward, null, characterOptions);
|
5701
|
+
while ( (currentPos = charIterator.next()) && unitsMoved < absCount ) {
|
5702
|
+
++unitsMoved;
|
5703
|
+
newPos = currentPos;
|
5704
|
+
}
|
5705
|
+
nextPos = currentPos;
|
5706
|
+
charIterator.dispose();
|
5707
|
+
break;
|
5708
|
+
case WORD:
|
5709
|
+
var tokenizedTextProvider = createTokenizedTextProvider(pos, characterOptions, wordOptions);
|
5710
|
+
var next = backward ? tokenizedTextProvider.previousStartToken : tokenizedTextProvider.nextEndToken;
|
5711
|
+
|
5712
|
+
while ( (token = next()) && unitsMoved < absCount ) {
|
5713
|
+
if (token.isWord) {
|
5714
|
+
++unitsMoved;
|
5715
|
+
newPos = backward ? token.chars[0] : token.chars[token.chars.length - 1];
|
5716
|
+
}
|
5717
|
+
}
|
5718
|
+
break;
|
5719
|
+
default:
|
5720
|
+
throw new Error("movePositionBy: unit '" + unit + "' not implemented");
|
5721
|
+
}
|
5722
|
+
|
5723
|
+
// Perform any necessary position tweaks
|
5724
|
+
if (backward) {
|
5725
|
+
newPos = newPos.previousVisible();
|
5726
|
+
unitsMoved = -unitsMoved;
|
5727
|
+
} else if (newPos && newPos.isLeadingSpace && !newPos.isTrailingSpace) {
|
5728
|
+
// Tweak the position for the case of a leading space. The problem is that an uncollapsed leading space
|
5729
|
+
// before a block element (for example, the line break between "1" and "2" in the following HTML:
|
5730
|
+
// "1<p>2</p>") is considered to be attached to the position immediately before the block element, which
|
5731
|
+
// corresponds with a different selection position in most browsers from the one we want (i.e. at the
|
5732
|
+
// start of the contents of the block element). We get round this by advancing the position returned to
|
5733
|
+
// the last possible equivalent visible position.
|
5734
|
+
if (unit == WORD) {
|
5735
|
+
charIterator = createCharacterIterator(pos, false, null, characterOptions);
|
5736
|
+
nextPos = charIterator.next();
|
5737
|
+
charIterator.dispose();
|
5738
|
+
}
|
5739
|
+
if (nextPos) {
|
5740
|
+
newPos = nextPos.previousVisible();
|
5741
|
+
}
|
5742
|
+
}
|
5743
|
+
}
|
5744
|
+
|
5745
|
+
|
5746
|
+
return {
|
5747
|
+
position: newPos,
|
5748
|
+
unitsMoved: unitsMoved
|
5749
|
+
};
|
5750
|
+
}
|
5751
|
+
|
5752
|
+
function createRangeCharacterIterator(session, range, characterOptions, backward) {
|
5753
|
+
var rangeStart = session.getRangeBoundaryPosition(range, true);
|
5754
|
+
var rangeEnd = session.getRangeBoundaryPosition(range, false);
|
5755
|
+
var itStart = backward ? rangeEnd : rangeStart;
|
5756
|
+
var itEnd = backward ? rangeStart : rangeEnd;
|
5757
|
+
|
5758
|
+
return createCharacterIterator(itStart, !!backward, itEnd, characterOptions);
|
5759
|
+
}
|
5760
|
+
|
5761
|
+
function getRangeCharacters(session, range, characterOptions) {
|
5762
|
+
|
5763
|
+
var chars = [], it = createRangeCharacterIterator(session, range, characterOptions), pos;
|
5764
|
+
while ( (pos = it.next()) ) {
|
5765
|
+
chars.push(pos);
|
5766
|
+
}
|
5767
|
+
|
5768
|
+
it.dispose();
|
5769
|
+
return chars;
|
5770
|
+
}
|
5771
|
+
|
5772
|
+
function isWholeWord(startPos, endPos, wordOptions) {
|
5773
|
+
var range = api.createRange(startPos.node);
|
5774
|
+
range.setStartAndEnd(startPos.node, startPos.offset, endPos.node, endPos.offset);
|
5775
|
+
return !range.expand("word", { wordOptions: wordOptions });
|
5776
|
+
}
|
5777
|
+
|
5778
|
+
function findTextFromPosition(initialPos, searchTerm, isRegex, searchScopeRange, findOptions) {
|
5779
|
+
var backward = isDirectionBackward(findOptions.direction);
|
5780
|
+
var it = createCharacterIterator(
|
5781
|
+
initialPos,
|
5782
|
+
backward,
|
5783
|
+
initialPos.session.getRangeBoundaryPosition(searchScopeRange, backward),
|
5784
|
+
findOptions.characterOptions
|
5785
|
+
);
|
5786
|
+
var text = "", chars = [], pos, currentChar, matchStartIndex, matchEndIndex;
|
5787
|
+
var result, insideRegexMatch;
|
5788
|
+
var returnValue = null;
|
5789
|
+
|
5790
|
+
function handleMatch(startIndex, endIndex) {
|
5791
|
+
var startPos = chars[startIndex].previousVisible();
|
5792
|
+
var endPos = chars[endIndex - 1];
|
5793
|
+
var valid = (!findOptions.wholeWordsOnly || isWholeWord(startPos, endPos, findOptions.wordOptions));
|
5794
|
+
|
5795
|
+
return {
|
5796
|
+
startPos: startPos,
|
5797
|
+
endPos: endPos,
|
5798
|
+
valid: valid
|
5799
|
+
};
|
5800
|
+
}
|
5801
|
+
|
5802
|
+
while ( (pos = it.next()) ) {
|
5803
|
+
currentChar = pos.character;
|
5804
|
+
if (!isRegex && !findOptions.caseSensitive) {
|
5805
|
+
currentChar = currentChar.toLowerCase();
|
5806
|
+
}
|
5807
|
+
|
5808
|
+
if (backward) {
|
5809
|
+
chars.unshift(pos);
|
5810
|
+
text = currentChar + text;
|
5811
|
+
} else {
|
5812
|
+
chars.push(pos);
|
5813
|
+
text += currentChar;
|
5814
|
+
}
|
5815
|
+
|
5816
|
+
if (isRegex) {
|
5817
|
+
result = searchTerm.exec(text);
|
5818
|
+
if (result) {
|
5819
|
+
matchStartIndex = result.index;
|
5820
|
+
matchEndIndex = matchStartIndex + result[0].length;
|
5821
|
+
if (insideRegexMatch) {
|
5822
|
+
// Check whether the match is now over
|
5823
|
+
if ((!backward && matchEndIndex < text.length) || (backward && matchStartIndex > 0)) {
|
5824
|
+
returnValue = handleMatch(matchStartIndex, matchEndIndex);
|
5825
|
+
break;
|
5826
|
+
}
|
5827
|
+
} else {
|
5828
|
+
insideRegexMatch = true;
|
5829
|
+
}
|
5830
|
+
}
|
5831
|
+
} else if ( (matchStartIndex = text.indexOf(searchTerm)) != -1 ) {
|
5832
|
+
returnValue = handleMatch(matchStartIndex, matchStartIndex + searchTerm.length);
|
5833
|
+
break;
|
5834
|
+
}
|
5835
|
+
}
|
5836
|
+
|
5837
|
+
// Check whether regex match extends to the end of the range
|
5838
|
+
if (insideRegexMatch) {
|
5839
|
+
returnValue = handleMatch(matchStartIndex, matchEndIndex);
|
5840
|
+
}
|
5841
|
+
it.dispose();
|
5842
|
+
|
5843
|
+
return returnValue;
|
5844
|
+
}
|
5845
|
+
|
5846
|
+
function createEntryPointFunction(func) {
|
5847
|
+
return function() {
|
5848
|
+
var sessionRunning = !!currentSession;
|
5849
|
+
var session = getSession();
|
5850
|
+
var args = [session].concat( util.toArray(arguments) );
|
5851
|
+
var returnValue = func.apply(this, args);
|
5852
|
+
if (!sessionRunning) {
|
5853
|
+
endSession();
|
5854
|
+
}
|
5855
|
+
return returnValue;
|
5856
|
+
};
|
5857
|
+
}
|
5858
|
+
|
5859
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
5860
|
+
|
5861
|
+
// Extensions to the Rangy Range object
|
5862
|
+
|
5863
|
+
function createRangeBoundaryMover(isStart, collapse) {
|
5864
|
+
/*
|
5865
|
+
Unit can be "character" or "word"
|
5866
|
+
Options:
|
5867
|
+
|
5868
|
+
- includeTrailingSpace
|
5869
|
+
- wordRegex
|
5870
|
+
- tokenizer
|
5871
|
+
- collapseSpaceBeforeLineBreak
|
5872
|
+
*/
|
5873
|
+
return createEntryPointFunction(
|
5874
|
+
function(session, unit, count, moveOptions) {
|
5875
|
+
if (typeof count == UNDEF) {
|
5876
|
+
count = unit;
|
5877
|
+
unit = CHARACTER;
|
5878
|
+
}
|
5879
|
+
moveOptions = createNestedOptions(moveOptions, defaultMoveOptions);
|
5880
|
+
|
5881
|
+
var boundaryIsStart = isStart;
|
5882
|
+
if (collapse) {
|
5883
|
+
boundaryIsStart = (count >= 0);
|
5884
|
+
this.collapse(!boundaryIsStart);
|
5885
|
+
}
|
5886
|
+
var moveResult = movePositionBy(session.getRangeBoundaryPosition(this, boundaryIsStart), unit, count, moveOptions.characterOptions, moveOptions.wordOptions);
|
5887
|
+
var newPos = moveResult.position;
|
5888
|
+
this[boundaryIsStart ? "setStart" : "setEnd"](newPos.node, newPos.offset);
|
5889
|
+
return moveResult.unitsMoved;
|
5890
|
+
}
|
5891
|
+
);
|
5892
|
+
}
|
5893
|
+
|
5894
|
+
function createRangeTrimmer(isStart) {
|
5895
|
+
return createEntryPointFunction(
|
5896
|
+
function(session, characterOptions) {
|
5897
|
+
characterOptions = createOptions(characterOptions, defaultCharacterOptions);
|
5898
|
+
var pos;
|
5899
|
+
var it = createRangeCharacterIterator(session, this, characterOptions, !isStart);
|
5900
|
+
var trimCharCount = 0;
|
5901
|
+
while ( (pos = it.next()) && allWhiteSpaceRegex.test(pos.character) ) {
|
5902
|
+
++trimCharCount;
|
5903
|
+
}
|
5904
|
+
it.dispose();
|
5905
|
+
var trimmed = (trimCharCount > 0);
|
5906
|
+
if (trimmed) {
|
5907
|
+
this[isStart ? "moveStart" : "moveEnd"](
|
5908
|
+
"character",
|
5909
|
+
isStart ? trimCharCount : -trimCharCount,
|
5910
|
+
{ characterOptions: characterOptions }
|
5911
|
+
);
|
5912
|
+
}
|
5913
|
+
return trimmed;
|
5914
|
+
}
|
5915
|
+
);
|
5916
|
+
}
|
5917
|
+
|
5918
|
+
extend(api.rangePrototype, {
|
5919
|
+
moveStart: createRangeBoundaryMover(true, false),
|
5920
|
+
|
5921
|
+
moveEnd: createRangeBoundaryMover(false, false),
|
5922
|
+
|
5923
|
+
move: createRangeBoundaryMover(true, true),
|
5924
|
+
|
5925
|
+
trimStart: createRangeTrimmer(true),
|
5926
|
+
|
5927
|
+
trimEnd: createRangeTrimmer(false),
|
5928
|
+
|
5929
|
+
trim: createEntryPointFunction(
|
5930
|
+
function(session, characterOptions) {
|
5931
|
+
var startTrimmed = this.trimStart(characterOptions), endTrimmed = this.trimEnd(characterOptions);
|
5932
|
+
return startTrimmed || endTrimmed;
|
5933
|
+
}
|
5934
|
+
),
|
5935
|
+
|
5936
|
+
expand: createEntryPointFunction(
|
5937
|
+
function(session, unit, expandOptions) {
|
5938
|
+
var moved = false;
|
5939
|
+
expandOptions = createNestedOptions(expandOptions, defaultExpandOptions);
|
5940
|
+
var characterOptions = expandOptions.characterOptions;
|
5941
|
+
if (!unit) {
|
5942
|
+
unit = CHARACTER;
|
5943
|
+
}
|
5944
|
+
if (unit == WORD) {
|
5945
|
+
var wordOptions = expandOptions.wordOptions;
|
5946
|
+
var startPos = session.getRangeBoundaryPosition(this, true);
|
5947
|
+
var endPos = session.getRangeBoundaryPosition(this, false);
|
5948
|
+
|
5949
|
+
var startTokenizedTextProvider = createTokenizedTextProvider(startPos, characterOptions, wordOptions);
|
5950
|
+
var startToken = startTokenizedTextProvider.nextEndToken();
|
5951
|
+
var newStartPos = startToken.chars[0].previousVisible();
|
5952
|
+
var endToken, newEndPos;
|
5953
|
+
|
5954
|
+
if (this.collapsed) {
|
5955
|
+
endToken = startToken;
|
5956
|
+
} else {
|
5957
|
+
var endTokenizedTextProvider = createTokenizedTextProvider(endPos, characterOptions, wordOptions);
|
5958
|
+
endToken = endTokenizedTextProvider.previousStartToken();
|
5959
|
+
}
|
5960
|
+
newEndPos = endToken.chars[endToken.chars.length - 1];
|
5961
|
+
|
5962
|
+
if (!newStartPos.equals(startPos)) {
|
5963
|
+
this.setStart(newStartPos.node, newStartPos.offset);
|
5964
|
+
moved = true;
|
5965
|
+
}
|
5966
|
+
if (newEndPos && !newEndPos.equals(endPos)) {
|
5967
|
+
this.setEnd(newEndPos.node, newEndPos.offset);
|
5968
|
+
moved = true;
|
5969
|
+
}
|
5970
|
+
|
5971
|
+
if (expandOptions.trim) {
|
5972
|
+
if (expandOptions.trimStart) {
|
5973
|
+
moved = this.trimStart(characterOptions) || moved;
|
5974
|
+
}
|
5975
|
+
if (expandOptions.trimEnd) {
|
5976
|
+
moved = this.trimEnd(characterOptions) || moved;
|
5977
|
+
}
|
5978
|
+
}
|
5979
|
+
|
5980
|
+
return moved;
|
5981
|
+
} else {
|
5982
|
+
return this.moveEnd(CHARACTER, 1, expandOptions);
|
5983
|
+
}
|
5984
|
+
}
|
5985
|
+
),
|
5986
|
+
|
5987
|
+
text: createEntryPointFunction(
|
5988
|
+
function(session, characterOptions) {
|
5989
|
+
return this.collapsed ?
|
5990
|
+
"" : getRangeCharacters(session, this, createOptions(characterOptions, defaultCharacterOptions)).join("");
|
5991
|
+
}
|
5992
|
+
),
|
5993
|
+
|
5994
|
+
selectCharacters: createEntryPointFunction(
|
5995
|
+
function(session, containerNode, startIndex, endIndex, characterOptions) {
|
5996
|
+
var moveOptions = { characterOptions: characterOptions };
|
5997
|
+
if (!containerNode) {
|
5998
|
+
containerNode = getBody( this.getDocument() );
|
5999
|
+
}
|
6000
|
+
this.selectNodeContents(containerNode);
|
6001
|
+
this.collapse(true);
|
6002
|
+
this.moveStart("character", startIndex, moveOptions);
|
6003
|
+
this.collapse(true);
|
6004
|
+
this.moveEnd("character", endIndex - startIndex, moveOptions);
|
6005
|
+
}
|
6006
|
+
),
|
6007
|
+
|
6008
|
+
// Character indexes are relative to the start of node
|
6009
|
+
toCharacterRange: createEntryPointFunction(
|
6010
|
+
function(session, containerNode, characterOptions) {
|
6011
|
+
if (!containerNode) {
|
6012
|
+
containerNode = getBody( this.getDocument() );
|
6013
|
+
}
|
6014
|
+
var parent = containerNode.parentNode, nodeIndex = dom.getNodeIndex(containerNode);
|
6015
|
+
var rangeStartsBeforeNode = (dom.comparePoints(this.startContainer, this.endContainer, parent, nodeIndex) == -1);
|
6016
|
+
var rangeBetween = this.cloneRange();
|
6017
|
+
var startIndex, endIndex;
|
6018
|
+
if (rangeStartsBeforeNode) {
|
6019
|
+
rangeBetween.setStartAndEnd(this.startContainer, this.startOffset, parent, nodeIndex);
|
6020
|
+
startIndex = -rangeBetween.text(characterOptions).length;
|
6021
|
+
} else {
|
6022
|
+
rangeBetween.setStartAndEnd(parent, nodeIndex, this.startContainer, this.startOffset);
|
6023
|
+
startIndex = rangeBetween.text(characterOptions).length;
|
6024
|
+
}
|
6025
|
+
endIndex = startIndex + this.text(characterOptions).length;
|
6026
|
+
|
6027
|
+
return {
|
6028
|
+
start: startIndex,
|
6029
|
+
end: endIndex
|
6030
|
+
};
|
6031
|
+
}
|
6032
|
+
),
|
6033
|
+
|
6034
|
+
findText: createEntryPointFunction(
|
6035
|
+
function(session, searchTermParam, findOptions) {
|
6036
|
+
// Set up options
|
6037
|
+
findOptions = createNestedOptions(findOptions, defaultFindOptions);
|
6038
|
+
|
6039
|
+
// Create word options if we're matching whole words only
|
6040
|
+
if (findOptions.wholeWordsOnly) {
|
6041
|
+
// We don't ever want trailing spaces for search results
|
6042
|
+
findOptions.wordOptions.includeTrailingSpace = false;
|
6043
|
+
}
|
6044
|
+
|
6045
|
+
var backward = isDirectionBackward(findOptions.direction);
|
6046
|
+
|
6047
|
+
// Create a range representing the search scope if none was provided
|
6048
|
+
var searchScopeRange = findOptions.withinRange;
|
6049
|
+
if (!searchScopeRange) {
|
6050
|
+
searchScopeRange = api.createRange();
|
6051
|
+
searchScopeRange.selectNodeContents(this.getDocument());
|
6052
|
+
}
|
6053
|
+
|
6054
|
+
// Examine and prepare the search term
|
6055
|
+
var searchTerm = searchTermParam, isRegex = false;
|
6056
|
+
if (typeof searchTerm == "string") {
|
6057
|
+
if (!findOptions.caseSensitive) {
|
6058
|
+
searchTerm = searchTerm.toLowerCase();
|
6059
|
+
}
|
6060
|
+
} else {
|
6061
|
+
isRegex = true;
|
6062
|
+
}
|
6063
|
+
|
6064
|
+
var initialPos = session.getRangeBoundaryPosition(this, !backward);
|
6065
|
+
|
6066
|
+
// Adjust initial position if it lies outside the search scope
|
6067
|
+
var comparison = searchScopeRange.comparePoint(initialPos.node, initialPos.offset);
|
6068
|
+
|
6069
|
+
if (comparison === -1) {
|
6070
|
+
initialPos = session.getRangeBoundaryPosition(searchScopeRange, true);
|
6071
|
+
} else if (comparison === 1) {
|
6072
|
+
initialPos = session.getRangeBoundaryPosition(searchScopeRange, false);
|
6073
|
+
}
|
6074
|
+
|
6075
|
+
var pos = initialPos;
|
6076
|
+
var wrappedAround = false;
|
6077
|
+
|
6078
|
+
// Try to find a match and ignore invalid ones
|
6079
|
+
var findResult;
|
6080
|
+
while (true) {
|
6081
|
+
findResult = findTextFromPosition(pos, searchTerm, isRegex, searchScopeRange, findOptions);
|
6082
|
+
|
6083
|
+
if (findResult) {
|
6084
|
+
if (findResult.valid) {
|
6085
|
+
this.setStartAndEnd(findResult.startPos.node, findResult.startPos.offset, findResult.endPos.node, findResult.endPos.offset);
|
6086
|
+
return true;
|
6087
|
+
} else {
|
6088
|
+
// We've found a match that is not a whole word, so we carry on searching from the point immediately
|
6089
|
+
// after the match
|
6090
|
+
pos = backward ? findResult.startPos : findResult.endPos;
|
6091
|
+
}
|
6092
|
+
} else if (findOptions.wrap && !wrappedAround) {
|
6093
|
+
// No result found but we're wrapping around and limiting the scope to the unsearched part of the range
|
6094
|
+
searchScopeRange = searchScopeRange.cloneRange();
|
6095
|
+
pos = session.getRangeBoundaryPosition(searchScopeRange, !backward);
|
6096
|
+
searchScopeRange.setBoundary(initialPos.node, initialPos.offset, backward);
|
6097
|
+
wrappedAround = true;
|
6098
|
+
} else {
|
6099
|
+
// Nothing found and we can't wrap around, so we're done
|
6100
|
+
return false;
|
6101
|
+
}
|
6102
|
+
}
|
6103
|
+
}
|
6104
|
+
),
|
6105
|
+
|
6106
|
+
pasteHtml: function(html) {
|
6107
|
+
this.deleteContents();
|
6108
|
+
if (html) {
|
6109
|
+
var frag = this.createContextualFragment(html);
|
6110
|
+
var lastChild = frag.lastChild;
|
6111
|
+
this.insertNode(frag);
|
6112
|
+
this.collapseAfter(lastChild);
|
6113
|
+
}
|
6114
|
+
}
|
6115
|
+
});
|
4145
6116
|
|
4146
|
-
|
4147
|
-
selProto.setEnd = createStartOrEndSetter(false);
|
6117
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4148
6118
|
|
4149
|
-
//
|
4150
|
-
api.rangePrototype.select = function(direction) {
|
4151
|
-
getSelection( this.getDocument() ).setSingleRange(this, direction);
|
4152
|
-
};
|
6119
|
+
// Extensions to the Rangy Selection object
|
4153
6120
|
|
4154
|
-
|
4155
|
-
|
4156
|
-
|
6121
|
+
function createSelectionTrimmer(methodName) {
|
6122
|
+
return createEntryPointFunction(
|
6123
|
+
function(session, characterOptions) {
|
6124
|
+
var trimmed = false;
|
6125
|
+
this.changeEachRange(function(range) {
|
6126
|
+
trimmed = range[methodName](characterOptions) || trimmed;
|
6127
|
+
});
|
6128
|
+
return trimmed;
|
6129
|
+
}
|
6130
|
+
);
|
6131
|
+
}
|
4157
6132
|
|
4158
|
-
|
4159
|
-
|
4160
|
-
|
4161
|
-
|
6133
|
+
extend(api.selectionPrototype, {
|
6134
|
+
expand: createEntryPointFunction(
|
6135
|
+
function(session, unit, expandOptions) {
|
6136
|
+
this.changeEachRange(function(range) {
|
6137
|
+
range.expand(unit, expandOptions);
|
6138
|
+
});
|
6139
|
+
}
|
6140
|
+
),
|
6141
|
+
|
6142
|
+
move: createEntryPointFunction(
|
6143
|
+
function(session, unit, count, options) {
|
6144
|
+
var unitsMoved = 0;
|
6145
|
+
if (this.focusNode) {
|
6146
|
+
this.collapse(this.focusNode, this.focusOffset);
|
6147
|
+
var range = this.getRangeAt(0);
|
6148
|
+
if (!options) {
|
6149
|
+
options = {};
|
6150
|
+
}
|
6151
|
+
options.characterOptions = createOptions(options.characterOptions, defaultCaretCharacterOptions);
|
6152
|
+
unitsMoved = range.move(unit, count, options);
|
6153
|
+
this.setSingleRange(range);
|
6154
|
+
}
|
6155
|
+
return unitsMoved;
|
6156
|
+
}
|
6157
|
+
),
|
4162
6158
|
|
4163
|
-
|
4164
|
-
|
4165
|
-
|
4166
|
-
} else {
|
4167
|
-
this.setRanges(ranges);
|
4168
|
-
}
|
4169
|
-
};
|
6159
|
+
trimStart: createSelectionTrimmer("trimStart"),
|
6160
|
+
trimEnd: createSelectionTrimmer("trimEnd"),
|
6161
|
+
trim: createSelectionTrimmer("trim"),
|
4170
6162
|
|
4171
|
-
|
4172
|
-
|
4173
|
-
|
4174
|
-
|
4175
|
-
|
6163
|
+
selectCharacters: createEntryPointFunction(
|
6164
|
+
function(session, containerNode, startIndex, endIndex, direction, characterOptions) {
|
6165
|
+
var range = api.createRange(containerNode);
|
6166
|
+
range.selectCharacters(containerNode, startIndex, endIndex, characterOptions);
|
6167
|
+
this.setSingleRange(range, direction);
|
6168
|
+
}
|
6169
|
+
),
|
4176
6170
|
|
4177
|
-
|
4178
|
-
|
4179
|
-
|
4180
|
-
|
4181
|
-
};
|
4182
|
-
};
|
6171
|
+
saveCharacterRanges: createEntryPointFunction(
|
6172
|
+
function(session, containerNode, characterOptions) {
|
6173
|
+
var ranges = this.getAllRanges(), rangeCount = ranges.length;
|
6174
|
+
var rangeInfos = [];
|
4183
6175
|
|
4184
|
-
|
4185
|
-
var selRanges = [];
|
4186
|
-
for (var i = 0, rangeBookmark, range; rangeBookmark = bookmark.rangeBookmarks[i++]; ) {
|
4187
|
-
range = api.createRange(this.win);
|
4188
|
-
range.moveToBookmark(rangeBookmark);
|
4189
|
-
selRanges.push(range);
|
4190
|
-
}
|
4191
|
-
if (bookmark.backward) {
|
4192
|
-
this.setSingleRange(selRanges[0], "backward");
|
4193
|
-
} else {
|
4194
|
-
this.setRanges(selRanges);
|
4195
|
-
}
|
4196
|
-
};
|
6176
|
+
var backward = rangeCount == 1 && this.isBackward();
|
4197
6177
|
|
4198
|
-
|
4199
|
-
|
4200
|
-
|
4201
|
-
|
4202
|
-
|
4203
|
-
|
6178
|
+
for (var i = 0, len = ranges.length; i < len; ++i) {
|
6179
|
+
rangeInfos[i] = {
|
6180
|
+
characterRange: ranges[i].toCharacterRange(containerNode, characterOptions),
|
6181
|
+
backward: backward,
|
6182
|
+
characterOptions: characterOptions
|
6183
|
+
};
|
6184
|
+
}
|
4204
6185
|
|
4205
|
-
|
4206
|
-
|
4207
|
-
|
4208
|
-
this.addRange(range, (selRanges.backward && i == 0));
|
4209
|
-
}
|
4210
|
-
};
|
6186
|
+
return rangeInfos;
|
6187
|
+
}
|
6188
|
+
),
|
4211
6189
|
|
4212
|
-
|
4213
|
-
|
4214
|
-
|
4215
|
-
|
4216
|
-
|
4217
|
-
|
4218
|
-
|
6190
|
+
restoreCharacterRanges: createEntryPointFunction(
|
6191
|
+
function(session, containerNode, saved) {
|
6192
|
+
this.removeAllRanges();
|
6193
|
+
for (var i = 0, len = saved.length, range, rangeInfo, characterRange; i < len; ++i) {
|
6194
|
+
rangeInfo = saved[i];
|
6195
|
+
characterRange = rangeInfo.characterRange;
|
6196
|
+
range = api.createRange(containerNode);
|
6197
|
+
range.selectCharacters(containerNode, characterRange.start, characterRange.end, rangeInfo.characterOptions);
|
6198
|
+
this.addRange(range, rangeInfo.backward);
|
6199
|
+
}
|
6200
|
+
}
|
6201
|
+
),
|
4219
6202
|
|
4220
|
-
|
4221
|
-
|
4222
|
-
|
4223
|
-
|
4224
|
-
|
4225
|
-
if (isTextRange(range)) {
|
4226
|
-
return range;
|
4227
|
-
} else {
|
4228
|
-
throw module.createError("getNativeTextRange: selection is a control selection");
|
6203
|
+
text: createEntryPointFunction(
|
6204
|
+
function(session, characterOptions) {
|
6205
|
+
var rangeTexts = [];
|
6206
|
+
for (var i = 0, len = this.rangeCount; i < len; ++i) {
|
6207
|
+
rangeTexts[i] = this.getRangeAt(i).text(characterOptions);
|
4229
6208
|
}
|
4230
|
-
|
4231
|
-
return api.WrappedTextRange.rangeToTextRange( this.getRangeAt(0) );
|
4232
|
-
} else {
|
4233
|
-
throw module.createError("getNativeTextRange: selection contains no range");
|
6209
|
+
return rangeTexts.join("");
|
4234
6210
|
}
|
4235
|
-
|
4236
|
-
}
|
6211
|
+
)
|
6212
|
+
});
|
4237
6213
|
|
4238
|
-
|
4239
|
-
var rangeInspects = [];
|
4240
|
-
var anchor = new DomPosition(sel.anchorNode, sel.anchorOffset);
|
4241
|
-
var focus = new DomPosition(sel.focusNode, sel.focusOffset);
|
4242
|
-
var name = (typeof sel.getName == "function") ? sel.getName() : "Selection";
|
6214
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4243
6215
|
|
4244
|
-
|
4245
|
-
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
|
4246
|
-
rangeInspects[i] = DomRange.inspect(sel.getRangeAt(i));
|
4247
|
-
}
|
4248
|
-
}
|
4249
|
-
return "[" + name + "(Ranges: " + rangeInspects.join(", ") +
|
4250
|
-
")(anchor: " + anchor.inspect() + ", focus: " + focus.inspect() + "]";
|
4251
|
-
}
|
6216
|
+
// Extensions to the core rangy object
|
4252
6217
|
|
4253
|
-
|
4254
|
-
|
6218
|
+
api.innerText = function(el, characterOptions) {
|
6219
|
+
var range = api.createRange(el);
|
6220
|
+
range.selectNodeContents(el);
|
6221
|
+
var text = range.text(characterOptions);
|
6222
|
+
return text;
|
4255
6223
|
};
|
4256
6224
|
|
4257
|
-
|
4258
|
-
|
4259
|
-
|
6225
|
+
api.createWordIterator = function(startNode, startOffset, iteratorOptions) {
|
6226
|
+
var session = getSession();
|
6227
|
+
iteratorOptions = createNestedOptions(iteratorOptions, defaultWordIteratorOptions);
|
6228
|
+
var startPos = session.getPosition(startNode, startOffset);
|
6229
|
+
var tokenizedTextProvider = createTokenizedTextProvider(startPos, iteratorOptions.characterOptions, iteratorOptions.wordOptions);
|
6230
|
+
var backward = isDirectionBackward(iteratorOptions.direction);
|
4260
6231
|
|
4261
|
-
|
4262
|
-
|
4263
|
-
|
4264
|
-
|
6232
|
+
return {
|
6233
|
+
next: function() {
|
6234
|
+
return backward ? tokenizedTextProvider.previousStartToken() : tokenizedTextProvider.nextEndToken();
|
6235
|
+
},
|
4265
6236
|
|
4266
|
-
|
4267
|
-
|
6237
|
+
dispose: function() {
|
6238
|
+
tokenizedTextProvider.dispose();
|
6239
|
+
this.next = function() {};
|
6240
|
+
}
|
6241
|
+
};
|
4268
6242
|
};
|
4269
6243
|
|
4270
|
-
|
4271
|
-
WrappedSelection.isDirectionBackward = isDirectionBackward;
|
6244
|
+
/*----------------------------------------------------------------------------------------------------------------*/
|
4272
6245
|
|
4273
|
-
api.
|
6246
|
+
api.noMutation = function(func) {
|
6247
|
+
var session = getSession();
|
6248
|
+
func(session);
|
6249
|
+
endSession();
|
6250
|
+
};
|
4274
6251
|
|
4275
|
-
api.
|
6252
|
+
api.noMutation.createEntryPointFunction = createEntryPointFunction;
|
4276
6253
|
|
4277
|
-
api.
|
4278
|
-
|
4279
|
-
|
4280
|
-
|
4281
|
-
|
4282
|
-
|
4283
|
-
|
4284
|
-
|
6254
|
+
api.textRange = {
|
6255
|
+
isBlockNode: isBlockNode,
|
6256
|
+
isCollapsedWhitespaceNode: isCollapsedWhitespaceNode,
|
6257
|
+
|
6258
|
+
createPosition: createEntryPointFunction(
|
6259
|
+
function(session, node, offset) {
|
6260
|
+
return session.getPosition(node, offset);
|
6261
|
+
}
|
6262
|
+
)
|
6263
|
+
};
|
4285
6264
|
});
|
4286
|
-
|
4287
|
-
|
4288
|
-
/*----------------------------------------------------------------------------------------------------------------*/
|
4289
|
-
|
4290
|
-
// Wait for document to load before initializing
|
4291
|
-
var docReady = false;
|
4292
|
-
|
4293
|
-
var loadHandler = function(e) {
|
4294
|
-
if (!docReady) {
|
4295
|
-
docReady = true;
|
4296
|
-
if (!api.initialized && api.config.autoInitialize) {
|
4297
|
-
init();
|
4298
|
-
}
|
4299
|
-
}
|
4300
|
-
};
|
4301
|
-
|
4302
|
-
if (isBrowser) {
|
4303
|
-
// Test whether the document has already been loaded and initialize immediately if so
|
4304
|
-
if (document.readyState == "complete") {
|
4305
|
-
loadHandler();
|
4306
|
-
} else {
|
4307
|
-
if (isHostMethod(document, "addEventListener")) {
|
4308
|
-
document.addEventListener("DOMContentLoaded", loadHandler, false);
|
4309
|
-
}
|
4310
|
-
|
4311
|
-
// Add a fallback in case the DOMContentLoaded event isn't supported
|
4312
|
-
addListener(window, "load", loadHandler);
|
4313
|
-
}
|
4314
|
-
}
|
4315
|
-
|
4316
|
-
return api;
|
6265
|
+
|
6266
|
+
return rangy;
|
4317
6267
|
}, this);;/**
|
4318
6268
|
* Selection save and restore module for Rangy.
|
4319
6269
|
* Saves and restores user selections using marker invisible elements in the DOM.
|
@@ -4730,7 +6680,11 @@ wysihtml5.browser = (function() {
|
|
4730
6680
|
if (navigator.appName == 'Microsoft Internet Explorer') {
|
4731
6681
|
re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
|
4732
6682
|
} else if (navigator.appName == 'Netscape') {
|
4733
|
-
|
6683
|
+
if (navigator.userAgent.indexOf("Trident") > -1) {
|
6684
|
+
re = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
|
6685
|
+
} else if ((/Edge\/(\d+)./i).test(navigator.userAgent)) {
|
6686
|
+
re = /Edge\/(\d+)./i;
|
6687
|
+
}
|
4734
6688
|
}
|
4735
6689
|
|
4736
6690
|
if (re && re.exec(navigator.userAgent) != null) {
|
@@ -4813,7 +6767,7 @@ wysihtml5.browser = (function() {
|
|
4813
6767
|
* Firefox sometimes shows a huge caret in the beginning after focusing
|
4814
6768
|
*/
|
4815
6769
|
displaysCaretInEmptyContentEditableCorrectly: function() {
|
4816
|
-
return isIE();
|
6770
|
+
return isIE(12, ">");
|
4817
6771
|
},
|
4818
6772
|
|
4819
6773
|
/**
|
@@ -4885,8 +6839,8 @@ wysihtml5.browser = (function() {
|
|
4885
6839
|
// When inserting unordered or ordered lists in Firefox, Chrome or Safari, the current selection or line gets
|
4886
6840
|
// converted into a list (<ul><li>...</li></ul>, <ol><li>...</li></ol>)
|
4887
6841
|
// IE and Opera act a bit different here as they convert the entire content of the current block element into a list
|
4888
|
-
"insertUnorderedList": isIE(9, ">="),
|
4889
|
-
"insertOrderedList": isIE(9, ">=")
|
6842
|
+
"insertUnorderedList": isIE(9, ">=") || isIE(12, "<="),
|
6843
|
+
"insertOrderedList": isIE(9, ">=")|| isIE(12, "<=")
|
4890
6844
|
};
|
4891
6845
|
|
4892
6846
|
// Firefox throws errors for queryCommandSupported, so we have to build up our own object of supported commands
|
@@ -5033,7 +6987,7 @@ wysihtml5.browser = (function() {
|
|
5033
6987
|
* IE is the only browser who fires the "focus" event not immediately when .focus() is called on an element
|
5034
6988
|
*/
|
5035
6989
|
doesAsyncFocus: function() {
|
5036
|
-
return isIE();
|
6990
|
+
return isIE(12, ">");
|
5037
6991
|
},
|
5038
6992
|
|
5039
6993
|
/**
|
@@ -7953,6 +9907,10 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
|
|
7953
9907
|
}
|
7954
9908
|
},
|
7955
9909
|
|
9910
|
+
destroy: function() {
|
9911
|
+
|
9912
|
+
},
|
9913
|
+
|
7956
9914
|
// creates a new contenteditable and initiates it
|
7957
9915
|
_createElement: function() {
|
7958
9916
|
var element = doc.createElement("div");
|
@@ -10555,6 +12513,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
10555
12513
|
this._selectLine_W3C();
|
10556
12514
|
} else if (this.doc.selection) {
|
10557
12515
|
this._selectLine_MSIE();
|
12516
|
+
} else {
|
12517
|
+
// For IE Edge as it ditched the old api and did not fully implement the new one (as expected)
|
12518
|
+
this._selectLineUniversal();
|
10558
12519
|
}
|
10559
12520
|
},
|
10560
12521
|
|
@@ -10562,9 +12523,20 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
10562
12523
|
* See https://developer.mozilla.org/en/DOM/Selection/modify
|
10563
12524
|
*/
|
10564
12525
|
_selectLine_W3C: function() {
|
10565
|
-
var selection = this.win.getSelection()
|
12526
|
+
var selection = this.win.getSelection(),
|
12527
|
+
initialBoundry = [selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset];
|
12528
|
+
|
10566
12529
|
selection.modify("move", "left", "lineboundary");
|
10567
12530
|
selection.modify("extend", "right", "lineboundary");
|
12531
|
+
|
12532
|
+
// IF lineboundary extending did not change selection try universal fallback (FF fails sometimes without a reason)
|
12533
|
+
if (selection.anchorNode === initialBoundry[0] &&
|
12534
|
+
selection.anchorOffset === initialBoundry[1] &&
|
12535
|
+
selection.focusNode === initialBoundry[2] &&
|
12536
|
+
selection.focusOffset === initialBoundry[3]
|
12537
|
+
) {
|
12538
|
+
this._selectLineUniversal();
|
12539
|
+
}
|
10568
12540
|
},
|
10569
12541
|
|
10570
12542
|
// collapses selection to current line beginning or end
|
@@ -10584,8 +12556,75 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
10584
12556
|
}
|
10585
12557
|
},
|
10586
12558
|
|
12559
|
+
getRangeRect: function(r) {
|
12560
|
+
var textNode = this.doc.createTextNode("i"),
|
12561
|
+
testNode = this.doc.createTextNode("i"),
|
12562
|
+
rect, cr;
|
12563
|
+
|
12564
|
+
/*testNode.style.visibility = "hidden";
|
12565
|
+
testNode.style.width = "0px";
|
12566
|
+
testNode.style.display = "inline-block";
|
12567
|
+
testNode.style.overflow = "hidden";
|
12568
|
+
testNode.appendChild(textNode);*/
|
12569
|
+
|
12570
|
+
if (r.collapsed) {
|
12571
|
+
r.insertNode(testNode);
|
12572
|
+
r.selectNode(testNode);
|
12573
|
+
rect = r.nativeRange.getBoundingClientRect();
|
12574
|
+
r.deleteContents();
|
12575
|
+
|
12576
|
+
} else {
|
12577
|
+
rect = r.nativeRange.getBoundingClientRect();
|
12578
|
+
}
|
12579
|
+
|
12580
|
+
return rect;
|
12581
|
+
|
12582
|
+
},
|
12583
|
+
|
12584
|
+
_selectLineUniversal: function() {
|
12585
|
+
var s = this.getSelection(),
|
12586
|
+
r = s.getRangeAt(0),
|
12587
|
+
rect,
|
12588
|
+
startRange, endRange, testRange,
|
12589
|
+
count = 0,
|
12590
|
+
amount, testRect, found;
|
12591
|
+
|
12592
|
+
startRange = r.cloneRange();
|
12593
|
+
endRange = r.cloneRange();
|
12594
|
+
|
12595
|
+
if (r.collapsed) {
|
12596
|
+
r.expand('word', 1);
|
12597
|
+
rect = r.nativeRange.getBoundingClientRect();
|
12598
|
+
}
|
12599
|
+
|
12600
|
+
do {
|
12601
|
+
amount = r.moveStart('character', -1);
|
12602
|
+
testRect = r.nativeRange.getBoundingClientRect();
|
12603
|
+
if (!testRect || Math.floor(testRect.top) !== Math.floor(rect.top)) {
|
12604
|
+
r.moveStart('character', 1);
|
12605
|
+
found = true;
|
12606
|
+
}
|
12607
|
+
count++;
|
12608
|
+
} while (amount !== 0 && !found && count < 2000);
|
12609
|
+
|
12610
|
+
count = 0;
|
12611
|
+
found = false;
|
12612
|
+
rect = r.nativeRange.getBoundingClientRect();
|
12613
|
+
do {
|
12614
|
+
amount = r.moveEnd('character', 1);
|
12615
|
+
testRect = r.nativeRange.getBoundingClientRect();
|
12616
|
+
if (!testRect || Math.floor(testRect.bottom) !== Math.floor(rect.bottom)) {
|
12617
|
+
r.moveEnd('character', -1);
|
12618
|
+
found = true;
|
12619
|
+
}
|
12620
|
+
count++;
|
12621
|
+
} while (amount !== 0 && !found && count < 2000);
|
12622
|
+
|
12623
|
+
r.select();
|
12624
|
+
},
|
12625
|
+
|
10587
12626
|
_selectLine_MSIE: function() {
|
10588
|
-
var range = this.doc.selection.createRange(),
|
12627
|
+
var range = this.doc.selection && this.doc.selection.createRange ? this.doc.selection.createRange() : this.doc.createRange(),
|
10589
12628
|
rangeTop = range.boundingTop,
|
10590
12629
|
scrollWidth = this.doc.body.scrollWidth,
|
10591
12630
|
rangeBottom,
|
@@ -10594,6 +12633,8 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
10594
12633
|
i,
|
10595
12634
|
j;
|
10596
12635
|
|
12636
|
+
window.r = range;
|
12637
|
+
|
10597
12638
|
if (!range.moveToPoint) {
|
10598
12639
|
return;
|
10599
12640
|
}
|
@@ -12290,16 +14331,19 @@ wysihtml5.Commands = Base.extend(
|
|
12290
14331
|
|
12291
14332
|
// Restore correct selection
|
12292
14333
|
if (bookmark) {
|
14334
|
+
wysihtml5.dom.removeInvisibleSpaces(composer.element);
|
12293
14335
|
rangy.restoreSelection(bookmark);
|
12294
14336
|
} else {
|
14337
|
+
wysihtml5.dom.removeInvisibleSpaces(composer.element);
|
14338
|
+
// Set selection to beging inside first created block element (beginning of it) and end inside (and after content) of last block element
|
14339
|
+
// TODO: Checking nodetype might be unnescescary as nodes inserted by formatBlock are nodetype 1 anyway
|
12295
14340
|
range = composer.selection.createRange();
|
12296
|
-
range.
|
12297
|
-
|
12298
|
-
|
14341
|
+
range.setStart(newBlockElements[0], 0);
|
14342
|
+
var lastEl = newBlockElements[newBlockElements.length - 1],
|
14343
|
+
lastOffset = (lastEl.nodeType === 1 && lastEl.childNodes) ? lastEl.childNodes.length | 0 : lastEl.length || 0;
|
14344
|
+
range.setEnd(lastEl, lastOffset);
|
14345
|
+
range.select();
|
12299
14346
|
}
|
12300
|
-
|
12301
|
-
wysihtml5.dom.removeInvisibleSpaces(composer.element);
|
12302
|
-
|
12303
14347
|
},
|
12304
14348
|
|
12305
14349
|
// If properties as null is passed returns status describing all block level elements
|
@@ -13327,27 +15371,33 @@ wysihtml5.Commands = Base.extend(
|
|
13327
15371
|
};
|
13328
15372
|
|
13329
15373
|
var createListFallback = function(nodeName, composer) {
|
13330
|
-
|
13331
|
-
composer.selection.executeAndRestoreRangy(function() {
|
13332
|
-
var tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
|
13333
|
-
tempElement = composer.selection.deblockAndSurround({
|
13334
|
-
"nodeName": "div",
|
13335
|
-
"className": tempClassName
|
13336
|
-
}),
|
13337
|
-
isEmpty, list;
|
15374
|
+
var sel;
|
13338
15375
|
|
13339
|
-
|
13340
|
-
|
13341
|
-
|
15376
|
+
if (!composer.selection.isCollapsed()) {
|
15377
|
+
sel = rangy.saveSelection(composer.win);
|
15378
|
+
}
|
13342
15379
|
|
13343
|
-
|
13344
|
-
|
13345
|
-
|
13346
|
-
|
13347
|
-
|
13348
|
-
}
|
15380
|
+
// Fallback for Create list
|
15381
|
+
var tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
|
15382
|
+
tempElement = composer.selection.deblockAndSurround({
|
15383
|
+
"nodeName": "div",
|
15384
|
+
"className": tempClassName
|
15385
|
+
}),
|
15386
|
+
isEmpty, list;
|
15387
|
+
|
15388
|
+
// This space causes new lists to never break on enter
|
15389
|
+
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
|
15390
|
+
tempElement.innerHTML = tempElement.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
15391
|
+
if (tempElement) {
|
15392
|
+
isEmpty = (/^(\s|(<br>))+$/i).test(tempElement.innerHTML);
|
15393
|
+
list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.classNames.uneditableContainer);
|
15394
|
+
if (sel) {
|
15395
|
+
rangy.restoreSelection(sel);
|
15396
|
+
}
|
15397
|
+
if (isEmpty) {
|
15398
|
+
composer.selection.selectNode(list.querySelector("li"), true);
|
13349
15399
|
}
|
13350
|
-
}
|
15400
|
+
}
|
13351
15401
|
};
|
13352
15402
|
|
13353
15403
|
return {
|
@@ -13357,7 +15407,6 @@ wysihtml5.Commands = Base.extend(
|
|
13357
15407
|
selectedNode = composer.selection.getSelectedNode(),
|
13358
15408
|
list = findListEl(selectedNode, nodeName, composer);
|
13359
15409
|
|
13360
|
-
|
13361
15410
|
if (!list.el) {
|
13362
15411
|
if (composer.commands.support(cmd)) {
|
13363
15412
|
doc.execCommand(cmd, false, null);
|
@@ -13527,6 +15576,24 @@ wysihtml5.Commands = Base.extend(
|
|
13527
15576
|
};
|
13528
15577
|
|
13529
15578
|
})(wysihtml5);
|
15579
|
+
;(function(wysihtml5) {
|
15580
|
+
|
15581
|
+
var nodeOptions = {
|
15582
|
+
styleProperty: "textAlign",
|
15583
|
+
styleValue: "justify",
|
15584
|
+
toggle: true
|
15585
|
+
};
|
15586
|
+
|
15587
|
+
wysihtml5.commands.alignJustifyStyle = {
|
15588
|
+
exec: function(composer, command) {
|
15589
|
+
return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
|
15590
|
+
},
|
15591
|
+
|
15592
|
+
state: function(composer, command) {
|
15593
|
+
return wysihtml5.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
|
15594
|
+
}
|
15595
|
+
};
|
15596
|
+
})(wysihtml5);
|
13530
15597
|
;(function(wysihtml5){
|
13531
15598
|
wysihtml5.commands.redo = {
|
13532
15599
|
exec: function(composer) {
|
@@ -14408,8 +16475,12 @@ wysihtml5.views.View = Base.extend(
|
|
14408
16475
|
var that = this,
|
14409
16476
|
supportsDisablingOfAutoLinking = browser.canDisableAutoLinking(),
|
14410
16477
|
supportsAutoLinking = browser.doesAutoLinkingInContentEditable();
|
16478
|
+
|
14411
16479
|
if (supportsDisablingOfAutoLinking) {
|
14412
|
-
|
16480
|
+
// I have no idea why IE edge deletes element content here when calling the command,
|
16481
|
+
var tmpHTML = this.element.innerHTML;
|
16482
|
+
this.commands.exec("AutoUrlDetect", false, false);
|
16483
|
+
this.element.innerHTML = tmpHTML;
|
14413
16484
|
}
|
14414
16485
|
|
14415
16486
|
if (!this.config.autoLink) {
|
@@ -15092,7 +17163,7 @@ wysihtml5.views.View = Base.extend(
|
|
15092
17163
|
target, parent;
|
15093
17164
|
|
15094
17165
|
// Select all (meta/ctrl + a)
|
15095
|
-
if ((event.ctrlKey || event.metaKey) && keyCode === 65) {
|
17166
|
+
if ((event.ctrlKey || event.metaKey) && !event.altKey && keyCode === 65) {
|
15096
17167
|
this.selection.selectAll();
|
15097
17168
|
event.preventDefault();
|
15098
17169
|
return;
|
@@ -15152,9 +17223,10 @@ wysihtml5.views.View = Base.extend(
|
|
15152
17223
|
// If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
|
15153
17224
|
var initTableHandling = function () {
|
15154
17225
|
var hideHandlers = function () {
|
17226
|
+
window.removeEventListener('load', hideHandlers);
|
15155
17227
|
this.doc.execCommand("enableObjectResizing", false, "false");
|
15156
17228
|
this.doc.execCommand("enableInlineTableEditing", false, "false");
|
15157
|
-
},
|
17229
|
+
}.bind(this),
|
15158
17230
|
iframeInitiator = (function() {
|
15159
17231
|
hideHandlers.call(this);
|
15160
17232
|
removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
@@ -15167,9 +17239,7 @@ wysihtml5.views.View = Base.extend(
|
|
15167
17239
|
if (this.sandbox.getIframe) {
|
15168
17240
|
addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
15169
17241
|
} else {
|
15170
|
-
|
15171
|
-
hideHandlers.call(this);
|
15172
|
-
}).bind(this), 0);
|
17242
|
+
window.addEventListener('load', hideHandlers);
|
15173
17243
|
}
|
15174
17244
|
}
|
15175
17245
|
this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
|
@@ -15643,6 +17713,16 @@ wysihtml5.views.View = Base.extend(
|
|
15643
17713
|
return this.currentView.hasPlaceholderSet();
|
15644
17714
|
},
|
15645
17715
|
|
17716
|
+
destroy: function() {
|
17717
|
+
if (this.composer && this.composer.sandbox) {
|
17718
|
+
this.composer.sandbox.destroy();
|
17719
|
+
}
|
17720
|
+
if (this.toolbar) {
|
17721
|
+
this.toolbar.destroy();
|
17722
|
+
}
|
17723
|
+
this.off();
|
17724
|
+
},
|
17725
|
+
|
15646
17726
|
parse: function(htmlOrElement, clearInternals) {
|
15647
17727
|
var parseContext = (this.config.contentEditableMode) ? document : ((this.composer) ? this.composer.sandbox.getDocument() : null);
|
15648
17728
|
var returnValue = this.config.parser(htmlOrElement, {
|
@@ -16211,12 +18291,15 @@ wysihtml5.views.View = Base.extend(
|
|
16211
18291
|
}
|
16212
18292
|
});
|
16213
18293
|
|
16214
|
-
this.
|
18294
|
+
this._ownerDocumentClick = function(event) {
|
16215
18295
|
if (!wysihtml5.dom.contains(that.container, event.target) && !wysihtml5.dom.contains(that.composer.element, event.target)) {
|
16216
18296
|
that._updateLinkStates();
|
16217
18297
|
that._preventInstantFocus();
|
16218
18298
|
}
|
16219
|
-
}
|
18299
|
+
};
|
18300
|
+
|
18301
|
+
this.container.ownerDocument.addEventListener("click", this._ownerDocumentClick, false);
|
18302
|
+
this.editor.on("destroy:composer", this.destroy.bind(this));
|
16220
18303
|
|
16221
18304
|
if (this.editor.config.handleTables) {
|
16222
18305
|
editor.on("tableselect:composer", function() {
|
@@ -16241,6 +18324,10 @@ wysihtml5.views.View = Base.extend(
|
|
16241
18324
|
});
|
16242
18325
|
},
|
16243
18326
|
|
18327
|
+
destroy: function() {
|
18328
|
+
this.container.ownerDocument.removeEventListener("click", this._ownerDocumentClick, false);
|
18329
|
+
},
|
18330
|
+
|
16244
18331
|
_hideAllDialogs: function() {
|
16245
18332
|
var commandMapping = this.commandMapping;
|
16246
18333
|
for (var i in commandMapping) {
|