on ee24119 help sign in
405 lines 14 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 toCss = Elm.Native.Color.make(elm).toCss;
12 var Text = Elm.Native.Text.make(elm);
13 var Signal = Elm.Signal.make(elm);
14 var newElement = Elm.Graphics.Element.make(elm).newElement;
15 var List = Elm.Native.List.make(elm);
16 var Utils = Elm.Native.Utils.make(elm);
17 var Tuple2 = Utils.Tuple2;
19 function input(initialValue) {
20 var signal = Signal.constant(initialValue);
21 return { _:{}, signal:signal, handle:signal };
22 }
24 function renderDropDown(signal, values) {
25 return function(_) {
26 var entries = List.toArray(values);
28 var drop = newNode('select');
29 drop.style.border = '0 solid';
30 drop.style.pointerEvents = 'auto';
31 for (var i = 0; i < entries.length; ++i) {
32 var option = newNode('option');
33 var name = entries[i]._0;
34 option.value = name;
35 option.innerHTML = name;
36 drop.appendChild(option);
37 }
38 drop.addEventListener('change', function() {
39 elm.notify(signal.id, entries[drop.selectedIndex]._1);
40 });
42 var t = drop.cloneNode(true);
43 t.style.visibility = "hidden";
45 elm.node.appendChild(t);
46 var style = window.getComputedStyle(t, null);
47 var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
48 var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
49 elm.node.removeChild(t);
50 return drop;
51 };
52 }
54 function updateDropDown(node, oldModel, newModel) {
55 }
57 function dropDown(signal, values) {
58 return A3(newElement, 100, 24, {
59 ctor: 'Custom',
60 type: 'DropDown',
61 render: renderDropDown(signal,values),
62 update: updateDropDown,
63 model: {}
64 });
65 }
67 function renderButton(model) {
68 var node = newNode('button');
69 node.style.display = 'block';
70 node.style.pointerEvents = 'auto';
71 node.elm_signal = model.signal;
72 node.elm_value = model.value;
73 function click() {
74 elm.notify(node.elm_signal.id, node.elm_value);
75 }
76 node.addEventListener('click', click);
77 node.innerHTML = model.text;
78 return node;
79 }
81 function updateButton(node, oldModel, newModel) {
82 node.elm_signal = newModel.signal;
83 node.elm_value = newModel.value;
84 var txt = newModel.text;
85 if (oldModel.text !== txt) node.innerHTML = txt;
86 }
88 function button(signal, value, text) {
89 return A3(newElement, 100, 40, {
90 ctor: 'Custom',
91 type: 'Button',
92 render: renderButton,
93 update: updateButton,
94 model: { signal:signal, value:value, text:text }
95 });
96 }
98 function renderCustomButton(model) {
99 var btn = newNode('div');
100 btn.style.pointerEvents = 'auto';
101 btn.elm_signal = model.signal;
102 btn.elm_value = model.value;
104 btn.elm_up = Render.render(model.up);
105 btn.elm_hover = Render.render(model.hover);
106 btn.elm_down = Render.render(model.down);
108 function replace(node) {
109 if (node !== btn.firstChild) {
110 btn.replaceChild(node, btn.firstChild);
111 }
112 }
113 var overCount = 0;
114 function over(e) {
115 if (overCount++ > 0) return;
116 replace(btn.elm_hover);
117 }
118 function out(e) {
119 if (btn.contains(e.toElement || e.relatedTarget)) return;
120 overCount = 0;
121 replace(btn.elm_up);
122 }
123 function up() {
124 replace(btn.elm_hover);
125 elm.notify(btn.elm_signal.id, btn.elm_value);
126 }
127 function down() {
128 replace(btn.elm_down);
129 }
130 btn.addEventListener('mouseover', over);
131 btn.addEventListener('mouseout' , out);
132 btn.addEventListener('mousedown', down);
133 btn.addEventListener('mouseup' , up);
135 btn.appendChild(btn.elm_up);
137 var clicker = newNode('div');
138 clicker.style.width = btn.elm_up.style.width;
139 clicker.style.height = btn.elm_up.style.height;
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 updateIfNeeded(css, attribute, latestAttribute) {
219 if (css[attribute] !== latestAttribute) {
220 css[attribute] = latestAttribute;
221 }
222 }
223 function cssDimensions(dimensions) {
224 return dimensions.top + 'px ' +
225 dimensions.right + 'px ' +
226 dimensions.bottom + 'px ' +
227 dimensions.left + 'px';
228 }
229 function updateFieldStyle(css, style) {
230 updateIfNeeded(css, 'padding', cssDimensions(style.padding));
232 var outline = style.outline;
233 updateIfNeeded(css, 'border-width', cssDimensions(outline.width));
234 updateIfNeeded(css, 'border-color', toCss(outline.color));
235 updateIfNeeded(css, 'border-radius', outline.radius + 'px');
237 var highlight = style.highlight;
238 if (highlight.width === 0) {
239 css.outline = 'none';
240 } else {
241 updateIfNeeded(css, 'outline-width', highlight.width + 'px');
242 updateIfNeeded(css, 'outline-color', toCss(highlight.color));
243 }
245 var textStyle = style.style;
246 updateIfNeeded(css, 'color', toCss(textStyle.color));
247 if (textStyle.typeface.ctor !== '[]') {
248 updateIfNeeded(css, 'font-family', Text.toTypefaces(textStyle.typeface));
249 }
250 if (textStyle.height.ctor !== "Nothing") {
251 updateIfNeeded(css, 'font-size', textStyle.height._0 + 'px');
252 }
253 updateIfNeeded(css, 'font-weight', textStyle.bold ? 'bold' : 'normal');
254 updateIfNeeded(css, 'font-style', textStyle.italic ? 'italic' : 'normal');
255 if (textStyle.line.ctor !== 'Nothing') {
256 updateIfNeeded(css, 'text-decoration', Text.toLine(textStyle.line._0));
257 }
258 }
260 function renderField(model) {
261 var field = newNode('input');
262 updateFieldStyle(field.style, model.style);
263 field.style.borderStyle = 'solid';
264 field.style.pointerEvents = 'auto';
266 field.type = model.type;
267 field.placeholder = model.placeHolder;
268 field.value = model.content.string;
270 field.elm_signal = model.signal;
271 field.elm_handler = model.handler;
272 field.elm_old_value = field.value;
274 function inputUpdate(event) {
275 var curr = field.elm_old_value;
276 var next = field.value;
277 if (curr === next) {
278 return;
279 }
281 var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
282 var start = field.selectionStart;
283 var end = field.selectionEnd;
284 field.value = field.elm_old_value;
286 elm.notify(field.elm_signal.id, field.elm_handler({
287 _:{},
288 string: next,
289 selection: {
290 _:{},
291 start: start,
292 end: end,
293 direction: { ctor: direction }
294 },
295 }));
296 }
298 function mouseUpdate(event) {
299 var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
300 elm.notify(field.elm_signal.id, field.elm_handler({
301 _:{},
302 string: field.value,
303 selection: {
304 _:{},
305 start: field.selectionStart,
306 end: field.selectionEnd,
307 direction: { ctor: direction }
308 },
309 }));
310 }
311 function mousedown(event) {
312 mouseUpdate(event);
313 elm.node.addEventListener('mouseup', mouseup);
314 }
315 function mouseup(event) {
316 mouseUpdate(event);
317 elm.node.removeEventListener('mouseup', mouseup)
318 }
319 field.addEventListener('input', inputUpdate);
320 field.addEventListener('mousedown', mousedown);
321 field.addEventListener('focus', function() {
322 field.elm_hasFocus = true;
323 });
324 field.addEventListener('blur', function() {
325 field.elm_hasFocus = false;
326 });
328 return field;
329 }
331 function updateField(field, oldModel, newModel) {
332 if (oldModel.style !== newModel.style) {
333 updateFieldStyle(field.style, newModel.style);
334 }
335 field.elm_signal = newModel.signal;
336 field.elm_handler = newModel.handler;
338 field.type = newModel.type;
339 field.placeholder = newModel.placeHolder;
340 var value = newModel.content.string;
341 field.value = value;
342 field.elm_old_value = value;
343 if (field.elm_hasFocus) {
344 var selection = newModel.content.selection;
345 var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
346 setRange(field, selection.start, selection.end, direction);
347 }
348 }
350 function mkField(type) {
351 function field(style, signal, handler, placeHolder, content) {
352 var padding = style.padding;
353 var outline = style.outline.width;
354 var adjustWidth = padding.left + padding.right + outline.left + outline.right;
355 var adjustHeight = padding.top + padding.bottom + outline.top + outline.bottom;
356 return A3(newElement, 200, 30, {
357 ctor: 'Custom',
358 type: type + 'Field',
359 adjustWidth: adjustWidth,
360 adjustHeight: adjustHeight,
361 render: renderField,
362 update: updateField,
363 model: {
364 signal:signal,
365 handler:handler,
366 placeHolder:placeHolder,
367 content:content,
368 style:style,
369 type:type
370 }
371 });
372 }
373 return F5(field);
374 }
376 function hoverable(signal, handler, elem) {
377 function onHover(bool) {
378 elm.notify(signal.id, handler(bool));
379 }
380 var props = Utils.replace([['hover',onHover]], elem.props);
381 return { props:props, element:elem.element };
382 }
384 function clickable(signal, value, elem) {
385 function onClick(bool) {
386 elm.notify(signal.id, value);
387 }
388 var props = Utils.replace([['click',onClick]], elem.props);
389 return { props:props, element:elem.element };
390 }
392 return elm.Native.Graphics.Input.values = {
393 input:input,
394 button:F3(button),
395 customButton:F5(customButton),
396 checkbox:F3(checkbox),
397 dropDown:F2(dropDown),
398 field:mkField('text'),
399 email:mkField('email'),
400 password:mkField('password'),
401 hoverable:F3(hoverable),
402 clickable:F3(clickable)
403 };
405};