diff --git a/core/menu_stack.js b/core/menu_stack.js index 454eb7da..0dcdf4d8 100644 --- a/core/menu_stack.js +++ b/core/menu_stack.js @@ -2,158 +2,155 @@ 'use strict'; // ENiGMA½ -var loadMenu = require('./menu_util.js').loadMenu; +const loadMenu = require('./menu_util.js').loadMenu; -var _ = require('lodash'); -var assert = require('assert'); +// deps +const _ = require('lodash'); +const assert = require('assert'); -module.exports = MenuStack; +// :TODO: Stack is backwards.... top should be most recent! :) -function MenuStack(client) { - this.client = client; - this.stack = []; - - var self = this; - - this.push = function(moduleInfo) { - return self.stack.push(moduleInfo); - }; - - this.pop = function() { - return self.stack.pop(); - }; - - this.peekPrev = function() { - if(this.stackSize() > 1) { - return self.stack[self.stack.length - 2]; - } - }; - - this.top = function() { - if(self.stackSize() > 0) { - return self.stack[self.stack.length - 1]; - } - }; - - this.stackSize = function() { - return self.stack.length; - }; -} - -MenuStack.prototype.next = function(cb) { - var currentModuleInfo = this.top(); - assert(currentModuleInfo, 'Empty menu stack!'); - - var menuConfig = currentModuleInfo.instance.menuConfig; - var next; - - if(_.isArray(menuConfig.next)) { - next = this.client.acs.getConditionalValue(menuConfig.next, 'next'); - if(!next) { - cb(new Error('No matching condition for \'next\'!')); - return; - } - } else if(_.isString(menuConfig.next)) { - next = menuConfig.next; - } else { - cb(new Error('Invalid or missing \'next\' member in menu config!')); - return; +module.exports = class MenuStack { + constructor(client) { + this.client = client; + this.stack = []; } - if(next === currentModuleInfo.name) { - cb(new Error('Menu config \'next\' specifies current menu!')); - return; + push(moduleInfo) { + return this.stack.push(moduleInfo); } - this.goto(next, { }, cb); -}; + pop() { + return this.stack.pop(); + } -MenuStack.prototype.prev = function(cb) { - this.pop().instance.leave(); // leave & remove current + peekPrev() { + if(this.stackSize > 1) { + return this.stack[this.stack.length - 2]; + } + } + + top() { + if(this.stackSize > 0) { + return this.stack[this.stack.length - 1]; + } + } + + get stackSize() { + return this.stack.length; + } + + get currentModule() { + const top = this.top(); + if(top) { + return top.instance; + } + } + + next(cb) { + const currentModuleInfo = this.top(); + assert(currentModuleInfo, 'Empty menu stack!'); + + const menuConfig = currentModuleInfo.instance.menuConfig; + let nextMenu; + + if(_.isArray(menuConfig.next)) { + nextMenu = this.client.acs.getConditionalValue(menuConfig.next, 'next'); + if(!nextMenu) { + return cb(new Error('No matching condition for \'next\'!')); + } + } else if(_.isString(menuConfig.next)) { + nextMenu = menuConfig.next; + } else { + return cb(new Error('Invalid or missing \'next\' member in menu config!')); + } + + if(nextMenu === currentModuleInfo.name) { + return cb(new Error('Menu config \'next\' specifies current menu!')); + } + + this.goto(nextMenu, { }, cb); + } + + prev(cb) { + // :TODO: leave() should really take a cb... + this.pop().instance.leave(); // leave & remove current - const previousModuleInfo = this.pop(); // get previous + const previousModuleInfo = this.pop(); // get previous - if(previousModuleInfo) { - this.goto( - previousModuleInfo.name, - { + if(previousModuleInfo) { + const opts = { extraArgs : previousModuleInfo.extraArgs, savedState : previousModuleInfo.savedState - }, - cb - ); - } else { - cb(new Error('No previous menu available!')); - } -}; + }; -MenuStack.prototype.goto = function(name, options, cb) { - var currentModuleInfo = this.top(); - - if(!cb && _.isFunction(options)) { - cb = options; - } - - const self = this; - - if(currentModuleInfo && name === currentModuleInfo.name) { - if(cb) { - cb(new Error('Already at supplied menu!')); + return this.goto(previousModuleInfo.name, opts, cb); } - return; + + return cb(new Error('No previous menu available!')); } - const loadOpts = { - name : name, - client : self.client, - }; + goto(name, options, cb) { + const currentModuleInfo = this.top(); - if(_.isObject(options)) { - loadOpts.extraArgs = options.extraArgs; - } + if(!cb && _.isFunction(options)) { + cb = options; + } - loadMenu(loadOpts, function menuLoaded(err, modInst) { - if(err) { - const errCb = cb || self.client.defaultHandlerMissingMod(); - errCb(err); - } else { - // :TODO: Move this log to caller - self.client.log.debug( { menuName : name }, 'Goto menu module'); - - if(currentModuleInfo) { - // save stack state - currentModuleInfo.savedState = currentModuleInfo.instance.getSaveState(); - - currentModuleInfo.instance.leave(); - } - - self.push({ - name : name, - instance : modInst, - extraArgs : loadOpts.extraArgs, - }); - - // restore previous state if requested - if(options && options.savedState) { - modInst.restoreSavedState(options.savedState); - } - - modInst.enter(); - - self.client.log.trace( - { stack : _.map(self.stack, function(si) { return si.name; } ) }, - 'Updated menu stack'); + const self = this; + if(currentModuleInfo && name === currentModuleInfo.name) { if(cb) { - cb(null); + cb(new Error('Already at supplied menu!')); } + return; } - }); -}; -MenuStack.prototype.getCurrentModule = function() { - var top = this.top(); - if(top) { - return top.instance; + const loadOpts = { + name : name, + client : self.client, + }; + + if(_.isObject(options)) { + loadOpts.extraArgs = options.extraArgs; + } + + loadMenu(loadOpts, (err, modInst) => { + if(err) { + // :TODO: probably should just require a cb... + const errCb = cb || self.client.defaultHandlerMissingMod(); + errCb(err); + } else { + self.client.log.debug( { menuName : name }, 'Goto menu module'); + + if(currentModuleInfo) { + // save stack state + currentModuleInfo.savedState = currentModuleInfo.instance.getSaveState(); + + currentModuleInfo.instance.leave(); + } + + self.push({ + name : name, + instance : modInst, + extraArgs : loadOpts.extraArgs, + }); + + // restore previous state if requested + if(options && options.savedState) { + modInst.restoreSavedState(options.savedState); + } + + modInst.enter(); + + self.client.log.trace( + { stack : _.map(self.stack, stackEntry => stackEntry.name) }, + 'Updated menu stack'); + + if(cb) { + cb(null); + } + } + }); } };