prosemirror-view 1.26.5 → 1.27.2

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
@@ -458,7 +458,7 @@ function coordsAtPos(view, pos, side) {
458
458
  else {
459
459
  to++;
460
460
  }
461
- return flattenV(singleRect(textRange(node, from, to), takeSide), takeSide < 0);
461
+ return flattenV(singleRect(textRange(node, from, to), 1), takeSide < 0);
462
462
  }
463
463
  }
464
464
  let $dom = view.state.doc.resolve(pos - (atom || 0));
@@ -1242,7 +1242,7 @@ class NodeViewDesc extends ViewDesc {
1242
1242
  let composition = view.composing ? this.localCompositionInfo(view, pos) : null;
1243
1243
  let localComposition = composition && composition.pos > -1 ? composition : null;
1244
1244
  let compositionInChild = composition && composition.pos < 0;
1245
- let updater = new ViewTreeUpdater(this, localComposition && localComposition.node);
1245
+ let updater = new ViewTreeUpdater(this, localComposition && localComposition.node, view);
1246
1246
  iterDeco(this.node, this.innerDeco, (widget, i, insideNode) => {
1247
1247
  if (widget.spec.marks)
1248
1248
  updater.syncToMarks(widget.spec.marks, inline, view);
@@ -1624,8 +1624,9 @@ function rm(dom) {
1624
1624
  // Helper class for incrementally updating a tree of mark descs and
1625
1625
  // the widget and node descs inside of them.
1626
1626
  class ViewTreeUpdater {
1627
- constructor(top, lock) {
1627
+ constructor(top, lock, view) {
1628
1628
  this.lock = lock;
1629
+ this.view = view;
1629
1630
  // Index into `this.top`'s child array, represents the current
1630
1631
  // update position.
1631
1632
  this.index = 0;
@@ -1799,7 +1800,8 @@ class ViewTreeUpdater {
1799
1800
  }
1800
1801
  if (!lastChild || // Empty textblock
1801
1802
  !(lastChild instanceof TextViewDesc) ||
1802
- /\n$/.test(lastChild.node.text)) {
1803
+ /\n$/.test(lastChild.node.text) ||
1804
+ (this.view.requiresGeckoHackNode && /\s$/.test(lastChild.node.text))) {
1803
1805
  // Avoid bugs in Safari's cursor drawing (#1165) and Chrome's mouse selection (#1152)
1804
1806
  if ((safari || chrome) && lastChild && lastChild.dom.contentEditable == "false")
1805
1807
  this.addHackNode("IMG", parent);
@@ -2772,7 +2774,8 @@ function addContext(slice, context) {
2772
2774
  // A collection of DOM events that occur within the editor, and callback functions
2773
2775
  // to invoke when the event fires.
2774
2776
  const handlers = {};
2775
- let editHandlers = {};
2777
+ const editHandlers = {};
2778
+ const passiveHandlers = { touchstart: true, touchmove: true };
2776
2779
  class InputState {
2777
2780
  constructor() {
2778
2781
  this.shiftKey = false;
@@ -2784,6 +2787,8 @@ class InputState {
2784
2787
  this.lastSelectionTime = 0;
2785
2788
  this.lastIOSEnter = 0;
2786
2789
  this.lastIOSEnterFallbackTimeout = -1;
2790
+ this.lastFocus = 0;
2791
+ this.lastTouch = 0;
2787
2792
  this.lastAndroidDelete = 0;
2788
2793
  this.composing = false;
2789
2794
  this.composingTimeout = -1;
@@ -2801,7 +2806,7 @@ function initInput(view) {
2801
2806
  if (eventBelongsToView(view, event) && !runCustomHandler(view, event) &&
2802
2807
  (view.editable || !(event.type in editHandlers)))
2803
2808
  handler(view, event);
2804
- });
2809
+ }, passiveHandlers[event] ? { passive: true } : undefined);
2805
2810
  }
2806
2811
  // On Safari, for reasons beyond my understanding, adding an input
2807
2812
  // event handler makes an issue where the composition vanishes when
@@ -3106,6 +3111,7 @@ class MouseDown {
3106
3111
  let pos = this.pos;
3107
3112
  if (this.view.state.doc != this.startDoc)
3108
3113
  pos = this.view.posAtCoords(eventCoords(event));
3114
+ this.updateAllowDefault(event);
3109
3115
  if (this.allowDefault || !pos) {
3110
3116
  setSelectionOrigin(this.view, "pointer");
3111
3117
  }
@@ -3133,18 +3139,26 @@ class MouseDown {
3133
3139
  }
3134
3140
  }
3135
3141
  move(event) {
3136
- if (!this.allowDefault && (Math.abs(this.event.x - event.clientX) > 4 ||
3137
- Math.abs(this.event.y - event.clientY) > 4))
3138
- this.allowDefault = true;
3142
+ this.updateAllowDefault(event);
3139
3143
  setSelectionOrigin(this.view, "pointer");
3140
3144
  if (event.buttons == 0)
3141
3145
  this.done();
3142
3146
  }
3147
+ updateAllowDefault(event) {
3148
+ if (!this.allowDefault && (Math.abs(this.event.x - event.clientX) > 4 ||
3149
+ Math.abs(this.event.y - event.clientY) > 4))
3150
+ this.allowDefault = true;
3151
+ }
3143
3152
  }
3144
- handlers.touchdown = view => {
3153
+ handlers.touchstart = view => {
3154
+ view.input.lastTouch = Date.now();
3145
3155
  forceDOMFlush(view);
3146
3156
  setSelectionOrigin(view, "pointer");
3147
3157
  };
3158
+ handlers.touchmove = view => {
3159
+ view.input.lastTouch = Date.now();
3160
+ setSelectionOrigin(view, "pointer");
3161
+ };
3148
3162
  handlers.contextmenu = view => forceDOMFlush(view);
3149
3163
  function inOrNearComposition(view, event) {
3150
3164
  if (view.composing)
@@ -3396,8 +3410,6 @@ editHandlers.drop = (view, _event) => {
3396
3410
  if (!eventPos)
3397
3411
  return;
3398
3412
  let $mouse = view.state.doc.resolve(eventPos.pos);
3399
- if (!$mouse)
3400
- return;
3401
3413
  let slice = dragging && dragging.slice;
3402
3414
  if (slice) {
3403
3415
  view.someProp("transformPasted", f => { slice = f(slice); });
@@ -3442,6 +3454,7 @@ editHandlers.drop = (view, _event) => {
3442
3454
  view.dispatch(tr.setMeta("uiEvent", "drop"));
3443
3455
  };
3444
3456
  handlers.focus = view => {
3457
+ view.input.lastFocus = Date.now();
3445
3458
  if (!view.focused) {
3446
3459
  view.domObserver.stop();
3447
3460
  view.dom.classList.add("ProseMirror-focused");
@@ -3956,23 +3969,27 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op
3956
3969
  let children = oldChildren.slice();
3957
3970
  // Mark the children that are directly touched by changes, and
3958
3971
  // move those that are after the changes.
3959
- let shift = (oldStart, oldEnd, newStart, newEnd) => {
3960
- for (let i = 0; i < children.length; i += 3) {
3961
- let end = children[i + 1], dSize;
3962
- if (end < 0 || oldStart > end + oldOffset)
3963
- continue;
3964
- let start = children[i] + oldOffset;
3965
- if (oldEnd >= start) {
3966
- children[i + 1] = oldStart <= start ? -2 : -1;
3967
- }
3968
- else if (newStart >= offset && (dSize = (newEnd - newStart) - (oldEnd - oldStart))) {
3969
- children[i] += dSize;
3970
- children[i + 1] += dSize;
3972
+ for (let i = 0, baseOffset = oldOffset; i < mapping.maps.length; i++) {
3973
+ let moved = 0;
3974
+ mapping.maps[i].forEach((oldStart, oldEnd, newStart, newEnd) => {
3975
+ let dSize = (newEnd - newStart) - (oldEnd - oldStart);
3976
+ for (let i = 0; i < children.length; i += 3) {
3977
+ let end = children[i + 1];
3978
+ if (end < 0 || oldStart > end + baseOffset - moved)
3979
+ continue;
3980
+ let start = children[i] + baseOffset - moved;
3981
+ if (oldEnd >= start) {
3982
+ children[i + 1] = oldStart <= start ? -2 : -1;
3983
+ }
3984
+ else if (newStart >= offset && dSize) {
3985
+ children[i] += dSize;
3986
+ children[i + 1] += dSize;
3987
+ }
3971
3988
  }
3972
- }
3973
- };
3974
- for (let i = 0; i < mapping.maps.length; i++)
3975
- mapping.maps[i].forEach(shift);
3989
+ moved += dSize;
3990
+ });
3991
+ baseOffset = mapping.maps[i].map(baseOffset, -1);
3992
+ }
3976
3993
  // Find the child nodes that still correspond to a single node,
3977
3994
  // recursively call mapInner on them and update their positions.
3978
3995
  let mustRebuild = false;
@@ -4300,17 +4317,18 @@ class DOMObserver {
4300
4317
  }
4301
4318
  }
4302
4319
  flush() {
4303
- if (!this.view.docView || this.flushingSoon > -1)
4320
+ let { view } = this;
4321
+ if (!view.docView || this.flushingSoon > -1)
4304
4322
  return;
4305
4323
  let mutations = this.observer ? this.observer.takeRecords() : [];
4306
4324
  if (this.queue.length) {
4307
4325
  mutations = this.queue.concat(mutations);
4308
4326
  this.queue.length = 0;
4309
4327
  }
4310
- let sel = this.view.domSelection();
4311
- let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel);
4328
+ let sel = view.domSelection();
4329
+ let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
4312
4330
  let from = -1, to = -1, typeOver = false, added = [];
4313
- if (this.view.editable) {
4331
+ if (view.editable) {
4314
4332
  for (let i = 0; i < mutations.length; i++) {
4315
4333
  let result = this.registerMutation(mutations[i], added);
4316
4334
  if (result) {
@@ -4331,16 +4349,29 @@ class DOMObserver {
4331
4349
  a.remove();
4332
4350
  }
4333
4351
  }
4334
- if (from > -1 || newSel) {
4352
+ let readSel = null;
4353
+ // If it looks like the browser has reset the selection to the
4354
+ // start of the document after focus, restore the selection from
4355
+ // the state
4356
+ if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
4357
+ view.input.lastTouch < Date.now() - 300 &&
4358
+ selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
4359
+ readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
4360
+ view.input.lastFocus = 0;
4361
+ selectionToDOM(view);
4362
+ this.currentSelection.set(sel);
4363
+ view.scrollToSelection();
4364
+ }
4365
+ else if (from > -1 || newSel) {
4335
4366
  if (from > -1) {
4336
- this.view.docView.markDirty(from, to);
4337
- checkCSS(this.view);
4367
+ view.docView.markDirty(from, to);
4368
+ checkCSS(view);
4338
4369
  }
4339
4370
  this.handleDOMChange(from, to, typeOver, added);
4340
- if (this.view.docView && this.view.docView.dirty)
4341
- this.view.updateState(this.view.state);
4371
+ if (view.docView && view.docView.dirty)
4372
+ view.updateState(view.state);
4342
4373
  else if (!this.currentSelection.eq(sel))
4343
- selectionToDOM(this.view);
4374
+ selectionToDOM(view);
4344
4375
  this.currentSelection.set(sel);
4345
4376
  }
4346
4377
  }
@@ -4397,13 +4428,19 @@ class DOMObserver {
4397
4428
  }
4398
4429
  }
4399
4430
  }
4400
- let cssChecked = false;
4431
+ let cssChecked = new WeakMap();
4432
+ let cssCheckWarned = false;
4401
4433
  function checkCSS(view) {
4402
- if (cssChecked)
4434
+ if (cssChecked.has(view))
4403
4435
  return;
4404
- cssChecked = true;
4405
- if (getComputedStyle(view.dom).whiteSpace == "normal")
4436
+ cssChecked.set(view, null);
4437
+ if (['normal', 'nowrap', 'pre-line'].indexOf(getComputedStyle(view.dom).whiteSpace) !== -1) {
4438
+ view.requiresGeckoHackNode = gecko;
4439
+ if (cssCheckWarned)
4440
+ return;
4406
4441
  console["warn"]("ProseMirror expects the CSS white-space property to be set, preferably to 'pre-wrap'. It is recommended to load style/prosemirror.css from the prosemirror-view package.");
4442
+ cssCheckWarned = true;
4443
+ }
4407
4444
  }
4408
4445
 
4409
4446
  // Note that all referencing and parsing is done with the
@@ -4499,13 +4536,6 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4499
4536
  to = view.state.doc.resolve(to).after(shared + 1);
4500
4537
  let sel = view.state.selection;
4501
4538
  let parse = parseBetween(view, from, to);
4502
- // Chrome sometimes leaves the cursor before the inserted text when
4503
- // composing after a cursor wrapper. This moves it forward.
4504
- if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from) {
4505
- let text = view.cursorWrapper.deco.type.toDOM.nextSibling;
4506
- let size = text && text.nodeValue ? text.nodeValue.length : 1;
4507
- parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
4508
- }
4509
4539
  let doc = view.state.doc, compare = doc.slice(parse.from, parse.to);
4510
4540
  let preferredPos, preferredSide;
4511
4541
  // Prefer anchoring to end when Backspace is pressed
@@ -4540,6 +4570,13 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4540
4570
  return;
4541
4571
  }
4542
4572
  }
4573
+ // Chrome sometimes leaves the cursor before the inserted text when
4574
+ // composing after a cursor wrapper. This moves it forward.
4575
+ if (chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
4576
+ parse.sel.head == parse.sel.anchor) {
4577
+ let size = change.endB - change.start;
4578
+ parse.sel = { anchor: parse.sel.anchor + size, head: parse.sel.anchor + size };
4579
+ }
4543
4580
  view.input.domChangeCount++;
4544
4581
  // Handle the case where overwriting a selection by typing matches
4545
4582
  // the start or end of the selected content, creating a change
@@ -4809,6 +4846,12 @@ class EditorView {
4809
4846
  this.prevDirectPlugins = [];
4810
4847
  this.pluginViews = [];
4811
4848
  /**
4849
+ Holds `true` when a hack node is needed in Firefox to prevent the
4850
+ [space is eaten issue](https://github.com/ProseMirror/prosemirror/issues/651)
4851
+ @internal
4852
+ */
4853
+ this.requiresGeckoHackNode = false;
4854
+ /**
4812
4855
  When editor content is being dragged, this object contains
4813
4856
  information about the dragged slice and whether it is being
4814
4857
  copied or moved. At any other time, it is null.
@@ -4959,21 +5002,27 @@ class EditorView {
4959
5002
  this.dom.scrollTop = 0;
4960
5003
  }
4961
5004
  else if (scroll == "to selection") {
4962
- let startDOM = this.domSelection().focusNode;
4963
- if (this.someProp("handleScrollToSelection", f => f(this))) ;
4964
- else if (state.selection instanceof NodeSelection) {
4965
- let target = this.docView.domAfterPos(state.selection.from);
4966
- if (target.nodeType == 1)
4967
- scrollRectIntoView(this, target.getBoundingClientRect(), startDOM);
4968
- }
4969
- else {
4970
- scrollRectIntoView(this, this.coordsAtPos(state.selection.head, 1), startDOM);
4971
- }
5005
+ this.scrollToSelection();
4972
5006
  }
4973
5007
  else if (oldScrollPos) {
4974
5008
  resetScrollPos(oldScrollPos);
4975
5009
  }
4976
5010
  }
5011
+ /**
5012
+ @internal
5013
+ */
5014
+ scrollToSelection() {
5015
+ let startDOM = this.domSelection().focusNode;
5016
+ if (this.someProp("handleScrollToSelection", f => f(this))) ;
5017
+ else if (this.state.selection instanceof NodeSelection) {
5018
+ let target = this.docView.domAfterPos(this.state.selection.from);
5019
+ if (target.nodeType == 1)
5020
+ scrollRectIntoView(this, target.getBoundingClientRect(), startDOM);
5021
+ }
5022
+ else {
5023
+ scrollRectIntoView(this, this.coordsAtPos(this.state.selection.head, 1), startDOM);
5024
+ }
5025
+ }
4977
5026
  destroyPluginViews() {
4978
5027
  let view;
4979
5028
  while (view = this.pluginViews.pop())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.26.5",
3
+ "version": "1.27.2",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/decoration.ts CHANGED
@@ -550,20 +550,25 @@ function mapChildren(
550
550
 
551
551
  // Mark the children that are directly touched by changes, and
552
552
  // move those that are after the changes.
553
- let shift = (oldStart: number, oldEnd: number, newStart: number, newEnd: number) => {
554
- for (let i = 0; i < children.length; i += 3) {
555
- let end = children[i + 1] as number, dSize
556
- if (end < 0 || oldStart > end + oldOffset) continue
557
- let start = (children[i] as number) + oldOffset
558
- if (oldEnd >= start) {
559
- children[i + 1] = oldStart <= start ? -2 : -1
560
- } else if (newStart >= offset && (dSize = (newEnd - newStart) - (oldEnd - oldStart))) {
561
- ;(children[i] as number) += dSize
562
- ;(children[i + 1] as number) += dSize
553
+ for (let i = 0, baseOffset = oldOffset; i < mapping.maps.length; i++) {
554
+ let moved = 0
555
+ mapping.maps[i].forEach((oldStart: number, oldEnd: number, newStart: number, newEnd: number) => {
556
+ let dSize = (newEnd - newStart) - (oldEnd - oldStart)
557
+ for (let i = 0; i < children.length; i += 3) {
558
+ let end = children[i + 1] as number
559
+ if (end < 0 || oldStart > end + baseOffset - moved) continue
560
+ let start = (children[i] as number) + baseOffset - moved
561
+ if (oldEnd >= start) {
562
+ children[i + 1] = oldStart <= start ? -2 : -1
563
+ } else if (newStart >= offset && dSize) {
564
+ ;(children[i] as number) += dSize
565
+ ;(children[i + 1] as number) += dSize
566
+ }
563
567
  }
564
- }
568
+ moved += dSize
569
+ })
570
+ baseOffset = mapping.maps[i].map(baseOffset, -1)
565
571
  }
566
- for (let i = 0; i < mapping.maps.length; i++) mapping.maps[i].forEach(shift)
567
572
 
568
573
  // Find the child nodes that still correspond to a single node,
569
574
  // recursively call mapInner on them and update their positions.
package/src/domchange.ts CHANGED
@@ -97,14 +97,6 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
97
97
  let sel = view.state.selection
98
98
  let parse = parseBetween(view, from, to)
99
99
 
100
- // Chrome sometimes leaves the cursor before the inserted text when
101
- // composing after a cursor wrapper. This moves it forward.
102
- if (browser.chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from) {
103
- let text = (view.cursorWrapper.deco.type as any).toDOM.nextSibling as DOMNode
104
- let size = text && text.nodeValue ? text.nodeValue.length : 1
105
- parse.sel = {anchor: parse.sel.anchor + size, head: parse.sel.anchor + size}
106
- }
107
-
108
100
  let doc = view.state.doc, compare = doc.slice(parse.from, parse.to)
109
101
  let preferredPos, preferredSide: "start" | "end"
110
102
  // Prefer anchoring to end when Backspace is pressed
@@ -137,6 +129,14 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
137
129
  return
138
130
  }
139
131
  }
132
+ // Chrome sometimes leaves the cursor before the inserted text when
133
+ // composing after a cursor wrapper. This moves it forward.
134
+ if (browser.chrome && view.cursorWrapper && parse.sel && parse.sel.anchor == view.cursorWrapper.deco.from &&
135
+ parse.sel.head == parse.sel.anchor) {
136
+ let size = change.endB - change.start
137
+ parse.sel = {anchor: parse.sel.anchor + size, head: parse.sel.anchor + size}
138
+ }
139
+
140
140
  view.input.domChangeCount++
141
141
  // Handle the case where overwriting a selection by typing matches
142
142
  // the start or end of the selected content, creating a change
package/src/domcoords.ts CHANGED
@@ -341,7 +341,7 @@ export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
341
341
  else if (side >= 0 && offset == node.nodeValue!.length) { from--; takeSide = 1 }
342
342
  else if (side < 0) { from-- }
343
343
  else { to ++ }
344
- return flattenV(singleRect(textRange(node as Text, from, to), takeSide), takeSide < 0)
344
+ return flattenV(singleRect(textRange(node as Text, from, to), 1), takeSide < 0)
345
345
  }
346
346
  }
347
347
 
@@ -1,6 +1,7 @@
1
+ import {Selection} from "prosemirror-state"
1
2
  import * as browser from "./browser"
2
- import {domIndex, isEquivalentPosition} from "./dom"
3
- import {hasFocusAndSelection, selectionToDOM} from "./selection"
3
+ import {domIndex, isEquivalentPosition, selectionCollapsed, DOMSelection} from "./dom"
4
+ import {hasFocusAndSelection, selectionToDOM, selectionFromDOM} from "./selection"
4
5
  import {EditorView} from "./index"
5
6
 
6
7
  const observeOptions = {
@@ -20,7 +21,7 @@ class SelectionState {
20
21
  focusNode: Node | null = null
21
22
  focusOffset: number = 0
22
23
 
23
- set(sel: Selection) {
24
+ set(sel: DOMSelection) {
24
25
  this.anchorNode = sel.anchorNode; this.anchorOffset = sel.anchorOffset
25
26
  this.focusNode = sel.focusNode; this.focusOffset = sel.focusOffset
26
27
  }
@@ -29,7 +30,7 @@ class SelectionState {
29
30
  this.anchorNode = this.focusNode = null
30
31
  }
31
32
 
32
- eq(sel: Selection) {
33
+ eq(sel: DOMSelection) {
33
34
  return sel.anchorNode == this.anchorNode && sel.anchorOffset == this.anchorOffset &&
34
35
  sel.focusNode == this.focusNode && sel.focusOffset == this.focusOffset
35
36
  }
@@ -138,7 +139,7 @@ export class DOMObserver {
138
139
  this.currentSelection.set(this.view.domSelection())
139
140
  }
140
141
 
141
- ignoreSelectionChange(sel: Selection) {
142
+ ignoreSelectionChange(sel: DOMSelection) {
142
143
  if (sel.rangeCount == 0) return true
143
144
  let container = sel.getRangeAt(0).commonAncestorContainer
144
145
  let desc = this.view.docView.nearestDesc(container)
@@ -152,18 +153,19 @@ export class DOMObserver {
152
153
  }
153
154
 
154
155
  flush() {
155
- if (!this.view.docView || this.flushingSoon > -1) return
156
+ let {view} = this
157
+ if (!view.docView || this.flushingSoon > -1) return
156
158
  let mutations = this.observer ? this.observer.takeRecords() : []
157
159
  if (this.queue.length) {
158
160
  mutations = this.queue.concat(mutations)
159
161
  this.queue.length = 0
160
162
  }
161
163
 
162
- let sel = this.view.domSelection()
163
- let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel)
164
+ let sel = view.domSelection()
165
+ let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
164
166
 
165
167
  let from = -1, to = -1, typeOver = false, added: Node[] = []
166
- if (this.view.editable) {
168
+ if (view.editable) {
167
169
  for (let i = 0; i < mutations.length; i++) {
168
170
  let result = this.registerMutation(mutations[i], added)
169
171
  if (result) {
@@ -183,14 +185,26 @@ export class DOMObserver {
183
185
  }
184
186
  }
185
187
 
186
- if (from > -1 || newSel) {
188
+ let readSel: Selection | null = null
189
+ // If it looks like the browser has reset the selection to the
190
+ // start of the document after focus, restore the selection from
191
+ // the state
192
+ if (from < 0 && newSel && view.input.lastFocus > Date.now() - 200 &&
193
+ view.input.lastTouch < Date.now() - 300 &&
194
+ selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) &&
195
+ readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
196
+ view.input.lastFocus = 0
197
+ selectionToDOM(view)
198
+ this.currentSelection.set(sel)
199
+ view.scrollToSelection()
200
+ } else if (from > -1 || newSel) {
187
201
  if (from > -1) {
188
- this.view.docView.markDirty(from, to)
189
- checkCSS(this.view)
202
+ view.docView.markDirty(from, to)
203
+ checkCSS(view)
190
204
  }
191
205
  this.handleDOMChange(from, to, typeOver, added)
192
- if (this.view.docView && this.view.docView.dirty) this.view.updateState(this.view.state)
193
- else if (!this.currentSelection.eq(sel)) selectionToDOM(this.view)
206
+ if (view.docView && view.docView.dirty) view.updateState(view.state)
207
+ else if (!this.currentSelection.eq(sel)) selectionToDOM(view)
194
208
  this.currentSelection.set(sel)
195
209
  }
196
210
  }
@@ -243,11 +257,16 @@ export class DOMObserver {
243
257
  }
244
258
  }
245
259
 
246
- let cssChecked = false
260
+ let cssChecked: WeakMap<EditorView, null> = new WeakMap()
261
+ let cssCheckWarned: boolean = false
247
262
 
248
263
  function checkCSS(view: EditorView) {
249
- if (cssChecked) return
250
- cssChecked = true
251
- if (getComputedStyle(view.dom).whiteSpace == "normal")
264
+ if (cssChecked.has(view)) return
265
+ cssChecked.set(view, null)
266
+ if (['normal', 'nowrap', 'pre-line'].indexOf(getComputedStyle(view.dom).whiteSpace) !== -1) {
267
+ view.requiresGeckoHackNode = browser.gecko
268
+ if (cssCheckWarned) return
252
269
  console["warn"]("ProseMirror expects the CSS white-space property to be set, preferably to 'pre-wrap'. It is recommended to load style/prosemirror.css from the prosemirror-view package.")
270
+ cssCheckWarned = true
271
+ }
253
272
  }