-
Notifications
You must be signed in to change notification settings - Fork 266
/
Copy pathdocumentListeners.js
127 lines (110 loc) · 4.07 KB
/
documentListeners.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
123
124
125
126
127
var textDiff = require('./textDiff');
exports.add = addDocumentListeners;
exports.inputSupportsSelection = inputSupportsSelection;
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#do-not-apply
// TODO: Date types support
function inputSupportsSelection(input) {
var type = input.type;
return (
type === 'text' ||
type === 'search' ||
type === 'url' ||
type === 'tel' ||
type === 'password'
);
}
function inputIsNumberValue(input) {
var type = input.type;
return (type === 'number' || (type === 'range' && !input.multiple));
}
function inputValue(input) {
return inputIsNumberValue(input) ? input.valueAsNumber : input.value;
}
function addDocumentListeners(doc) {
doc.addEventListener('input', documentInput, true);
doc.addEventListener('change', documentChange, true);
// Listen to more events for versions of IE with buggy input event implementations
if (parseFloat(window.navigator.appVersion.split('MSIE ')[1]) <= 9) {
// We're listening on selectionchange because there's no other event emitted when
// the user clicks 'delete' from a context menu when right clicking on selected text.
// So although this event fires overly aggressively, it's the only real way
// to ensure that we can detect all changes to the input value in IE <= 9
doc.addEventListener('selectionchange', function(e){
if (document.activeElement) {
documentInput({target: document.activeElement}); // selectionchange evts don't have the e.target we need
}
}, true);
} else { //cannot set input.type to 'number' in IE9
// For some reason valueAsNumber returns NaN for number inputs in IE
// until a new IE version that handles this is released, parse input.value as a fallback
var input = document.createElement('input');
input.type = 'number';
input.value = '7';
if (input.valueAsNumber !== input.valueAsNumber) {
var oldInputValue = inputValue;
inputValue = function(input) {
if (input.type === 'number') {
return inputIsNumberValue(input) ? parseFloat(input.value) : input.value;
} else {
return oldInputValue.apply(this, arguments);
}
};
}
}
}
function documentInput(e) {
var target = e.target;
if (target.tagName === 'INPUT') {
setInputValue(e, target);
} else if (target.tagName === 'TEXTAREA' && target.childNodes.length === 1) {
var binding = target.firstChild && target.firstChild.$bindNode;
if (!binding || binding.isUnbound()) return;
var pass = {$event: e};
textDiffBinding(binding, target.value, pass);
}
}
function documentChange(e) {
var target = e.target;
if (target.tagName === 'INPUT') {
setBoundProperty(target, 'checked');
setInputValue(e, target);
} else if (target.tagName === 'SELECT') {
setOptionBindings(target);
}
}
function setBoundProperty(node, property) {
var binding = node.$bindAttributes && node.$bindAttributes[property];
if (!binding || binding.isUnbound()) return;
var value = node[property];
binding.template.expression.set(binding.context, value);
}
function setInputValue(e, target) {
var binding = target.$bindAttributes && target.$bindAttributes.value;
if (!binding || binding.isUnbound()) return;
if (inputSupportsSelection(target)) {
var pass = {$event: e};
textDiffBinding(binding, target.value, pass);
} else {
var value = inputValue(target);
binding.template.expression.set(binding.context, value);
}
}
function textDiffBinding(binding, value, pass) {
var expression = binding.template.expression;
var segments = expression.pathSegments(binding.context);
if (segments) {
var model = binding.context.controller.model.pass(pass);
textDiff.onTextInput(model, segments, value);
} else if (expression.set) {
expression.set(binding.context, value);
}
}
function setOptionBindings(parent) {
for (var node = parent.firstChild; node; node = node.nextSibling) {
if (node.tagName === 'OPTION') {
setBoundProperty(node, 'selected');
} else if (node.hasChildNodes()) {
setOptionBindings(node);
}
}
}