@zzish/math-rich-input 0.1.26 → 0.1.27

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
@@ -21348,6 +21348,7 @@ function findNodeAndOffsetForGlobalOffset(node, globalOffset) {
21348
21348
 
21349
21349
  if (isTextNode(node)) {
21350
21350
  result.node = node;
21351
+ result.lastTextNode = node;
21351
21352
  result.offset += node.nodeValue.length;
21352
21353
  if (nodeStartsWithSmallSpace(node)) result.offset -= 1; // console.log("result.offset now: "+result.offset)
21353
21354
 
@@ -21480,12 +21481,34 @@ function setRangeParamsFromGlobalOffsets(editableDiv, params) {
21480
21481
  };
21481
21482
  findNodeAndOffsetForGlobalOffset(editableDiv, params.endGlobalOffset, endResult); // console.log(endResult.node.nodeType+":"+endResult.node.tagName+":"+
21482
21483
  // endResult.node.nodeValue+":"+endResult.offset)
21484
+ // So the next bit of code should never be called in principle, but if we have
21485
+ // failed to find an appropriate node (this could happen if the global offset is bigger
21486
+ // than the length of the text in the node), then lets try and fix it
21483
21487
 
21484
- if (!startResult.node || !endResult.node || !startResult.offset || !endResult.offset) {
21485
- console.log("Can't set range with start and end results");
21488
+ if (startResult.node === null || endResult.node === null) {
21489
+ console.warn("Can't set range from global offsets, as could not find text nodes");
21486
21490
  console.log(startResult);
21487
21491
  console.log(endResult);
21488
- throw new Error("Could not extract valid nodes and offsets from params");
21492
+
21493
+ if (startResult.node === null) {
21494
+ if (params.startGlobalOffset > 0 && startResult.lastTextNode !== null) {
21495
+ startResult.node = startResult.lastTextNode;
21496
+ startResult.offset = startResult.lastTextNode.nodeValue.length;
21497
+ } else {
21498
+ startResult.node = findFirstTextNode(editableDiv);
21499
+ startResult.offset = 0;
21500
+ }
21501
+ }
21502
+
21503
+ if (endResult.node === null) {
21504
+ if (params.endGlobalOffset > 0 && endResult.lastTextNode !== null) {
21505
+ endResult.node = endResult.lastTextNode;
21506
+ endResult.offset = endResult.lastTextNode.nodeValue.length;
21507
+ } else {
21508
+ endResult.node = findFirstTextNode(editableDiv);
21509
+ endResult.offset = 0;
21510
+ }
21511
+ }
21489
21512
  }
21490
21513
 
21491
21514
  if (startResult.offset === 0 && startResult.node.nodeValue.length > 0 && nodeStartsWithSmallSpace(startResult.node)) {
@@ -22068,11 +22091,12 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22068
22091
  }
22069
22092
 
22070
22093
  if (deleteToEmpty) {
22094
+ console.log("deleteToEmpty");
22071
22095
  var _new_rangeParams = {
22072
22096
  startNodeIndex: 0,
22073
- startOffset: 0,
22097
+ startOffset: 1,
22074
22098
  endNodeIndex: 0,
22075
- endOffset: 0,
22099
+ endOffset: 1,
22076
22100
  startGlobalOffset: 0,
22077
22101
  endGlobalOffset: 0
22078
22102
  };
@@ -22540,8 +22564,11 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22540
22564
 
22541
22565
  if (_this2.handleKeyDownAccentHelper(e)) return; // Check if inserting character in element that starts with small space
22542
22566
 
22543
- if (e.key.length === 1 && params.startOffset === 0 && _this2.nodeStartsWithSmallSpace(startNode)) {
22544
- _this2.replaceNextLetter(e.key);
22567
+ if (e.key.length === 1 && // ie, it's not something like "arrowLeft"
22568
+ params.startOffset === 0 && _this2.nodeStartsWithSmallSpace(startNode)) {
22569
+ console.log("Handling character input at start of text node with small space");
22570
+
22571
+ _this2.insertAfterSmallSpace(e.key, params);
22545
22572
 
22546
22573
  e.preventDefault();
22547
22574
  return;
@@ -23362,50 +23389,33 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23362
23389
  }
23363
23390
  });
23364
23391
 
23365
- _defineProperty(_assertThisInitialized(_this2), "replaceNextLetter", function (new_char) {
23366
- try {
23367
- var rangeParams = _this2._getRangeParams();
23368
-
23369
- var node = _this2._findNodeWithIndex(rangeParams.endNodeIndex);
23370
-
23371
- var offset = rangeParams.endOffset + 1; // next letter
23372
-
23373
- var raw_text = elementToMarkedRawText(_this2.editableDiv, node, offset, _this2.enableHtml());
23374
- var new_raw_text = null;
23375
-
23376
- var new_rangeParams = _objectSpread2({}, rangeParams);
23377
-
23378
- new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
23379
- if (rangeParams.startNodeIndex === rangeParams.endNodeIndex && rangeParams.startOffset === rangeParams.endOffset) new_rangeParams.startOffset = new_rangeParams.startOffset + new_char.length + 1;
23380
- new_rangeParams.endOffset = new_rangeParams.endOffset + new_char.length + 1;
23381
- new_raw_text = removeEncodingIfPlainText(new_raw_text, _this2.props.mimeType, _this2.enableHtml());
23382
-
23383
- _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23384
- } catch (error) {
23385
- console.error(error);
23386
- }
23387
- });
23388
-
23389
23392
  _defineProperty(_assertThisInitialized(_this2), "insertAccentLetter", function (event, old_char, new_char) {
23390
23393
  try {
23391
23394
  var rangeParams = _this2._getRangeParams();
23392
23395
 
23393
- var node = _this2._findNodeWithIndex(rangeParams.endNodeIndex);
23396
+ var node = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
23394
23397
 
23395
- var offset = rangeParams.endOffset;
23398
+ var offset = rangeParams.startOffset;
23396
23399
  var raw_text = elementToMarkedRawText(_this2.editableDiv, node, offset, _this2.enableHtml());
23397
23400
  var new_raw_text = null;
23398
-
23399
- var new_rangeParams = _objectSpread2({}, rangeParams);
23400
-
23401
- new_rangeParams.startGlobalOffset++;
23402
- new_rangeParams.endGlobalOffset++;
23401
+ var new_rangeParams = null;
23403
23402
 
23404
23403
  if (old_char === null) {
23404
+ // insert new char
23405
23405
  new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
23406
- if (rangeParams.startNodeIndex === rangeParams.endNodeIndex && rangeParams.startOffset === rangeParams.endOffset) new_rangeParams.startOffset = new_rangeParams.startOffset + new_char.length;
23407
- new_rangeParams.endOffset = new_rangeParams.endOffset + new_char.length;
23408
- } else new_raw_text = removeMarks(replaceCharacterBeforeMarks(raw_text, new_char));
23406
+ new_rangeParams = {
23407
+ startNodeIndex: rangeParams.startNodeIndex,
23408
+ startOffset: rangeParams.startOffset + new_char.length,
23409
+ endNodeIndex: rangeParams.startNodeIndex,
23410
+ endOffset: rangeParams.startOffset + new_char.length,
23411
+ startGlobalOffset: rangeParams.startGlobalOffset + new_char.length,
23412
+ endGlobalOffset: rangeParams.startGlobalOffset + new_char.length
23413
+ };
23414
+ } else {
23415
+ // replace old char
23416
+ new_raw_text = removeMarks(replaceCharacterBeforeMarks(raw_text, new_char));
23417
+ new_rangeParams = _objectSpread2({}, rangeParams);
23418
+ }
23409
23419
 
23410
23420
  new_raw_text = removeEncodingIfPlainText(new_raw_text, _this2.props.mimeType, _this2.enableHtml());
23411
23421
 
@@ -23645,6 +23655,32 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23645
23655
  console.error(error);
23646
23656
  }
23647
23657
  }
23658
+ }, {
23659
+ key: "insertAfterSmallSpace",
23660
+ value: function insertAfterSmallSpace(new_char, rangeParams) {
23661
+ try {
23662
+ if (rangeParams === null || rangeParams === undefined) rangeParams = this._getRangeParams();
23663
+ if (rangeParams.startOffset !== 0) console.warn("Unexpected startOffset " + rangeParams.startOffset + " in insertAfterSmallSpace");
23664
+
23665
+ var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
23666
+
23667
+ var raw_text = elementToMarkedRawText(this.editableDiv, node, 1, // after small space
23668
+ this.enableHtml());
23669
+ var new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
23670
+ var new_rangeParams = {
23671
+ startNodeIndex: rangeParams.startNodeIndex,
23672
+ startNodeOffset: 1 + new_char.length,
23673
+ endNodeIndex: rangeParams.startNodeIndex,
23674
+ endNodeOffset: 1 + new_char.length,
23675
+ startGlobalOffset: rangeParams.startGlobalOffset + new_char.length,
23676
+ endGlobalOffset: rangeParams.endGlobalOffset + new_char.length
23677
+ };
23678
+ new_raw_text = removeEncodingIfPlainText(new_raw_text, this.props.mimeType, this.enableHtml());
23679
+ this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab);
23680
+ } catch (error) {
23681
+ console.error(error);
23682
+ }
23683
+ }
23648
23684
  }, {
23649
23685
  key: "render",
23650
23686
  value: function render() {
@@ -78,6 +78,136 @@
78
78
  padding: 2px;
79
79
  margin: 0px 2px;
80
80
  }
81
+ /* Note that the top left position of the toolbar is set programatically in componentDidMount */
82
+
83
+ .Toolbar {
84
+ position: absolute;
85
+ z-index: 1;
86
+ display: flex;
87
+ height: 42px;
88
+ background-color: white;
89
+ border: 1px solid #d0d0d0;
90
+ border-radius: 5px;
91
+ box-shadow: 1px 1px 5px #a0a0a0;
92
+ animation: 0.5s fadeIn1;
93
+ animation-fill-mode: forwards;
94
+ }
95
+
96
+ @keyframes fadeIn1 {
97
+ 0% {
98
+ opacity: 0;
99
+ }
100
+ 100% {
101
+ opacity: 1;
102
+ }
103
+ }
104
+
105
+ .Toolbar-button {
106
+ font-family: sans-serif;
107
+ font-size: 16px;
108
+ width: 40px;
109
+ height: 36px;
110
+ background-color: white;
111
+ border-radius: 5px;
112
+ border: 0px;
113
+ padding: 1px;
114
+ margin: 2px;
115
+ }
116
+
117
+ .Toolbar-button:hover {
118
+ color: white;
119
+ background: #663676;
120
+ cursor: pointer;
121
+ }
122
+
123
+ .Toolbar-button:active {
124
+ background: #808080;
125
+ color: white;
126
+ cursor: pointer;
127
+ }
128
+
129
+ .TB-narrow {
130
+ width: 30px;
131
+ }
132
+
133
+ .TB-wide {
134
+ width: 46px;
135
+ }
136
+
137
+ .TB-selected {
138
+ background: #d0d0d0;
139
+ color: black;
140
+ cursor: pointer;
141
+ animation: 0.2s TB-fadeIn;
142
+ animation-fill-mode: forwards;
143
+ }
144
+
145
+ .TB-selected:hover {
146
+ color: white;
147
+ background: #663676;
148
+ cursor: pointer;
149
+ }
150
+
151
+ @keyframes TB-fadeIn {
152
+ 00% {
153
+ background: white;
154
+ }
155
+ 100% {
156
+ background: #d0d0d0;
157
+ }
158
+ }
159
+
160
+ /* Tooltip text */
161
+ .Toolbar-button .tooltiptext {
162
+ font-weight: normal;
163
+ visibility: hidden;
164
+ width: 100px;
165
+ background-color: #222;
166
+ color: #fff;
167
+ /*font-weight: bold;*/
168
+ font-size: 12px;
169
+ text-align: center;
170
+ padding: 4px 10px;
171
+ border-radius: 6px;
172
+
173
+ /* Position the tooltip text - see examples below! */
174
+ position: absolute;
175
+ z-index: 2;
176
+ top: -90%; /* Must leave a small gap between tooltip and button */
177
+ left: 50px;
178
+
179
+ /* transform: translate (50%,0%); */
180
+ }
181
+
182
+ /* Show the tooltip text when you mouse over the tooltip container */
183
+ .Toolbar-button:hover .tooltiptext {
184
+ animation: 0.8s fadeIn2;
185
+ animation-fill-mode: forwards;
186
+ visibility: visible;
187
+ /* width: 100px; */
188
+ /* top: -90%; */ /* Must leave a small gap between tooltip and button */
189
+ /* left: 20%; */
190
+ /* margin-left: -50px; */
191
+ }
192
+
193
+ @keyframes fadeIn2 {
194
+ 00% {
195
+ opacity: 0;
196
+ }
197
+ 70% {
198
+ opacity: 0;
199
+ }
200
+ 100% {
201
+ visibility: visible;
202
+ opacity: 0.9;
203
+ }
204
+ }
205
+
206
+ /* .tooltip {
207
+ position: relative;
208
+ display: inline-block;
209
+ border-bottom: 1px dotted black; /* If you want dots under the hoverable text
210
+ } */
81
211
  .EquationEditorModal-background {
82
212
  position: fixed;
83
213
  top: 0;
@@ -233,136 +363,6 @@
233
363
  .EquationEditorModal .slider.round:before {
234
364
  border-radius: 50%;
235
365
  }
236
- /* Note that the top left position of the toolbar is set programatically in componentDidMount */
237
-
238
- .Toolbar {
239
- position: absolute;
240
- z-index: 1;
241
- display: flex;
242
- height: 42px;
243
- background-color: white;
244
- border: 1px solid #d0d0d0;
245
- border-radius: 5px;
246
- box-shadow: 1px 1px 5px #a0a0a0;
247
- animation: 0.5s fadeIn1;
248
- animation-fill-mode: forwards;
249
- }
250
-
251
- @keyframes fadeIn1 {
252
- 0% {
253
- opacity: 0;
254
- }
255
- 100% {
256
- opacity: 1;
257
- }
258
- }
259
-
260
- .Toolbar-button {
261
- font-family: sans-serif;
262
- font-size: 16px;
263
- width: 40px;
264
- height: 36px;
265
- background-color: white;
266
- border-radius: 5px;
267
- border: 0px;
268
- padding: 1px;
269
- margin: 2px;
270
- }
271
-
272
- .Toolbar-button:hover {
273
- color: white;
274
- background: #663676;
275
- cursor: pointer;
276
- }
277
-
278
- .Toolbar-button:active {
279
- background: #808080;
280
- color: white;
281
- cursor: pointer;
282
- }
283
-
284
- .TB-narrow {
285
- width: 30px;
286
- }
287
-
288
- .TB-wide {
289
- width: 46px;
290
- }
291
-
292
- .TB-selected {
293
- background: #d0d0d0;
294
- color: black;
295
- cursor: pointer;
296
- animation: 0.2s TB-fadeIn;
297
- animation-fill-mode: forwards;
298
- }
299
-
300
- .TB-selected:hover {
301
- color: white;
302
- background: #663676;
303
- cursor: pointer;
304
- }
305
-
306
- @keyframes TB-fadeIn {
307
- 00% {
308
- background: white;
309
- }
310
- 100% {
311
- background: #d0d0d0;
312
- }
313
- }
314
-
315
- /* Tooltip text */
316
- .Toolbar-button .tooltiptext {
317
- font-weight: normal;
318
- visibility: hidden;
319
- width: 100px;
320
- background-color: #222;
321
- color: #fff;
322
- /*font-weight: bold;*/
323
- font-size: 12px;
324
- text-align: center;
325
- padding: 4px 10px;
326
- border-radius: 6px;
327
-
328
- /* Position the tooltip text - see examples below! */
329
- position: absolute;
330
- z-index: 2;
331
- top: -90%; /* Must leave a small gap between tooltip and button */
332
- left: 50px;
333
-
334
- /* transform: translate (50%,0%); */
335
- }
336
-
337
- /* Show the tooltip text when you mouse over the tooltip container */
338
- .Toolbar-button:hover .tooltiptext {
339
- animation: 0.8s fadeIn2;
340
- animation-fill-mode: forwards;
341
- visibility: visible;
342
- /* width: 100px; */
343
- /* top: -90%; */ /* Must leave a small gap between tooltip and button */
344
- /* left: 20%; */
345
- /* margin-left: -50px; */
346
- }
347
-
348
- @keyframes fadeIn2 {
349
- 00% {
350
- opacity: 0;
351
- }
352
- 70% {
353
- opacity: 0;
354
- }
355
- 100% {
356
- visibility: visible;
357
- opacity: 0.9;
358
- }
359
- }
360
-
361
- /* .tooltip {
362
- position: relative;
363
- display: inline-block;
364
- border-bottom: 1px dotted black; /* If you want dots under the hoverable text
365
- } */
366
366
  /* Note that the top left position of the accent bar is set programatically in componentDidMount */
367
367
 
368
368
  .AccentBar {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzish/math-rich-input",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "React component for user to enter rich text with embedded math equations.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -461,11 +461,12 @@ export default class MathRichInput extends React.Component {
461
461
  }
462
462
 
463
463
  if (deleteToEmpty) {
464
+ console.log("deleteToEmpty")
464
465
  let new_rangeParams = {
465
466
  startNodeIndex: 0,
466
- startOffset: 0,
467
+ startOffset: 1,
467
468
  endNodeIndex: 0,
468
- endOffset: 0,
469
+ endOffset: 1,
469
470
  startGlobalOffset: 0,
470
471
  endGlobalOffset: 0,
471
472
  };
@@ -1193,15 +1194,15 @@ export default class MathRichInput extends React.Component {
1193
1194
  }
1194
1195
  }
1195
1196
 
1196
- if (this.handleKeyDownAccentHelper(e)) return;
1197
+ if (this.handleKeyDownAccentHelper(e))
1198
+ return;
1197
1199
 
1198
1200
  // Check if inserting character in element that starts with small space
1199
- if (
1200
- e.key.length === 1 &&
1201
- params.startOffset === 0 &&
1202
- this.nodeStartsWithSmallSpace(startNode)
1203
- ) {
1204
- this.replaceNextLetter(e.key);
1201
+ if (e.key.length === 1 && // ie, it's not something like "arrowLeft"
1202
+ params.startOffset === 0 &&
1203
+ this.nodeStartsWithSmallSpace(startNode)) {
1204
+ console.log("Handling character input at start of text node with small space")
1205
+ this.insertAfterSmallSpace(e.key, params);
1205
1206
  e.preventDefault();
1206
1207
  return;
1207
1208
  }
@@ -1910,6 +1911,7 @@ export default class MathRichInput extends React.Component {
1910
1911
  console.error(error);
1911
1912
  }
1912
1913
  };
1914
+
1913
1915
  // https://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
1914
1916
  pasteHtmlAtCaret = (html, selectPastedContent) => {
1915
1917
  var sel, range;
@@ -1957,6 +1959,7 @@ export default class MathRichInput extends React.Component {
1957
1959
  }
1958
1960
  }
1959
1961
  };
1962
+
1960
1963
  insertHtml = (new_html) => {
1961
1964
  try {
1962
1965
  // console.log("New html: "+new_html)
@@ -2154,37 +2157,40 @@ export default class MathRichInput extends React.Component {
2154
2157
  }
2155
2158
  };
2156
2159
 
2157
- replaceNextLetter = (new_char) => {
2160
+ insertAfterSmallSpace(new_char, rangeParams) {
2158
2161
  try {
2159
- let rangeParams = this._getRangeParams();
2160
- let node = this._findNodeWithIndex(rangeParams.endNodeIndex);
2161
- let offset = rangeParams.endOffset + 1; // next letter
2162
+ if (rangeParams === null || rangeParams === undefined)
2163
+ rangeParams = this._getRangeParams();
2162
2164
 
2165
+ if (rangeParams.startOffset !== 0)
2166
+ console.warn("Unexpected startOffset "+rangeParams.startOffset+" in insertAfterSmallSpace")
2167
+
2168
+ let node = this._findNodeWithIndex(rangeParams.startNodeIndex);
2163
2169
  let raw_text = elementToMarkedRawText(
2164
- this.editableDiv,
2165
- node,
2166
- offset,
2167
- this.enableHtml()
2168
- );
2169
- let new_raw_text = null;
2170
- let new_rangeParams = { ...rangeParams };
2171
- new_raw_text = removeMarks(
2172
- insertCharacterBeforeMarks(raw_text, new_char)
2173
- );
2174
- if (
2175
- rangeParams.startNodeIndex === rangeParams.endNodeIndex &&
2176
- rangeParams.startOffset === rangeParams.endOffset
2177
- )
2178
- new_rangeParams.startOffset =
2179
- new_rangeParams.startOffset + new_char.length + 1;
2180
- new_rangeParams.endOffset =
2181
- new_rangeParams.endOffset + new_char.length + 1;
2170
+ this.editableDiv,
2171
+ node,
2172
+ 1, // after small space
2173
+ this.enableHtml()
2174
+ );
2182
2175
 
2176
+ let new_raw_text = removeMarks(
2177
+ insertCharacterBeforeMarks(raw_text, new_char))
2178
+
2179
+ let new_rangeParams = {
2180
+ startNodeIndex:rangeParams.startNodeIndex,
2181
+ startNodeOffset:1+new_char.length,
2182
+ endNodeIndex:rangeParams.startNodeIndex,
2183
+ endNodeOffset:1+new_char.length,
2184
+ startGlobalOffset:rangeParams.startGlobalOffset+new_char.length,
2185
+ endGlobalOffset:rangeParams.endGlobalOffset+new_char.length,
2186
+ }
2187
+
2183
2188
  new_raw_text = removeEncodingIfPlainText(
2184
2189
  new_raw_text,
2185
2190
  this.props.mimeType,
2186
2191
  this.enableHtml()
2187
2192
  );
2193
+
2188
2194
  this.applyChangesToComponent(
2189
2195
  new_raw_text,
2190
2196
  this.props.mimeType,
@@ -2200,8 +2206,8 @@ export default class MathRichInput extends React.Component {
2200
2206
  insertAccentLetter = (event, old_char, new_char) => {
2201
2207
  try {
2202
2208
  let rangeParams = this._getRangeParams();
2203
- let node = this._findNodeWithIndex(rangeParams.endNodeIndex);
2204
- let offset = rangeParams.endOffset;
2209
+ let node = this._findNodeWithIndex(rangeParams.startNodeIndex);
2210
+ let offset = rangeParams.startOffset;
2205
2211
 
2206
2212
  let raw_text = elementToMarkedRawText(
2207
2213
  this.editableDiv,
@@ -2209,32 +2215,31 @@ export default class MathRichInput extends React.Component {
2209
2215
  offset,
2210
2216
  this.enableHtml()
2211
2217
  );
2218
+
2212
2219
  let new_raw_text = null;
2213
- let new_rangeParams = { ...rangeParams };
2214
- new_rangeParams.startGlobalOffset++;
2215
- new_rangeParams.endGlobalOffset++;
2220
+ let new_rangeParams = null;
2216
2221
 
2217
- if (old_char === null) {
2218
- new_raw_text = removeMarks(
2219
- insertCharacterBeforeMarks(raw_text, new_char)
2220
- );
2221
- if (
2222
- rangeParams.startNodeIndex === rangeParams.endNodeIndex &&
2223
- rangeParams.startOffset === rangeParams.endOffset
2224
- )
2225
- new_rangeParams.startOffset =
2226
- new_rangeParams.startOffset + new_char.length;
2227
- new_rangeParams.endOffset = new_rangeParams.endOffset + new_char.length;
2228
- } else
2229
- new_raw_text = removeMarks(
2230
- replaceCharacterBeforeMarks(raw_text, new_char)
2231
- );
2222
+ if (old_char === null) { // insert new char
2223
+ new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
2224
+ new_rangeParams = {
2225
+ startNodeIndex:rangeParams.startNodeIndex,
2226
+ startOffset:rangeParams.startOffset + new_char.length,
2227
+ endNodeIndex:rangeParams.startNodeIndex,
2228
+ endOffset:rangeParams.startOffset + new_char.length,
2229
+ startGlobalOffset:rangeParams.startGlobalOffset + new_char.length,
2230
+ endGlobalOffset:rangeParams.startGlobalOffset + new_char.length
2231
+ }
2232
+ } else { // replace old char
2233
+ new_raw_text = removeMarks(replaceCharacterBeforeMarks(raw_text, new_char));
2234
+ new_rangeParams = { ...rangeParams };
2235
+ }
2232
2236
 
2233
2237
  new_raw_text = removeEncodingIfPlainText(
2234
2238
  new_raw_text,
2235
2239
  this.props.mimeType,
2236
2240
  this.enableHtml()
2237
2241
  );
2242
+
2238
2243
  this.applyChangesToComponent(
2239
2244
  new_raw_text,
2240
2245
  this.props.mimeType,
@@ -729,6 +729,7 @@ function findNodeAndOffsetForGlobalOffset(node, globalOffset, result=null) {
729
729
  }
730
730
  if (isTextNode(node)) {
731
731
  result.node = node
732
+ result.lastTextNode = node
732
733
  result.offset += node.nodeValue.length
733
734
  if (nodeStartsWithSmallSpace(node))
734
735
  result.offset -= 1
@@ -859,13 +860,35 @@ function setRangeParamsFromGlobalOffsets(editableDiv, params) {
859
860
  findNodeAndOffsetForGlobalOffset(editableDiv, params.endGlobalOffset, endResult)
860
861
  // console.log(endResult.node.nodeType+":"+endResult.node.tagName+":"+
861
862
  // endResult.node.nodeValue+":"+endResult.offset)
862
- if (!startResult.node || !endResult.node ||
863
- !startResult.offset || !endResult.offset) {
864
- console.log("Can't set range with start and end results")
863
+
864
+ // So the next bit of code should never be called in principle, but if we have
865
+ // failed to find an appropriate node (this could happen if the global offset is bigger
866
+ // than the length of the text in the node), then lets try and fix it
867
+ if (startResult.node === null || endResult.node === null) {
868
+ console.warn("Can't set range from global offsets, as could not find text nodes")
865
869
  console.log(startResult)
866
- console.log(endResult)
867
- throw new Error("Could not extract valid nodes and offsets from params")
870
+ console.log(endResult)
871
+
872
+ if (startResult.node === null) {
873
+ if (params.startGlobalOffset > 0 && startResult.lastTextNode !== null) {
874
+ startResult.node = startResult.lastTextNode
875
+ startResult.offset = startResult.lastTextNode.nodeValue.length
876
+ } else {
877
+ startResult.node = findFirstTextNode(editableDiv)
878
+ startResult.offset = 0
879
+ }
880
+ }
881
+ if (endResult.node === null) {
882
+ if (params.endGlobalOffset > 0 && endResult.lastTextNode !== null) {
883
+ endResult.node = endResult.lastTextNode
884
+ endResult.offset = endResult.lastTextNode.nodeValue.length
885
+ } else {
886
+ endResult.node = findFirstTextNode(editableDiv)
887
+ endResult.offset = 0
888
+ }
889
+ }
868
890
  }
891
+
869
892
  if (startResult.offset === 0 &&
870
893
  startResult.node.nodeValue.length > 0 &&
871
894
  nodeStartsWithSmallSpace(startResult.node)) {