elm-core-sources-1.0.0: src/Native/Graphics/Input.js
// setup
Elm.Native = Elm.Native || {};
Elm.Native.Graphics = Elm.Native.Graphics || {};
Elm.Native.Graphics.Input = Elm.Native.Graphics.Input || {};
// definition
Elm.Native.Graphics.Input.make = function(localRuntime) {
'use strict';
// attempt to short-circuit
if ('values' in Elm.Native.Graphics.Input) {
return Elm.Native.Graphics.Input.values;
}
var Color = Elm.Native.Color.make(localRuntime);
var List = Elm.Native.List.make(localRuntime);
var Text = Elm.Native.Text.make(localRuntime);
var Utils = Elm.Native.Utils.make(localRuntime);
var Element = Elm.Graphics.Element.make(localRuntime);
var NativeElement = Elm.Native.Graphics.Element.make(localRuntime);
function renderDropDown(model) {
var drop = NativeElement.createNode('select');
drop.style.border = '0 solid';
drop.style.pointerEvents = 'auto';
drop.style.display = 'block';
drop.elm_values = List.toArray(model.values);
drop.elm_handler = model.handler;
var values = drop.elm_values;
for (var i = 0; i < values.length; ++i) {
var option = NativeElement.createNode('option');
var name = values[i]._0;
option.value = name;
option.innerHTML = name;
drop.appendChild(option);
}
drop.addEventListener('change', function() {
drop.elm_handler(drop.elm_values[drop.selectedIndex]._1)();
});
return drop;
}
function updateDropDown(node, oldModel, newModel) {
node.elm_values = List.toArray(newModel.values);
node.elm_handler = newModel.handler;
var values = node.elm_values;
var kids = node.childNodes;
var kidsLength = kids.length;
var i = 0;
for (; i < kidsLength && i < values.length; ++i) {
var option = kids[i];
var name = values[i]._0;
option.value = name;
option.innerHTML = name;
}
for (; i < kidsLength; ++i) {
node.removeChild(node.lastChild);
}
for (; i < values.length; ++i) {
var option = NativeElement.createNode('option');
var name = values[i]._0;
option.value = name;
option.innerHTML = name;
node.appendChild(option);
}
return node;
}
function dropDown(handler, values) {
return A3(Element.newElement, 100, 24, {
ctor: 'Custom',
type: 'DropDown',
render: renderDropDown,
update: updateDropDown,
model: {
values: values,
handler: handler
}
});
}
function renderButton(model) {
var node = NativeElement.createNode('button');
node.style.display = 'block';
node.style.pointerEvents = 'auto';
node.elm_message = model.message;
function click() {
node.elm_message();
}
node.addEventListener('click', click);
node.innerHTML = model.text;
return node;
}
function updateButton(node, oldModel, newModel) {
node.elm_message = newModel.message;
var txt = newModel.text;
if (oldModel.text !== txt) {
node.innerHTML = txt;
}
return node;
}
function button(message, text) {
return A3(Element.newElement, 100, 40, {
ctor: 'Custom',
type: 'Button',
render: renderButton,
update: updateButton,
model: {
message: message,
text:text
}
});
}
function renderCustomButton(model) {
var btn = NativeElement.createNode('div');
btn.style.pointerEvents = 'auto';
btn.elm_message = model.message;
btn.elm_up = NativeElement.render(model.up);
btn.elm_hover = NativeElement.render(model.hover);
btn.elm_down = NativeElement.render(model.down);
btn.elm_up.style.display = 'block';
btn.elm_hover.style.display = 'none';
btn.elm_down.style.display = 'none';
btn.appendChild(btn.elm_up);
btn.appendChild(btn.elm_hover);
btn.appendChild(btn.elm_down);
function swap(visibleNode, hiddenNode1, hiddenNode2) {
visibleNode.style.display = 'block';
hiddenNode1.style.display = 'none';
hiddenNode2.style.display = 'none';
}
var overCount = 0;
function over(e) {
if (overCount++ > 0) return;
swap(btn.elm_hover, btn.elm_down, btn.elm_up);
}
function out(e) {
if (btn.contains(e.toElement || e.relatedTarget)) return;
overCount = 0;
swap(btn.elm_up, btn.elm_down, btn.elm_hover);
}
function up() {
swap(btn.elm_hover, btn.elm_down, btn.elm_up);
btn.elm_message();
}
function down() {
swap(btn.elm_down, btn.elm_hover, btn.elm_up);
}
btn.addEventListener('mouseover', over);
btn.addEventListener('mouseout' , out);
btn.addEventListener('mousedown', down);
btn.addEventListener('mouseup' , up);
return btn;
}
function updateCustomButton(node, oldModel, newModel) {
node.elm_message = newModel.message;
var kids = node.childNodes;
var styleUp = kids[0].style.display;
var styleHover = kids[1].style.display;
var styleDown = kids[2].style.display;
NativeElement.updateAndReplace(kids[0], oldModel.up, newModel.up);
NativeElement.updateAndReplace(kids[1], oldModel.hover, newModel.hover);
NativeElement.updateAndReplace(kids[2], oldModel.down, newModel.down);
var kids = node.childNodes;
kids[0].style.display = styleUp;
kids[1].style.display = styleHover;
kids[2].style.display = styleDown;
return node;
}
function max3(a,b,c) {
var ab = a > b ? a : b;
return ab > c ? ab : c;
}
function customButton(message, up, hover, down) {
return A3(Element.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: {
message: message,
up: up,
hover: hover,
down: down
}
});
}
function renderCheckbox(model) {
var node = NativeElement.createNode('input');
node.type = 'checkbox';
node.checked = model.checked;
node.style.display = 'block';
node.style.pointerEvents = 'auto';
node.elm_handler = model.handler;
function change() {
node.elm_handler(node.checked)();
}
node.addEventListener('change', change);
return node;
}
function updateCheckbox(node, oldModel, newModel) {
node.elm_handler = newModel.handler;
node.checked = newModel.checked;
return node;
}
function checkbox(handler, checked) {
return A3(Element.newElement, 13, 13, {
ctor: 'Custom',
type: 'CheckBox',
render: renderCheckbox,
update: updateCheckbox,
model: { 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 updateIfNeeded(css, attribute, latestAttribute) {
if (css[attribute] !== latestAttribute) {
css[attribute] = latestAttribute;
}
}
function cssDimensions(dimensions) {
return dimensions.top + 'px ' +
dimensions.right + 'px ' +
dimensions.bottom + 'px ' +
dimensions.left + 'px';
}
function updateFieldStyle(css, style) {
updateIfNeeded(css, 'padding', cssDimensions(style.padding));
var outline = style.outline;
updateIfNeeded(css, 'border-width', cssDimensions(outline.width));
updateIfNeeded(css, 'border-color', Color.toCss(outline.color));
updateIfNeeded(css, 'border-radius', outline.radius + 'px');
var highlight = style.highlight;
if (highlight.width === 0) {
css.outline = 'none';
} else {
updateIfNeeded(css, 'outline-width', highlight.width + 'px');
updateIfNeeded(css, 'outline-color', Color.toCss(highlight.color));
}
var textStyle = style.style;
updateIfNeeded(css, 'color', Color.toCss(textStyle.color));
if (textStyle.typeface.ctor !== '[]') {
updateIfNeeded(css, 'font-family', Text.toTypefaces(textStyle.typeface));
}
if (textStyle.height.ctor !== "Nothing") {
updateIfNeeded(css, 'font-size', textStyle.height._0 + 'px');
}
updateIfNeeded(css, 'font-weight', textStyle.bold ? 'bold' : 'normal');
updateIfNeeded(css, 'font-style', textStyle.italic ? 'italic' : 'normal');
if (textStyle.line.ctor !== 'Nothing') {
updateIfNeeded(css, 'text-decoration', Text.toLine(textStyle.line._0));
}
}
function renderField(model) {
var field = NativeElement.createNode('input');
updateFieldStyle(field.style, model.style);
field.style.borderStyle = 'solid';
field.style.pointerEvents = 'auto';
field.type = model.type;
field.placeholder = model.placeHolder;
field.value = model.content.string;
field.elm_handler = model.handler;
field.elm_old_value = field.value;
function inputUpdate(event) {
var curr = field.elm_old_value;
var next = field.value;
if (curr === next) {
return;
}
var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
var start = field.selectionStart;
var end = field.selectionEnd;
field.value = field.elm_old_value;
field.elm_handler({
_:{},
string: next,
selection: {
_:{},
start: start,
end: end,
direction: { ctor: direction }
}
})();
}
field.addEventListener('input', inputUpdate);
field.addEventListener('focus', function() {
field.elm_hasFocus = true;
});
field.addEventListener('blur', function() {
field.elm_hasFocus = false;
});
return field;
}
function updateField(field, oldModel, newModel) {
if (oldModel.style !== newModel.style) {
updateFieldStyle(field.style, newModel.style);
}
field.elm_handler = newModel.handler;
field.type = newModel.type;
field.placeholder = newModel.placeHolder;
var value = newModel.content.string;
field.value = value;
field.elm_old_value = value;
if (field.elm_hasFocus) {
var selection = newModel.content.selection;
var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
setRange(field, selection.start, selection.end, direction);
}
return field;
}
function mkField(type) {
function field(style, handler, placeHolder, content) {
var padding = style.padding;
var outline = style.outline.width;
var adjustWidth = padding.left + padding.right + outline.left + outline.right;
var adjustHeight = padding.top + padding.bottom + outline.top + outline.bottom;
return A3(Element.newElement, 200, 30, {
ctor: 'Custom',
type: type + 'Field',
adjustWidth: adjustWidth,
adjustHeight: adjustHeight,
render: renderField,
update: updateField,
model: {
handler:handler,
placeHolder:placeHolder,
content:content,
style:style,
type:type
}
});
}
return F4(field);
}
function hoverable(handler, elem) {
function onHover(bool) {
handler(bool)();
}
var props = Utils.replace([['hover',onHover]], elem.props);
return { props:props, element:elem.element };
}
function clickable(message, elem) {
function onClick() {
message();
}
var props = Utils.replace([['click',onClick]], elem.props);
return { props:props, element:elem.element };
}
return Elm.Native.Graphics.Input.values = {
button: F2(button),
customButton: F4(customButton),
checkbox: F2(checkbox),
dropDown: F2(dropDown),
field: mkField('text'),
email: mkField('email'),
password: mkField('password'),
hoverable: F2(hoverable),
clickable: F2(clickable)
};
};