on 61aab62 help sign in
367 lines 12 kB history raw
1Elm.Native.Graphics.Input = {};
2Elm.Native.Graphics.Input.make = function(elm) {
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;
8 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
9 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
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;
17 function input(initialValue) {
18 var signal = Signal.constant(initialValue);
19 return { _:{}, signal:signal, handle:signal };
20 }
22 function renderDropDown(signal, values) {
23 return function(_) {
24 var entries = JS.fromList(values);
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 });
40 var t = drop.cloneNode(true);
41 t.style.visibility = "hidden";
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 }
52 function updateDropDown(node, oldModel, newModel) {
53 }
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 }
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 }
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 }
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 }
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;
102 btn.elm_up = Render.render(model.up);
103 btn.elm_hover = Render.render(model.hover);
104 btn.elm_down = Render.render(model.down);
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);
133 btn.appendChild(btn.elm_up);
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);
142 return btn;
143 }
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;
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;
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 }
161 function max3(a,b,c) {
162 var ab = a > b ? a : b;
163 return ab > c ? ab : c;
164 }
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 }
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 }
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 }
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 }
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 }
218 function renderField(model) {
219 var field = newNode('input');
220 field.style.border = 'none';
221 field.style.outline = 'none';
222 field.style.backgroundColor = 'transparent';
223 field.style.pointerEvents = 'auto';
225 field.type = model.type;
226 field.placeholder = JS.fromString(model.placeHolder);
227 field.value = JS.fromString(model.content.string);
228 var selection = model.content.selection;
229 var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
230 setRange(field, selection.start, selection.end, direction);
232 field.elm_signal = model.signal;
233 field.elm_handler = model.handler;
234 field.elm_old_value = field.value;
236 function keyUpdate(event) {
237 var curr = field.value;
238 var next = (curr.slice(0, field.selectionStart) +
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({
243 _:{},
244 string: JS.toString(next),
245 selection: {
246 start: pos,
247 end: pos,
248 direction: { ctor:'Forward' }
249 },
250 }));
251 event.preventDefault();
252 }
254 function inputUpdate(event) {
255 var curr = field.elm_old_value;
256 var next = field.value;
257 if (curr === next) {
258 return;
259 }
261 var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
262 var start = field.selectionStart;
263 var end = field.selectionEnd;
264 field.value = field.elm_old_value;
266 elm.notify(field.elm_signal.id, field.elm_handler({
267 _:{},
268 string: JS.toString(next),
269 selection: {
270 start: start,
271 end: end,
272 direction: { ctor: direction }
273 },
274 }));
275 }
277 function mouseUpdate(event) {
278 var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
279 elm.notify(field.elm_signal.id, field.elm_handler({
280 _:{},
281 string: field.value,
282 selection: {
283 start: field.selectionStart,
284 end: field.selectionEnd,
285 direction: { ctor: direction }
286 },
287 }));
288 }
289 function mousedown(event) {
290 mouseUpdate(event);
291 elm.node.addEventListener('mouseup', mouseup);
292 }
293 function mouseup(event) {
294 mouseUpdate(event);
295 elm.node.removeEventListener('mouseup', mouseup)
296 }
297 field.addEventListener('keypress', keyUpdate);
298 field.addEventListener('input', inputUpdate);
299 field.addEventListener('mousedown', mousedown);
301 return field;
302 }
304 function updateField(field, oldModel, newModel) {
305 field.elm_signal = newModel.signal;
306 field.elm_handler = newModel.handler;
308 field.type = newModel.type;
309 field.placeholder = JS.fromString(newModel.placeHolder);
310 var value = JS.fromString(newModel.content.string);
311 field.value = value;
312 field.elm_old_value = value;
313 var selection = newModel.content.selection;
314 var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
315 setRange(field, selection.start, selection.end, direction);
316 }
318 function mkField(type) {
319 function field(signal, handler, placeHolder, content) {
320 return A3(newElement, 200, 30, {
321 ctor: 'Custom',
322 type: type + 'Input',
323 render: renderField,
324 update: updateField,
325 model: {
326 signal:signal,
327 handler:handler,
328 placeHolder:placeHolder,
329 content:content,
330 type:type
331 }
332 });
333 }
334 return F4(field);
335 }
338 function hoverable(signal, handler, elem) {
339 function onHover(bool) {
340 elm.notify(signal.id, handler(bool));
341 }
342 var props = Utils.replace([['hover',onHover]], elem.props);
343 return { props:props, element:elem.element };
344 }
346 function clickable(signal, value, elem) {
347 function onClick(bool) {
348 elm.notify(signal.id, value);
349 }
350 var props = Utils.replace([['click',onClick]], elem.props);
351 return { props:props, element:elem.element };
352 }
354 return elm.Native.Graphics.Input.values = {
355 input:input,
356 button:F3(button),
357 customButton:F5(customButton),
358 checkbox:F3(checkbox),
359 dropDown:F2(dropDown),
360 field:mkField('text'),
361 email:mkField('email'),
362 password:mkField('password'),
363 hoverable:F3(hoverable),
364 clickable:F3(clickable)
365 };
367};