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