forked from 2is10/selectionchange-polyfill
-
Notifications
You must be signed in to change notification settings - Fork 0
/
selectionchange.js
122 lines (106 loc) · 3.17 KB
/
selectionchange.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// github.com/2is10/selectionchange-polyfill
var selectionchange = (function (undefined) {
var SELECT_ALL_MODIFIER = /^Mac/.test(navigator.platform) ? 'metaKey' : 'ctrlKey';
var RANGE_PROPS = ['startContainer', 'startOffset', 'endContainer', 'endOffset'];
var ranges;
return {
start: function (doc) {
var d = doc || document;
if (ranges || !hasNativeSupport(d) && (ranges = newWeakMap())) {
if (!ranges.has(d)) {
ranges.set(d, getSelectionRange(d));
on(d, 'keydown', onKeyDown);
on(d, 'mousedown', onMouseDown);
on(d, 'mousemove', onMouseMove);
on(d, 'mouseup', onMouseUp);
on(d.defaultView, 'focus', onFocus);
}
}
},
stop: function (doc) {
var d = doc || document;
if (ranges && ranges.has(d)) {
ranges['delete'](d);
off(d, 'keydown', onKeyDown);
off(d, 'mousedown', onMouseDown);
off(d, 'mousemove', onMouseMove);
off(d, 'mouseup', onMouseUp);
off(d.defaultView, 'focus', onFocus);
}
}
};
function hasNativeSupport(doc) {
var osc = doc.onselectionchange;
if (osc !== undefined) {
try {
doc.onselectionchange = 0;
return doc.onselectionchange === null;
} catch (e) {
} finally {
doc.onselectionchange = osc;
}
}
return false;
}
function newWeakMap() {
if (typeof WeakMap !== 'undefined') {
return new WeakMap();
} else {
console.error('selectionchange: WeakMap not supported');
return null;
}
}
function getSelectionRange(doc) {
var s = doc.getSelection();
return s.rangeCount ? s.getRangeAt(0) : null;
}
function on(el, eventType, handler) {
el.addEventListener(eventType, handler, true);
}
function off(el, eventType, handler) {
el.removeEventListener(eventType, handler, true);
}
function onKeyDown(e) {
var code = e.keyCode;
if (code === 65 && e[SELECT_ALL_MODIFIER] && !e.shiftKey && !e.altKey || // Ctrl-A or Cmd-A
(code <= 40 && code >= 37) && e.shiftKey) { // (Alt-)Shift-arrow
setTimeout(dispatchIfChanged.bind(null, this), 0);
}
}
function onMouseDown(e) {
if (e.button === 0) {
on(this, 'mousemove', onMouseMove);
setTimeout(dispatchIfChanged.bind(null, this), 0);
}
}
function onMouseMove(e) { // only needed while primary button is down
if (e.buttons & 1) {
dispatchIfChanged(this);
} else {
off(this, 'mousemove', onMouseMove);
}
}
function onMouseUp(e) {
if (e.button === 0) {
setTimeout(dispatchIfChanged.bind(null, this), 0);
} else {
off(this, 'mousemove', onMouseMove);
}
}
function onFocus() {
setTimeout(dispatchIfChanged.bind(null, this.document), 0);
}
function dispatchIfChanged(doc) {
var rOld = ranges.get(doc);
var rNew = getSelectionRange(doc);
if (!sameRange(rNew, rOld)) {
ranges.set(doc, rNew);
setTimeout(doc.dispatchEvent.bind(doc, new Event('selectionchange')), 0);
}
}
function sameRange(r1, r2) {
return r1 === r2 || r1 && r2 && RANGE_PROPS.every(function (prop) {
return r1[prop] === r2[prop];
});
}
})();