UNPKG

nodegame-widgets

Version:

Collections of useful and reusable javascript / HTML snippets for nodeGame

398 lines (354 loc) 12.7 kB
/** * # VisualStage * Copyright(c) 2019 Stefano Balietti * MIT Licensed * * Shows the name of the current, previous and next step. * * www.nodegame.org */ (function(node) { "use strict"; var Table = W.Table; node.widgets.register('VisualStage', VisualStage); // ## Meta-data VisualStage.version = '0.11.0'; VisualStage.description = 'Displays the name of the current, previous and next step of the game.'; VisualStage.title = false; VisualStage.className = 'visualstage'; VisualStage.texts = { miss: '', current: 'Stage: ', previous: 'Prev: ', next: 'Next: ' }; // ## Dependencies VisualStage.dependencies = { JSUS: {}, Table: {} }; /** * ## VisualStage constructor */ function VisualStage() { // ### VisualStage.displayMode // // The display mode: 'compact', 'table'. this.displayMode = 'inline'; // ### VisualStage.table // // The HTML element containing the information in 'table' mode this.table = null; // ### VisualStage.preprocess // // A callback function preprocessing the information displayed this.preprocess = null; // ### VisualStage.order // // The order in which information is displayed, if available. // // In 'init' it gets reassigned based on displayMode. this.order = [ 'current', 'next', 'previous' ]; // ### VisualStage.capitalize // // If TRUE, the name/id of a step is capitalized. Default: TRUE. this.capitalize = true; // ### VisualStage.replaceUnderscore // // If TRUE, underscores are replaced with spaces. Default: TRUE. this.replaceUnderscore = true; // Default display settings. // ### VisualStage.addRound // // If TRUE, round number is added to the name of steps in repeat stages this.addRound = true; // ### VisualStage.showPrevious // // If TRUE, the name of the previuos step is displayed. this.showPrevious = false; // ### VisualStage.showCurrent // // If TRUE, the name of the current step is displayed. this.showCurrent = true; // ### VisualStage.showNext // // If TRUE, the name of the next step is displayed. this.showNext = true; } // ## VisualStage methods VisualStage.prototype.init = function(opts) { var err; if ('undefined' !== typeof opts.displayMode) { if (opts.displayMode !== 'inline' && opts.displayMode !== 'table') { throw new TypeError('VisualStage.init: displayMode must be ' + '"inline", "table" or undefined. ' + 'Found: ' + opts.displayMode); } this.displayMode = opts.displayMode; } if ('undefined' !== typeof opts.addRound) { this.addRound = !!opts.addRound; } if ('undefined' !== typeof opts.previous) { this.showPrevious = !!opts.previous; } if ('undefined' !== typeof opts.next) { this.showNext = !!opts.next; } if ('undefined' !== typeof opts.current) { this.showCurrent = !!opts.current; } if ('undefined' !== typeof opts.order) { if (!J.isArray(opts.order) || opts.order.length !== 3) { throw new TypeError('VisualStage.init: order must be ' + 'an array of length 3 or undefined. ' + 'Found: ' + opts.order); } err = checkOrderOption(opts.order, this.order.slice(0)); if (err) { throw new TypeError('VisualStage.init: order contains ' + 'errors: ' + opts.order); } this.order = opts.order; } else { if (this.displayMode === 'inline') { this.order = [ 'previous', 'current', 'next' ]; } } if ('undefined' !== typeof opts.preprocess) { if ('function' !== typeof opts.preprocess) { throw new TypeError('VisualStage.init: preprocess must be ' + 'function or undefined. Found: ' + opts.preprocess); } this.preprocess = opts.preprocess; } if ('undefined' !== typeof opts.capitalize) { this.capitalize = !!opts.capitalize; } if ('undefined' !== typeof opts.replaceUnderscore) { this.replaceUnderscore = !!opts.replaceUnderscore; } }; /** * ### VisualStage.append * * Appends widget to `this.bodyDiv` and writes the stage * * @see VisualStage.updateDisplay */ VisualStage.prototype.append = function() { if (this.displayMode === 'table') { this.table = new Table(); this.bodyDiv.appendChild(this.table.table); } else { this.div = W.append('div', this.bodyDiv); } this.updateDisplay(); }; VisualStage.prototype.listeners = function() { var that = this; node.on('STEP_CALLBACK_EXECUTED', function() { that.updateDisplay(); }); // Game over and init? }; /** * ### VisualStage.updateDisplay * * Writes the current, previous and next step names * * It uses the step property `name`, if existing, otherwise `id`. * Depending on current settings, it capitalizes it, and preprocess it. * * @see VisualStage.getStepName */ VisualStage.prototype.updateDisplay = function() { var curStep, nextStep, prevStep; var curStepName, nextStepName, prevStepName; var order, t; order = {}; curStep = node.game.getCurrentGameStage(); if (curStep) { if (this.showCurrent) { curStepName = this.getStepName(curStep, curStep, 'current'); order.current = curStepName; } if (this.showNext) { nextStep = node.game.plot.next(curStep); if (nextStep) { nextStepName = this.getStepName(nextStep, curStep, 'next'); order.next = nextStepName; } } if (this.showPrevious) { prevStep = node.game.plot.previous(curStep); if (prevStep) { prevStepName = this.getStepName(prevStep, curStep, 'previous'); order.previous = prevStepName; } } } if (this.displayMode === 'table') { this.table.clear(true); addRow(this, 0, order); addRow(this, 1, order); addRow(this, 2, order); // t = this.table.selexec('y', '=', 0); t.addClass('strong'); // t.selexec('x', '=', 1).addClass('underline'); this.table.parse(); } else { this.div.innerHTML = ''; addSpan(this, 0, order); addSpan(this, 1, order); addSpan(this, 2, order); } }; /** * ### VisualStage.getStepName * * Returns the step name of a given step * * @param {GameStage} gameStage The game stage we want to to get the name * @param {GameStage} curStage The current game stage * @param {string} mod A modifier: 'current', 'previous', 'next'. * * @return {string} name The name of the step */ VisualStage.prototype.getStepName = function(gameStage, curStage, mod) { var name, round, preprocess, addRound; // Get the name. If no step property is defined, use the id and // do some text replacing. name = node.game.plot.getProperty(gameStage, 'name'); if ('function' === typeof name) { preprocess = name; name = null; } else if ('object' === typeof name && name !== null) { preprocess = name.preprocess; addRound = name.addRound; name = name.name; } if (!name) { name = node.game.plot.getStep(gameStage); if (!name) { name = this.getText('miss'); } else { name = name.id; if (this.replaceUnderscore) name = name.replace(/_/g, " "); if (this.capitalize) name = capitalize(name); } } if (!preprocess) preprocess = this.preprocess; if ('undefined' === typeof addRound) addRound = this.addRound; round = getRound(gameStage, curStage, mod); // If function, executes it. if (preprocess) name = preprocess.call(node.game, name, mod, round); if (addRound && round) name += ' ' + round; return name; }; // ## Helper functions. /** * ### getRound * * Returns the round for a given step, if its stage is a repeat stage * * @param {GameStage} gameStage The game stage we want to to get the round * @param {GameStage} gameStage The current game stage * @param {string} A modifier: 'current', 'previous', 'next'. * * @return {number} round The round for the step * * @see getName */ function getRound(gameStage, curStage, mod) { var round, totRounds; if (!gameStage.stage) return; totRounds = node.game.plot.stager.sequence[(gameStage.stage - 1)].num; if (!totRounds) return; round = node.game.getRound(); // Same stage: can be current, next, or previous. if (curStage.stage === gameStage.stage) { if (mod === 'next') round++; else if (mod === 'previous') round--; } // This is a previous stage. else if (curStage.stage > gameStage.stage) { round = totRounds; } // This is a next stage. else { round = 1; } return round; } function capitalize(str) { var tks, i, len; tks = str.split(' '); str = capWord(tks[0]); len = tks.length; if (len > 1) str += ' ' + capWord(tks[1]); if (len > 2) { for (i = 2; i < len; i++) { str += ' ' + capWord(tks[i]); } } return str; } function capWord(word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); } function addRow(that, idx, order) { var row, str, type, name, className, obj; type = that.order[idx]; str = that.getText(type); name = order[type]; if (!name) return; className = 'visualstage-' + type; obj = { className: className, content: name }; if (str === false) row = [ obj ]; else if (type === 'current') row = [ { content: name, colspan: 2 } ]; else row = [ { className: className, content: str }, obj ]; that.table.addRow(row); } function addSpan(that, idx, order) { var str, tmp; tmp = that.order[idx]; str = order[tmp]; if (!str) return; if (tmp !== 'current') { str = '<span class="strong">' + that.getText(tmp) + '</span>' + str; } W.add('span', that.div, { innerHTML: str, className: 'visualstage-' + tmp }); } function checkOrderOption(order, arr) { var i; i = arr.indexOf(order[0]); if (i === -1) return 'unknown item: ' + order[0]; arr.splice(i,1); i = arr.indexOf(order[1]); if (i === -1) return 'unknown item: ' + order[1]; arr.splice(i,1); i = arr.indexOf(order[2]); if (i === -1) return 'unknown item: ' + order[2]; arr.splice(i,1); if (arr.length) return 'duplicated entry: ' + arr[0]; return; } })(node);