@@ -1,303 +1,367 @@
1Elm.Native.Graphics.Input = {};
1Elm.Native.Graphics.Input = {};
2Elm.Native.Graphics.Input.make = function(elm) {
2Elm.Native.Graphics.Input.make = function(elm) {
4− elm.Native = elm.Native || {};
3+ elm.Native = elm.Native || {};
5− elm.Native.Graphics = elm.Native.Graphics || {};
4+ elm.Native.Graphics = elm.Native.Graphics || {};
6− elm.Native.Graphics.Input = elm.Native.Graphics.Input || {};
5+ elm.Native.Graphics.Input = elm.Native.Graphics.Input || {};
7− if (elm.Native.Graphics.Input.values) return elm.Native.Graphics.Input.values;
6+ if (elm.Native.Graphics.Input.values) return elm.Native.Graphics.Input.values;
9− var Render = ElmRuntime.use(ElmRuntime.Render.Element);
8+ var Render = ElmRuntime.use(ElmRuntime.Render.Element);
10− var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
9+ var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
12− var Signal = Elm.Signal.make(elm);
11+ var Signal = Elm.Signal.make(elm);
13− var newElement = Elm.Graphics.Element.make(elm).newElement;
12+ var newElement = Elm.Graphics.Element.make(elm).newElement;
14− var JS = Elm.Native.JavaScript.make(elm);
13+ var JS = Elm.Native.JavaScript.make(elm);
15− var Utils = Elm.Native.Utils.make(elm);
14+ var Utils = Elm.Native.Utils.make(elm);
16− var Tuple2 = Utils.Tuple2;
15+ var Tuple2 = Utils.Tuple2;
17+ function input(initialValue) {
18+ var signal = Signal.constant(initialValue);
19+ return { _:{}, signal:signal, handle:signal };
18− function dropDown(values) {
22+ function renderDropDown(signal, values) {
19− var entries = JS.fromList(values);
24+ var entries = JS.fromList(values);
20− var events = Signal.constant(entries[0]._1);
25+
22− var drop = newNode('select');
26+ var drop = newNode('select');
23− drop.style.border = '0 solid';
27+ drop.style.border = '0 solid';
28+ drop.style.pointerEvents = 'auto';
24− for (var i = 0; i < entries.length; ++i) {
29+ for (var i = 0; i < entries.length; ++i) {
25− var option = newNode('option');
30+ var option = newNode('option');
26− var name = JS.fromString(entries[i]._0);
31+ var name = JS.fromString(entries[i]._0);
27− option.value = name;
32+ option.value = name;
28− option.innerHTML = name;
33+ option.innerHTML = name;
29− drop.appendChild(option);
34+ drop.appendChild(option);
31− drop.addEventListener('change', function() {
36+ drop.addEventListener('change', function() {
32− elm.notify(events.id, entries[drop.selectedIndex]._1);
37+ elm.notify(signal.id, entries[drop.selectedIndex]._1);
35− var t = drop.cloneNode(true);
40+ var t = drop.cloneNode(true);
36− t.style.visibility = "hidden";
41+ t.style.visibility = "hidden";
38− elm.node.appendChild(t);
43+ elm.node.appendChild(t);
39− var style = window.getComputedStyle(t, null);
44+ var style = window.getComputedStyle(t, null);
40− var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
45+ var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
41− var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
46+ var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
42− elm.node.removeChild(t);
47+ elm.node.removeChild(t);
52+ function updateDropDown(node, oldModel, newModel) {
55+ function dropDown(signal, values) {
44− var element = A3(newElement, w, h, {
56+ return A3(newElement, 100, 24, {
45− ctor: 'Custom',
57+ ctor: 'Custom',
46− type: 'DropDown',
58+ type: 'DropDown',
47− render: function render(model) { return drop; },
59+ render: renderDropDown(signal,values),
48− update: function update(node, oldModel, newModel) {},
60+ update: updateDropDown,
49− model: {}
61+ model: {}
52− return Tuple2(Signal.constant(element), events);
55− function buttons(defaultValue) {
65+ function renderButton(model) {
56− var events = Signal.constant(defaultValue);
66+ var node = newNode('button');
57−
67+ node.style.display = 'block';
58− function render(model) {
68+ node.style.pointerEvents = 'auto';
59− var b = newNode('button');
69+ node.elm_signal = model.signal;
60− b.style.display = 'block';
70+ node.elm_value = model.value;
61− b.elmEvent = model.event;
62− function click() { elm.notify(events.id, b.elmEvent); }
71+ function click() {
72+ elm.notify(node.elm_signal.id, node.elm_value);
63− b.addEventListener('click', click);
74+ node.addEventListener('click', click);
64− b.innerHTML = model.text;
75+ node.innerHTML = model.text;
65− return b;
76+ return node;
68− function update(node, oldModel, newModel) {
79+ function updateButton(node, oldModel, newModel) {
69− node.elmEvent = newModel.event;
80+ node.elm_signal = newModel.signal;
81+ node.elm_value = nemModel.value;
70− var txt = newModel.text;
82+ var txt = newModel.text;
71− if (oldModel.text !== txt) node.innerHTML = txt;
83+ if (oldModel.text !== txt) node.innerHTML = txt;
74− function button(evnt, txt) {
86+ function button(signal, value, text) {
75− return A3(newElement, 100, 40, {
87+ return A3(newElement, 100, 40, {
76− ctor: 'Custom',
88+ ctor: 'Custom',
77− type: 'Button',
89+ type: 'Button',
78− render: render,
90+ render: renderButton,
79− update: update,
91+ update: updateButton,
80− model: { event:evnt, text:JS.fromString(txt) }
92+ model: { signal:signal, value:value, text:JS.fromString(text) }
84− return { _:{}, button:F2(button), events:events };
96+ function renderCustomButton(model) {
85− }
97+ var btn = newNode('div');
86−
98+ btn.style.pointerEvents = 'auto';
87− function customButtons(defaultValue) {
99+ btn.elm_signal = model.signal;
88− var events = Signal.constant(defaultValue);
100+ btn.elm_value = model.value;
90− function render(model) {
91− var btn = newNode('div');
92− btn.elmEvent = model.event;
94− btn.elmUp = Render.render(model.up);
102+ btn.elm_up = Render.render(model.up);
95− btn.elmHover = Render.render(model.hover);
103+ btn.elm_hover = Render.render(model.hover);
96− btn.elmDown = Render.render(model.down);
104+ btn.elm_down = Render.render(model.down);
98− function replace(node) {
106+ function replace(node) {
107+ if (node !== btn.firstChild) {
99− if (node !== btn.firstChild) btn.replaceChild(node, btn.firstChild);
108+ btn.replaceChild(node, btn.firstChild);
101− var overCount = 0;
111+ var overCount = 0;
102− function over(e) {
112+ function over(e) {
103− if (overCount++ > 0) return;
113+ if (overCount++ > 0) return;
104− replace(btn.elmHover);
114+ replace(btn.elm_hover);
106− function out(e) {
116+ function out(e) {
107− if (btn.contains(e.toElement || e.relatedTarget)) return;
117+ if (btn.contains(e.toElement || e.relatedTarget)) return;
108− overCount = 0;
118+ overCount = 0;
109− replace(btn.elmUp);
119+ replace(btn.elm_up);
111− function up() {
121+ function up() {
112− replace(btn.elmHover);
122+ replace(btn.elm_hover);
113− elm.notify(events.id, btn.elmEvent);
123+ elm.notify(btn.elm_signal.id, btn.elm_value);
115− function down() { replace(btn.elmDown); }
125+ function down() {
126+ replace(btn.elm_down);
116− btn.addEventListener('mouseover', over);
128+ btn.addEventListener('mouseover', over);
117− btn.addEventListener('mouseout' , out);
129+ btn.addEventListener('mouseout' , out);
118− btn.addEventListener('mousedown', down);
130+ btn.addEventListener('mousedown', down);
119− btn.addEventListener('mouseup' , up);
131+ btn.addEventListener('mouseup' , up);
121− btn.appendChild(btn.elmUp);
133+ btn.appendChild(btn.elm_up);
123− var clicker = newNode('div');
135+ var clicker = newNode('div');
124− clicker.style.width = btn.elmUp.style.width;
136+ clicker.style.width = btn.elm_up.style.width;
125− clicker.style.height = btn.elmUp.style.height;
137+ clicker.style.height = btn.elm_up.style.height;
126− clicker.style.position = 'absolute';
138+ clicker.style.position = 'absolute';
127− clicker.style.top = 0;
139+ clicker.style.top = 0;
128− btn.appendChild(clicker);
140+ btn.appendChild(clicker);
130− return btn;
142+ return btn;
133− function update(node, oldModel, newModel) {
145+ function updateCustomButton(node, oldModel, newModel) {
134− node.elmEvent = newModel.event;
146+ var signal = newModel.signal;
147+ node.elm_up.elm_signal = signal;
148+ node.elm_hover.elm_signal = signal;
149+ node.elm_down.elm_signal = signal;
151+ var value = newModel.value;
152+ node.elm_up.elm_value = value;
153+ node.elm_hover.elm_value = value;
154+ node.elm_down.elm_value = value;
135− Render.update(node.elmUp, oldModel.up, newModel.up)
156+ Render.update(node.elm_up, oldModel.up, newModel.up)
136− Render.update(node.elmHover, oldModel.hover, newModel.hover)
157+ Render.update(node.elm_hover, oldModel.hover, newModel.hover)
137− Render.update(node.elmDown, oldModel.down, newModel.down)
158+ Render.update(node.elm_down, oldModel.down, newModel.down)
161+ function max3(a,b,c) {
162+ var ab = a > b ? a : b;
163+ return ab > c ? ab : c;
140− function button(evnt, up, hover, down) {
166+ function customButton(signal, value, up, hover, down) {
141− return A3(newElement,
167+ return A3(newElement,
142− Math.max(up.props.width, hover.props.width, down.props.width),
168+ max3(up.props.width, hover.props.width, down.props.width),
143− Math.max(up.props.height, hover.props.height, down.props.height),
169+ max3(up.props.height, hover.props.height, down.props.height),
144− { ctor: 'Custom',
170+ { ctor: 'Custom',
145− type: 'CustomButton',
171+ type: 'CustomButton',
146− render: render,
172+ render: renderCustomButton,
147− update: update,
173+ update: updateCustomButton,
148− model: { event:evnt, up:up, hover:hover, down:down }
174+ model: { signal:signal, value:value, up:up, hover:hover, down:down }
152− return { _:{}, customButton:F4(button), events:events };
155−
178+ function renderCheckbox(model) {
156− function hoverables(defaultValue) {
179+ var node = newNode('input');
157− var events = Signal.constant(defaultValue);
180+ node.type = 'checkbox';
158− function hoverable(handler, elem) {
181+ node.checked = model.checked;
159− function onHover(bool) {
182+ node.style.display = 'block';
160− elm.notify(events.id, handler(bool));
183+ node.style.pointerEvents = 'auto';
161− }
184+ node.elm_signal = model.signal;
162− var props = Utils.replace([['hover',onHover]], elem.props);
163− return { props:props, element:elem.element };
165− return { _:{}, hoverable:F2(hoverable), events:events };
169− function checkboxes(defaultValue) {
170− var events = Signal.constant(defaultValue);
172− function render(model) {
173− var b = newNode('input');
174− b.type = 'checkbox';
175− b.checked = model.checked;
176− b.style.display = 'block';
177− b.elmHandler = model.handler;
185+ node.elm_handler = model.handler;
178− function change() { elm.notify(events.id, b.elmHandler(b.checked)); }
186+ function change() {
187+ elm.notify(node.elm_signal.id, node.elm_handler(node.checked));
179− b.addEventListener('change', change);
189+ node.addEventListener('change', change);
180− return b;
190+ return node;
183− function update(node, oldModel, newModel) {
193+ function updateCheckbox(node, oldModel, newModel) {
184− node.elmHandler = newModel.handler;
194+ node.elm_signal = newModel.signal;
195+ node.elm_handler = newModel.handler;
185− node.checked = newModel.checked;
196+ node.checked = newModel.checked;
186− return true;
197+ return true;
189− function box(handler, checked) {
200+ function checkbox(signal, handler, checked) {
190− return A3(newElement, 13, 13, {
201+ return A3(newElement, 13, 13, {
191− ctor: 'Custom',
202+ ctor: 'Custom',
192− type: 'CheckBox',
203+ type: 'CheckBox',
193− render: render,
204+ render: renderCheckbox,
194− update: update,
205+ update: updateCheckbox,
195− model: { checked:checked, handler:handler }
206+ model: { signal:signal, handler:handler, checked:checked }
199− return { _:{}, checkbox:F2(box), events:events };
202− function setRange(node, start, end, dir) {
210+ function setRange(node, start, end, dir) {
203− if (node.parentNode) {
211+ if (node.parentNode) {
204− node.setSelectionRange(start, end, dir);
212+ node.setSelectionRange(start, end, dir);
205− } else {
213+ } else {
206− setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
214+ setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
210− function mkTextPool(type) { return function fields(defaultValue) {
218+ function renderField(model) {
211− var events = Signal.constant(defaultValue);
219+ var field = newNode('input');
212−
220+ field.style.border = 'none';
213− var state = null;
221+ field.style.outline = 'none';
214−
222+ field.style.backgroundColor = 'transparent';
215− function render(model) {
223+ field.style.pointerEvents = 'auto';
216− var field = newNode('input');
224+
217− field.elmHandler = model.handler;
220− field.type = type;
225+ field.type = model.type;
221− field.placeholder = JS.fromString(model.placeHolder);
226+ field.placeholder = JS.fromString(model.placeHolder);
222− field.value = JS.fromString(model.state.string);
227+ field.value = JS.fromString(model.content.string);
223− setRange(field, model.state.selectionStart, model.state.selectionEnd, 'forward');
228+ var selection = model.content.selection;
224− field.style.border = 'none';
229+ var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
225− state = model.state;
230+ setRange(field, selection.start, selection.end, direction);
227− function update() {
232+ field.elm_signal = model.signal;
228− var start = field.selectionStart,
233+ field.elm_handler = model.handler;
229− end = field.selectionEnd;
234+ field.elm_old_value = field.value;
230− if (field.selectionDirection === 'backward') {
235+
231− start = end;
236+ function keyUpdate(event) {
232− end = field.selectionStart;
237+ var curr = field.value;
233− }
238+ var next = (curr.slice(0, field.selectionStart) +
234− state = { _:{},
239+ String.fromCharCode(event.keyCode) +
240+ curr.slice(field.selectionEnd));
241+ var pos = field.selectionEnd + 1;
242+ elm.notify(field.elm_signal.id, field.elm_handler({
235− string:JS.toString(field.value),
244+ string: JS.toString(next),
248+ direction: { ctor:'Forward' }
251+ event.preventDefault();
254+ function inputUpdate(event) {
255+ var curr = field.elm_old_value;
256+ var next = field.value;
257+ if (curr === next) {
261+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
236− selectionStart:start,
262+ var start = field.selectionStart;
237− selectionEnd:end };
263+ var end = field.selectionEnd;
264+ field.value = field.elm_old_value;
266+ elm.notify(field.elm_signal.id, field.elm_handler({
268+ string: JS.toString(next),
272+ direction: { ctor: direction }
277+ function mouseUpdate(event) {
278+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
238− elm.notify(events.id, field.elmHandler(state));
279+ elm.notify(field.elm_signal.id, field.elm_handler({
281+ string: field.value,
283+ start: field.selectionStart,
284+ end: field.selectionEnd,
285+ direction: { ctor: direction }
240− function mousedown() {
289+ function mousedown(event) {
241− update();
290+ mouseUpdate(event);
242− elm.node.addEventListener('mouseup', mouseup);
291+ elm.node.addEventListener('mouseup', mouseup);
244− function mouseup() {
293+ function mouseup(event) {
245− update();
294+ mouseUpdate(event);
246− elm.node.removeEventListener('mouseup', mouseup)
295+ elm.node.removeEventListener('mouseup', mouseup)
297+ field.addEventListener('keypress', keyUpdate);
248− field.addEventListener('keyup', update);
298+ field.addEventListener('input', inputUpdate);
249− field.addEventListener('mousedown', mousedown);
299+ field.addEventListener('mousedown', mousedown);
251− return field;
301+ return field;
254− function update(node, oldModel, newModel) {
304+ function updateField(field, oldModel, newModel) {
255− node.elmHandler = newModel.handler;
305+ field.elm_signal = newModel.signal;
256− if (state === newModel.state) return;
306+ field.elm_handler = newModel.handler;
308+ field.type = newModel.type;
309+ field.placeholder = JS.fromString(newModel.placeHolder);
257− var newStr = JS.fromString(newModel.state.string);
310+ var value = JS.fromString(newModel.content.string);
258− if (node.value !== newStr) node.value = newStr;
311+ field.value = value;
259−
312+ field.elm_old_value = value;
260− var start = newModel.state.selectionStart;
313+ var selection = newModel.content.selection;
261− var end = newModel.state.selectionEnd;
314+ var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
262− var direction = 'forward';
265− end = newModel.state.selectionStart;
266− direction = 'backward';
269− if (node.selectionStart !== start
270− || node.selectionEnd !== end
271− || node.selectionDirection !== direction) {
272− setRange(node, start, end, direction);
315+ setRange(field, selection.start, selection.end, direction);
275−
318+ function mkField(type) {
276− function field(handler, placeHolder, state) {
319+ function field(signal, handler, placeHolder, content) {
277− return A3(newElement, 200, 30,
320+ return A3(newElement, 200, 30, {
278− { ctor: 'Custom',
321+ ctor: 'Custom',
279− type: type + 'Input',
322+ type: type + 'Input',
280− render: render,
323+ render: renderField,
281− update: update,
324+ update: updateField,
282− model: { handler:handler,
327+ handler:handler,
283− placeHolder:placeHolder,
328+ placeHolder:placeHolder,
284− state:state }
329+ content:content,
288− return { _:{}, field:F3(field), events:events };
337+
338+ function hoverable(signal, handler, elem) {
339+ function onHover(bool) {
340+ elm.notify(signal.id, handler(bool));
342+ var props = Utils.replace([['hover',onHover]], elem.props);
343+ return { props:props, element:elem.element };
346+ function clickable(signal, value, elem) {
347+ function onClick(bool) {
348+ elm.notify(signal.id, value);
291−
350+ var props = Utils.replace([['click',onClick]], elem.props);
351+ return { props:props, element:elem.element };
292− return elm.Native.Graphics.Input.values = {
354+ return elm.Native.Graphics.Input.values = {
293− buttons:buttons,
355+ input:input,
294− customButtons:customButtons,
356+ button:F3(button),
295− hoverables:hoverables,
357+ customButton:F5(customButton),
296− checkboxes:checkboxes,
358+ checkbox:F3(checkbox),
297− fields:mkTextPool('text'),
359+ dropDown:F2(dropDown),
298− emails:mkTextPool('email'),
360+ field:mkField('text'),
299− passwords:mkTextPool('password'),
361+ email:mkField('email'),
300− dropDown:dropDown
362+ password:mkField('password'),
363+ hoverable:F3(hoverable),
364+ clickable:F3(clickable)