on 89bbbb9 help sign in
elk/libraries/Native/Signal/Keyboard.js
151 lines 4.7 kB history raw
1Elm.Native.Keyboard = {};
2Elm.Native.Keyboard.make = function(elm) {
4 elm.Native = elm.Native || {};
5 elm.Native.Keyboard = elm.Native.Keyboard || {};
6 if (elm.Native.Keyboard.values) return elm.Native.Keyboard.values;
8 // Duplicated from Native.Signal
9 function send(node, timestep, changed) {
10 var kids = node.kids;
11 for (var i = kids.length; i--; ) {
12 kids[i].recv(timestep, changed, node.id);
13 }
14 }
16 var Signal = Elm.Signal.make(elm);
17 var NList = Elm.Native.List.make(elm);
18 var Utils = Elm.Native.Utils.make(elm);
20 var downEvents = Signal.constant(null);
21 var upEvents = Signal.constant(null);
22 var blurEvents = Signal.constant(null);
24 elm.addListener([downEvents.id], document, 'keydown', function down(e) {
25 elm.notify(downEvents.id, e);
26 });
28 elm.addListener([upEvents.id], document, 'keyup', function up(e) {
29 elm.notify(upEvents.id, e);
30 });
32 elm.addListener([blurEvents.id], window, 'blur', function blur(e) {
33 elm.notify(blurEvents.id, null);
34 });
36 function state(alt, meta, keyCodes) {
37 return {
38 alt: alt,
39 meta: meta,
40 keyCodes: keyCodes
41 };
42 }
43 var emptyState = state(false, false, NList.Nil);
45 function KeyMerge(down, up, blur) {
46 var args = [down,up,blur];
47 this.id = Utils.guid();
48 // Ignore starting values here
49 this.value = emptyState;
50 this.kids = [];
52 var n = args.length;
53 var count = 0;
54 var isChanged = false;
56 this.recv = function(timestep, changed, parentID) {
57 ++count;
58 if (changed) {
59 // We know this a change must only be one of the following cases
60 if (parentID === down.id && !A2(NList.member, down.value.keyCode, this.value.keyCodes)) {
61 isChanged = true;
62 var v = down.value;
63 var newCodes = NList.Cons(v.keyCode, this.value.keyCodes);
64 this.value = state(v.altKey, v.metaKey, newCodes);
65 }
66 else if (parentID === up.id) {
67 isChanged = true;
68 var v = up.value;
69 var notEq = function(kc) { return kc !== v.keyCode };
70 var newCodes = A2(NList.filter, notEq, this.value.keyCodes);
71 this.value = state(v.altKey, v.metaKey, newCodes);
72 }
73 else if (parentID === blur.id) {
74 isChanged = true;
75 this.value = emptyState;
76 }
77 }
78 if (count == n) {
79 send(this, timestep, isChanged);
80 isChanged = false;
81 count = 0;
82 }
83 };
85 for (var i = n; i--; ) {
86 args[i].kids.push(this);
87 args[i].defaultNumberOfKids += 1;
88 }
89 }
91 var keyMerge = new KeyMerge(downEvents,upEvents,blurEvents);
93 // select a part of a keyMerge and dropRepeats the result
94 function keySignal(f) {
95 var signal = A2(Signal.lift, f, keyMerge);
96 // must set the default number of kids to make it possible to filter
97 // these signals if they are not actually used.
98 keyMerge.defaultNumberOfKids += 1;
99 signal.defaultNumberOfKids = 1;
100 var filtered = Signal.dropRepeats(signal);
101 filtered.defaultNumberOfKids = 0;
102 return filtered;
103 }
105 // break keyMerge into parts
106 var keysDown = keySignal(function getKeyCodes(v) {
107 return v.keyCodes;
108 });
109 var alt = keySignal(function getKeyCodes(v) {
110 return v.alt;
111 });
112 var meta = keySignal(function getKeyCodes(v) {
113 return v.meta;
114 });
116 function dir(up, down, left, right) {
117 function toDirections(state) {
118 var keyCodes = state.keyCodes;
119 var x = 0, y = 0;
120 while (keyCodes.ctor === "::") {
121 switch (keyCodes._0) {
122 case left : --x; break;
123 case right: ++x; break;
124 case up : ++y; break;
125 case down : --y; break;
126 }
127 keyCodes = keyCodes._1;
128 }
129 return { _:{}, x:x, y:y };
130 }
131 return keySignal(toDirections);
132 }
134 function is(key) {
135 return keySignal(function(v) {
136 return A2( NList.member, key, v.keyCodes );
137 });
138 }
140 var lastPressed = downEvents;
142 return elm.Native.Keyboard.values = {
143 isDown:is,
144 alt: alt,
145 meta: meta,
146 directions:F4(dir),
147 keysDown:keysDown,
148 lastPressed:lastPressed
149 };
151};