elm/libraries/Native/Graphics/Input.js
2014-02-17 20:57:57 +01:00

315 lines
10 KiB
JavaScript

Elm.Native.Graphics.Input = {};
Elm.Native.Graphics.Input.make = function(elm) {
elm.Native = elm.Native || {};
elm.Native.Graphics = elm.Native.Graphics || {};
elm.Native.Graphics.Input = elm.Native.Graphics.Input || {};
if (elm.Native.Graphics.Input.values) return elm.Native.Graphics.Input.values;
var Render = ElmRuntime.use(ElmRuntime.Render.Element);
var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
var Signal = Elm.Signal.make(elm);
var newElement = Elm.Graphics.Element.make(elm).newElement;
var JS = Elm.Native.JavaScript.make(elm);
var Utils = Elm.Native.Utils.make(elm);
var Tuple2 = Utils.Tuple2;
function input(initialValue) {
var sig = Signal.constant(initialValue);
return Tuple2(sig, sig);
}
function renderDropDown(signal, values) {
return function(_) {
var entries = JS.fromList(values);
var drop = newNode('select');
drop.style.border = '0 solid';
for (var i = 0; i < entries.length; ++i) {
var option = newNode('option');
var name = JS.fromString(entries[i]._0);
option.value = name;
option.innerHTML = name;
drop.appendChild(option);
}
drop.addEventListener('change', function() {
elm.notify(signal.id, entries[drop.selectedIndex]._1);
});
var t = drop.cloneNode(true);
t.style.visibility = "hidden";
elm.node.appendChild(t);
var style = window.getComputedStyle(t, null);
var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
elm.node.removeChild(t);
return drop;
};
}
function updateDropDown(node, oldModel, newModel) {
}
function dropDown(signal, values) {
return A3(newElement, w, h, {
ctor: 'Custom',
type: 'DropDown',
render: renderDropDown(signal,values),
update: updateDropDown,
model: {}
});
}
function renderButton(model) {
var node = newNode('button');
node.style.display = 'block';
node.elm_signal = model.signal;
node.elm_value = model.value;
function click() {
elm.notify(node.elm_signal.id, node.elm_value);
}
node.addEventListener('click', click);
node.innerHTML = model.text;
return node;
}
function updateButton(node, oldModel, newModel) {
node.elm_signal = newModel.signal;
node.elm_value = nemModel.value;
var txt = newModel.text;
if (oldModel.text !== txt) node.innerHTML = txt;
}
function button(signal, value, text) {
return A3(newElement, 100, 40, {
ctor: 'Custom',
type: 'Button',
render: renderButton,
update: updateButton,
model: { signal:signal, value:value, text:JS.fromString(text) }
});
}
function renderCustomButton(model) {
var btn = newNode('div');
btn.elm_value = model.value;
btn.elm_up = Render.render(model.up);
btn.elm_hover = Render.render(model.hover);
btn.elm_down = Render.render(model.down);
function replace(node) {
if (node !== btn.firstChild) {
btn.replaceChild(node, btn.firstChild);
}
}
var overCount = 0;
function over(e) {
if (overCount++ > 0) return;
replace(btn.elm_hover);
}
function out(e) {
if (btn.contains(e.toElement || e.relatedTarget)) return;
overCount = 0;
replace(btn.elm_up);
}
function up() {
replace(btn.elm_hover);
elm.notify(btn.elm_signal.id, btn.elm_value);
}
function down() {
replace(btn.elm_down);
}
btn.addEventListener('mouseover', over);
btn.addEventListener('mouseout' , out);
btn.addEventListener('mousedown', down);
btn.addEventListener('mouseup' , up);
btn.appendChild(btn.elm_up);
var clicker = newNode('div');
clicker.style.width = btn.elm_up.style.width;
clicker.style.height = btn.elm_up.style.height;
clicker.style.position = 'absolute';
clicker.style.top = 0;
btn.appendChild(clicker);
return btn;
}
function updateCustomButton(node, oldModel, newModel) {
node.elm_signal = newModel.signal;
node.elm_value = newModel.value;
Render.update(node.elm_up, oldModel.up, newModel.up)
Render.update(node.elm_hover, oldModel.hover, newModel.hover)
Render.update(node.elm_down, oldModel.down, newModel.down)
}
function max3(a,b,c) {
var ab = a > b ? a : b;
return ab > c ? ab : c;
}
function customButton(signal, value, up, hover, down) {
return A3(newElement,
max3(up.props.width, hover.props.width, down.props.width),
max3(up.props.height, hover.props.height, down.props.height),
{ ctor: 'Custom',
type: 'CustomButton',
render: renderCustomButton,
update: updateCustomButton,
model: { signal:signal, value:value, up:up, hover:hover, down:down }
});
}
function renderCheckbox(model) {
var node = newNode('input');
node.type = 'checkbox';
node.checked = model.checked;
node.style.display = 'block';
node.elm_signal = model.signal;
node.elm_handler = model.handler;
function change() {
elm.notify(node.elm_signal.id, node.elm_handler(node.checked));
}
node.addEventListener('change', change);
return node;
}
function updateCheckbox(node, oldModel, newModel) {
node.elm_signal = newModel.signal;
node.elm_handler = newModel.handler;
node.checked = newModel.checked;
return true;
}
function checkbox(signal, handler, checked) {
return A3(newElement, 13, 13, {
ctor: 'Custom',
type: 'CheckBox',
render: renderCheckbox,
update: updateCheckbox,
model: { signal:signal, handler:handler, checked:checked }
});
}
function setRange(node, start, end, dir) {
if (node.parentNode) {
node.setSelectionRange(start, end, dir);
} else {
setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
}
}
function renderField(model) {
var field = newNode('input');
field.elm_signal = model.signal;
field.elm_handler = model.handler;
field.type = model.type;
field.placeholder = JS.fromString(model.placeHolder);
field.value = JS.fromString(model.content.string);
var selection = model.content.selection;
var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
setRange(field, selection.start, selection.end, direction);
field.style.border = 'none';
function update() {
var direction = field.selectionDirection === 'backward' ? 'Backward' : 'Forward';
elm.notify(field.elm_signal.id, field.elm_handler({
_:{},
string:JS.toString(field.value),
selection: {
start:field.selectionStart,
end:field.selectionEnd,
direction: { ctor:direction }
},
}));
}
function mousedown() {
update();
elm.node.addEventListener('mouseup', mouseup);
}
function mouseup() {
update();
elm.node.removeEventListener('mouseup', mouseup)
}
field.addEventListener('keyup', update);
field.addEventListener('mousedown', mousedown);
return field;
}
function updateField(node, oldModel, newModel) {
node.elmHandler = newModel.handler;
var newStr = JS.fromString(newModel.content.string);
if (node.value !== newStr) node.value = newStr;
var selection = newModel.content.selection;
var start = selection.start;
var end = selection.end;
var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
if (end < start) {
start = end;
end = selection.start;
}
if (node.selectionStart !== start
|| node.selectionEnd !== end
|| node.selectionDirection !== direction) {
setRange(node, start, end, direction);
}
}
function mkField(type) {
function field(signal, handler, placeHolder, content) {
return A3(newElement, 200, 30, {
ctor: 'Custom',
type: type + 'Input',
render: renderField,
update: updateField,
model: {
signal:signal,
handler:handler,
placeHolder:placeHolder,
content:content,
type:type
}
});
}
return F4(field);
}
function hoverable(signal, handler, element) {
function onHover(bool) {
elm.notify(signal.id, handler(bool));
}
var props = Utils.replace([['hover',onHover]], elem.props);
return { props:props, element:elem.element };
}
function clickable(signal, value, element) {
function onClick(bool) {
elm.notify(signal.id, value);
}
var props = Utils.replace([['click',onClick]], elem.props);
return { props:props, element:elem.element };
}
return elm.Native.Graphics.Input.values = {
input:input,
button:F3(button),
customButton:F5(customButton),
checkbox:F3(checkbox),
dropDown:F2(dropDown),
field:mkField('text'),
email:mkField('email'),
password:mkField('password'),
hoverable:F3(hoverable),
clickable:F3(clickable)
};
};