function UI(buttons, screen) { return { animation: animation, cinematic: cinematic, frame: frame, menu: menu, text: text }; 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 = {}; for(var key in controls) { mapping[key] = function() { buttons.pop(); remote.pause(); controls[key](); }; } return function(f) { buttons.push(mapping); remote.run(); f(); }; } function frame(id) { screen.clear(); screen.frame(id); } function menu(config) { var cursor = 0; var m = screen.menu(config.entries); var sync = function() { screen.select(cursor); }; var mapping = { 'Up': function() { cursor = Math.max(0, cursor - 1); sync(); }, 'Down': function() { cursor = Math.min(config.entries.length - 1, cursor + 1); sync(); }, 'A': function() { config.entries[cursor].action(); } }; if(config.cancel != undefined) { mapping['B'] = function() { buttons.pop(); screen.clear(m); config.cancel(); } } buttons.push(mapping); } 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: 50}); } frames.push({action: function() { ready = true; screen.markAsRead(); }, delay: null}); var remote = animation(frames); return function(f) { buttons.push({ 'A': function() { if(ready) { f(); } else { remote.pause(); screen.text(message); ready = true; } } }); screen.text(''); remote.run(); }; } function text(message) { return textScreen(message); } }