@zzish/math-rich-input 0.1.50 → 0.1.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,10 +3,12 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
+ var ReactDOM = require('react-dom');
6
7
 
7
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
9
 
9
10
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
11
+ var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
10
12
 
11
13
  function _callSuper(t, o, e) {
12
14
  return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
@@ -19311,31 +19313,50 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19311
19313
  console.log("🔍 Focusing equation editor...");
19312
19314
  if (_this.equationEditor) {
19313
19315
  console.log("🔍 EquationEditor ref found");
19314
- if (_this.state.useExpertMode) {
19315
- // Focus textarea in expert mode
19316
- console.log("🔍 Expert mode - focusing latexInput");
19317
- if (_this.equationEditor.latexInput) {
19318
- _this.equationEditor.latexInput.focus();
19319
- console.log("✅ LatexInput focused");
19320
- } else {
19321
- console.log("❌ LatexInput not found");
19322
- }
19323
- } else {
19324
- // Focus MathQuill in normal mode
19325
- console.log("🔍 Normal mode - focusing mathQuill");
19326
- if (_this.equationEditor.mathQuill) {
19327
- _this.equationEditor.mathQuill.focus();
19328
- console.log(" MathQuill focused");
19316
+
19317
+ // Prevent scroll jumping during focus
19318
+ var currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;
19319
+ try {
19320
+ if (_this.state.useExpertMode) {
19321
+ // Focus textarea in expert mode
19322
+ console.log("🔍 Expert mode - focusing latexInput");
19323
+ if (_this.equationEditor.latexInput) {
19324
+ // Use preventScroll option if available
19325
+ _this.equationEditor.latexInput.focus({
19326
+ preventScroll: true
19327
+ });
19328
+ console.log("✅ LatexInput focused");
19329
+ } else {
19330
+ console.log(" LatexInput not found");
19331
+ }
19329
19332
  } else {
19330
- console.log("❌ MathQuill not found");
19333
+ // Focus MathQuill in normal mode
19334
+ console.log("🔍 Normal mode - focusing mathQuill");
19335
+ if (_this.equationEditor.mathQuill) {
19336
+ _this.equationEditor.mathQuill.focus();
19337
+ console.log("✅ MathQuill focused");
19338
+ } else {
19339
+ console.log("❌ MathQuill not found");
19340
+ }
19331
19341
  }
19342
+ } catch (error) {
19343
+ console.warn("🔍 Focus failed:", error);
19332
19344
  }
19345
+
19346
+ // Ensure scroll position hasn't changed
19347
+ setTimeout(function () {
19348
+ var newScrollTop = window.pageYOffset || document.documentElement.scrollTop;
19349
+ if (newScrollTop !== currentScrollTop) {
19350
+ window.scrollTo(0, currentScrollTop);
19351
+ }
19352
+ }, 10);
19333
19353
  } else {
19334
19354
  console.log("❌ EquationEditor ref not found");
19335
19355
  }
19336
19356
  });
19337
19357
  _this.state = {
19338
- useExpertMode: GLOBAL_lastSelectedExpertMode
19358
+ useExpertMode: GLOBAL_lastSelectedExpertMode,
19359
+ animationReady: false
19339
19360
  };
19340
19361
  _this.handleCloseCallback = props.handleClose;
19341
19362
  _this.handleInsertCallback = props.handleInsert;
@@ -19346,20 +19367,117 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19346
19367
  key: "componentDidMount",
19347
19368
  value: function componentDidMount() {
19348
19369
  var _this2 = this;
19349
- // Auto-focus when modal opens
19350
- // Use setTimeout to ensure child components are fully mounted
19351
- setTimeout(function () {
19352
- _this2.focusEquationEditor();
19353
- }, 100);
19370
+ // Store current scroll position to prevent jumping
19371
+ this.originalScrollTop = window.pageYOffset || document.documentElement.scrollTop;
19372
+
19373
+ // Create portal container if it doesn't exist
19374
+ this.ensurePortalContainer();
19375
+
19376
+ // Lock body scroll to prevent jumping in transform containers
19377
+ this.lockBodyScroll();
19378
+
19379
+ // Enable animation after portal is ready (ensures smooth slide-in)
19380
+ // Need multiple RAF frames for proper CSS animation timing
19381
+ requestAnimationFrame(function () {
19382
+ requestAnimationFrame(function () {
19383
+ _this2.setState({
19384
+ animationReady: true
19385
+ }, function () {
19386
+ // Auto-focus after animation completes (0.3s CSS animation + buffer)
19387
+ setTimeout(function () {
19388
+ _this2.focusEquationEditor();
19389
+ }, 400); // Wait for animation to complete
19390
+ });
19391
+ });
19392
+ });
19393
+ }
19394
+ }, {
19395
+ key: "componentWillUnmount",
19396
+ value: function componentWillUnmount() {
19397
+ var _this3 = this;
19398
+ // Hide portal immediately to prevent flash
19399
+ if (this.portalContainer) {
19400
+ this.portalContainer.style.visibility = "hidden";
19401
+ }
19402
+
19403
+ // Unlock body scroll and restore original scroll position
19404
+ this.unlockBodyScroll();
19405
+
19406
+ // Clean up portal container after a small delay
19407
+ if (this.createdPortalContainer && this.portalContainer) {
19408
+ setTimeout(function () {
19409
+ if (_this3.portalContainer && _this3.portalContainer.parentNode) {
19410
+ document.body.removeChild(_this3.portalContainer);
19411
+ }
19412
+ }, 100);
19413
+ }
19414
+ }
19415
+ }, {
19416
+ key: "lockBodyScroll",
19417
+ value: function lockBodyScroll() {
19418
+ // Store original overflow styles and scroll position
19419
+ this.originalBodyOverflow = document.body.style.overflow;
19420
+ this.originalDocumentOverflow = document.documentElement.style.overflow;
19421
+ this.originalBodyPosition = document.body.style.position;
19422
+ this.originalBodyTop = document.body.style.top;
19423
+ this.originalBodyWidth = document.body.style.width;
19424
+
19425
+ // Get current scroll position
19426
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
19427
+
19428
+ // Apply position: fixed to prevent scrolling while preserving position
19429
+ document.body.style.position = "fixed";
19430
+ document.body.style.top = "-".concat(scrollTop, "px");
19431
+ document.body.style.width = "100%";
19432
+ document.body.style.overflow = "hidden";
19433
+ }
19434
+ }, {
19435
+ key: "unlockBodyScroll",
19436
+ value: function unlockBodyScroll() {
19437
+ // Get the scroll position from the fixed top value
19438
+ var scrollTop = Math.abs(parseInt(document.body.style.top) || 0);
19439
+
19440
+ // Restore original styles
19441
+ document.body.style.position = this.originalBodyPosition || "";
19442
+ document.body.style.top = this.originalBodyTop || "";
19443
+ document.body.style.width = this.originalBodyWidth || "";
19444
+ document.body.style.overflow = this.originalBodyOverflow || "";
19445
+ document.documentElement.style.overflow = this.originalDocumentOverflow || "";
19446
+
19447
+ // Restore scroll position smoothly
19448
+ if (scrollTop > 0) {
19449
+ window.scrollTo(0, scrollTop);
19450
+ }
19451
+ }
19452
+ }, {
19453
+ key: "ensurePortalContainer",
19454
+ value: function ensurePortalContainer() {
19455
+ // Look for existing portal container
19456
+ this.portalContainer = document.getElementById("math-rich-input-modal-portal");
19457
+ if (!this.portalContainer) {
19458
+ // Create new portal container
19459
+ this.portalContainer = document.createElement("div");
19460
+ this.portalContainer.id = "math-rich-input-modal-portal";
19461
+ // Portal container should not interfere with content positioning
19462
+ // Start invisible until animation is ready
19463
+ this.portalContainer.style.cssText = "position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 999990; visibility: hidden;";
19464
+ document.body.appendChild(this.portalContainer);
19465
+ this.createdPortalContainer = true;
19466
+ }
19354
19467
  }
19355
19468
  }, {
19356
19469
  key: "componentDidUpdate",
19357
19470
  value: function componentDidUpdate(prevProps, prevState) {
19358
- var _this3 = this;
19471
+ var _this4 = this;
19472
+ // Show portal container when animation is ready
19473
+ if (!prevState.animationReady && this.state.animationReady && this.portalContainer) {
19474
+ this.portalContainer.style.visibility = "visible";
19475
+ }
19476
+
19359
19477
  // Re-focus when expert mode changes
19360
19478
  if (prevState.useExpertMode !== this.state.useExpertMode) {
19361
19479
  setTimeout(function () {
19362
- _this3.focusEquationEditor();
19480
+ _this4.focusEquationEditor();
19363
19481
  }, 100);
19364
19482
  }
19365
19483
  }
@@ -19379,16 +19497,27 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19379
19497
  }, {
19380
19498
  key: "render",
19381
19499
  value: function render() {
19382
- var _this4 = this;
19500
+ var _this5 = this;
19383
19501
  var useClass = "EquationEditorModal";
19384
19502
  if (this.isMobile()) useClass = "EquationEditorModal-mobile";
19385
- return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
19386
- className: "EquationEditorModal-background",
19387
- onClick: this.handleClose
19503
+
19504
+ // Add support for disabling animations (useful for Svelte transitions)
19505
+ if (this.props.disableAnimation) {
19506
+ useClass += " no-animation";
19507
+ }
19508
+ var modalContent = /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
19509
+ className: "EquationEditorModal-background".concat(this.props.disableAnimation ? " no-animation" : ""),
19510
+ onClick: this.handleClose,
19511
+ style: {
19512
+ pointerEvents: "auto"
19513
+ }
19388
19514
  }), /*#__PURE__*/React__default["default"].createElement("div", {
19389
19515
  className: useClass,
19390
19516
  ref: function ref(r) {
19391
- return _this4.modalDiv = r;
19517
+ return _this5.modalDiv = r;
19518
+ },
19519
+ style: {
19520
+ pointerEvents: "auto"
19392
19521
  }
19393
19522
  }, !this.isMobile() && /*#__PURE__*/React__default["default"].createElement("div", {
19394
19523
  className: "title"
@@ -19399,23 +19528,31 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19399
19528
  }, /*#__PURE__*/React__default["default"].createElement("input", {
19400
19529
  type: "checkbox",
19401
19530
  ref: function ref(r) {
19402
- return _this4.expertModeSwitch = r;
19531
+ return _this5.expertModeSwitch = r;
19403
19532
  },
19404
19533
  onChange: function onChange(e) {
19405
- return _this4.handleExpertModeSwitchChange(e);
19534
+ return _this5.handleExpertModeSwitchChange(e);
19406
19535
  },
19407
19536
  checked: this.state.useExpertMode
19408
19537
  }), /*#__PURE__*/React__default["default"].createElement("span", {
19409
19538
  className: "slider round"
19410
19539
  })))), /*#__PURE__*/React__default["default"].createElement(EquationEditor, {
19411
19540
  ref: function ref(r) {
19412
- return _this4.equationEditor = r;
19541
+ return _this5.equationEditor = r;
19413
19542
  },
19414
19543
  latex: this.props.latex,
19415
19544
  useExpertMode: this.state.useExpertMode,
19416
19545
  handleInsert: this.handleInsert,
19417
19546
  handleCancel: this.handleClose
19418
19547
  })));
19548
+
19549
+ // Use portal to render modal outside of parent container stacking context
19550
+ if (this.portalContainer && this.state.animationReady) {
19551
+ return /*#__PURE__*/ReactDOM__default["default"].createPortal(modalContent, this.portalContainer);
19552
+ }
19553
+
19554
+ // Show nothing until portal and animation are ready (prevents flash)
19555
+ return null;
19419
19556
  }
19420
19557
  }]);
19421
19558
  }(React__default["default"].Component);
@@ -20146,9 +20283,6 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20146
20283
  var _this;
20147
20284
  _classCallCheck(this, Toolbar);
20148
20285
  _this = _callSuper(this, Toolbar, [props]);
20149
- _defineProperty(_this, "handleButtonClick", function (event, buttonName) {
20150
- _this.suppliedClickHandler(event, buttonName);
20151
- });
20152
20286
  _defineProperty(_this, "showAccentBar", function (event) {
20153
20287
  if (!_this.state.showAccents) _this.accentLetter = _this.suppliedAccentLetterFetcher();
20154
20288
  _this.setState({
@@ -20168,6 +20302,8 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20168
20302
  });
20169
20303
  });
20170
20304
  _defineProperty(_this, "handleButtonClick", function (event, buttonName) {
20305
+ event.preventDefault();
20306
+ event.stopPropagation();
20171
20307
  _this.suppliedClickHandler(event, buttonName);
20172
20308
  });
20173
20309
  _defineProperty(_this, "handleAccentButtonClick", function (event, oldCharacter, newCharacter) {
@@ -20342,6 +20478,9 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20342
20478
  }, "\xE1"), enableMath && /*#__PURE__*/React__default["default"].createElement("button", {
20343
20479
  name: "Equation",
20344
20480
  className: "Toolbar-button TB-wide",
20481
+ onClick: function onClick(e) {
20482
+ return e.preventDefault();
20483
+ },
20345
20484
  onMouseDown: function onMouseDown(e) {
20346
20485
  return _this2.handleButtonClick(e, "Equation");
20347
20486
  }
@@ -20531,6 +20670,73 @@ function determineMimeType(text) {
20531
20670
  {this.test("$a bcd> e$", "application/x-tex")}
20532
20671
  */
20533
20672
 
20673
+ var DEBUG_MATH_RICH_INPUT = false;
20674
+ function getNodePath(node) {
20675
+ var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
20676
+ if (!node) return null;
20677
+ var parts = [];
20678
+ var current = node;
20679
+ while (current && current !== root && parts.length < 8) {
20680
+ if (current.nodeType === 3) {
20681
+ parts.unshift("#text(\"".concat(current.nodeValue, "\")"));
20682
+ } else {
20683
+ var name = current.nodeName ? current.nodeName.toLowerCase() : "unknown";
20684
+ var className = current.className && typeof current.className === "string" ? ".".concat(current.className.split(" ").filter(Boolean).join(".")) : "";
20685
+ parts.unshift("".concat(name).concat(className));
20686
+ }
20687
+ current = current.parentNode;
20688
+ }
20689
+ if (root) parts.unshift("root");
20690
+ return parts.join(" > ");
20691
+ }
20692
+ function describeNode(node) {
20693
+ var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
20694
+ if (!node) return null;
20695
+ return {
20696
+ nodeName: node.nodeName,
20697
+ nodeType: node.nodeType,
20698
+ text: node.nodeType === 3 && typeof node.nodeValue === "string" ? node.nodeValue : undefined,
20699
+ textLength: node.nodeType === 3 && typeof node.nodeValue === "string" ? node.nodeValue.length : undefined,
20700
+ childCount: node.childNodes ? node.childNodes.length : undefined,
20701
+ path: getNodePath(node, root)
20702
+ };
20703
+ }
20704
+ function getSelectionSnapshot() {
20705
+ var editableDiv = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
20706
+ if (typeof window === "undefined" || !window.getSelection) return null;
20707
+ var selection = window.getSelection();
20708
+ if (!selection) return null;
20709
+ var snapshot = {
20710
+ activeElement: typeof document !== "undefined" && document.activeElement ? {
20711
+ nodeName: document.activeElement.nodeName,
20712
+ className: document.activeElement.className
20713
+ } : null,
20714
+ rangeCount: selection.rangeCount,
20715
+ isCollapsed: selection.isCollapsed,
20716
+ anchorOffset: selection.anchorOffset,
20717
+ focusOffset: selection.focusOffset,
20718
+ anchorNode: describeNode(selection.anchorNode, editableDiv),
20719
+ focusNode: describeNode(selection.focusNode, editableDiv)
20720
+ };
20721
+ if (selection.rangeCount > 0) {
20722
+ var range = selection.getRangeAt(0);
20723
+ snapshot.range = {
20724
+ collapsed: range.collapsed,
20725
+ startOffset: range.startOffset,
20726
+ endOffset: range.endOffset,
20727
+ startContainer: describeNode(range.startContainer, editableDiv),
20728
+ endContainer: describeNode(range.endContainer, editableDiv)
20729
+ };
20730
+ }
20731
+ return snapshot;
20732
+ }
20733
+ function debugMathRichInput(label) {
20734
+ return;
20735
+ }
20736
+ if (typeof window !== "undefined") {
20737
+ window.__MRI_DEBUG__ = DEBUG_MATH_RICH_INPUT;
20738
+ }
20739
+
20534
20740
  var RAW_TEXT_MATH_START_TAG_TEX = "\\[";
20535
20741
  var RAW_TEXT_MATH_END_TAG_TEX = "\\]";
20536
20742
  var RAW_TEXT_MATH_START_TAG_HTML_MATH = "<math>";
@@ -21172,6 +21378,55 @@ function findFirstTextNode(node) {
21172
21378
  }
21173
21379
  return null;
21174
21380
  }
21381
+ function findLastTextNode(node) {
21382
+ if (isTextNode(node)) return node;
21383
+ var nodes = node.childNodes;
21384
+ for (var i = nodes.length - 1; i >= 0; i--) {
21385
+ var found_node = findLastTextNode(nodes[i]);
21386
+ if (found_node !== null) return found_node;
21387
+ }
21388
+ return null;
21389
+ }
21390
+ function getTextPositionFromElementRangeEndpoint(container, offset) {
21391
+ if (isTextNode(container)) return {
21392
+ node: container,
21393
+ offset: offset
21394
+ };
21395
+ var nodes = container.childNodes;
21396
+ if (nodes === null || nodes === undefined || nodes.length === 0) return null;
21397
+
21398
+ // A collapsed selection inside contentEditable may report the editable SPAN
21399
+ // itself as the container and the child index as the offset. The previous
21400
+ // implementation always picked the first descendant text node, which moved
21401
+ // the caret to the beginning after React re-rendered the controlled input.
21402
+ if (offset > 0) {
21403
+ var previousChild = nodes[Math.min(offset - 1, nodes.length - 1)];
21404
+ var previousTextNode = findLastTextNode(previousChild);
21405
+ if (previousTextNode !== null) {
21406
+ return {
21407
+ node: previousTextNode,
21408
+ offset: previousTextNode.nodeValue.length
21409
+ };
21410
+ }
21411
+ }
21412
+ if (offset < nodes.length) {
21413
+ var nextTextNode = findFirstTextNode(nodes[offset]);
21414
+ if (nextTextNode !== null) {
21415
+ return {
21416
+ node: nextTextNode,
21417
+ offset: 0
21418
+ };
21419
+ }
21420
+ }
21421
+ var fallbackTextNode = findFirstTextNode(container);
21422
+ if (fallbackTextNode !== null) {
21423
+ return {
21424
+ node: fallbackTextNode,
21425
+ offset: 0
21426
+ };
21427
+ }
21428
+ return null;
21429
+ }
21175
21430
  function findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, globalOffset) {
21176
21431
  var result = {
21177
21432
  offset: 0
@@ -21213,6 +21468,10 @@ function findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, globalOffset
21213
21468
  function getRangeParams(editableDiv) {
21214
21469
  // console.log ("getRangeParams")
21215
21470
  // console.log("Inner html: "+editableDiv.innerHTML)
21471
+ debugMathRichInput("getRangeParams:start", {
21472
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21473
+ selection: getSelectionSnapshot(editableDiv)
21474
+ });
21216
21475
  var isSupported = typeof window.getSelection !== "undefined";
21217
21476
  if (!isSupported) {
21218
21477
  console.error("Can't get range params, browser does not support window.getSelection");
@@ -21224,15 +21483,27 @@ function getRangeParams(editableDiv) {
21224
21483
  return null;
21225
21484
  }
21226
21485
  var range = selection.getRangeAt(0);
21486
+ debugMathRichInput("getRangeParams:range-before-normalize", {
21487
+ startContainer: describeNode(range.startContainer, editableDiv),
21488
+ startOffset: range.startOffset,
21489
+ endContainer: describeNode(range.endContainer, editableDiv),
21490
+ endOffset: range.endOffset
21491
+ });
21227
21492
 
21228
21493
  // logAllDescendants(editableDiv)
21229
- // If the range is not within a text node, move it to a text node
21230
- // This typically happens when the end of a range is at the start of a new line
21494
+ // If the range is not within a text node, move it to the text node implied
21495
+ // by the element-container offset. This typically happens when the browser
21496
+ // reports a collapsed contentEditable selection as (SPAN, childIndex).
21231
21497
  if (range.startContainer.nodeType !== 3) {
21232
- var textNode = findFirstTextNode(range.startContainer);
21233
- if (textNode !== null) {
21234
- console.warn("Adjusting start pos in getRangeParams as container was of type: " + range.startContainer.nodeName);
21235
- range.setStart(textNode, 0);
21498
+ var textPosition = getTextPositionFromElementRangeEndpoint(range.startContainer, range.startOffset);
21499
+ if (textPosition !== null) {
21500
+ debugMathRichInput("getRangeParams:normalize-start-element", {
21501
+ from: describeNode(range.startContainer, editableDiv),
21502
+ fromOffset: range.startOffset,
21503
+ to: describeNode(textPosition.node, editableDiv),
21504
+ toOffset: textPosition.offset
21505
+ });
21506
+ range.setStart(textPosition.node, textPosition.offset);
21236
21507
  } else {
21237
21508
  // Handle empty content case - return default range params
21238
21509
  // This is normal behavior after selecting all and deleting content
@@ -21248,10 +21519,15 @@ function getRangeParams(editableDiv) {
21248
21519
  }
21249
21520
  }
21250
21521
  if (range.endContainer.nodeType !== 3) {
21251
- var _textNode = findFirstTextNode(range.endContainer);
21252
- if (_textNode !== null) {
21253
- console.warn("Adjusting end pos in getRangeParams as container was of type: " + range.endContainer.nodeName);
21254
- range.setEnd(_textNode, 0);
21522
+ var _textPosition = getTextPositionFromElementRangeEndpoint(range.endContainer, range.endOffset);
21523
+ if (_textPosition !== null) {
21524
+ debugMathRichInput("getRangeParams:normalize-end-element", {
21525
+ from: describeNode(range.endContainer, editableDiv),
21526
+ fromOffset: range.endOffset,
21527
+ to: describeNode(_textPosition.node, editableDiv),
21528
+ toOffset: _textPosition.offset
21529
+ });
21530
+ range.setEnd(_textPosition.node, _textPosition.offset);
21255
21531
  } else {
21256
21532
  // Handle empty content case - return default range params
21257
21533
  // This is normal behavior after selecting all and deleting content
@@ -21297,11 +21573,20 @@ function getRangeParams(editableDiv) {
21297
21573
  startGlobalOffset: startResult.offset,
21298
21574
  endGlobalOffset: endResult.offset
21299
21575
  };
21576
+ debugMathRichInput("getRangeParams:result", {
21577
+ rangeParams: rangeParams,
21578
+ selection: getSelectionSnapshot(editableDiv)
21579
+ });
21300
21580
  return rangeParams;
21301
21581
  }
21302
21582
  function setRangeParamsFromGlobalOffsets(editableDiv, params) {
21303
21583
  var new_range = null;
21304
21584
  try {
21585
+ debugMathRichInput("setRangeParamsFromGlobalOffsets:start", {
21586
+ params: params,
21587
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21588
+ selectionBefore: getSelectionSnapshot(editableDiv)
21589
+ });
21305
21590
  if (params === null || params === undefined) throw new Error("Can't set range params: params === " + params);
21306
21591
  var startResult = findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, params.startGlobalOffset);
21307
21592
  var endResult = findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, params.endGlobalOffset);
@@ -21321,6 +21606,18 @@ function setRangeParamsFromGlobalOffsets(editableDiv, params) {
21321
21606
  var selection = window.getSelection();
21322
21607
  selection.removeAllRanges();
21323
21608
  selection.addRange(new_range);
21609
+ debugMathRichInput("setRangeParamsFromGlobalOffsets:after", {
21610
+ params: params,
21611
+ startResult: {
21612
+ node: describeNode(startResult.node, editableDiv),
21613
+ offset: startResult.offset
21614
+ },
21615
+ endResult: {
21616
+ node: describeNode(endResult.node, editableDiv),
21617
+ offset: endResult.offset
21618
+ },
21619
+ selectionAfter: getSelectionSnapshot(editableDiv)
21620
+ });
21324
21621
  } catch (error) {
21325
21622
  console.error(error);
21326
21623
  console.log("Trying to set range:");
@@ -21334,6 +21631,11 @@ function setRangeParams(editableDiv, params) {
21334
21631
 
21335
21632
  var new_range = null;
21336
21633
  try {
21634
+ debugMathRichInput("setRangeParams:start", {
21635
+ params: params,
21636
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21637
+ selectionBefore: getSelectionSnapshot(editableDiv)
21638
+ });
21337
21639
  if (params === null || params === undefined) {
21338
21640
  console.error("Can't set range params: params === " + params);
21339
21641
  return;
@@ -21393,6 +21695,7 @@ function setRangeParams(editableDiv, params) {
21393
21695
  startNode = findFirstTextNode(editableDiv);
21394
21696
  startOffset = 0;
21395
21697
  }
21698
+ if (startNode === null) return;
21396
21699
  if (!isTextNode(startNode)) {
21397
21700
  console.warn("Start node found for range start is not a text a node");
21398
21701
  console.log(editableDiv.childNodes);
@@ -21408,6 +21711,7 @@ function setRangeParams(editableDiv, params) {
21408
21711
  endNode = findFirstTextNode(editableDiv);
21409
21712
  endOffset = 0;
21410
21713
  }
21714
+ if (endNode === null) return;
21411
21715
  if (!isTextNode(endNode)) {
21412
21716
  console.warn("End node found for range start is not a text a node");
21413
21717
  console.log(editableDiv.childNodes);
@@ -21420,7 +21724,7 @@ function setRangeParams(editableDiv, params) {
21420
21724
  }
21421
21725
  new_range = document.createRange();
21422
21726
  if (startOffset > startNode.nodeValue.length) {
21423
- console.warn("endOffset > endNode.nodeValue.length when setting range: " + startOffset + " > " + startNode.nodeValue.length + " - " + stateNode.nodeType + " - " + startNode.nodeValue);
21727
+ console.warn("endOffset > endNode.nodeValue.length when setting range: " + startOffset + " > " + startNode.nodeValue.length + " - " + startNode.nodeType + " - " + startNode.nodeValue);
21424
21728
  startOffset = startNode.nodeValue.length;
21425
21729
  }
21426
21730
  new_range.setStart(startNode, startOffset);
@@ -21435,6 +21739,14 @@ function setRangeParams(editableDiv, params) {
21435
21739
  var selection = window.getSelection();
21436
21740
  selection.removeAllRanges();
21437
21741
  selection.addRange(new_range);
21742
+ debugMathRichInput("setRangeParams:after", {
21743
+ params: params,
21744
+ startNode: describeNode(startNode, editableDiv),
21745
+ startOffset: startOffset,
21746
+ endNode: describeNode(endNode, editableDiv),
21747
+ endOffset: endOffset,
21748
+ selectionAfter: getSelectionSnapshot(editableDiv)
21749
+ });
21438
21750
  } catch (error) {
21439
21751
  console.error(error);
21440
21752
  console.log("Trying to set range:");
@@ -21661,6 +21973,29 @@ var SMALL_SPACE_LENGTH = SMALL_SPACE.length;
21661
21973
  // const SMALL_SPACE_REG_EXP = new RegExp(SMALL_SPACE, "g")
21662
21974
 
21663
21975
  var MARK = getMark();
21976
+ var INLINE_STYLE_COMMANDS = {
21977
+ bold: "bold",
21978
+ italic: "italic",
21979
+ underline: "underline",
21980
+ subscript: "subscript",
21981
+ superscript: "superscript"
21982
+ };
21983
+ var INLINE_STYLE_TAGS = {
21984
+ bold: ["b", "strong"],
21985
+ italic: ["i", "em"],
21986
+ underline: ["u"],
21987
+ subscript: ["sub"],
21988
+ superscript: ["sup"]
21989
+ };
21990
+ var INLINE_STYLE_TAG_NAMES = ["b", "strong", "i", "em", "u", "sub", "sup"];
21991
+ var INLINE_STYLE_WRAPPER_TAGS = {
21992
+ bold: "strong",
21993
+ italic: "em",
21994
+ underline: "u",
21995
+ subscript: "sub",
21996
+ superscript: "sup"
21997
+ };
21998
+ var EMPTY_INLINE_FORMATTING_REG_EXP = /<(B|STRONG|I|EM|U|SUB|SUP)>(?:\s|&nbsp;|\u00a0|\u200b|\ufeff)*<\/\1>/gi;
21664
21999
 
21665
22000
  // const LATEX_MARKER_START = "<annotation encoding=\"application/x-tex\">"
21666
22001
  // const LATEX_MARKER_END = "</annotation>"
@@ -21802,12 +22137,34 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21802
22137
  return countNodeTypes(_this2.editableDiv);
21803
22138
  });
21804
22139
  _defineProperty(_this2, "applyChangesToComponent", function (value, mimeType, rangeParams, isExpertMode, selectedTab) {
22140
+ var applyOptions = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
21805
22141
  // rangeParams, isExpertMode and selectedTab can be null, in which case we will set them
21806
22142
  if (rangeParams === null) rangeParams = _this2.lastSetRangeParams;
21807
- _this2.afterComponentUpdateData = {
22143
+ debugMathRichInput("applyChangesToComponent:start", {
22144
+ value: value,
22145
+ mimeType: mimeType,
21808
22146
  rangeParams: rangeParams,
21809
- value: value
21810
- };
22147
+ propsValue: _this2.props.value,
22148
+ propsMimeType: _this2.props.mimeType,
22149
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
22150
+ selection: getSelectionSnapshot(_this2.editableDiv),
22151
+ skipControlledRender: applyOptions.skipControlledRender === true
22152
+ });
22153
+ if (applyOptions.skipControlledRender === true) {
22154
+ // Native contenteditable input has already mutated the DOM and kept the
22155
+ // browser selection in the right place. Re-rendering the same value via
22156
+ // dangerouslySetInnerHTML can replace text nodes under React 19 and move
22157
+ // the caret to the beginning. Programmatic edits still use the normal
22158
+ // render-and-restore path.
22159
+ _this2.skipNextControlledValueRender = true;
22160
+ _this2.skipNextControlledValue = value;
22161
+ _this2.afterComponentUpdateData = null;
22162
+ } else {
22163
+ _this2.afterComponentUpdateData = {
22164
+ rangeParams: rangeParams,
22165
+ value: value
22166
+ };
22167
+ }
21811
22168
  var options = _this2.props.options || DEFAULT_OPTIONS;
21812
22169
  if (isExpertMode === null) isExpertMode = options.isExpertMode;
21813
22170
  if (selectedTab === null) selectedTab = options.selectedTab;
@@ -21831,6 +22188,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21831
22188
  isExpertMode: isExpertMode,
21832
22189
  selectedTab: selectedTab
21833
22190
  });
22191
+ debugMathRichInput("applyChangesToComponent:after-onChange", {
22192
+ afterComponentUpdateData: _this2.afterComponentUpdateData,
22193
+ selection: getSelectionSnapshot(_this2.editableDiv)
22194
+ });
21834
22195
  });
21835
22196
  _defineProperty(_this2, "initialiseHistory", function () {
21836
22197
  if (_this2.initialHistoryState == null) _this2.initialHistoryState = {
@@ -21874,14 +22235,43 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21874
22235
  });
21875
22236
  _defineProperty(_this2, "_setRangeParams", function (params) {
21876
22237
  try {
22238
+ debugMathRichInput("MathRichInput:_setRangeParams:start", {
22239
+ params: params,
22240
+ selectionBefore: getSelectionSnapshot(_this2.editableDiv)
22241
+ });
21877
22242
  setRangeParams(_this2.editableDiv, params);
21878
22243
  _this2.setOldRangeParams(params);
21879
- _this2.lastSetRangeParmas = params;
22244
+ _this2.lastSetRangeParams = params;
22245
+ debugMathRichInput("MathRichInput:_setRangeParams:after", {
22246
+ params: params,
22247
+ oldRangeParams: _this2.oldRangeParams,
22248
+ selectionAfter: getSelectionSnapshot(_this2.editableDiv)
22249
+ });
21880
22250
  } catch (error) {
21881
22251
  console.error(error);
21882
22252
  // console.log(this.editableDiv.childNodes);
21883
22253
  }
21884
22254
  });
22255
+ _defineProperty(_this2, "restoreRangeAfterBrowserWork", function (params, reason) {
22256
+ if (params === null || params === undefined) return;
22257
+ var restore = function restore(delay) {
22258
+ window.setTimeout(function () {
22259
+ if (_this2.state.showEquationEditor) return;
22260
+ if (!_this2.state.hasFocus && document.activeElement !== _this2.editableDiv) {
22261
+ return;
22262
+ }
22263
+ debugMathRichInput("restoreRangeAfterBrowserWork", {
22264
+ reason: reason,
22265
+ delay: delay,
22266
+ params: params,
22267
+ selectionBefore: getSelectionSnapshot(_this2.editableDiv)
22268
+ });
22269
+ _this2._setRangeParams(params);
22270
+ }, delay);
22271
+ };
22272
+ restore(0);
22273
+ restore(50);
22274
+ });
21885
22275
  _defineProperty(_this2, "handleKeyDownBackspace", function (e) {
21886
22276
  var rangeParams = _this2._getRangeParams();
21887
22277
  var start_node_index = rangeParams.startNodeIndex;
@@ -22009,7 +22399,11 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22009
22399
  _defineProperty(_this2, "isWordModifier", function (e) {
22010
22400
  var _this2$getPlatformMod = _this2.getPlatformModifiers(),
22011
22401
  isMac = _this2$getPlatformMod.isMac;
22012
- return isMac ? e.altKey : e.ctrlKey;
22402
+ // Borough production is pinned to math-rich-input 0.1.48, where Option +
22403
+ // Arrow did not get custom word navigation. Keep Mac Option + Arrow as
22404
+ // normal character movement to preserve the editor's existing UX; Command +
22405
+ // Arrow is handled by isLineModifier below.
22406
+ return isMac ? false : e.ctrlKey;
22013
22407
  });
22014
22408
  // Check if line navigation modifier is pressed
22015
22409
  _defineProperty(_this2, "isLineModifier", function (e) {
@@ -22466,6 +22860,17 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22466
22860
  });
22467
22861
  _defineProperty(_this2, "handleKeyDown", function (e) {
22468
22862
  try {
22863
+ debugMathRichInput("handleKeyDown:start", {
22864
+ key: e.key,
22865
+ altKey: e.altKey,
22866
+ metaKey: e.metaKey,
22867
+ ctrlKey: e.ctrlKey,
22868
+ shiftKey: e.shiftKey,
22869
+ hasFocus: _this2.state.hasFocus,
22870
+ propsValue: _this2.props.value,
22871
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
22872
+ selection: getSelectionSnapshot(_this2.editableDiv)
22873
+ });
22469
22874
  // Lets set up or update the undo redo history
22470
22875
  _this2.initialiseHistory();
22471
22876
  if ((e.key === "z" || e.key === "Z") && (e.ctrlKey || e.metaKey)) {
@@ -22522,7 +22927,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22522
22927
  }
22523
22928
  }
22524
22929
  }
22525
- if (e.altKey && e.key === "Alt") {
22930
+ var _this2$getPlatformMod3 = _this2.getPlatformModifiers(),
22931
+ isMac = _this2$getPlatformMod3.isMac;
22932
+ if (!isMac && e.altKey && e.key === "Alt") {
22526
22933
  var key = _this2.fetchAccentLetter();
22527
22934
  if (key === "a" || key === "e" || key === "i" || key === "o" || key === "u" || key === "c" || key === "l" || key === "n" || key === "s" || key === "?" || key === "!") {
22528
22935
  // console.log("showing/hiding accent bar");
@@ -22539,9 +22946,11 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22539
22946
  return;
22540
22947
  }
22541
22948
  _this2.keyPressed = e.key; // store for use in methods
22542
- _this2.setState({
22543
- keyPressed: e.key
22544
- });
22949
+ if (_this2.state.showAccentBar) {
22950
+ _this2.setState({
22951
+ keyPressed: e.key
22952
+ });
22953
+ }
22545
22954
 
22546
22955
  // Hide the accent bar if a key is typed except when it is an automatic key repeat press
22547
22956
  // If it is automatic key repeat, then keyPressStartTime will be greater than 0
@@ -22561,10 +22970,16 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22561
22970
  }
22562
22971
  if (e.key === "ArrowLeft") {
22563
22972
  _this2.handleKeyDownArrowLeft(e);
22973
+ debugMathRichInput("handleKeyDown:after-arrow-left", {
22974
+ selection: getSelectionSnapshot(_this2.editableDiv)
22975
+ });
22564
22976
  return;
22565
22977
  }
22566
22978
  if (e.key === "ArrowRight") {
22567
22979
  _this2.handleKeyDownArrowRight(e);
22980
+ debugMathRichInput("handleKeyDown:after-arrow-right", {
22981
+ selection: getSelectionSnapshot(_this2.editableDiv)
22982
+ });
22568
22983
  return;
22569
22984
  }
22570
22985
  if (e.key === "ArrowUp") {
@@ -22657,6 +23072,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22657
23072
  });
22658
23073
  _defineProperty(_this2, "handleKeyUp", function (e) {
22659
23074
  try {
23075
+ debugMathRichInput("handleKeyUp:start", {
23076
+ key: e.key,
23077
+ altKey: e.altKey,
23078
+ metaKey: e.metaKey,
23079
+ ctrlKey: e.ctrlKey,
23080
+ shiftKey: e.shiftKey,
23081
+ selection: getSelectionSnapshot(_this2.editableDiv)
23082
+ });
22660
23083
  _this2.keyPressed = e.key;
22661
23084
  _this2.keyPressStartTime = -1;
22662
23085
 
@@ -22732,8 +23155,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22732
23155
  try {
22733
23156
  // console.log("handleInput")
22734
23157
  if (_this2.isComposing) return;
23158
+ debugMathRichInput("handleInput:start", {
23159
+ inputType: event && event.nativeEvent ? event.nativeEvent.inputType : null,
23160
+ data: event && event.nativeEvent ? event.nativeEvent.data : null,
23161
+ propsValue: _this2.props.value,
23162
+ propsMimeType: _this2.props.mimeType,
23163
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23164
+ selection: getSelectionSnapshot(_this2.editableDiv)
23165
+ });
23166
+ _this2.cleanupEmptyInlineFormattingElements();
22735
23167
  var raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22736
23168
  var params = _this2._getRangeParams();
23169
+ debugMathRichInput("handleInput:after-range", {
23170
+ raw_text: raw_text,
23171
+ params: params,
23172
+ selection: getSelectionSnapshot(_this2.editableDiv)
23173
+ });
22737
23174
  if (params === null || params === undefined) {
22738
23175
  // console.log(
22739
23176
  // "handleInput: Empty content detected, using default range params"
@@ -22752,7 +23189,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22752
23189
  var _mimeType = _this2.getMimeType(_nodeCounts.html > 0, _nodeCounts.math > 0);
22753
23190
  var _raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22754
23191
  _raw_text = removeEncodingIfPlainText(_raw_text, _mimeType, _this2.enableHtml());
22755
- _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23192
+ _raw_text = _this2.stripEmptyInlineFormattingTags(_raw_text);
23193
+ _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23194
+ skipControlledRender: true
23195
+ });
22756
23196
  return;
22757
23197
  } else if (params.startNodeIndex === null || params.startNodeIndex === undefined) {
22758
23198
  // console.log(
@@ -22773,6 +23213,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22773
23213
  var hasMath = hasMathContent || nodeCounts.math > 0;
22774
23214
  var mimeType = _this2.getMimeType(hasHtml, hasMath);
22775
23215
  raw_text = removeEncodingIfPlainText(raw_text, mimeType, _this2.enableHtml());
23216
+ raw_text = _this2.stripEmptyInlineFormattingTags(raw_text);
22776
23217
 
22777
23218
  // Use valid params or fallback to defaults
22778
23219
  var validParams = params || {
@@ -22783,7 +23224,16 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22783
23224
  startGlobalOffset: 0,
22784
23225
  endGlobalOffset: 0
22785
23226
  };
22786
- _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23227
+ _this2.setOldRangeParams(validParams);
23228
+ _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23229
+ skipControlledRender: true
23230
+ });
23231
+ debugMathRichInput("handleInput:after-apply", {
23232
+ raw_text: raw_text,
23233
+ mimeType: mimeType,
23234
+ validParams: validParams,
23235
+ selection: getSelectionSnapshot(_this2.editableDiv)
23236
+ });
22787
23237
  } catch (error) {
22788
23238
  console.error(error);
22789
23239
  }
@@ -22828,44 +23278,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22828
23278
  });
22829
23279
  return;
22830
23280
  }
22831
- var node = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
22832
- var bold = false;
22833
- var italic = false;
22834
- var underline = false;
22835
- var superscript = false;
22836
- var subscript = false;
22837
- do {
22838
- if (node.nodeType === 1) {
22839
- var nodeName = node.nodeName.toLowerCase();
22840
- if (nodeName === "b" || nodeName === "strong") bold = true;else if (nodeName === "i" || nodeName === "em") italic = true;else if (nodeName === "u") underline = true;else if (nodeName === "sub") subscript = true;else if (nodeName === "sup") superscript = true;
22841
- } else if (node.classList !== null && node.classList !== undefined && node.classList.contains("MathRichInput")) break;
22842
- node = node.parentNode;
22843
- } while (node !== null);
22844
- var activeButtons = {
22845
- bold: bold,
22846
- italic: italic,
22847
- underline: underline,
22848
- subscript: subscript,
22849
- superscript: superscript
22850
- };
22851
- // console.log(activeButtons)
22852
-
22853
- var update = true;
22854
- if (_this2.lastSetActiveButtons !== null) {
22855
- update = false;
22856
- for (var style in activeButtons) {
22857
- if (activeButtons[style] !== _this2.lastSetActiveButtons[style]) {
22858
- update = true;
22859
- break;
22860
- }
22861
- }
22862
- }
22863
- if (update) {
22864
- _this2.lastSetActiveButtons = activeButtons;
22865
- _this2.setState({
22866
- activeButtons: activeButtons
22867
- });
22868
- }
23281
+ var activeButtons = _this2.getActiveButtonsFromSelection(rangeParams);
23282
+ _this2.setActiveButtonsIfChanged(activeButtons);
22869
23283
  } catch (error) {
22870
23284
  console.error(error);
22871
23285
  }
@@ -23100,6 +23514,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23100
23514
  var node = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
23101
23515
  try {
23102
23516
  var rangeParams = _this2._getRangeParams();
23517
+ debugMathRichInput("showEquationEditor:start", {
23518
+ moveCursorOnInsert: moveCursorOnInsert,
23519
+ nodeProvided: Boolean(node),
23520
+ rangeParams: rangeParams,
23521
+ oldRangeParams: _this2.oldRangeParams,
23522
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23523
+ selection: getSelectionSnapshot(_this2.editableDiv)
23524
+ });
23103
23525
  if (rangeParams.startNodeIndex < 0) {
23104
23526
  console.error("Could not find position of cursor in node list");
23105
23527
  _this2.setOldRangeParams({
@@ -23142,6 +23564,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23142
23564
  _this2.editingExistingEquation = false;
23143
23565
  _this2.editingNode = null;
23144
23566
  }
23567
+ _this2.setOldRangeParams(rangeParams);
23145
23568
  _this2.markedRawText = elementToMarkedRawText(_this2.editableDiv, node, offset, _this2.enableHtml());
23146
23569
  // console.log("Marked text:"+this.markedRawText)
23147
23570
  _this2.moveCursorOnInsert = moveCursorOnInsert;
@@ -23149,11 +23572,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23149
23572
  showEquationEditor: true,
23150
23573
  equationEditorLatex: latex
23151
23574
  });
23575
+ debugMathRichInput("showEquationEditor:after-setState", {
23576
+ rangeParams: rangeParams,
23577
+ markedRawText: _this2.markedRawText,
23578
+ latex: latex,
23579
+ selection: getSelectionSnapshot(_this2.editableDiv)
23580
+ });
23152
23581
  } catch (error) {
23153
23582
  console.error(error);
23154
23583
  }
23155
23584
  });
23156
23585
  _defineProperty(_this2, "handleMouseDown", function (event) {
23586
+ _this2.isMouseDownInEditable = true;
23587
+ window.setTimeout(function () {
23588
+ _this2.isMouseDownInEditable = false;
23589
+ }, 0);
23157
23590
  _this2.showEquationEditorOnMouseUp = false;
23158
23591
  var node = null;
23159
23592
  try {
@@ -23189,6 +23622,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23189
23622
  // Clear selection anchor on mouse click
23190
23623
  _this2.selectionAnchor = null;
23191
23624
  var params = _this2._getRangeParams();
23625
+ _this2.setOldRangeParams(params);
23192
23626
  var startNode = _this2._findNodeWithIndex(params.startNodeIndex);
23193
23627
  var endNode = _this2._findNodeWithIndex(params.endNodeIndex);
23194
23628
  if (startNode === null || endNode === null) {
@@ -23244,7 +23678,15 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23244
23678
  _defineProperty(_this2, "handleEquationEditorInsert", function (latex) {
23245
23679
  try {
23246
23680
  // console.log("Inserting latex: "+latex)
23247
-
23681
+ debugMathRichInput("handleEquationEditorInsert:start", {
23682
+ latex: latex,
23683
+ markedRawText: _this2.markedRawText,
23684
+ oldRangeParams: _this2.getOldRangeParams(),
23685
+ moveCursorOnInsert: _this2.moveCursorOnInsert,
23686
+ propsValue: _this2.props.value,
23687
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23688
+ selection: getSelectionSnapshot(_this2.editableDiv)
23689
+ });
23248
23690
  var start_pos = _this2.markedRawText.indexOf(MARK);
23249
23691
  var end_pos = _this2.markedRawText.indexOf(MARK, start_pos + MARK.length);
23250
23692
  var oldRangeParams = _this2.getOldRangeParams();
@@ -23286,6 +23728,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23286
23728
  _this2.applyChangesToComponent(_new_raw_text, _mimeType2, _new_rangeParams7, _this2.props.useExpertMode, _this2.props.selectedTab);
23287
23729
  _this2.setState({
23288
23730
  showEquationEditor: false
23731
+ }, function () {
23732
+ _this2.editableDiv.focus();
23733
+ _this2._setRangeParams(_new_rangeParams7);
23734
+ _this2.restoreRangeAfterBrowserWork(_new_rangeParams7, "equation-empty");
23289
23735
  });
23290
23736
  return;
23291
23737
  }
@@ -23304,28 +23750,48 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23304
23750
  endOffset: SMALL_SPACE_LENGTH
23305
23751
  };
23306
23752
  } else {
23307
- // Position cursor after the inserted math element using globalOffset
23308
- // Find the position right after the last math tag in the content
23309
- var mathTagMatches = _toConsumableArray(new_raw_text.matchAll(/<math>[^<]*<\/math>/gi));
23310
- if (mathTagMatches.length > 0) {
23311
- var lastMatch = mathTagMatches[mathTagMatches.length - 1];
23312
- var afterMathOffset = lastMatch.index + lastMatch[0].length;
23753
+ // In this editor a rendered equation counts as one logical character
23754
+ // in global-offset space. Put the caret immediately after that math
23755
+ // character instead of guessing rendered child-node indexes.
23756
+ var _oldRangeParams = _this2.getOldRangeParams();
23757
+ if (_oldRangeParams !== null && _oldRangeParams.startGlobalOffset !== null && _oldRangeParams.startGlobalOffset !== undefined) {
23758
+ var afterInsertedMathOffset = _oldRangeParams.startGlobalOffset + 1;
23313
23759
  new_rangeParams = {
23314
- startGlobalOffset: afterMathOffset,
23315
- endGlobalOffset: afterMathOffset
23760
+ startGlobalOffset: afterInsertedMathOffset,
23761
+ endGlobalOffset: afterInsertedMathOffset
23762
+ };
23763
+ } else if (_oldRangeParams !== null && _oldRangeParams.startNodeIndex !== null && _oldRangeParams.startNodeIndex !== undefined) {
23764
+ new_rangeParams = {
23765
+ startNodeIndex: _oldRangeParams.startNodeIndex + 2,
23766
+ startOffset: SMALL_SPACE_LENGTH,
23767
+ endNodeIndex: _oldRangeParams.startNodeIndex + 2,
23768
+ endOffset: SMALL_SPACE_LENGTH
23316
23769
  };
23317
23770
  }
23318
- // If no math tags found, keep old range params
23771
+ // If old range params are unavailable, keep the current fallback.
23319
23772
  }
23320
23773
  }
23321
23774
  // console.log("New cursor pos: "+new_rangeParams.startNodeIndex+"->"+new_rangeParams.startOffset)
23322
23775
  _this2.setOldRangeParams(new_rangeParams);
23776
+ debugMathRichInput("handleEquationEditorInsert:before-apply", {
23777
+ new_raw_text: new_raw_text,
23778
+ new_rangeParams: new_rangeParams,
23779
+ selection: getSelectionSnapshot(_this2.editableDiv)
23780
+ });
23323
23781
  var nodeCounts = _this2._countNodeTypes();
23324
23782
  var mimeType = _this2.getMimeType(nodeCounts.html > 0, true);
23325
23783
  new_raw_text = removeEncodingIfPlainText(new_raw_text, mimeType, _this2.enableHtml());
23326
23784
  _this2.applyChangesToComponent(new_raw_text, mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23327
23785
  _this2.setState({
23328
23786
  showEquationEditor: false
23787
+ }, function () {
23788
+ _this2.editableDiv.focus();
23789
+ _this2._setRangeParams(new_rangeParams);
23790
+ _this2.restoreRangeAfterBrowserWork(new_rangeParams, "equation-insert");
23791
+ debugMathRichInput("handleEquationEditorInsert:after-close-restore", {
23792
+ new_rangeParams: new_rangeParams,
23793
+ selection: getSelectionSnapshot(_this2.editableDiv)
23794
+ });
23329
23795
  });
23330
23796
  } catch (error) {
23331
23797
  console.error(error);
@@ -23342,13 +23808,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23342
23808
  _defineProperty(_this2, "styleText", function (event, style) {
23343
23809
  try {
23344
23810
  style = style.toLowerCase();
23811
+ var command = _this2.getStyleCommand(style);
23812
+ var activeBefore = _this2.getActiveButtonsFromSelection();
23813
+ var selection = document.getSelection();
23814
+ var isCollapsed = selection === null || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed;
23345
23815
  var success = false;
23816
+ var insertedTypingStylePlaceholder = false;
23346
23817
 
23347
23818
  // This first part is a workaround whereby executing subscript or superscript
23348
23819
  // within an existing subscript or superscript makes the text even smaller instead
23349
23820
  // of toggling it off. Thus we manually toggle it off with some jigerry pokery!
23350
- if (style === "subscript" && _this2.state.activeButtons.subscript || style === "superscript" && _this2.state.activeButtons.superscript) {
23351
- var selection = document.getSelection();
23821
+ if (isCollapsed && activeBefore[style] && _this2.moveTrailingWhitespaceOutsideInlineStyle(style, command)) {
23822
+ success = true;
23823
+ } else if (isCollapsed && !activeBefore[style] && selection && selection.rangeCount > 0) {
23824
+ success = _this2.insertInlineStylePlaceholder(style);
23825
+ insertedTypingStylePlaceholder = success;
23826
+ } else if (style === "subscript" && activeBefore.subscript || style === "superscript" && activeBefore.superscript) {
23352
23827
  var range = selection.getRangeAt(0);
23353
23828
  if (range.endContainer === range.startContainer && range.endOffset === range.startOffset) {
23354
23829
  success = document.execCommand("insertText", false, " ");
@@ -23357,78 +23832,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23357
23832
  selection.addRange(range);
23358
23833
  }
23359
23834
  success = document.execCommand("removeFormat", false, null);
23360
- if (_this2.state.activeButtons.bold) success = document.execCommand("bold", false, null);
23361
- if (_this2.state.activeButtons.italic) success = document.execCommand("italic", false, null);
23362
- if (_this2.state.activeButtons.underline) success = document.execCommand("underline", false, null);
23835
+ if (activeBefore.bold) success = document.execCommand("bold", false, null);
23836
+ if (activeBefore.italic) success = document.execCommand("italic", false, null);
23837
+ if (activeBefore.underline) success = document.execCommand("underline", false, null);
23363
23838
  } else {
23364
- var _selection = document.getSelection();
23365
- if (_selection && _selection.rangeCount > 0) {
23366
- var _range = _selection.getRangeAt(0);
23367
- if (_range.collapsed) {
23368
- // For cursor position (no selection), use insertHTML with styled wrapper
23369
- // This creates a formatted element that will maintain styling for future typing
23370
- var isToggleOff = _this2.state.activeButtons[style];
23371
- if (!isToggleOff) {
23372
- // Turning style ON - insert a styled wrapper element
23373
- var wrapperTag;
23374
- switch (style) {
23375
- case "bold":
23376
- wrapperTag = "strong";
23377
- break;
23378
- case "italic":
23379
- wrapperTag = "em";
23380
- break;
23381
- case "underline":
23382
- wrapperTag = "u";
23383
- break;
23384
- case "subscript":
23385
- wrapperTag = "sub";
23386
- break;
23387
- case "superscript":
23388
- wrapperTag = "sup";
23389
- break;
23390
- default:
23391
- wrapperTag = "span";
23392
- break;
23393
- }
23394
-
23395
- // Insert an invisible character wrapped in the styled element
23396
- // This provides a "landing zone" for future typing
23397
- var styledElement = "<".concat(wrapperTag, ">\u200B</").concat(wrapperTag, ">");
23398
- success = document.execCommand("insertHTML", false, styledElement);
23399
-
23400
- // Move cursor inside the styled element
23401
- setTimeout(function () {
23402
- var newSelection = document.getSelection();
23403
- if (newSelection && newSelection.rangeCount > 0) {
23404
- var newRange = newSelection.getRangeAt(0);
23405
- var styledNode = newRange.startContainer.parentElement;
23406
- if (styledNode && styledNode.tagName.toLowerCase() === wrapperTag) {
23407
- newRange.setStart(styledNode, 0);
23408
- newRange.setEnd(styledNode, 0);
23409
- newSelection.removeAllRanges();
23410
- newSelection.addRange(newRange);
23411
- }
23412
- }
23413
- }, 0);
23414
- } else {
23415
- // Turning style OFF - just apply execCommand normally
23416
- success = document.execCommand(style, false, null);
23417
- }
23418
- } else {
23419
- // For text selection, use regular execCommand
23420
- success = document.execCommand(style, false, null);
23421
- }
23839
+ if (selection && selection.rangeCount > 0) {
23840
+ success = document.execCommand(command, false, null);
23422
23841
  }
23423
23842
  }
23424
- var buttonState = _this2.state.activeButtons[style];
23425
- if (buttonState === null || buttonState === undefined) return success;
23426
- buttonState = !buttonState;
23427
- var new_activeButtons = _objectSpread2({}, _this2.state.activeButtons);
23428
- new_activeButtons[style] = buttonState;
23429
- _this2.setState({
23430
- activeButtons: new_activeButtons
23431
- });
23843
+ var rangeParams = _this2._getRangeParams();
23844
+ var removedEmptyTags = !insertedTypingStylePlaceholder && _this2.cleanupEmptyInlineFormattingElements();
23845
+ if (removedEmptyTags && rangeParams !== null && rangeParams !== undefined) {
23846
+ _this2._setRangeParams(rangeParams);
23847
+ _this2.applyCurrentEditableDomToComponent(rangeParams);
23848
+ }
23849
+ _this2.setActiveButtonsIfChanged(_this2.getActiveButtonsFromSelection(rangeParams));
23432
23850
  return success;
23433
23851
  } catch (error) {
23434
23852
  console.error(error);
@@ -23436,8 +23854,15 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23436
23854
  });
23437
23855
  _defineProperty(_this2, "handleToolbarButtonClick", function (event, buttonName) {
23438
23856
  try {
23857
+ debugMathRichInput("handleToolbarButtonClick:start", {
23858
+ buttonName: buttonName,
23859
+ eventType: event ? event.type : null,
23860
+ selection: getSelectionSnapshot(_this2.editableDiv),
23861
+ oldRangeParams: _this2.oldRangeParams
23862
+ });
23439
23863
  if (buttonName === "Equation") {
23440
23864
  _this2.showEquationEditor(true);
23865
+ return;
23441
23866
  }
23442
23867
  var style = buttonName;
23443
23868
  var success = _this2.styleText(event, style);
@@ -23749,13 +24174,36 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23749
24174
  });
23750
24175
  _defineProperty(_this2, "handleFocus", function (event) {
23751
24176
  var onFocus = _this2.props.onFocus;
24177
+ var currentRangeParams = null;
24178
+ try {
24179
+ currentRangeParams = _this2._getRangeParams();
24180
+ } catch (error) {
24181
+ currentRangeParams = null;
24182
+ }
24183
+ var savedRangeParams = _this2.getOldRangeParams();
24184
+ var shouldRestoreSavedRange = !_this2.isMouseDownInEditable && _this2.props.value !== "" && _this2.rangeIsAtStart(currentRangeParams) && _this2.rangeHasNonZeroPosition(savedRangeParams);
24185
+ debugMathRichInput("handleFocus:start", {
24186
+ propsValue: _this2.props.value,
24187
+ oldRangeParams: _this2.oldRangeParams,
24188
+ currentRangeParams: currentRangeParams,
24189
+ shouldRestoreSavedRange: shouldRestoreSavedRange,
24190
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
24191
+ selection: getSelectionSnapshot(_this2.editableDiv)
24192
+ });
23752
24193
  if (onFocus != null) {
23753
24194
  onFocus();
23754
24195
  }
23755
24196
  try {
23756
- _this2._setRangeParams(_this2.getOldRangeParams());
23757
24197
  _this2.setState({
23758
24198
  hasFocus: true
24199
+ }, function () {
24200
+ if (shouldRestoreSavedRange) {
24201
+ _this2._setRangeParams(savedRangeParams);
24202
+ _this2.restoreRangeAfterBrowserWork(savedRangeParams, "focus-restore");
24203
+ }
24204
+ debugMathRichInput("handleFocus:after-setState", {
24205
+ selection: getSelectionSnapshot(_this2.editableDiv)
24206
+ });
23759
24207
  });
23760
24208
  } catch (error) {
23761
24209
  console.error(error);
@@ -23763,6 +24211,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23763
24211
  });
23764
24212
  _defineProperty(_this2, "handleBlur", function (event) {
23765
24213
  var onBlur = _this2.props.onBlur;
24214
+ var currentRangeParams = null;
24215
+ try {
24216
+ currentRangeParams = _this2._getRangeParams();
24217
+ if (_this2.rangeHasNonZeroPosition(currentRangeParams)) {
24218
+ _this2.setOldRangeParams(currentRangeParams);
24219
+ }
24220
+ } catch (error) {
24221
+ currentRangeParams = null;
24222
+ }
24223
+ debugMathRichInput("handleBlur:start", {
24224
+ propsValue: _this2.props.value,
24225
+ oldRangeParams: _this2.oldRangeParams,
24226
+ currentRangeParams: currentRangeParams,
24227
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
24228
+ selection: getSelectionSnapshot(_this2.editableDiv)
24229
+ });
23766
24230
  if (onBlur != null) {
23767
24231
  onBlur();
23768
24232
  }
@@ -23772,6 +24236,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23772
24236
  hasFocus: false,
23773
24237
  showAccentBar: false
23774
24238
  });
24239
+ debugMathRichInput("handleBlur:after-setState", {
24240
+ selection: getSelectionSnapshot(_this2.editableDiv)
24241
+ });
23775
24242
  } catch (error) {
23776
24243
  console.error(error);
23777
24244
  }
@@ -23903,18 +24370,20 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23903
24370
 
23904
24371
  // Elements that need to be set before the equation editor is shown
23905
24372
  _this2.markedRawText = null;
23906
- _this2.oldRangeParams = {
23907
- startNodeIndex: 0,
23908
- startOffset: 0,
23909
- endNodeIndex: 0,
23910
- endOffset: 0
23911
- };
24373
+ // No selection has been captured yet. Do not default this to the start of
24374
+ // the editor: React 19 focus/click ordering makes handleFocus restore that
24375
+ // synthetic 0,0 range and moves the caret to the beginning on every click.
24376
+ _this2.oldRangeParams = null;
23912
24377
  _this2.moveCursorOnInsert = false;
23913
24378
 
23914
24379
  // Elements defining the current state of range
23915
24380
  _this2.afterComponentUpdateData = null; // Set this to update the selection after rendering
23916
- _this2.lastSetRangeParmas = null; // This is set automatically each time the range params are set with setRangeParams
23917
-
24381
+ _this2.lastSetRangeParams = null; // This is set automatically each time the range params are set with setRangeParams
24382
+ _this2.skipNextControlledValueRender = false;
24383
+ _this2.skipNextControlledValue = null;
24384
+ _this2.isMouseDownInEditable = false;
24385
+ _this2.lastRenderedEditableHtml = null;
24386
+ _this2.preserveNativeDomRangeParams = null;
23918
24387
  _this2.lastSetActiveButtons = null;
23919
24388
  _this2.onChangeCallback = _this2.props.onChange;
23920
24389
 
@@ -24004,12 +24473,403 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24004
24473
  var params = _objectSpread2({}, this.oldRangeParams);
24005
24474
  return params;
24006
24475
  }
24476
+ }, {
24477
+ key: "rangeIsAtStart",
24478
+ value: function rangeIsAtStart(params) {
24479
+ if (params === null || params === undefined) return false;
24480
+ if (params.startGlobalOffset !== null && params.startGlobalOffset !== undefined && params.endGlobalOffset !== null && params.endGlobalOffset !== undefined) {
24481
+ return params.startGlobalOffset === 0 && params.endGlobalOffset === 0;
24482
+ }
24483
+ return params.startNodeIndex === 0 && params.endNodeIndex === 0 && params.startOffset === 0 && params.endOffset === 0;
24484
+ }
24485
+ }, {
24486
+ key: "rangeHasNonZeroPosition",
24487
+ value: function rangeHasNonZeroPosition(params) {
24488
+ if (params === null || params === undefined) return false;
24489
+ if (params.startGlobalOffset !== null && params.startGlobalOffset !== undefined) {
24490
+ return params.startGlobalOffset > 0;
24491
+ }
24492
+ return params.startNodeIndex > 0 || params.endNodeIndex > 0 || params.startOffset > 0 || params.endOffset > 0;
24493
+ }
24494
+ }, {
24495
+ key: "getCurrentRawTextForProps",
24496
+ value: function getCurrentRawTextForProps(nextProps) {
24497
+ if (!this.editableDiv) return null;
24498
+ try {
24499
+ var rawText = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24500
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24501
+ return removeEncodingIfPlainText(rawText, nextProps.mimeType, this.enableHtml());
24502
+ } catch (error) {
24503
+ debugMathRichInput("getCurrentRawTextForProps:error", {
24504
+ error: error,
24505
+ selection: getSelectionSnapshot(this.editableDiv)
24506
+ });
24507
+ return null;
24508
+ }
24509
+ }
24510
+ }, {
24511
+ key: "shouldSkipRedundantFocusedRender",
24512
+ value: function shouldSkipRedundantFocusedRender(nextProps, nextState) {
24513
+ if (!this.editableDiv) return false;
24514
+ if (nextState !== this.state) return false;
24515
+ if (nextState.hasFocus !== true) return false;
24516
+ if (this.afterComponentUpdateData !== null && this.afterComponentUpdateData !== undefined) {
24517
+ return false;
24518
+ }
24519
+ var currentRawText = this.getCurrentRawTextForProps(nextProps);
24520
+ var shouldSkip = currentRawText === nextProps.value;
24521
+ debugMathRichInput("shouldComponentUpdate:redundant-focused-check", {
24522
+ shouldSkip: shouldSkip,
24523
+ currentRawText: currentRawText,
24524
+ nextValue: nextProps.value,
24525
+ currentPropsValue: this.props.value,
24526
+ nextMimeType: nextProps.mimeType,
24527
+ currentMimeType: this.props.mimeType,
24528
+ selection: getSelectionSnapshot(this.editableDiv)
24529
+ });
24530
+ if (shouldSkip) {
24531
+ var currentRangeParams = this.getRangeParamsPreservingInlinePlaceholder();
24532
+ if (this.rangeHasNonZeroPosition(currentRangeParams)) {
24533
+ this.setOldRangeParams(currentRangeParams);
24534
+ }
24535
+ }
24536
+ return shouldSkip;
24537
+ }
24538
+ }, {
24539
+ key: "isSelectionInsideInlinePlaceholder",
24540
+ value: function isSelectionInsideInlinePlaceholder() {
24541
+ if (typeof document === "undefined") return false;
24542
+ var selection = document.getSelection ? document.getSelection() : null;
24543
+ if (!selection || selection.rangeCount === 0) return false;
24544
+ var range = selection.getRangeAt(0);
24545
+ if (!range.collapsed) return false;
24546
+ var node = range.startContainer;
24547
+ if (!node || node.nodeType !== 3) return false;
24548
+ if (node.nodeValue !== SMALL_SPACE) return false;
24549
+ if (range.startOffset !== SMALL_SPACE_LENGTH) return false;
24550
+ var parent = node.parentNode;
24551
+ if (!parent || parent.nodeType !== 1) return false;
24552
+ var tagName = parent.nodeName.toLowerCase();
24553
+ if (!INLINE_STYLE_TAG_NAMES.includes(tagName)) return false;
24554
+ return parent.textContent === SMALL_SPACE;
24555
+ }
24556
+ }, {
24557
+ key: "getRangeParamsPreservingInlinePlaceholder",
24558
+ value: function getRangeParamsPreservingInlinePlaceholder() {
24559
+ var selectionInsideInlinePlaceholder = this.isSelectionInsideInlinePlaceholder();
24560
+ var rangeParams = this._getRangeParams();
24561
+ if (!selectionInsideInlinePlaceholder || !rangeParams) {
24562
+ return rangeParams;
24563
+ }
24564
+
24565
+ // Global offsets ignore the zero-width placeholder; node index preserves it.
24566
+ return _objectSpread2(_objectSpread2({}, rangeParams), {}, {
24567
+ startGlobalOffset: null,
24568
+ endGlobalOffset: null
24569
+ });
24570
+ }
24571
+ }, {
24572
+ key: "getEditableHtmlForRender",
24573
+ value: function getEditableHtmlForRender(html) {
24574
+ var currentRawText = this.editableDiv ? this.getCurrentRawTextForProps(this.props) : null;
24575
+ var liveEditableHtml = this.editableDiv ? this.editableDiv.innerHTML : null;
24576
+ var canPreserveNativeDom = this.state.hasFocus === true && this.editableDiv !== null && this.editableDiv !== undefined && (this.afterComponentUpdateData === null || this.afterComponentUpdateData === undefined) && this.lastRenderedEditableHtml !== null && currentRawText === this.props.value;
24577
+ var preserveRangeParams = canPreserveNativeDom ? this.getRangeParamsPreservingInlinePlaceholder() : null;
24578
+ debugMathRichInput("render:editable-html", {
24579
+ canPreserveNativeDom: canPreserveNativeDom,
24580
+ selectionInsideInlinePlaceholder: this.isSelectionInsideInlinePlaceholder(),
24581
+ propsValue: this.props.value,
24582
+ generatedHtml: html,
24583
+ renderedHtml: canPreserveNativeDom ? liveEditableHtml : html,
24584
+ liveEditableHtml: liveEditableHtml,
24585
+ lastRenderedEditableHtml: this.lastRenderedEditableHtml,
24586
+ currentRawText: currentRawText,
24587
+ preserveRangeParams: preserveRangeParams,
24588
+ afterComponentUpdateData: this.afterComponentUpdateData,
24589
+ hasFocus: this.state.hasFocus,
24590
+ selection: getSelectionSnapshot(this.editableDiv)
24591
+ });
24592
+ if (canPreserveNativeDom) {
24593
+ this.lastRenderedEditableHtml = liveEditableHtml;
24594
+ this.preserveNativeDomRangeParams = preserveRangeParams;
24595
+ return liveEditableHtml;
24596
+ }
24597
+ this.lastRenderedEditableHtml = html;
24598
+ this.preserveNativeDomRangeParams = null;
24599
+ return html;
24600
+ }
24601
+ }, {
24602
+ key: "stripEmptyInlineFormattingTags",
24603
+ value: function stripEmptyInlineFormattingTags(rawText) {
24604
+ if (rawText === null || rawText === undefined) return rawText;
24605
+ var cleaned = rawText;
24606
+ var previous = null;
24607
+ while (previous !== cleaned) {
24608
+ previous = cleaned;
24609
+ cleaned = cleaned.replace(EMPTY_INLINE_FORMATTING_REG_EXP, "");
24610
+ }
24611
+ return cleaned;
24612
+ }
24613
+ }, {
24614
+ key: "cleanupEmptyInlineFormattingElements",
24615
+ value: function cleanupEmptyInlineFormattingElements() {
24616
+ if (!this.editableDiv) return false;
24617
+ var removed = false;
24618
+ this.editableDiv.querySelectorAll("b,strong,i,em,u,sub,sup").forEach(function (node) {
24619
+ var text = node.textContent || "";
24620
+ var hasMeaningfulText = text.replace(/[\s\u00a0\u200b\ufeff]/g, "") !== "";
24621
+ var hasProtectedContent = node.querySelector("br,img,math,.katex") !== null;
24622
+ if (!hasMeaningfulText && !hasProtectedContent) {
24623
+ node.remove();
24624
+ removed = true;
24625
+ }
24626
+ });
24627
+ if (removed) {
24628
+ this.editableDiv.normalize();
24629
+ }
24630
+ return removed;
24631
+ }
24632
+ }, {
24633
+ key: "getStyleCommand",
24634
+ value: function getStyleCommand(style) {
24635
+ return INLINE_STYLE_COMMANDS[style] || style;
24636
+ }
24637
+ }, {
24638
+ key: "insertInlineStylePlaceholder",
24639
+ value: function insertInlineStylePlaceholder(style) {
24640
+ var wrapperTag = INLINE_STYLE_WRAPPER_TAGS[style] || "span";
24641
+ var selection = document.getSelection();
24642
+ if (!selection || selection.rangeCount === 0) return false;
24643
+ var range = selection.getRangeAt(0);
24644
+ if (!range.collapsed) return false;
24645
+ range.deleteContents();
24646
+ var wrapper = document.createElement(wrapperTag);
24647
+ var textNode = document.createTextNode(SMALL_SPACE);
24648
+ wrapper.appendChild(textNode);
24649
+ range.insertNode(wrapper);
24650
+ var nextRange = document.createRange();
24651
+ nextRange.setStart(textNode, SMALL_SPACE_LENGTH);
24652
+ nextRange.collapse(true);
24653
+ selection.removeAllRanges();
24654
+ selection.addRange(nextRange);
24655
+ var rangeParams = this.getRangeParamsPreservingInlinePlaceholder();
24656
+ if (rangeParams) this.setOldRangeParams(rangeParams);
24657
+ debugMathRichInput("insertInlineStylePlaceholder:after", {
24658
+ style: style,
24659
+ wrapperTag: wrapperTag,
24660
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24661
+ rangeParams: rangeParams,
24662
+ selection: getSelectionSnapshot(this.editableDiv)
24663
+ });
24664
+ return true;
24665
+ }
24666
+ }, {
24667
+ key: "getActiveButtonsFromCommandState",
24668
+ value: function getActiveButtonsFromCommandState() {
24669
+ var query = function query(command) {
24670
+ try {
24671
+ return document.queryCommandState(command);
24672
+ } catch (error) {
24673
+ return false;
24674
+ }
24675
+ };
24676
+ return {
24677
+ bold: query("bold"),
24678
+ italic: query("italic"),
24679
+ underline: query("underline"),
24680
+ subscript: query("subscript"),
24681
+ superscript: query("superscript")
24682
+ };
24683
+ }
24684
+ }, {
24685
+ key: "isSelectionCollapsed",
24686
+ value: function isSelectionCollapsed() {
24687
+ var selection = document.getSelection();
24688
+ if (!selection || selection.rangeCount === 0) return true;
24689
+ return selection.getRangeAt(0).collapsed;
24690
+ }
24691
+ }, {
24692
+ key: "getActiveButtonsFromAncestorState",
24693
+ value: function getActiveButtonsFromAncestorState() {
24694
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24695
+ if (rangeParams === null || rangeParams === undefined) {
24696
+ rangeParams = this._getRangeParams();
24697
+ }
24698
+ var activeButtons = {
24699
+ bold: false,
24700
+ italic: false,
24701
+ underline: false,
24702
+ subscript: false,
24703
+ superscript: false
24704
+ };
24705
+ if (rangeParams === null || rangeParams === undefined || rangeParams.startNodeIndex < 0) {
24706
+ return activeButtons;
24707
+ }
24708
+ var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
24709
+ if (node === null || node === undefined) return activeButtons;
24710
+ do {
24711
+ if (node.nodeType === 1) {
24712
+ var nodeName = node.nodeName.toLowerCase();
24713
+ if (nodeName === "b" || nodeName === "strong") activeButtons.bold = true;else if (nodeName === "i" || nodeName === "em") activeButtons.italic = true;else if (nodeName === "u") activeButtons.underline = true;else if (nodeName === "sub") activeButtons.subscript = true;else if (nodeName === "sup") activeButtons.superscript = true;
24714
+ } else if (node.classList !== null && node.classList !== undefined && node.classList.contains("MathRichInput")) break;
24715
+ node = node.parentNode;
24716
+ } while (node !== null);
24717
+ return activeButtons;
24718
+ }
24719
+ }, {
24720
+ key: "getActiveButtonsFromSelection",
24721
+ value: function getActiveButtonsFromSelection() {
24722
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24723
+ var commandState = this.getActiveButtonsFromCommandState();
24724
+ var ancestorState = this.getActiveButtonsFromAncestorState(rangeParams);
24725
+ return {
24726
+ bold: commandState.bold || ancestorState.bold,
24727
+ italic: commandState.italic || ancestorState.italic,
24728
+ underline: commandState.underline || ancestorState.underline,
24729
+ subscript: commandState.subscript || ancestorState.subscript,
24730
+ superscript: commandState.superscript || ancestorState.superscript
24731
+ };
24732
+ }
24733
+ }, {
24734
+ key: "setActiveButtonsIfChanged",
24735
+ value: function setActiveButtonsIfChanged(activeButtons) {
24736
+ var update = true;
24737
+ if (this.lastSetActiveButtons !== null) {
24738
+ update = false;
24739
+ for (var style in activeButtons) {
24740
+ if (activeButtons[style] !== this.lastSetActiveButtons[style]) {
24741
+ update = true;
24742
+ break;
24743
+ }
24744
+ }
24745
+ }
24746
+ if (update) {
24747
+ this.lastSetActiveButtons = activeButtons;
24748
+ this.setState({
24749
+ activeButtons: activeButtons
24750
+ });
24751
+ }
24752
+ }
24753
+ }, {
24754
+ key: "getInlineStyleAncestor",
24755
+ value: function getInlineStyleAncestor(style) {
24756
+ var tags = INLINE_STYLE_TAGS[style];
24757
+ if (!tags) return null;
24758
+ var selection = document.getSelection();
24759
+ if (!selection || selection.rangeCount === 0) return null;
24760
+ var node = selection.getRangeAt(0).startContainer;
24761
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentNode;
24762
+ while (node && node !== this.editableDiv) {
24763
+ if (node.nodeType === Node.ELEMENT_NODE && tags.includes(node.nodeName.toLowerCase())) {
24764
+ return node;
24765
+ }
24766
+ node = node.parentNode;
24767
+ }
24768
+ return null;
24769
+ }
24770
+ }, {
24771
+ key: "isRangeAtEndOfNode",
24772
+ value: function isRangeAtEndOfNode(range, node) {
24773
+ try {
24774
+ var afterRange = document.createRange();
24775
+ afterRange.setStart(range.startContainer, range.startOffset);
24776
+ afterRange.setEnd(node, node.childNodes.length);
24777
+ var textAfterCaret = afterRange.toString().replace(/[\s\u00a0\u200b\ufeff]/g, "");
24778
+ if (textAfterCaret === "") return true;
24779
+ } catch (error) {
24780
+ // Fall back to boundary comparison below if the browser rejects the range.
24781
+ }
24782
+ var nodeEndRange = document.createRange();
24783
+ nodeEndRange.selectNodeContents(node);
24784
+ nodeEndRange.collapse(false);
24785
+ return range.compareBoundaryPoints(Range.START_TO_START, nodeEndRange) === 0;
24786
+ }
24787
+ }, {
24788
+ key: "moveTrailingWhitespaceOutsideInlineStyle",
24789
+ value: function moveTrailingWhitespaceOutsideInlineStyle(style, command) {
24790
+ var selection = document.getSelection();
24791
+ if (!selection || selection.rangeCount === 0) return false;
24792
+ var range = selection.getRangeAt(0);
24793
+ if (!range.collapsed) return false;
24794
+ var ancestor = this.getInlineStyleAncestor(style);
24795
+ if (!ancestor || !this.isRangeAtEndOfNode(range, ancestor)) return false;
24796
+ if (range.startContainer.nodeType !== Node.TEXT_NODE) return false;
24797
+ var textNode = range.startContainer;
24798
+ var text = textNode.nodeValue || "";
24799
+ var moveStart = range.startOffset;
24800
+ while (moveStart > 0) {
24801
+ var _char = text.charAt(moveStart - 1);
24802
+ if (_char !== " " && _char !== "\xA0") break;
24803
+ moveStart -= 1;
24804
+ }
24805
+ if (moveStart === range.startOffset) return false;
24806
+ var movedText = text.substring(moveStart, range.startOffset);
24807
+ textNode.nodeValue = text.substring(0, moveStart) + text.substring(range.startOffset);
24808
+ var outsideNode = ancestor.nextSibling;
24809
+ if (!outsideNode || outsideNode.nodeType !== Node.TEXT_NODE) {
24810
+ outsideNode = document.createTextNode(movedText);
24811
+ ancestor.parentNode.insertBefore(outsideNode, ancestor.nextSibling);
24812
+ } else {
24813
+ outsideNode.nodeValue = movedText + outsideNode.nodeValue;
24814
+ }
24815
+ var nextRange = document.createRange();
24816
+ nextRange.setStart(outsideNode, movedText.length);
24817
+ nextRange.collapse(true);
24818
+ selection.removeAllRanges();
24819
+ selection.addRange(nextRange);
24820
+ try {
24821
+ if (document.queryCommandState(command)) {
24822
+ document.execCommand(command, false, null);
24823
+ }
24824
+ } catch (error) {
24825
+ // DOM position is the source of truth here; command state is best effort.
24826
+ }
24827
+ var rangeParams = this._getRangeParams();
24828
+ if (rangeParams) {
24829
+ this.setOldRangeParams(rangeParams);
24830
+ this.applyCurrentEditableDomToComponent(rangeParams);
24831
+ }
24832
+ debugMathRichInput("moveTrailingWhitespaceOutsideInlineStyle:after", {
24833
+ style: style,
24834
+ command: command,
24835
+ movedText: movedText,
24836
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24837
+ rangeParams: rangeParams,
24838
+ selection: getSelectionSnapshot(this.editableDiv)
24839
+ });
24840
+ return true;
24841
+ }
24842
+ }, {
24843
+ key: "applyCurrentEditableDomToComponent",
24844
+ value: function applyCurrentEditableDomToComponent() {
24845
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24846
+ if (!this.editableDiv) return;
24847
+ if (rangeParams === null || rangeParams === undefined) {
24848
+ rangeParams = this._getRangeParams();
24849
+ }
24850
+ if (rangeParams === null || rangeParams === undefined) return;
24851
+ var rawText = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24852
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24853
+ var nodeCounts = this._countNodeTypes();
24854
+ var hasMath = /<math>/i.test(rawText) || nodeCounts.math > 0;
24855
+ var hasHtml = nodeCounts.html > 0 || /<[^>]+>/i.test(rawText);
24856
+ var mimeType = this.getMimeType(hasHtml, hasMath);
24857
+ rawText = removeEncodingIfPlainText(rawText, mimeType, this.enableHtml());
24858
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24859
+ this.applyChangesToComponent(rawText, mimeType, rangeParams, this.props.useExpertMode, this.props.selectedTab, {
24860
+ skipControlledRender: true
24861
+ });
24862
+ }
24007
24863
  }, {
24008
24864
  key: "_getRangeParams",
24009
24865
  value: function _getRangeParams() {
24010
24866
  var editableDiv = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24011
24867
  if (editableDiv === null) editableDiv = this.editableDiv;
24012
24868
  var rangeParams = getRangeParams(editableDiv);
24869
+ debugMathRichInput("MathRichInput:_getRangeParams", {
24870
+ rangeParams: rangeParams,
24871
+ selection: getSelectionSnapshot(editableDiv)
24872
+ });
24013
24873
  return rangeParams;
24014
24874
  }
24015
24875
  }, {
@@ -24038,6 +24898,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24038
24898
  value: function componentDidMount() {
24039
24899
  try {
24040
24900
  setupKatex();
24901
+ debugMathRichInput("componentDidMount", {
24902
+ propsValue: this.props.value,
24903
+ propsMimeType: this.props.mimeType,
24904
+ autofocus: this.props.autofocus,
24905
+ autoFocus: this.props.autoFocus,
24906
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24907
+ selection: getSelectionSnapshot(this.editableDiv)
24908
+ });
24041
24909
  this.editableDiv.addEventListener("keydown", this.handleKeyDown);
24042
24910
  this.editableDiv.addEventListener("keyup", this.handleKeyUp);
24043
24911
  this.editableDiv.addEventListener("paste", this.handlePaste);
@@ -24059,14 +24927,70 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24059
24927
  console.error(error);
24060
24928
  }
24061
24929
  }
24930
+ }, {
24931
+ key: "shouldComponentUpdate",
24932
+ value: function shouldComponentUpdate(nextProps, nextState) {
24933
+ if (this.skipNextControlledValueRender === true) {
24934
+ var canSkip = nextProps.value === this.skipNextControlledValue && nextState === this.state;
24935
+ debugMathRichInput("shouldComponentUpdate:native-input-gate", {
24936
+ canSkip: canSkip,
24937
+ nextValue: nextProps.value,
24938
+ skipNextControlledValue: this.skipNextControlledValue,
24939
+ currentPropsValue: this.props.value,
24940
+ stateChanged: nextState !== this.state,
24941
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24942
+ selection: getSelectionSnapshot(this.editableDiv)
24943
+ });
24944
+ this.skipNextControlledValueRender = false;
24945
+ this.skipNextControlledValue = null;
24946
+ if (canSkip) return false;
24947
+ }
24948
+ if (this.shouldSkipRedundantFocusedRender(nextProps, nextState)) {
24949
+ debugMathRichInput("shouldComponentUpdate:skip-redundant-focused-render", {
24950
+ nextValue: nextProps.value,
24951
+ currentPropsValue: this.props.value,
24952
+ selection: getSelectionSnapshot(this.editableDiv)
24953
+ });
24954
+ return false;
24955
+ }
24956
+ return true;
24957
+ }
24062
24958
  }, {
24063
24959
  key: "componentDidUpdate",
24064
24960
  value: function componentDidUpdate() {
24065
24961
  try {
24962
+ debugMathRichInput("componentDidUpdate:start", {
24963
+ propsValue: this.props.value,
24964
+ propsMimeType: this.props.mimeType,
24965
+ afterComponentUpdateData: this.afterComponentUpdateData,
24966
+ hasFocus: this.state.hasFocus,
24967
+ showEquationEditor: this.state.showEquationEditor,
24968
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24969
+ selection: getSelectionSnapshot(this.editableDiv)
24970
+ });
24066
24971
  if (this.afterComponentUpdateData !== null && this.afterComponentUpdateData !== undefined && this.afterComponentUpdateData.value === this.props.value) {
24972
+ debugMathRichInput("componentDidUpdate:restore-before", {
24973
+ rangeParams: this.afterComponentUpdateData.rangeParams,
24974
+ selection: getSelectionSnapshot(this.editableDiv)
24975
+ });
24067
24976
  this._setRangeParams(this.afterComponentUpdateData.rangeParams);
24068
24977
  this.afterComponentUpdateData = null;
24069
24978
  this.updateActiveButtons();
24979
+ debugMathRichInput("componentDidUpdate:restore-after", {
24980
+ selection: getSelectionSnapshot(this.editableDiv)
24981
+ });
24982
+ }
24983
+ if (this.preserveNativeDomRangeParams !== null && this.preserveNativeDomRangeParams !== undefined && this.state.hasFocus === true) {
24984
+ var rangeParams = this.preserveNativeDomRangeParams;
24985
+ this.preserveNativeDomRangeParams = null;
24986
+ debugMathRichInput("componentDidUpdate:preserve-restore-before", {
24987
+ rangeParams: rangeParams,
24988
+ selection: getSelectionSnapshot(this.editableDiv)
24989
+ });
24990
+ this._setRangeParams(rangeParams);
24991
+ debugMathRichInput("componentDidUpdate:preserve-restore-after", {
24992
+ selection: getSelectionSnapshot(this.editableDiv)
24993
+ });
24070
24994
  }
24071
24995
  } catch (error) {
24072
24996
  console.error(error);
@@ -24079,20 +25003,31 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24079
25003
  if (rangeParams === null || rangeParams === undefined) rangeParams = this._getRangeParams();
24080
25004
  if (rangeParams.startOffset !== 0) console.warn("Unexpected startOffset " + rangeParams.startOffset + " in insertAfterSmallSpace");
24081
25005
  var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
24082
- var raw_text = elementToMarkedRawText(this.editableDiv, node, 1,
24083
- // after small space
24084
- this.enableHtml());
24085
- var new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
25006
+ if (node === null || node === undefined || node.nodeValue === null) {
25007
+ console.warn("Could not find small-space text node");
25008
+ return;
25009
+ }
25010
+
25011
+ // The initial empty editor contains a hidden zero-width space. Handling
25012
+ // the first printable key by re-rendering through React replaces the
25013
+ // text node and drops Chrome's selection to the start under React 19.
25014
+ // Mutate the already-focused DOM node, then skip the matching controlled
25015
+ // render exactly like the normal contenteditable input path.
25016
+ node.nodeValue = node.nodeValue.substring(0, 1) + new_char + node.nodeValue.substring(1);
24086
25017
  var new_rangeParams = {
24087
25018
  startNodeIndex: rangeParams.startNodeIndex,
24088
- startNodeOffset: 1 + new_char.length,
25019
+ startOffset: 1 + new_char.length,
24089
25020
  endNodeIndex: rangeParams.startNodeIndex,
24090
- endNodeOffset: 1 + new_char.length,
25021
+ endOffset: 1 + new_char.length,
24091
25022
  startGlobalOffset: rangeParams.startGlobalOffset + new_char.length,
24092
25023
  endGlobalOffset: rangeParams.endGlobalOffset + new_char.length
24093
25024
  };
25025
+ this._setRangeParams(new_rangeParams);
25026
+ var new_raw_text = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24094
25027
  new_raw_text = removeEncodingIfPlainText(new_raw_text, this.props.mimeType, this.enableHtml());
24095
- this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab);
25028
+ this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab, {
25029
+ skipControlledRender: true
25030
+ });
24096
25031
  } catch (error) {
24097
25032
  console.error(error);
24098
25033
  }
@@ -24106,6 +25041,23 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24106
25041
  console.log(this.props);
24107
25042
  }
24108
25043
  var html = rawTextToHtml(katex$1, this.props.value || "", this.props.mimeType);
25044
+ var editableHtml = this.getEditableHtmlForRender(html);
25045
+ debugMathRichInput("render", {
25046
+ propsValue: this.props.value,
25047
+ propsMimeType: this.props.mimeType,
25048
+ html: html,
25049
+ editableHtml: editableHtml,
25050
+ state: {
25051
+ hasFocus: this.state.hasFocus,
25052
+ showEquationEditor: this.state.showEquationEditor,
25053
+ showAccentBar: this.state.showAccentBar,
25054
+ keyPressed: this.state.keyPressed
25055
+ },
25056
+ afterComponentUpdateData: this.afterComponentUpdateData,
25057
+ oldRangeParams: this.oldRangeParams,
25058
+ editableInnerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
25059
+ selection: getSelectionSnapshot(this.editableDiv)
25060
+ });
24109
25061
 
24110
25062
  // Debug render math
24111
25063
  // if (this.props.value && this.props.value.includes("<math>")) {
@@ -24164,7 +25116,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24164
25116
  onCompositionStart: this.handleCompositionStart,
24165
25117
  onCompositionEnd: this.handleCompositionEnd,
24166
25118
  dangerouslySetInnerHTML: {
24167
- __html: html
25119
+ __html: editableHtml
24168
25120
  },
24169
25121
  "data-placeholder": this.props.placeholder || ""
24170
25122
  }))), this.state.showEquationEditor && /*#__PURE__*/React__default["default"].createElement(EquationEditorModal, {
@@ -24173,7 +25125,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24173
25125
  },
24174
25126
  handleClose: this.handleEquationEditorClose,
24175
25127
  handleInsert: this.handleEquationEditorInsert,
24176
- latex: this.state.equationEditorLatex
25128
+ latex: this.state.equationEditorLatex,
25129
+ disableAnimation: this.props.disableAnimation
24177
25130
  }));
24178
25131
  }
24179
25132
  }]);
@@ -24182,7 +25135,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24182
25135
  function getAllTextNodes(node) {
24183
25136
  var nodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
24184
25137
  if (node === null) {
24185
- console.warn("Deverloper supplied a null node. Ignoring");
25138
+ console.warn("Developer supplied a null node. Ignoring");
24186
25139
  return nodes;
24187
25140
  }
24188
25141
  if (nodes === null) {
@@ -24207,8 +25160,9 @@ function getAllTextNodes(node) {
24207
25160
  return nodes;
24208
25161
  }
24209
25162
  function renderMathInNode(node) {
25163
+ if (node === null || node === undefined) return;
24210
25164
  try {
24211
- var textNodes = getAllTextNodes(node);
25165
+ var textNodes = getAllTextNodes(node, []);
24212
25166
  for (var i = 0; i < textNodes.length; i++) {
24213
25167
  try {
24214
25168
  var text = textNodes[i].nodeValue;