function UI(async, buttons, dom, screen, session) { var lineWidth = 36; var textSpeeds = { slow: 100, medium: 50, fast: 20 }; var textSpeed = session.get('options').textSpeed; return { animation: animation, ask: ask, cinematic: cinematic, frame: frame, menu: menu, text: text, setLayout: setLayout, setTextSpeed: setTextSpeed }; function animation(frames) { var frameCounter = 0; var scheduled; var step = function() { if(frameCounter < frames.length) { scheduled = null; frames[frameCounter].action(); if(frames[frameCounter].delay != undefined) { scheduled = setTimeout(step, frames[frameCounter].delay); } frameCounter++ } }; return { run: step, pause: function() { if(scheduled != undefined) { clearTimeout(scheduled); } } }; } function cinematic(frames, controls) { var remote = animation(frames); var mapping = {}; var escaper = function(f) { return function() { buttons.pop(); remote.pause(); f(); }; } for(var key in controls) { mapping[key] = escaper(controls[key]); } return function(f) { buttons.push(mapping); remote.run(); f(); }; } function close(elem) { buttons.pop(); screen.clear(elem); } function frame(id) { screen.clear(); screen.frame(id); } function menu(config) { var cursor = config.selected || 0; config.direction = config.direction || 'vertical'; var m = screen.menu(config); var sync = function() { screen.select(cursor); }; var back = function() { cursor = (config.entries.length + cursor - 1) % config.entries.length; sync(); }; var forth = function() { cursor = (cursor + 1) % config.entries.length; sync(); }; var mapping = { A: function() { var entry = config.entries[cursor]; if(entry.close) { close(m); } if(entry.action != undefined) { entry.action(m); } } }; mapping[config.direction == 'vertical' ? 'Up' : 'Left'] = back; mapping[config.direction == 'vertical' ? 'Down' : 'Right'] = forth; if(config.cancel != undefined) { mapping['B'] = function() { close(m); config.cancel(); } } buttons.push(mapping); } function ask(config) { var menuConfig = { cancel: config.cancel, direction: config.direction, entries: [], name: config.name, selected: config.selected }; var apply = function(f, val) { return function() { return f(val); }; }; return function(f) { for(var i = 0; i < config.entries.length; i++) { var entry; if(config.entries[i].label != undefined) { entry = {label: config.entries[i].label, action: apply(f, config.entries[i].value), close: true}; } else if(config.entries[i].picto != undefined) { entry = {picto: config.entries[i].picto, action: apply(f, config.entries[i].value), close: true}; } else { entry = {label: '___', action: promptValue(config.entries[i].size, f)}; } menuConfig.entries.push(entry); } menu(menuConfig); }; } function promptValue(size, f) { return function(m) { var input = screen.input(size); input.focus(); buttons.push({}); input.addEventListener('keydown', function(event) { switch(event.key) { case 'Escape': close(input); break; case 'Enter': var value = input.value; close(input); close(m); return f(value); } }); }; } function textScreen(message) { var characters = message.split(''); var frames = []; var ready = false; var append = function(c) { return function() { screen.appendText(c); }; }; for(var i = 0; i < characters.length; i++) { frames.push( {action: append(characters[i]), delay: textSpeeds[textSpeed]} ); } frames.push({action: function() { ready = true; screen.markAsRead(); }, delay: null}); var remote = animation(frames); return function(f) { buttons.push({ 'A': function() { if(ready) { buttons.pop(); f(); } else { remote.pause(); screen.text(message); ready = true; } } }); screen.text(''); remote.run(); }; } function text(message) { var words = message.split(' '); var i = 0; var line = ''; var screen = null; var screens = []; var width = 0; while (i < words.length) { if(width == 0) { line = words[i]; width += line.length; i++; } else { if(width + words[i].length + 1 <= lineWidth) { line += ' ' + words[i]; width += 1 + words[i].length; i++; } else { if(screen === null) { screen = line; } else { screens.push(screen + ' ' + line); screen = null; } line = ''; width = 0; } } } screens.push(screen === null ? line : screen + ' ' + line); return async.sequence.apply(null, screens.map(function(s) { return textScreen(s); }) ); } function keyPromptLabel(key, button) { return button + " : " + (key != undefined ? "[" + key + "]" : "???"); } function setLayout() { var layout = session.get('options').layout var buttonsMenu = { cancel: function() {}, entries: [], name: "layout" }; for(var key in layout) { buttonsMenu.entries[buttons.code(layout[key])] = { label: keyPromptLabel(key, layout[key]), action: setKey(layout[key]) }; } menu(buttonsMenu); } function setKey(button) { return function(f) { buttons.push({}); var elem = dom.make('div', { class: ['framed', 'center'], textContent: "Press the key you wanna map to " + button }); screen.currentMenu().getElementsByClassName('selected')[0].textContent = keyPromptLabel(null, button); var promptKey = function(event) { var assignment = buttons.assign(event.key, button); session.update({options: {layout: assignment.layoutDiff}}); screen.clear(elem); screen.currentMenu().getElementsByClassName('selected')[0].textContent = keyPromptLabel(event.key, button); document.removeEventListener('keydown', promptKey); buttons.pop(); if(assignment.button != undefined) { screen.select(buttons.code(assignment.button)); return setKey(assignment.button)(function () { screen.select(buttons.code(button)); }); } return f(); }; screen.show(elem); document.addEventListener('keydown', promptKey); } } function setTextSpeed(choices) { var config = { entries: [], cancel: choices.cancel, name: choices.name }; for(var i = 0; i < choices.entries.length; i++) { config.entries[i] = choices.entries[i]; if(choices.entries[i].value == textSpeed) { config.selected = i; } } ask(config)(function(value) { session.update({options: {textSpeed: value}}); textSpeed = value; }); } }