diff --git a/lib/editor/atto/readme_moodle.txt b/lib/editor/atto/readme_moodle.txt
index fc977de8544..9dd3bb960e6 100644
--- a/lib/editor/atto/readme_moodle.txt
+++ b/lib/editor/atto/readme_moodle.txt
@@ -1,20 +1,22 @@
Description of the import of libraries associated with the Atto editor.
-1) Rangy (version 1.2.3)
- * Download the latest stable release;
+ * Download the latest stable release from https://github.com/timdown/rangy/releases ("rangy-X.Z.Y"
+ rather than the "Source code" version)
* Delete all files in yui/src/rangy/js
* Copy the content of the 'currentrelease/uncompressed' folder into yui/src/rangy/js
* Patch out the AMD / module support from rangy (because we are loading it with YUI)
- To do this - change the code start of each js file to look like (just delete the other lines):
+ To do this - change the code start of each js file except rangy-core.js to look like (just delete the other lines):
(function(factory, root) {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
})(function(rangy) {
+ * rangy-core.js should look like this:
+
+(function(factory, root) {
+ // No AMD or CommonJS support so we place Rangy in (probably) the global variable
+ root.rangy = factory();
+})(function() {
+
* Run shifter against yui/src/rangy
-
-
- Notes:
- * We have patched 1.2.3 with a backport fix from the next release of Rangy which addresses an incompatibility
- between Rangy and HTML5Shiv which is used in the bootstrapclean theme. See MDL-44798 for further information.
diff --git a/lib/editor/atto/thirdpartylibs.xml b/lib/editor/atto/thirdpartylibs.xml
index f809e9600da..166f0966073 100644
--- a/lib/editor/atto/thirdpartylibs.xml
+++ b/lib/editor/atto/thirdpartylibs.xml
@@ -4,7 +4,7 @@
yui/src/rangy/js/*.*
Rangy
A cross-browser JavaScript range and selection library.
- 1.3.0
+ 1.3.1
MIT
https://github.com/timdown/rangy
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
index 9ed2584265e..a364afddb01 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
@@ -1,460 +1,460 @@
-/**
- * Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
-
-(function(factory, root) {
- // No AMD or CommonJS support so we place Rangy in (probably) the global variable
- root.rangy = factory();
-})(function() {
-
- var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
-
- // Minimal set of properties required for DOM Level 2 Range compliance. Comparison constants such as START_TO_START
- // are omitted because ranges in KHTML do not have them but otherwise work perfectly well. See issue 113.
- var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
- "commonAncestorContainer"];
-
- // Minimal set of methods required for DOM Level 2 Range compliance
- var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
- "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
- "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
-
- var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
-
- // Subset of TextRange's full set of methods that we're interested in
- var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "moveToElementText", "parentElement", "select",
- "setEndPoint", "getBoundingClientRect"];
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
- // Trio of functions taken from Peter Michaux's article:
- // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
- function isHostMethod(o, p) {
- var t = typeof o[p];
- return t == FUNCTION || (!!(t == OBJECT && o[p])) || t == "unknown";
- }
-
- function isHostObject(o, p) {
- return !!(typeof o[p] == OBJECT && o[p]);
- }
-
- function isHostProperty(o, p) {
- return typeof o[p] != UNDEFINED;
- }
-
- // Creates a convenience function to save verbose repeated calls to tests functions
- function createMultiplePropertyTest(testFunc) {
- return function(o, props) {
- var i = props.length;
- while (i--) {
- if (!testFunc(o, props[i])) {
- return false;
- }
- }
- return true;
- };
- }
-
- // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
- var areHostMethods = createMultiplePropertyTest(isHostMethod);
- var areHostObjects = createMultiplePropertyTest(isHostObject);
- var areHostProperties = createMultiplePropertyTest(isHostProperty);
-
- function isTextRange(range) {
- return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
- }
-
- function getBody(doc) {
- return isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
- }
-
- var forEach = [].forEach ?
- function(arr, func) {
- arr.forEach(func);
- } :
- function(arr, func) {
- for (var i = 0, len = arr.length; i < len; ++i) {
- func(arr[i], i);
- }
- };
-
- var modules = {};
-
- var isBrowser = (typeof window != UNDEFINED && typeof document != UNDEFINED);
-
- var util = {
- isHostMethod: isHostMethod,
- isHostObject: isHostObject,
- isHostProperty: isHostProperty,
- areHostMethods: areHostMethods,
- areHostObjects: areHostObjects,
- areHostProperties: areHostProperties,
- isTextRange: isTextRange,
- getBody: getBody,
- forEach: forEach
- };
-
- var api = {
- version: "1.3.0",
- initialized: false,
- isBrowser: isBrowser,
- supported: true,
- util: util,
- features: {},
- modules: modules,
- config: {
- alertOnFail: false,
- alertOnWarn: false,
- preferTextRange: false,
- autoInitialize: (typeof rangyAutoInitialize == UNDEFINED) ? true : rangyAutoInitialize
- }
- };
-
- function consoleLog(msg) {
- if (typeof console != UNDEFINED && isHostMethod(console, "log")) {
- console.log(msg);
- }
- }
-
- function alertOrLog(msg, shouldAlert) {
- if (isBrowser && shouldAlert) {
- alert(msg);
- } else {
- consoleLog(msg);
- }
- }
-
- function fail(reason) {
- api.initialized = true;
- api.supported = false;
- alertOrLog("Rangy is not supported in this environment. Reason: " + reason, api.config.alertOnFail);
- }
-
- api.fail = fail;
-
- function warn(msg) {
- alertOrLog("Rangy warning: " + msg, api.config.alertOnWarn);
- }
-
- api.warn = warn;
-
- // Add utility extend() method
- var extend;
- if ({}.hasOwnProperty) {
- util.extend = extend = function(obj, props, deep) {
- var o, p;
- for (var i in props) {
- if (props.hasOwnProperty(i)) {
- o = obj[i];
- p = props[i];
- if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
- extend(o, p, true);
- }
- obj[i] = p;
- }
- }
- // Special case for toString, which does not show up in for...in loops in IE <= 8
- if (props.hasOwnProperty("toString")) {
- obj.toString = props.toString;
- }
- return obj;
- };
-
- util.createOptions = function(optionsParam, defaults) {
- var options = {};
- extend(options, defaults);
- if (optionsParam) {
- extend(options, optionsParam);
- }
- return options;
- };
- } else {
- fail("hasOwnProperty not supported");
- }
-
- // Test whether we're in a browser and bail out if not
- if (!isBrowser) {
- fail("Rangy can only run in a browser");
- }
-
- // Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
- (function() {
- var toArray;
-
- if (isBrowser) {
- var el = document.createElement("div");
- el.appendChild(document.createElement("span"));
- var slice = [].slice;
- try {
- if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
- toArray = function(arrayLike) {
- return slice.call(arrayLike, 0);
- };
- }
- } catch (e) {}
- }
-
- if (!toArray) {
- toArray = function(arrayLike) {
- var arr = [];
- for (var i = 0, len = arrayLike.length; i < len; ++i) {
- arr[i] = arrayLike[i];
- }
- return arr;
- };
- }
-
- util.toArray = toArray;
- })();
-
- // Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
- // normalization of event properties
- var addListener;
- if (isBrowser) {
- if (isHostMethod(document, "addEventListener")) {
- addListener = function(obj, eventType, listener) {
- obj.addEventListener(eventType, listener, false);
- };
- } else if (isHostMethod(document, "attachEvent")) {
- addListener = function(obj, eventType, listener) {
- obj.attachEvent("on" + eventType, listener);
- };
- } else {
- fail("Document does not have required addEventListener or attachEvent method");
- }
-
- util.addListener = addListener;
- }
-
- var initListeners = [];
-
- function getErrorDesc(ex) {
- return ex.message || ex.description || String(ex);
- }
-
- // Initialization
- function init() {
- if (!isBrowser || api.initialized) {
- return;
- }
- var testRange;
- var implementsDomRange = false, implementsTextRange = false;
-
- // First, perform basic feature tests
-
- if (isHostMethod(document, "createRange")) {
- testRange = document.createRange();
- if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
- implementsDomRange = true;
- }
- }
-
- var body = getBody(document);
- if (!body || body.nodeName.toLowerCase() != "body") {
- fail("No body element found");
- return;
- }
-
- if (body && isHostMethod(body, "createTextRange")) {
- testRange = body.createTextRange();
- if (isTextRange(testRange)) {
- implementsTextRange = true;
- }
- }
-
- if (!implementsDomRange && !implementsTextRange) {
- fail("Neither Range nor TextRange are available");
- return;
- }
-
- api.initialized = true;
- api.features = {
- implementsDomRange: implementsDomRange,
- implementsTextRange: implementsTextRange
- };
-
- // Initialize modules
- var module, errorMessage;
- for (var moduleName in modules) {
- if ( (module = modules[moduleName]) instanceof Module ) {
- module.init(module, api);
- }
- }
-
- // Call init listeners
- for (var i = 0, len = initListeners.length; i < len; ++i) {
- try {
- initListeners[i](api);
- } catch (ex) {
- errorMessage = "Rangy init listener threw an exception. Continuing. Detail: " + getErrorDesc(ex);
- consoleLog(errorMessage);
- }
- }
- }
-
- function deprecationNotice(deprecated, replacement, module) {
- if (module) {
- deprecated += " in module " + module.name;
- }
- api.warn("DEPRECATED: " + deprecated + " is deprecated. Please use " +
- replacement + " instead.");
- }
-
- function createAliasForDeprecatedMethod(owner, deprecated, replacement, module) {
- owner[deprecated] = function() {
- deprecationNotice(deprecated, replacement, module);
- return owner[replacement].apply(owner, util.toArray(arguments));
- };
- }
-
- util.deprecationNotice = deprecationNotice;
- util.createAliasForDeprecatedMethod = createAliasForDeprecatedMethod;
-
- // Allow external scripts to initialize this library in case it's loaded after the document has loaded
- api.init = init;
-
- // Execute listener immediately if already initialized
- api.addInitListener = function(listener) {
- if (api.initialized) {
- listener(api);
- } else {
- initListeners.push(listener);
- }
- };
-
- var shimListeners = [];
-
- api.addShimListener = function(listener) {
- shimListeners.push(listener);
- };
-
- function shim(win) {
- win = win || window;
- init();
-
- // Notify listeners
- for (var i = 0, len = shimListeners.length; i < len; ++i) {
- shimListeners[i](win);
- }
- }
-
- if (isBrowser) {
- api.shim = api.createMissingNativeApi = shim;
- createAliasForDeprecatedMethod(api, "createMissingNativeApi", "shim");
- }
-
- function Module(name, dependencies, initializer) {
- this.name = name;
- this.dependencies = dependencies;
- this.initialized = false;
- this.supported = false;
- this.initializer = initializer;
- }
-
- Module.prototype = {
- init: function() {
- var requiredModuleNames = this.dependencies || [];
- for (var i = 0, len = requiredModuleNames.length, requiredModule, moduleName; i < len; ++i) {
- moduleName = requiredModuleNames[i];
-
- requiredModule = modules[moduleName];
- if (!requiredModule || !(requiredModule instanceof Module)) {
- throw new Error("required module '" + moduleName + "' not found");
- }
-
- requiredModule.init();
-
- if (!requiredModule.supported) {
- throw new Error("required module '" + moduleName + "' not supported");
- }
- }
-
- // Now run initializer
- this.initializer(this);
- },
-
- fail: function(reason) {
- this.initialized = true;
- this.supported = false;
- throw new Error(reason);
- },
-
- warn: function(msg) {
- api.warn("Module " + this.name + ": " + msg);
- },
-
- deprecationNotice: function(deprecated, replacement) {
- api.warn("DEPRECATED: " + deprecated + " in module " + this.name + " is deprecated. Please use " +
- replacement + " instead");
- },
-
- createError: function(msg) {
- return new Error("Error in Rangy " + this.name + " module: " + msg);
- }
- };
-
- function createModule(name, dependencies, initFunc) {
- var newModule = new Module(name, dependencies, function(module) {
- if (!module.initialized) {
- module.initialized = true;
- try {
- initFunc(api, module);
- module.supported = true;
- } catch (ex) {
- var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
- consoleLog(errorMessage);
- if (ex.stack) {
- consoleLog(ex.stack);
- }
- }
- }
- });
- modules[name] = newModule;
- return newModule;
- }
-
- api.createModule = function(name) {
- // Allow 2 or 3 arguments (second argument is an optional array of dependencies)
- var initFunc, dependencies;
- if (arguments.length == 2) {
- initFunc = arguments[1];
- dependencies = [];
- } else {
- initFunc = arguments[2];
- dependencies = arguments[1];
- }
-
- var module = createModule(name, dependencies, initFunc);
-
- // Initialize the module immediately if the core is already initialized
- if (api.initialized && api.supported) {
- module.init();
- }
- };
-
- api.createCoreModule = function(name, dependencies, initFunc) {
- createModule(name, dependencies, initFunc);
- };
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
- // Ensure rangy.rangePrototype and rangy.selectionPrototype are available immediately
-
- function RangePrototype() {}
- api.RangePrototype = RangePrototype;
- api.rangePrototype = new RangePrototype();
-
- function SelectionPrototype() {}
- api.selectionPrototype = new SelectionPrototype();
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
+/**
+ * Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Copyright 2022, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.1
+ * Build date: 17 August 2022
+ */
+
+(function(factory, root) {
+ // No AMD or CommonJS support so we place Rangy in (probably) the global variable
+ root.rangy = factory();
+})(function() {
+
+ var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
+
+ // Minimal set of properties required for DOM Level 2 Range compliance. Comparison constants such as START_TO_START
+ // are omitted because ranges in KHTML do not have them but otherwise work perfectly well. See issue 113.
+ var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
+ "commonAncestorContainer"];
+
+ // Minimal set of methods required for DOM Level 2 Range compliance
+ var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
+ "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
+ "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
+
+ var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
+
+ // Subset of TextRange's full set of methods that we're interested in
+ var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "moveToElementText", "parentElement", "select",
+ "setEndPoint", "getBoundingClientRect"];
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Trio of functions taken from Peter Michaux's article:
+ // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
+ function isHostMethod(o, p) {
+ var t = typeof o[p];
+ return t == FUNCTION || (!!(t == OBJECT && o[p])) || t == "unknown";
+ }
+
+ function isHostObject(o, p) {
+ return !!(typeof o[p] == OBJECT && o[p]);
+ }
+
+ function isHostProperty(o, p) {
+ return typeof o[p] != UNDEFINED;
+ }
+
+ // Creates a convenience function to save verbose repeated calls to tests functions
+ function createMultiplePropertyTest(testFunc) {
+ return function(o, props) {
+ var i = props.length;
+ while (i--) {
+ if (!testFunc(o, props[i])) {
+ return false;
+ }
+ }
+ return true;
+ };
+ }
+
+ // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
+ var areHostMethods = createMultiplePropertyTest(isHostMethod);
+ var areHostObjects = createMultiplePropertyTest(isHostObject);
+ var areHostProperties = createMultiplePropertyTest(isHostProperty);
+
+ function isTextRange(range) {
+ return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
+ }
+
+ function getBody(doc) {
+ return isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
+ }
+
+ var forEach = [].forEach ?
+ function(arr, func) {
+ arr.forEach(func);
+ } :
+ function(arr, func) {
+ for (var i = 0, len = arr.length; i < len; ++i) {
+ func(arr[i], i);
+ }
+ };
+
+ var modules = {};
+
+ var isBrowser = (typeof window != UNDEFINED && typeof document != UNDEFINED);
+
+ var util = {
+ isHostMethod: isHostMethod,
+ isHostObject: isHostObject,
+ isHostProperty: isHostProperty,
+ areHostMethods: areHostMethods,
+ areHostObjects: areHostObjects,
+ areHostProperties: areHostProperties,
+ isTextRange: isTextRange,
+ getBody: getBody,
+ forEach: forEach
+ };
+
+ var api = {
+ version: "1.3.1",
+ initialized: false,
+ isBrowser: isBrowser,
+ supported: true,
+ util: util,
+ features: {},
+ modules: modules,
+ config: {
+ alertOnFail: false,
+ alertOnWarn: false,
+ preferTextRange: false,
+ autoInitialize: (typeof rangyAutoInitialize == UNDEFINED) ? true : rangyAutoInitialize
+ }
+ };
+
+ function consoleLog(msg) {
+ if (typeof console != UNDEFINED && isHostMethod(console, "log")) {
+ console.log(msg);
+ }
+ }
+
+ function alertOrLog(msg, shouldAlert) {
+ if (isBrowser && shouldAlert) {
+ alert(msg);
+ } else {
+ consoleLog(msg);
+ }
+ }
+
+ function fail(reason) {
+ api.initialized = true;
+ api.supported = false;
+ alertOrLog("Rangy is not supported in this environment. Reason: " + reason, api.config.alertOnFail);
+ }
+
+ api.fail = fail;
+
+ function warn(msg) {
+ alertOrLog("Rangy warning: " + msg, api.config.alertOnWarn);
+ }
+
+ api.warn = warn;
+
+ // Add utility extend() method
+ var extend;
+ if ({}.hasOwnProperty) {
+ util.extend = extend = function(obj, props, deep) {
+ var o, p;
+ for (var i in props) {
+ if (props.hasOwnProperty(i)) {
+ o = obj[i];
+ p = props[i];
+ if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
+ extend(o, p, true);
+ }
+ obj[i] = p;
+ }
+ }
+ // Special case for toString, which does not show up in for...in loops in IE <= 8
+ if (props.hasOwnProperty("toString")) {
+ obj.toString = props.toString;
+ }
+ return obj;
+ };
+
+ util.createOptions = function(optionsParam, defaults) {
+ var options = {};
+ extend(options, defaults);
+ if (optionsParam) {
+ extend(options, optionsParam);
+ }
+ return options;
+ };
+ } else {
+ fail("hasOwnProperty not supported");
+ }
+
+ // Test whether we're in a browser and bail out if not
+ if (!isBrowser) {
+ fail("Rangy can only run in a browser");
+ }
+
+ // Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
+ (function() {
+ var toArray;
+
+ if (isBrowser) {
+ var el = document.createElement("div");
+ el.appendChild(document.createElement("span"));
+ var slice = [].slice;
+ try {
+ if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
+ toArray = function(arrayLike) {
+ return slice.call(arrayLike, 0);
+ };
+ }
+ } catch (e) {}
+ }
+
+ if (!toArray) {
+ toArray = function(arrayLike) {
+ var arr = [];
+ for (var i = 0, len = arrayLike.length; i < len; ++i) {
+ arr[i] = arrayLike[i];
+ }
+ return arr;
+ };
+ }
+
+ util.toArray = toArray;
+ })();
+
+ // Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
+ // normalization of event properties because we don't need this.
+ var addListener;
+ if (isBrowser) {
+ if (isHostMethod(document, "addEventListener")) {
+ addListener = function(obj, eventType, listener) {
+ obj.addEventListener(eventType, listener, false);
+ };
+ } else if (isHostMethod(document, "attachEvent")) {
+ addListener = function(obj, eventType, listener) {
+ obj.attachEvent("on" + eventType, listener);
+ };
+ } else {
+ fail("Document does not have required addEventListener or attachEvent method");
+ }
+
+ util.addListener = addListener;
+ }
+
+ var initListeners = [];
+
+ function getErrorDesc(ex) {
+ return ex.message || ex.description || String(ex);
+ }
+
+ // Initialization
+ function init() {
+ if (!isBrowser || api.initialized) {
+ return;
+ }
+ var testRange;
+ var implementsDomRange = false, implementsTextRange = false;
+
+ // First, perform basic feature tests
+
+ if (isHostMethod(document, "createRange")) {
+ testRange = document.createRange();
+ if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
+ implementsDomRange = true;
+ }
+ }
+
+ var body = getBody(document);
+ if (!body || body.nodeName.toLowerCase() != "body") {
+ fail("No body element found");
+ return;
+ }
+
+ if (body && isHostMethod(body, "createTextRange")) {
+ testRange = body.createTextRange();
+ if (isTextRange(testRange)) {
+ implementsTextRange = true;
+ }
+ }
+
+ if (!implementsDomRange && !implementsTextRange) {
+ fail("Neither Range nor TextRange are available");
+ return;
+ }
+
+ api.initialized = true;
+ api.features = {
+ implementsDomRange: implementsDomRange,
+ implementsTextRange: implementsTextRange
+ };
+
+ // Initialize modules
+ var module, errorMessage;
+ for (var moduleName in modules) {
+ if ( (module = modules[moduleName]) instanceof Module ) {
+ module.init(module, api);
+ }
+ }
+
+ // Call init listeners
+ for (var i = 0, len = initListeners.length; i < len; ++i) {
+ try {
+ initListeners[i](api);
+ } catch (ex) {
+ errorMessage = "Rangy init listener threw an exception. Continuing. Detail: " + getErrorDesc(ex);
+ consoleLog(errorMessage);
+ }
+ }
+ }
+
+ function deprecationNotice(deprecated, replacement, module) {
+ if (module) {
+ deprecated += " in module " + module.name;
+ }
+ api.warn("DEPRECATED: " + deprecated + " is deprecated. Please use " +
+ replacement + " instead.");
+ }
+
+ function createAliasForDeprecatedMethod(owner, deprecated, replacement, module) {
+ owner[deprecated] = function() {
+ deprecationNotice(deprecated, replacement, module);
+ return owner[replacement].apply(owner, util.toArray(arguments));
+ };
+ }
+
+ util.deprecationNotice = deprecationNotice;
+ util.createAliasForDeprecatedMethod = createAliasForDeprecatedMethod;
+
+ // Allow external scripts to initialize this library in case it's loaded after the document has loaded
+ api.init = init;
+
+ // Execute listener immediately if already initialized
+ api.addInitListener = function(listener) {
+ if (api.initialized) {
+ listener(api);
+ } else {
+ initListeners.push(listener);
+ }
+ };
+
+ var shimListeners = [];
+
+ api.addShimListener = function(listener) {
+ shimListeners.push(listener);
+ };
+
+ function shim(win) {
+ win = win || window;
+ init();
+
+ // Notify listeners
+ for (var i = 0, len = shimListeners.length; i < len; ++i) {
+ shimListeners[i](win);
+ }
+ }
+
+ if (isBrowser) {
+ api.shim = api.createMissingNativeApi = shim;
+ createAliasForDeprecatedMethod(api, "createMissingNativeApi", "shim");
+ }
+
+ function Module(name, dependencies, initializer) {
+ this.name = name;
+ this.dependencies = dependencies;
+ this.initialized = false;
+ this.supported = false;
+ this.initializer = initializer;
+ }
+
+ Module.prototype = {
+ init: function() {
+ var requiredModuleNames = this.dependencies || [];
+ for (var i = 0, len = requiredModuleNames.length, requiredModule, moduleName; i < len; ++i) {
+ moduleName = requiredModuleNames[i];
+
+ requiredModule = modules[moduleName];
+ if (!requiredModule || !(requiredModule instanceof Module)) {
+ throw new Error("required module '" + moduleName + "' not found");
+ }
+
+ requiredModule.init();
+
+ if (!requiredModule.supported) {
+ throw new Error("required module '" + moduleName + "' not supported");
+ }
+ }
+
+ // Now run initializer
+ this.initializer(this);
+ },
+
+ fail: function(reason) {
+ this.initialized = true;
+ this.supported = false;
+ throw new Error(reason);
+ },
+
+ warn: function(msg) {
+ api.warn("Module " + this.name + ": " + msg);
+ },
+
+ deprecationNotice: function(deprecated, replacement) {
+ api.warn("DEPRECATED: " + deprecated + " in module " + this.name + " is deprecated. Please use " +
+ replacement + " instead");
+ },
+
+ createError: function(msg) {
+ return new Error("Error in Rangy " + this.name + " module: " + msg);
+ }
+ };
+
+ function createModule(name, dependencies, initFunc) {
+ var newModule = new Module(name, dependencies, function(module) {
+ if (!module.initialized) {
+ module.initialized = true;
+ try {
+ initFunc(api, module);
+ module.supported = true;
+ } catch (ex) {
+ var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
+ consoleLog(errorMessage);
+ if (ex.stack) {
+ consoleLog(ex.stack);
+ }
+ }
+ }
+ });
+ modules[name] = newModule;
+ return newModule;
+ }
+
+ api.createModule = function(name) {
+ // Allow 2 or 3 arguments (second argument is an optional array of dependencies)
+ var initFunc, dependencies;
+ if (arguments.length == 2) {
+ initFunc = arguments[1];
+ dependencies = [];
+ } else {
+ initFunc = arguments[2];
+ dependencies = arguments[1];
+ }
+
+ var module = createModule(name, dependencies, initFunc);
+
+ // Initialize the module immediately if the core is already initialized
+ if (api.initialized && api.supported) {
+ module.init();
+ }
+ };
+
+ api.createCoreModule = function(name, dependencies, initFunc) {
+ createModule(name, dependencies, initFunc);
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Ensure rangy.rangePrototype and rangy.selectionPrototype are available immediately
+
+ function RangePrototype() {}
+ api.RangePrototype = RangePrototype;
+ api.rangePrototype = new RangePrototype();
+
+ function SelectionPrototype() {}
+ api.selectionPrototype = new SelectionPrototype();
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
// DOM utility methods used by Rangy
api.createCoreModule("DomUtil", [], function(api, module) {
var UNDEF = "undefined";
@@ -953,10 +953,10 @@
};
api.DOMException = DOMException;
- });
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
+ });
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
// Pure JavaScript implementation of DOM Range
api.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
var dom = api.dom;
@@ -1295,6 +1295,7 @@
var getDocumentOrFragmentContainer = createAncestorFinder( [9, 11] );
var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
var getDocTypeNotationEntityAncestor = createAncestorFinder( [6, 10, 12] );
+ var getElementAncestor = createAncestorFinder( [1] );
function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
@@ -1357,7 +1358,7 @@
var htmlParsingConforms = false;
try {
styleEl.innerHTML = "x";
- htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
+ htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Pre-Blink Opera incorrectly creates an element node
} catch (e) {
// IE 6 and 7 throw
}
@@ -1958,6 +1959,12 @@
break;
}
+ assertNoDocTypeNotationEntityAncestor(sc, true);
+ assertValidOffset(sc, so);
+
+ assertNoDocTypeNotationEntityAncestor(ec, true);
+ assertValidOffset(ec, eo);
+
boundaryUpdater(this, sc, so, ec, eo);
},
@@ -2120,6 +2127,12 @@
assertNoDocTypeNotationEntityAncestor(node, true);
assertValidOffset(node, offset);
this.setStartAndEnd(node, offset);
+ },
+
+ parentElement: function() {
+ assertRangeValid(this);
+ var parentNode = this.commonAncestorContainer;
+ return parentNode ? getElementAncestor(this.commonAncestorContainer, true) : null;
}
});
@@ -2141,17 +2154,11 @@
range.endContainer = endContainer;
range.endOffset = endOffset;
range.document = dom.getDocument(startContainer);
-
updateCollapsedAndCommonAncestor(range);
}
function Range(doc) {
- this.startContainer = doc;
- this.startOffset = 0;
- this.endContainer = doc;
- this.endOffset = 0;
- this.document = doc;
- updateCollapsedAndCommonAncestor(this);
+ updateBoundaries(this, doc, 0, doc, 0);
}
createPrototypeRange(Range, updateBoundaries);
@@ -2173,10 +2180,10 @@
});
api.DomRange = Range;
- });
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
+ });
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
// Wrappers for the browser's native DOM Range and/or TextRange implementation
api.createCoreModule("WrappedRange", ["DomRange"], function(api, module) {
var WrappedRange, WrappedTextRange;
@@ -2780,12 +2787,12 @@
}
doc = win = null;
});
- });
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
+ });
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
// This module creates a selection object wrapper that conforms as closely as possible to the Selection specification
- // in the HTML Editing spec (http://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#selections)
+ // in the W3C Selection API spec (https://www.w3.org/TR/selection-api)
api.createCoreModule("WrappedSelection", ["DomRange", "WrappedRange"], function(api, module) {
api.config.checkSelectionRanges = true;
@@ -2893,6 +2900,10 @@
var selectionHasExtend = isHostMethod(testSelection, "extend");
features.selectionHasExtend = selectionHasExtend;
+ // Test for existence of native selection setBaseAndExtent() method
+ var selectionHasSetBaseAndExtent = isHostMethod(testSelection, "setBaseAndExtent");
+ features.selectionHasSetBaseAndExtent = selectionHasSetBaseAndExtent;
+
// Test if rangeCount exists
var selectionHasRangeCount = (typeof testSelection.rangeCount == NUMBER);
features.selectionHasRangeCount = selectionHasRangeCount;
@@ -2917,7 +2928,7 @@
// performed on the current document's selection. See issue 109.
// Note also that if a selection previously existed, it is wiped and later restored by these tests. This
- // will result in the selection direction begin reversed if the original selection was backwards and the
+ // will result in the selection direction being reversed if the original selection was backwards and the
// browser does not support setting backwards selections (Internet Explorer, I'm looking at you).
var sel = window.getSelection();
if (sel) {
@@ -3032,6 +3043,11 @@
sel.rangeCount = 0;
sel.isCollapsed = true;
sel._ranges.length = 0;
+ updateType(sel);
+ }
+
+ function updateType(sel) {
+ sel.type = (sel.rangeCount == 0) ? "None" : (selectionIsCollapsed(sel) ? "Caret" : "Range");
}
function getNativeRange(range) {
@@ -3081,6 +3097,7 @@
updateAnchorAndFocusFromRange(sel, wrappedRange, false);
sel.rangeCount = 1;
sel.isCollapsed = wrappedRange.collapsed;
+ updateType(sel);
}
function updateControlSelection(sel) {
@@ -3105,6 +3122,7 @@
}
sel.isCollapsed = sel.rangeCount == 1 && sel._ranges[0].collapsed;
updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], false);
+ updateType(sel);
}
}
}
@@ -3174,6 +3192,7 @@
sel.win = sel.anchorNode = sel.focusNode = sel._ranges = null;
sel.rangeCount = sel.anchorOffset = sel.focusOffset = 0;
sel.detached = true;
+ updateType(sel);
}
var cachedRangySelections = [];
@@ -3300,6 +3319,7 @@
this._ranges[this.rangeCount - 1] = range;
updateAnchorAndFocusFromRange(this, range, selectionIsBackward(this.nativeSelection));
this.isCollapsed = selectionIsCollapsed(this);
+ updateType(this);
} else {
// The range was not added successfully. The simplest thing is to refresh
this.refresh();
@@ -3368,6 +3388,7 @@
this.rangeCount = 1;
this.isCollapsed = this._ranges[0].collapsed;
updateAnchorAndFocusFromRange(this, range, false);
+ updateType(this);
}
};
@@ -3426,6 +3447,7 @@
}
updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], selectionIsBackward(sel.nativeSelection));
sel.isCollapsed = selectionIsCollapsed(sel);
+ updateType(sel);
} else {
updateEmptySelection(sel);
}
@@ -3440,6 +3462,7 @@
sel.rangeCount = 1;
updateAnchorAndFocusFromNativeSelection(sel);
sel.isCollapsed = selectionIsCollapsed(sel);
+ updateType(sel);
} else {
updateEmptySelection(sel);
}
@@ -3558,6 +3581,12 @@
}
}
+ function assertValidOffset(node, offset) {
+ if (offset < 0 || offset > (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length)) {
+ throw new DOMException("INDEX_SIZE_ERR");
+ }
+ }
+
// No current browser conforms fully to the spec for this method, so Rangy's own method is always used
selProto.collapse = function(node, offset) {
assertNodeInSameDocument(this, node);
@@ -3594,6 +3623,28 @@
this.setSingleRange(range);
};
+ if (selectionHasSetBaseAndExtent) {
+ selProto.setBaseAndExtent = function(anchorNode, anchorOffset, focusNode, focusOffset) {
+ this.nativeSelection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
+ this.refresh();
+ };
+ } else if (selectionHasExtend) {
+ selProto.setBaseAndExtent = function(anchorNode, anchorOffset, focusNode, focusOffset) {
+ assertValidOffset(anchorNode, anchorOffset);
+ assertValidOffset(focusNode, focusOffset);
+ assertNodeInSameDocument(this, anchorNode);
+ assertNodeInSameDocument(this, focusNode);
+ var range = api.createRange(node);
+ var isBackwards = (dom.comparePoints(anchorNode, anchorOffset, focusNode, focusOffset) == -1);
+ if (isBackwards) {
+ range.setStartAndEnd(focusNode, focusOffset, anchorNode, anchorOffset);
+ } else {
+ range.setStartAndEnd(anchorNode, anchorOffset, focusNode, focusOffset);
+ }
+ this.setSingleRange(range, isBackwards);
+ };
+ }
+
selProto.deleteFromDocument = function() {
// Sepcial behaviour required for IE's control selections
if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
@@ -3803,58 +3854,57 @@
win = null;
});
});
-
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
- // Wait for document to load before initializing
- var docReady = false;
-
- var loadHandler = function(e) {
- if (!docReady) {
- docReady = true;
- if (!api.initialized && api.config.autoInitialize) {
- init();
- }
- }
- };
-
- if (isBrowser) {
- // Test whether the document has already been loaded and initialize immediately if so
- if (document.readyState == "complete") {
- loadHandler();
- } else {
- if (isHostMethod(document, "addEventListener")) {
- document.addEventListener("DOMContentLoaded", loadHandler, false);
- }
-
- // Add a fallback in case the DOMContentLoaded event isn't supported
- addListener(window, "load", loadHandler);
- }
- }
-
- return api;
-}, this);
-/**
- * Selection save and restore module for Rangy.
- * Saves and restores user selections using marker invisible elements in the DOM.
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Depends on Rangy core.
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Wait for document to load before initializing
+ var docReady = false;
+
+ var loadHandler = function(e) {
+ if (!docReady) {
+ docReady = true;
+ if (!api.initialized && api.config.autoInitialize) {
+ init();
+ }
+ }
+ };
+
+ if (isBrowser) {
+ // Test whether the document has already been loaded and initialize immediately if so
+ if (document.readyState == "complete") {
+ loadHandler();
+ } else {
+ if (isHostMethod(document, "addEventListener")) {
+ document.addEventListener("DOMContentLoaded", loadHandler, false);
+ }
+
+ // Add a fallback in case the DOMContentLoaded event isn't supported
+ addListener(window, "load", loadHandler);
+ }
+ }
+
+ return api;
+}, this);
+/**
+ * Selection save and restore module for Rangy.
+ * Saves and restores user selections using marker invisible elements in the DOM.
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2022, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.1
+ * Build date: 17 August 2022
+ */
(function(factory, root) {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
})(function(rangy) {
- rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
+ rangy.createModule("SaveRestore", ["WrappedSelection"], function(api, module) {
var dom = api.dom;
var removeNode = dom.removeNode;
var isDirectionBackward = api.Selection.isDirectionBackward;
@@ -4077,25 +4127,24 @@
removeMarkers: removeMarkers
});
});
-
+
return rangy;
}, this);
-
-/**
- * Serializer module for Rangy.
- * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
- * cookie or local storage and restore it on the user's next visit to the same page.
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Depends on Rangy core.
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
+/**
+ * Serializer module for Rangy.
+ * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
+ * cookie or local storage and restore it on the user's next visit to the same page.
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2022, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.1
+ * Build date: 17 August 2022
+ */
(function(factory, root) {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
@@ -4384,24 +4433,23 @@
util.crc32 = crc32;
});
-
+
return rangy;
}, this);
-
-/**
- * Class Applier module for Rangy.
- * Adds, removes and toggles classes on Ranges and Selections
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Depends on Rangy core.
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
+/**
+ * Class Applier module for Rangy.
+ * Adds, removes and toggles classes on Ranges and Selections
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2022, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.1
+ * Build date: 17 August 2022
+ */
(function(factory, root) {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
@@ -4831,7 +4879,7 @@
var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
-
+
function Merge(firstNode) {
this.isElementMerge = (firstNode.nodeType == 1);
this.textNodes = [];
@@ -4865,7 +4913,7 @@
// Handle case where both text nodes precede the position within the same parent node
if (position.node == parent && position.offset > firstTextNodeIndex) {
--position.offset;
- if (position.offset == firstTextNodeIndex + 1 && i < len - 1) {
+ if (position.offset == firstTextNodeIndex + 1 && i < textNodes.length - 1) {
position.node = firstTextNode;
position.offset = combinedTextLength;
}
@@ -5080,13 +5128,10 @@
// Normalizes nodes after applying a class to a Range.
postApply: function(textNodes, range, positionsToPreserve, isUndo) {
var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
-
var merges = [], currentMerge;
-
var rangeStartNode = firstNode, rangeEndNode = lastNode;
var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
-
- var textNode, precedingTextNode;
+ var precedingTextNode;
// Check for every required merge and create a Merge object for each
forEach(textNodes, function(textNode) {
@@ -5123,7 +5168,7 @@
// Apply the merges
if (merges.length) {
- for (i = 0, len = merges.length; i < len; ++i) {
+ for (var i = 0, len = merges.length; i < len; ++i) {
merges[i].doMerge(positionsToPreserve);
}
@@ -5480,21 +5525,21 @@
api.createClassApplier = createClassApplier;
util.createAliasForDeprecatedMethod(api, "createCssClassApplier", "createClassApplier", module);
});
-
- return rangy;
-}, this);
-/**
- * Highlighter module for Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Depends on Rangy core, ClassApplier and optionally TextRange modules.
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
+ return rangy;
+}, this);
+
+/**
+ * Highlighter module for Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Depends on Rangy core, ClassApplier and optionally TextRange modules.
+ *
+ * Copyright 2022, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.1
+ * Build date: 17 August 2022
+ */
(function(factory, root) {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
@@ -5938,13 +5983,12 @@
options = createOptions(options, {
containerElementId: null,
- selection: api.getSelection(this.doc),
exclusive: true
});
var containerElementId = options.containerElementId;
var exclusive = options.exclusive;
- var selection = options.selection;
+ var selection = options.selection || api.getSelection(this.doc);
var doc = selection.win.document;
var containerElement = getContainerElement(doc, containerElementId);
@@ -6093,75 +6137,75 @@
return new Highlighter(doc, rangeCharacterOffsetConverterType);
};
});
-
- return rangy;
-}, this);
-/**
- * Text range module for Rangy.
- * Text-based manipulation and searching of ranges and selections.
- *
- * Features
- *
- * - Ability to move range boundaries by character or word offsets
- * - Customizable word tokenizer
- * - Ignores text nodes inside