<commit_before>/* jslint node: true */
'use strict';

//	ENiGMA½
var loadMenu	= require('./menu_util.js').loadMenu;

var _			= require('lodash');
var assert		= require('assert');

module.exports	= MenuStack;

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

	if(next === currentModuleInfo.name) {
		cb(new Error('Menu config \'next\' specifies current menu!'));
		return;
	}

	this.goto(next, { }, cb);
};

MenuStack.prototype.prev = function(cb) {
	this.pop().instance.leave();			//	leave & remove current
	
	const previousModuleInfo = this.pop();	//	get previous

	if(previousModuleInfo) {
		this.goto(
			previousModuleInfo.name, 
			{ 
				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;
	}

	const loadOpts = {
		name		: name,
		client		: self.client, 
	};

	if(_.isObject(options)) {
		loadOpts.extraArgs = options.extraArgs;
	}

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

			if(cb) {
				cb(null);
			}
		}
	});
};

MenuStack.prototype.getCurrentModule = function() {
	var top = this.top();
	if(top) {
		return top.instance;
	}
};
<commit_msg>ES6 menu stack<commit_after>/* jslint node: true */
'use strict';

//	ENiGMA½
const loadMenu	= require('./menu_util.js').loadMenu;

//	deps
const _			= require('lodash');
const assert	= require('assert');

//	:TODO: Stack is backwards.... top should be most recent! :)

module.exports = class MenuStack {
	constructor(client) {
		this.client	= client;
		this.stack	= [];
	}

	push(moduleInfo) {
		return this.stack.push(moduleInfo);
	}

	pop() {
		return this.stack.pop();
	}

	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

		if(previousModuleInfo) {
			const opts = {
				extraArgs	: previousModuleInfo.extraArgs, 
				savedState	: previousModuleInfo.savedState
			};

			return this.goto(previousModuleInfo.name, opts, cb);
		}
		
		return cb(new Error('No previous menu available!'));		
	}

	goto(name, options, cb) {
		const 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;
		}

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

{'fraction_non_alphanumeric': 0.15422127400182872, 'fraction_numerical': 0.0013715330691862237, 'mean_word_length': 3.220578778135048, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5066499', 'n_tokens_mistral': 2618, 'n_tokens_neox': 2249, 'n_words': 465}