forked from elm/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Keyboard.js
154 lines (134 loc) · 4.72 KB
/
Keyboard.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Elm.Native.Keyboard = {};
Elm.Native.Keyboard.make = function(elm) {
elm.Native = elm.Native || {};
elm.Native.Keyboard = elm.Native.Keyboard || {};
if (elm.Native.Keyboard.values) return elm.Native.Keyboard.values;
// Duplicated from Native.Signal
function send(node, timestep, changed) {
var kids = node.kids;
for (var i = kids.length; i--; ) {
kids[i].recv(timestep, changed, node.id);
}
}
var Signal = Elm.Signal.make(elm);
var NList = Elm.Native.List.make(elm);
var Utils = Elm.Native.Utils.make(elm);
var downEvents = Signal.constant(null);
var upEvents = Signal.constant(null);
var blurEvents = Signal.constant(null);
elm.addListener([downEvents.id], document, 'keydown', function down(e) {
elm.notify(downEvents.id, e);
});
elm.addListener([upEvents.id], document, 'keyup', function up(e) {
elm.notify(upEvents.id, e);
});
elm.addListener([blurEvents.id], window, 'blur', function blur(e) {
elm.notify(blurEvents.id, null);
});
function state(alt, meta, keyCodes) {
return {
alt: alt,
meta: meta,
keyCodes: keyCodes
};
}
var emptyState = state(false, false, NList.Nil);
function KeyMerge(down, up, blur) {
var args = [down,up,blur];
this.id = Utils.guid();
// Ignore starting values here
this.value = emptyState;
this.kids = [];
var n = args.length;
var count = 0;
var isChanged = false;
this.recv = function(timestep, changed, parentID) {
++count;
if (changed) {
// We know this a change must only be one of the following cases
if (parentID === down.id && !A2(NList.member, down.value.keyCode, this.value.keyCodes)) {
isChanged = true;
var v = down.value;
var newCodes = NList.Cons(v.keyCode, this.value.keyCodes);
this.value = state(v.altKey, v.metaKey, newCodes);
}
else if (parentID === up.id) {
isChanged = true;
var v = up.value;
var notEq = function(kc) { return kc !== v.keyCode };
var newCodes = A2(NList.filter, notEq, this.value.keyCodes);
this.value = state(v.altKey, v.metaKey, newCodes);
}
else if (parentID === blur.id) {
isChanged = true;
this.value = emptyState;
}
}
if (count == n) {
send(this, timestep, isChanged);
isChanged = false;
count = 0;
}
};
for (var i = n; i--; ) {
args[i].kids.push(this);
args[i].defaultNumberOfKids += 1;
}
}
var keyMerge = new KeyMerge(downEvents,upEvents,blurEvents);
// select a part of a keyMerge and dropRepeats the result
function keySignal(f) {
var signal = A2(Signal.map, f, keyMerge);
// must set the default number of kids to make it possible to filter
// these signals if they are not actually used.
keyMerge.defaultNumberOfKids += 1;
signal.defaultNumberOfKids = 1;
var filtered = Signal.dropRepeats(signal);
filtered.defaultNumberOfKids = 0;
return filtered;
}
// break keyMerge into parts
var keysDown = keySignal(function getKeyCodes(v) {
return v.keyCodes;
});
var alt = keySignal(function getKeyCodes(v) {
return v.alt;
});
var meta = keySignal(function getKeyCodes(v) {
return v.meta;
});
function dir(up, down, left, right) {
function toDirections(state) {
var keyCodes = state.keyCodes;
var x = 0, y = 0;
while (keyCodes.ctor === "::") {
switch (keyCodes._0) {
case left : --x; break;
case right: ++x; break;
case up : ++y; break;
case down : --y; break;
}
keyCodes = keyCodes._1;
}
return { _:{}, x:x, y:y };
}
return keySignal(toDirections);
}
function is(key) {
return keySignal(function(v) {
return A2( NList.member, key, v.keyCodes );
});
}
var lastPressed = A2(Signal.map, function(e) {
return e ? e.keyCode : 0;
}, downEvents);
downEvents.defaultNumberOfKids += 1;
return elm.Native.Keyboard.values = {
isDown:is,
alt: alt,
meta: meta,
directions:F4(dir),
keysDown:keysDown,
lastPressed:lastPressed
};
};