nodegame-widgets
Version:
Collections of useful and reusable javascript / HTML snippets for nodeGame
300 lines (268 loc) • 9.59 kB
JavaScript
/**
* # DoneButton
* Copyright(c) 2020 Stefano Balietti <ste@nodegame.org>
* MIT Licensed
*
* Creates a button that if pressed emits node.done()
*
* www.nodegame.org
*/
(function(node) {
"use strict";
node.widgets.register('DoneButton', DoneButton);
// ## Meta-data
DoneButton.version = '1.1.0';
DoneButton.description = 'Creates a button that if ' +
'pressed emits node.done().';
DoneButton.title = false;
DoneButton.className = 'donebutton';
DoneButton.texts.done = 'Done';
// ## Dependencies
DoneButton.dependencies = {
JSUS: {}
};
/**
* ## DoneButton constructor
*
* Creates a new instance of DoneButton
*
* @param {object} options Optional. Configuration options.
* If a `button` option is specified, it sets it as the clickable
* button. All other options are passed to the init method.
*
* @see DoneButton.init
*/
function DoneButton(options) {
var that;
that = this;
/**
* ### DoneButton.button
*
* The HTML element triggering node.done() when pressed
*/
if ('object' === typeof options.button) {
this.button = options.button;
}
else if ('undefined' === typeof options.button) {
this.button = document.createElement('input');
this.button.type = 'button';
}
else {
throw new TypeError('DoneButton constructor: options.button must ' +
'be object or undefined. Found: ' +
options.button);
}
this.button.onclick = function() {
if (that.onclick && false === that.onclick()) return;
if (node.done()) that.disable();
};
/**
* ### DoneButton.onclick
*
* A callback executed after the button is clicked
*
* If it return FALSE, node.done() is not called.
*/
this.onclick = null;
/**
* ### DoneButton.disableOnDisconnect
*
* If TRUE, the button is automatically disableb upon disconnection
*/
this.disableOnDisconnect = null;
/**
* ### DoneButton.delayOnPlaying
*
* The number of milliseconds to wait to enable at a new step
*
* A small delay prevents accidental double clicking between steps.
*/
this.delayOnPlaying = 800;
}
// ## DoneButton methods
/**
* ### DoneButton.init
*
* Initializes the instance
*
* Available options are:
*
* - id: id of the HTML button, or false to have none. Default:
* DoneButton.className
* - className: the className of the button (string, array), or false
* to have none. Default bootstrap classes: 'btn btn-lg btn-primary'
* - text: the text on the button. Default: DoneButton.text
* - onclick: a callback executed when the button is clicked. Default: null
* - disableOnDisconnect: TRUE to disable upon disconnection. Default: TRUE
* - delayOnPlaying: number of milliseconds to wait to enable after
* the `PLAYING` event is fired (e.g., a new step begins). Default: 800
*
* @param {object} opts Optional. Configuration options
*/
DoneButton.prototype.init = function(opts) {
var tmp;
opts = opts || {};
//Button
if ('undefined' === typeof opts.id) {
tmp = DoneButton.className;
}
else if ('string' === typeof opts.id) {
tmp = opts.id;
}
else if (false === opts.id) {
tmp = false;
}
else {
throw new TypeError('DoneButton.init: id must ' +
'be string, false, or undefined. Found: ' +
opts.id);
}
if (tmp) this.button.id = tmp;
// Button className.
if ('undefined' === typeof opts.className) {
tmp = 'btn btn-lg btn-primary';
}
else if (opts.className === false) {
tmp = '';
}
else if ('string' === typeof opts.className) {
tmp = opts.className;
}
else if (J.isArray(opts.className)) {
tmp = opts.className.join(' ');
}
else {
throw new TypeError('DoneButton.init: className must ' +
'be string, array, or undefined. Found: ' +
opts.className);
}
this.button.className = tmp;
// Button text.
this.button.value = 'string' === typeof opts.text ?
opts.text : this.getText('done');
this.disableOnDisconnect =
'undefined' === typeof opts.disableOnDisconnect ?
true : !! opts.disableOnDisconnect;
tmp = opts.delayOnPlaying;
if ('number' === typeof tmp) {
this.delayOnPlaying = tmp;
}
else if ('undefined' !== typeof tmp) {
throw new TypeError('DoneButton.init: delayOnPlaying must ' +
'be number or undefined. Found: ' + tmp);
}
tmp = opts.onclick;
if (tmp) {
if ('function' !== typeof tmp) {
throw new TypeError('DoneButton.init: onclick must function ' +
'or undefined. Found: ' + tmp);
}
this.onclick = tmp;
}
};
DoneButton.prototype.append = function() {
// If added in init, it must stay disabled until the step property
// of first step is evaluated.
if (!node.game.isReady()) {
this.disabled = true;
this.button.disabled = true;
}
this.bodyDiv.appendChild(this.button);
};
DoneButton.prototype.listeners = function() {
var that, disabled;
that = this;
// This is normally executed after the PLAYING listener of
// GameWindow where lockUnlockedInputs takes place.
// In case of a timeup, the donebutton will be locked and
// then unlocked by GameWindow, but otherwise it must be
// done here.
node.on('PLAYING', function() {
var prop, step, delay;
step = node.game.getCurrentGameStage();
prop = node.game.plot.getProperty(step, 'donebutton');
if (prop === false || (prop && prop.enableOnPlaying === false)) {
// It might be disabled already, but we do it again.
that.disable();
}
else {
if (prop && prop.hasOwnProperty &&
prop.hasOwnProperty('delayOnPlaying')) {
delay = prop.delayOnPlaying;
}
else {
delay = that.delayOnPlaying;
}
if (delay) {
setTimeout(function () {
// If not disabled because of a disconnection,
// enable it.
if (!disabled) that.enable();
}, delay);
}
else {
// It might be enabled already, but we do it again.
that.enable();
}
}
if ('string' === typeof prop) that.button.value = prop;
else if (prop && prop.text) that.button.value = prop.text;
});
if (this.disableOnDisconnect) {
node.on('SOCKET_DISCONNECT', function() {
if (!that.isDisabled()) {
that.disable();
disabled = true;
}
});
node.on('SOCKET_CONNECT', function() {
if (disabled) {
if (that.isDisabled()) that.enable();
disabled = false;
}
});
}
};
/**
* ### DoneButton.updateText
*
* Updates the text on the button, possibly for a given duration only
*
* @param {string} text The new text
* @param {number} duration Optional. The number of milliseconds the new
* text is displayed. If undefined, the new text stays indefinitely.
*/
DoneButton.prototype.updateText = function(text, duration) {
var oldText, that;
if (duration) {
that = this;
oldText = this.button.value;
node.timer.setTimeout(function() {
that.button.value = oldText;
}, duration);
}
this.button.value = text;
};
/**
* ### DoneButton.disable
*
* Disables the done button
*/
DoneButton.prototype.disable = function(opts) {
if (this.disabled) return;
this.disabled = true;
this.button.disabled = true;
this.emit('disabled', opts);
};
/**
* ### DoneButton.enable
*
* Enables the done button
*/
DoneButton.prototype.enable = function(opts) {
if (!this.disabled) return;
this.disabled = false;
this.button.disabled = false;
this.emit('enabled', opts);
};
})(node);