Skip to content

Commit 2c7788f

Browse files
authored
Merge pull request #163 from livingdocsIO/fix-async-ranges-on-focus
Fix: Async ranges on focus
2 parents c2107cc + 337eb85 commit 2c7788f

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

spec/create-default-events.spec.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import $ from 'jquery'
2+
import rangy from 'rangy'
3+
4+
import Cursor from '../src/cursor'
5+
import Editable from '../src/core'
6+
7+
describe('Default Events', () => {
8+
9+
// create a Cursor object and set the selection to it
10+
function createCursor ($elem, range) {
11+
const cursor = new Cursor($elem[0], range)
12+
cursor.setSelection()
13+
return cursor
14+
}
15+
16+
function createRangeAtEnd (node) {
17+
const range = rangy.createRange()
18+
range.selectNodeContents(node)
19+
range.collapse(false)
20+
return range
21+
}
22+
23+
let onListener
24+
// register one listener per test
25+
function on (editable, eventName, func) {
26+
// off() // make sure the last listener is unregistered
27+
const obj = { calls: 0 }
28+
function proxy () {
29+
obj.calls += 1
30+
func.apply(this, arguments)
31+
}
32+
onListener = { event: eventName, listener: proxy }
33+
editable.on(eventName, proxy)
34+
return obj
35+
}
36+
37+
// unregister the event listener registered with 'on'
38+
function off (editable) {
39+
if (onListener) {
40+
editable.unload()
41+
onListener = undefined
42+
}
43+
}
44+
45+
describe('for editable', () => {
46+
47+
48+
describe('on focus', () => {
49+
50+
beforeEach(function () {
51+
this.focus = $.Event('focus')
52+
this.blur = $.Event('blur')
53+
this.$elem = $('<div />')
54+
$(document.body).append(this.$elem)
55+
this.editable = new Editable()
56+
this.editable.add(this.$elem)
57+
this.$elem.focus()
58+
})
59+
afterEach(function () {
60+
off(this.editable)
61+
this.editable.dispatcher.off()
62+
this.$elem.remove()
63+
})
64+
65+
it('always dispatches with virtual and native ranges in sync.', function () {
66+
// <div>foo\</div>
67+
this.$elem.html('foo')
68+
createCursor(this.$elem, createRangeAtEnd(this.$elem[0]))
69+
70+
const onFocus = on(this.editable, 'focus', (element, selection) => {
71+
if (!selection) return
72+
expect(element).toEqual(this.$elem[0])
73+
const range = selection.range
74+
const nativeRange = range.nativeRange
75+
expect(range.startContainer).toEqual(nativeRange.startContainer)
76+
expect(range.endContainer).toEqual(nativeRange.endContainer)
77+
expect(range.startOffset).toEqual(nativeRange.startOffset)
78+
expect(range.endOffset).toEqual(nativeRange.endOffset)
79+
})
80+
81+
this.$elem.trigger(this.focus)
82+
this.$elem.trigger(this.blur)
83+
this.$elem.trigger(this.focus)
84+
expect(onFocus.calls).toEqual(2)
85+
})
86+
})
87+
})
88+
})

src/dispatcher.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export default class Dispatcher {
8484
this.$document
8585
.on('focus.editable', selector, function (event) {
8686
if (this.getAttribute(config.pastingAttribute)) return
87+
self.selectionWatcher.syncSelection()
8788
self.notify('focus', this)
8889
})
8990

src/selection-watcher.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ export default class SelectionWatcher {
2222
this.currentRange = undefined
2323
}
2424

25+
/**
26+
* Updates the internal selection pointer to the current rangy selection.
27+
*/
28+
syncSelection () {
29+
this.rangySelection = rangy.getSelection(this.win)
30+
}
31+
2532
/**
2633
* Return a RangeContainer if the current selection is within an editable
2734
* otherwise return an empty RangeContainer
2835
*/
2936
getRangeContainer () {
30-
this.rangySelection = rangy.getSelection(this.win)
37+
this.syncSelection()
3138

3239
// rangeCount is 0 or 1 in all browsers except firefox
3340
// firefox can work with multiple ranges

0 commit comments

Comments
 (0)