client/src/ui.js

194 lines
4.2 KiB
JavaScript

function UI(async, buttons, screen, session) {
var lineWidth = 36;
var textSpeeds = {
slow: 100,
medium: 50,
fast: 20
};
var textDelay = textSpeeds[session.getOptions().textSpeed];
return {
animation: animation,
ask: ask,
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 = {};
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 = 0;
var m = screen.menu(config.name + 'Menu', config.entries);
var sync = function() { screen.select(cursor); };
var mapping = {
Up: function() {
cursor = (config.entries.length + cursor - 1) % config.entries.length;
sync();
},
Down: function() {
cursor = (cursor + 1) % config.entries.length;
sync();
},
A: function() {
var entry = config.entries[cursor];
if(entry.action != undefined) {
entry.action(m);
} else if(entry.quit != undefined) {
close(m);
entry.quit();
}
}
};
if(config.cancel != undefined) {
mapping['B'] = function() { close(m); config.cancel(); }
}
buttons.push(mapping);
}
function ask(config) {
var menuConfig = {entries: [], name: config.name};
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, quit: apply(f, config.entries[i].value)};
} 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: textDelay});
}
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) {
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); })
);
}
}