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