client/src/js/ui.js

276 lines
6.6 KiB
JavaScript

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;
});
}
}