From 0d9add70bd0a05eef1f3d1f65cdd46c4129da087 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 15 May 2015 23:02:58 -0600 Subject: [PATCH] * Moved menus -> menus.json::menus * Move prompts -> prompts.json::prompts * Default to 80x25 if NAWS/ENV/CPR term size negotiations fail. Seeing this with Netrunner. May need more work. --- core/art.js | 2 + core/connect.js | 76 +++- core/mask_edit_text_view.js | 39 +- core/menu_util.js | 6 +- core/servers/telnet.js | 8 +- core/view_controller.js | 14 +- mods/menu.json | 705 ++++++++++++++++++------------------ mods/prompt.json | 115 +++--- 8 files changed, 511 insertions(+), 454 deletions(-) diff --git a/core/art.js b/core/art.js index 369f56c1..63be4b70 100644 --- a/core/art.js +++ b/core/art.js @@ -454,6 +454,7 @@ function display(options, cb) { var nextPauseTermHeight = options.client.term.termHeight; var continous = false; + /* parser.on('row update', function rowUpdate(row) { if(row >= nextPauseTermHeight) { if(!continous && 'termHeight' === options.pause) { @@ -469,6 +470,7 @@ function display(options, cb) { nextPauseTermHeight += options.client.term.termHeight; } }); + */ parser.on('mci', function mciEncountered(mciInfo) { diff --git a/core/connect.js b/core/connect.js index 1683ab5c..074dfabd 100644 --- a/core/connect.js +++ b/core/connect.js @@ -14,33 +14,56 @@ var util = require('util'); exports.connectEntry = connectEntry; -function ansiQueryTermSizeIfNeeded(client) { +function ansiQueryTermSizeIfNeeded(client, cb) { if(client.term.termHeight > 0 || client.term.termWidth > 0) { + cb(true); return; } - var onCPR = function(pos) { + var cprListener = function(pos) { // // If we've already found out, disregard // if(client.term.termHeight > 0 || client.term.termWidth > 0) { + cb(true); return; } assert(2 === pos.length); - client.term.termHeight = pos[0]; - client.term.termWidth = pos[1]; + var h = pos[0]; + var w = pos[1]; + + // + // Netrunner for example gives us 1x1 here. Not really useful. Ignore + // values that seem obviously bad. + // + if(h < 10 || w < 10) { + Log.warn( + { height : h, width : w }, + 'Ignoring ANSI CPR screen size query response due to very small values'); + cb(false); + return; + } + + client.term.termHeight = h; + client.term.termWidth = w; Log.debug( - { termWidth : client.term.termWidth, termHeight : client.term.termHeight, updateSource : 'ANSI CPR' }, - 'Window size updated'); + { + termWidth : client.term.termWidth, + termHeight : client.term.termHeight, + source : 'ANSI CPR' + }, + 'Window size updated' + ); }; - client.once('cursor position report', onCPR); + client.once('cursor position report', cprListener); // give up after 2s setTimeout(function onTimeout() { - client.removeListener('cursor position report', onCPR); + client.removeListener('cursor position report', cprListener); + cb(true); }, 2000); client.term.write(ansi.queryScreenSize()); @@ -53,10 +76,10 @@ function prepareTerminal(term) { } function displayBanner(term) { - // :TODO: add URL to banner + // :TODO: add URL(s) to banner term.write(ansi.fromPipeCode(util.format('' + - '|33Conected to |32EN|33|01i|32|22GMA|32|01½|00 |33BBS version|31|01 %s\n' + - '|00|33Copyright (c) 2014 Bryan Ashby\n' + + '|33Conected to |32EN|33|01i|00|32|22GMA|32|01½|00 |33BBS version|31|01 %s\n' + + '|00|33Copyright (c) 2014-2015 Bryan Ashby\n' + '|00', packageJson.version))); } @@ -67,17 +90,30 @@ function connectEntry(client) { // If we don't yet know the client term width/height, // try with a nonstandard ANSI DSR type request. // - ansiQueryTermSizeIfNeeded(client); + ansiQueryTermSizeIfNeeded(client, function ansiCprResult(result) { - prepareTerminal(term); + if(!result) { + // + // We still don't have something good for term height/width. + // Default to DOS size 80x25. + // + // :TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing??? + Log.warn('Failed to negotiate term size; Defaulting to 80x25!'); + + term.termHeight = 25; + term.termWidth = 80; + } - // - // Always show a ENiGMA½ banner - // - displayBanner(term); + prepareTerminal(term); - setTimeout(function onTimeout() { - client.gotoMenuModule( { name : Config.firstMenu }); - }, 500); + // + // Always show a ENiGMA½ banner + // + displayBanner(term); + + setTimeout(function onTimeout() { + client.gotoMenuModule( { name : Config.firstMenu }); + }, 500); + }); } diff --git a/core/mask_edit_text_view.js b/core/mask_edit_text_view.js index 9cd90ea9..63aa2fef 100644 --- a/core/mask_edit_text_view.js +++ b/core/mask_edit_text_view.js @@ -37,7 +37,7 @@ function MaskEditTextView(options) { this.maskPattern = options.maskPattern || ''; this.clientBackspace = function() { - var fillCharSGR = this.getStyleSGR(2) || this.getSGR(); + var fillCharSGR = this.getStyleSGR(3) || this.getSGR(); this.client.term.write('\b' + fillCharSGR + this.fillChar + '\b' + this.getFocusSGR()); }; @@ -55,10 +55,11 @@ function MaskEditTextView(options) { self.client.term.write((self.hasFocus ? self.getFocusSGR() : self.getSGR()) + textToDraw[t]); t++; } else { - self.client.term.write((self.getStyleSGR(2) || '') + self.fillChar); + self.client.term.write((self.getStyleSGR(3) || '') + self.fillChar); } } else { - self.client.term.write((self.getStyleSGR(1) || '') + self.maskPattern[i]); + var styleSgr = this.hasFocus ? (self.getStyleSGR(2) || '') : (self.getStyleSGR(1) || ''); + self.client.term.write(styleSgr + self.maskPattern[i]); } i++; } @@ -79,6 +80,10 @@ function MaskEditTextView(options) { } }; + this.getCursorEditYPosition = function() { + return this.position.y + this.patternArrayPos; + }; + this.buildPattern(); } @@ -92,6 +97,21 @@ MaskEditTextView.maskPatternCharacterRegEx = { '&' : /[\w\d\s]/, // Any "printable" 32-126, 128-255 }; +MaskEditTextView.prototype.setFocus = function(focused) { + // :TODO: can't call super unless we want wasted redraw stuff. This seems sloppy & should probably be looked into + //MaskEditTextView.super_.prototype.setFocus.call(this, focused); + assert(this.acceptsFocus, 'View does not accept focus'); + + this.hasFocus = focused; + this.restoreCursor(); + + this.redraw(); + + // position & SGR for cursor + this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition())); + this.client.term.write(this.getFocusSGR()); +}; + MaskEditTextView.prototype.setMaskPattern = function(pattern) { this.dimens.width = pattern.length; @@ -123,8 +143,8 @@ MaskEditTextView.prototype.onKeyPress = function(key, isSpecial) { } this.redraw(); - } - + this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition())); + } MaskEditTextView.super_.prototype.onKeyPress.call(this, key, isSpecial); }; @@ -140,21 +160,14 @@ MaskEditTextView.prototype.onSpecialKeyPress = function(keyName) { this.text = this.text.substr(0, this.text.length - 1); this.clientBackspace(); } else { - var offset = -1; while(this.patternArrayPos > 0) { if(_.isRegExp(this.patternArray[this.patternArrayPos])) { this.text = this.text.substr(0, this.text.length - 1); - this.client.term.write(ansi.goto(this.position.x, this.position.y + (this.text.length - offset))); - // :TODO: use better ANSI Position code to just go back - // this.client.term.write(ansi.goto(this.position.x, yPosition)); - + this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition() + 1)); this.clientBackspace(); break; } - console.log('skip past ' + this.patternArray[this.patternArrayPos]) - //this.client.term.write(ansi.back() + ansi.back()); this.patternArrayPos--; - offset++; } } } diff --git a/core/menu_util.js b/core/menu_util.js index f8a9c17a..f44cdb7f 100644 --- a/core/menu_util.js +++ b/core/menu_util.js @@ -48,8 +48,8 @@ function getMenuConfig(name, cb) { }); }, function locateMenuConfig(menuJson, callback) { - if(_.isObject(menuJson[name])) { - menuConfig = menuJson[name]; + if(_.has(menuJson, [ 'menus', name ])) { + menuConfig = menuJson.menus[name]; callback(null); } else { callback(new Error('No menu entry for \'' + name + '\'')); @@ -66,7 +66,7 @@ function getMenuConfig(name, cb) { }, function locatePromptConfig(promptJson, callback) { if(promptJson) { - if(_.isObject(promptJson[menuConfig.prompt])) { + if(_.has(promptJson, [ 'prompts', menuConfig.prompt ])) { menuConfig.promptConfig = promptJson[menuConfig.prompt]; } else { callback(new Error('No prompt entry for \'' + menuConfig.prompt + '\'')); diff --git a/core/servers/telnet.js b/core/servers/telnet.js index 2da43c26..6aab0a0b 100644 --- a/core/servers/telnet.js +++ b/core/servers/telnet.js @@ -538,7 +538,7 @@ TelnetClient.prototype.setTermType = function(ttype) { this.term.env['TERM'] = ttype; this.term.termType = ttype; - Log.debug({ termType : ttype }, 'Set terminal type'); + Log.debug( { termType : ttype }, 'Set terminal type'); } TelnetClient.prototype.handleSbCommand = function(evt) { @@ -571,10 +571,10 @@ TelnetClient.prototype.handleSbCommand = function(evt) { self.setTermType(evt.envVars[name]); } else if('COLUMNS' === name && 0 === self.term.termWidth) { self.term.termWidth = parseInt(evt.envVars[name]); - Log.debug({ termWidth : self.term.termWidth, updateSource : 'NEW-ENVIRON'}, 'Window width updated'); + Log.debug({ termWidth : self.term.termWidth, source : 'NEW-ENVIRON'}, 'Window width updated'); } else if('ROWS' === name && 0 === self.term.termHeight) { self.term.termHeight = parseInt(evt.envVars[name]); - Log.debug({ termHeight : self.term.termHeight, updateSource : 'NEW-ENVIRON'}, 'Window height updated'); + Log.debug({ termHeight : self.term.termHeight, source : 'NEW-ENVIRON'}, 'Window height updated'); } else { if(name in self.term.env) { assert(evt.type === SB_COMMANDS.INFO); @@ -604,7 +604,7 @@ TelnetClient.prototype.handleSbCommand = function(evt) { self.term.env['ROWS'] = evt.height; } - Log.debug({ termWidth : evt.width , termHeight : evt.height, updateSource : 'NAWS' }, 'Window size updated'); + Log.debug({ termWidth : evt.width , termHeight : evt.height, source : 'NAWS' }, 'Window size updated'); } else { console.log('unhandled SB: ' + JSON.stringify(evt)); } diff --git a/core/view_controller.js b/core/view_controller.js index d64f77ad..f37469d1 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -194,16 +194,18 @@ function ViewController(options) { setViewProp('maxLength'); - - ['styleSGR1', 'styleSGR2'].forEach(function styleSgr(style) { - setViewProp(style, function(v) { + // + // styleSGRx: 1..25 + // + for(var i = 1; i <= 25; i++) { + setViewProp('styleSGR' + i, function(v) { if(_.isObject(v)) { - view[style] = ansi.getSGRFromGraphicRendition(v, true); + view['styleSGR' + i] = ansi.getSGRFromGraphicRendition(v, true); } else if(_.isString(v)) { - view[style] = ansi.fromPipeCode(v); + view['styleSGR' + i] = ansi.fromPipeCode(v); } }); - }); + } setViewProp('fillChar', function(v) { if(_.isNumber(v)) { diff --git a/mods/menu.json b/mods/menu.json index 1cf63b98..041d495d 100644 --- a/mods/menu.json +++ b/mods/menu.json @@ -33,394 +33,397 @@ } } */ - "connected" : { + "menus" : { "art" : "CONNECT", - "next" : "matrix", - "options" : { - "cls" : true, - "nextTimeout" : 1500 - } - }, - "matrix" : { - "art" : "matrix", - "form" : { - "0" : { // :TODO: Make form "0" the default if missing (e.g. optional)... not sure how with current structure though - "VM1" : { - "mci" : { - "VM1" : { - "submit" : true, - "focus" : true, - // :TODO: need a good way to localize these ... Standard Orig->Lookup seems good. - "items" : [ "Login", "Apply", "Log Off" ]//, - } - }, - "submit" : { - "*" : [ - { - "value" : { "1" : 0 }, - "action" : "@menu:login" - }, - { - "value" : { "1" : 1 }, - "action" : "@menu:apply" - }, - { - "value" : { "1" : 2 }, - "action" : "@menu:logoff" + "connected" : { + "next" : "matrix", + "options" : { + "cls" : true, + "nextTimeout" : 1500 + } + }, + "matrix" : { + "art" : "matrix", + "form" : { + "0" : { // :TODO: Make form "0" the default if missing (e.g. optional)... not sure how with current structure though + "VM1" : { + "mci" : { + "VM1" : { + "submit" : true, + "focus" : true, + // :TODO: need a good way to localize these ... Standard Orig->Lookup seems good. + "items" : [ "Login", "Apply", "Log Off" ]//, } - ] + }, + "submit" : { + "*" : [ + { + "value" : { "1" : 0 }, + "action" : "@menu:login" + }, + { + "value" : { "1" : 1 }, + "action" : "@menu:apply" + }, + { + "value" : { "1" : 2 }, + "action" : "@menu:logoff" + } + ] + } + } + } + }, + "options" : { + "cls" : true + } + }, + "login" : { + // :TODO: may want { "prompt" : { "name" : "blah", "action" : ... }} + "prompt" : "userCredentials", + "fallback" : "matrix", + "next" : "newUserActive", + "action" : "@systemMethod:login", + + // :TODO: support alt submit method for prompts + // if present, standard filters apply. No need for multiple submit ID's + // since a prompt can only utilize one: + "submit" : [ + { + "value" : { "1" : "thing" }, + "action" : "@method:doThings" + } + ], + + "options" : { + "cls" : true + } + }, + "logoff" : { + "art" : "LOGOFF", + //"module" : "logoff", + "action" : "@systemMethod:logoff", + "options" : { "cls" : true } + }, + "apply" : { + "art" : "APPLY", + "next" : "newUserActive", + "form" : { + "0" : { + "BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : { + "mci" : { + "ET1" : { + "focus" : true, + "argName" : "username", + "maxLength" : "@config:users.usernameMax" + }, + "ET2" : { + "argName" : "realName", + "maxLength" : 32 + }, + "ET3" : { + "argName" : "age", + "width" : 3, + "maxLength" : 3 + }, + "ET4" : { + "argName" : "sex", + "width" : 1, + "maxLength" : 1 + }, + "ET5" : { + "argName" : "location", + "maxLength" : 32 + }, + "ET6" : { + "argName" : "affils", + "maxLength" : 32 + }, + "ET7" : { + "argName" : "email", + "maxLength" : 255 + }, + "ET8" : { + "argName" : "web", + "maxLength" : 255 + }, + "ET9" : { + "argName" : "password", + "password" : true, + "maxLength" : "@config:users.passwordMax" + }, + "ET10" : { + "argName" : "passwordConfirm", + "password" : true, + "maxLength" : "@config:users.passwordMax" + }, + "BT12" : { + "submit" : true, + "text" : "Apply" + }, + "BT13" : { + "submit" : [ "esc" ], + "text" : "Cancel" + } + }, + "submit" : { + "12" : [ // Apply + { + "value" : 12, // :TODO: better, this should be optional; if not present it's a any match + "action" : "@method:apply/submitApplication", + "extraArgs" : { + "inactive" : "userNeedsActivated", + "error" : "newUserCreateError" + } + } + ], + "13" : [ // Cancel + { + "value" : 13, + "action" : "@menu:matrix" + } + ] + } + } + } + }, + "options" : { + "cls" : true + } + }, + "newUserActive" : { + "art" : "userstats", + //"prompt" : "pause", + "options" : { + // :TODO: implement MCI codes for this + "cls" : true + } + }, + "demoMain" : { + "art" : "demo_selection_vm.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "VM1" : { + "mci" : { + "VM1" : { + "items" : [ + "Single Line Text Editing Views", + "Spinner & Toggle Views", + "Mask Edit Views", + "Vertical Menu Views", + "Horizontal Menu Views", + "Art Display", + "Some More Stuff", + "Other" + ], + "height" : 10, + "itemSpacing" : 1, + "justify" : "center", + "focusTextStyle" : "small i" + } + }, + "submit" : { + "*" : [ + { + "value" : { "1" : 0 }, + "action" : "@menu:demoEditTextView" + }, + { + "value" : { "1" : 1 }, + "action" : "@menu:demoSpinAndToggleView" + }, + { + "value" : { "1" : 2 }, + "action" : "@menu:demoMaskEditView" + }, + { + "value" : { "1" : 5 }, + "action" : "@menu:demoArtDisplay" + } + ] + } } } } }, - "options" : { - "cls" : true - } - }, - "login" : { - // :TODO: may want { "prompt" : { "name" : "blah", "action" : ... }} - "prompt" : "userCredentials", - "fallback" : "matrix", - "next" : "newUserActive", - "action" : "@systemMethod:login", - - // :TODO: support alt submit method for prompts - // if present, standard filters apply. No need for multiple submit ID's - // since a prompt can only utilize one: - "submit" : [ - { - "value" : { "1" : "thing" }, - "action" : "@method:doThings" - } - ], - - "options" : { - "cls" : true - } - }, - "logoff" : { - "art" : "LOGOFF", - //"module" : "logoff", - "action" : "@systemMethod:logoff", - "options" : { "cls" : true } - }, - "apply" : { - "art" : "APPLY", - "next" : "newUserActive", - "form" : { - "0" : { - "BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : { - "mci" : { - "ET1" : { - "focus" : true, - "argName" : "username", - "maxLength" : "@config:users.usernameMax" + "demoEditTextView" : { + "art" : "demo_edit_text_view1.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "BT5ET1ET2ET3ET4" : { + "mci" : { + "ET1" : { + "width" : 20, + "maxLength" : 20 + }, + "ET2" : { + "width" : 20, + "maxLength" : 40, + "textOverflow" : "..." + }, + "ET3" : { + "width" : 20, + "fillChar" : "-", + "styleSGR1" : "|00|36", + "maxLength" : 20 + }, + "ET4" : { + "width" : 20, + "maxLength" : 20, + "password" : true + }, + "BT5" : { + "width" : 8, + "text" : "< Back", + "submit" : [ "esc" ] + } }, - "ET2" : { - "argName" : "realName", - "maxLength" : 32 - }, - "ET3" : { - "argName" : "age", - "width" : 3, - "maxLength" : 3 - }, - "ET4" : { - "argName" : "sex", - "width" : 1, - "maxLength" : 1 - }, - "ET5" : { - "argName" : "location", - "maxLength" : 32 - }, - "ET6" : { - "argName" : "affils", - "maxLength" : 32 - }, - "ET7" : { - "argName" : "email", - "maxLength" : 255 - }, - "ET8" : { - "argName" : "web", - "maxLength" : 255 - }, - "ET9" : { - "argName" : "password", - "password" : true, - "maxLength" : "@config:users.passwordMax" - }, - "ET10" : { - "argName" : "passwordConfirm", - "password" : true, - "maxLength" : "@config:users.passwordMax" - }, - "BT12" : { - "submit" : true, - "text" : "Apply" - }, - "BT13" : { - "submit" : [ "esc" ], - "text" : "Cancel" + "submit" : { + "*" : [ + { + "value" : 5, + "action" : "@menu:demoMain" + } + ] } - }, - "submit" : { - "12" : [ // Apply - { - "value" : 12, // :TODO: better, this should be optional; if not present it's a any match - "action" : "@method:apply/submitApplication", - "extraArgs" : { - "inactive" : "userNeedsActivated", - "error" : "newUserCreateError" - } - } - ], - "13" : [ // Cancel - { - "value" : 13, - "action" : "@menu:matrix" - } - ] } } } }, - "options" : { - "cls" : true - } - }, - "newUserActive" : { - "art" : "userstats", - //"prompt" : "pause", - "options" : { - // :TODO: implement MCI codes for this - "cls" : true - } - }, - "demoMain" : { - "art" : "demo_selection_vm.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "VM1" : { - "mci" : { - "VM1" : { - "items" : [ - "Single Line Text Editing Views", - "Spinner & Toggle Views", - "Mask Edit Views", - "Vertical Menu Views", - "Horizontal Menu Views", - "Art Display", - "Some More Stuff", - "Other" - ], - "height" : 10, - "itemSpacing" : 1, - "justify" : "center", - "focusTextStyle" : "small i" - } - }, - "submit" : { - "*" : [ - { - "value" : { "1" : 0 }, - "action" : "@menu:demoEditTextView" + "demoSpinAndToggleView" : { + "art" : "demo_spin_and_toggle.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "BT8SM1SM2TM3" : { + "mci" : { + "SM1" : { + "items" : [ "Henry Morgan", "François l'Ollonais", "Roche Braziliano", "Black Bart", "Blackbeard" ] }, - { - "value" : { "1" : 1 }, - "action" : "@menu:demoSpinAndToggleView" + "SM2" : { + "items" : [ "Razor 1911", "DrinkOrDie", "TRSI" ] }, - { - "value" : { "1" : 2 }, - "action" : "@menu:demoMaskEditView" + "TM3" : { + "items" : [ "Yarly", "Nowaii" ], + "styleSGR1" : "|00|30|01", + "hotkeys" : { "Y" : 0, "N" : 1 } }, - { - "value" : { "1" : 5 }, - "action" : "@menu:demoArtDisplay" + "BT8" : { + "text" : "< Back", + "submit" : [ "esc" ] } - ] + }, + "submit" : { + "*" : [ + { + "value" : 8, + "action" : "@menu:demoMain" + } + ] + } } } } - } - }, - "demoEditTextView" : { - "art" : "demo_edit_text_view1.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "BT5ET1ET2ET3ET4" : { - "mci" : { - "ET1" : { - "width" : 20, - "maxLength" : 20 - }, - "ET2" : { - "width" : 20, - "maxLength" : 40, - "textOverflow" : "..." - }, - "ET3" : { - "width" : 20, - "fillChar" : "-", - "styleSGR1" : "|00|36", - "maxLength" : 20 - }, - "ET4" : { - "width" : 20, - "maxLength" : 20, - "password" : true - }, - "BT5" : { - "width" : 8, - "text" : "< Back", - "submit" : [ "esc" ] - } - }, - "submit" : { - "*" : [ - { - "value" : 5, - "action" : "@menu:demoMain" + }, + "demoMaskEditView" : { + "art" : "demo_mask_edit_text_view1.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "BT5ME1ME2" : { + "mci" : { + "ME1" : { + "maskPattern" : "##/##/##", + "styleSGR1" : "|00|30|01", + "styleSGR2" : "|00|45|01" } - ] + } } } } - } - }, - "demoSpinAndToggleView" : { - "art" : "demo_spin_and_toggle.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "BT8SM1SM2TM3" : { - "mci" : { - "SM1" : { - "items" : [ "Henry Morgan", "François l'Ollonais", "Roche Braziliano", "Black Bart", "Blackbeard" ] - }, - "SM2" : { - "items" : [ "Razor 1911", "DrinkOrDie", "TRSI" ] - }, - "TM3" : { - "items" : [ "Yarly", "Nowaii" ], - "styleSGR1" : "|00|30|01", - "hotkeys" : { "Y" : 0, "N" : 1 } - }, - "BT8" : { - "text" : "< Back", - "submit" : [ "esc" ] - } - }, - "submit" : { - "*" : [ - { - "value" : 8, - "action" : "@menu:demoMain" + }, + "demoArtDisplay" : { + "art" : "demo_selection_vm.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "VM1" : { + "mci" : { + "VM1" : { + "items" : [ + "Defaults - DOS ANSI", + "Defaults - Amiga", + "Pause at Term Height" + ], + // :TODO: justify not working?? + "focusTextStyle" : "small i" } - ] - } - } - } - } - }, - "demoMaskEditView" : { - "art" : "demo_mask_edit_text_view1.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "BT5ME1ME2" : { - "mci" : { - "ME1" : { - "maskPattern" : "##/##/##", - "styleSGR1" : "|00|30|01" + }, + "submit" : { + "*" : [ + { + "value" : { "1" : 0 }, + "action" : "@menu:demoDefaultsDosAnsi" + } + ] } } } } + }, + "demoDefaultsDosAnsi" : { + "art" : "WE-CIZB.ANS", + "options" : { "cls" : true } } - }, - "demoArtDisplay" : { - "art" : "demo_selection_vm.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "VM1" : { - "mci" : { - "VM1" : { - "items" : [ - "Defaults - DOS ANSI", - "Defaults - Amiga", - "Pause at Term Height" - ], - // :TODO: justify not working?? - "focusTextStyle" : "small i" - } - }, - "submit" : { - "*" : [ - { - "value" : { "1" : 0 }, - "action" : "@menu:demoDefaultsDosAnsi" - } - ] - } - } - } - } - }, - "demoDefaultsDosAnsi" : { - "art" : "WE-CIZB.ANS", - "options" : { "cls" : true } - } - - /* - :TODO: conceptual simplified menus -- actions/etc. without forms + + /* + :TODO: conceptual simplified menus -- actions/etc. without forms - { - "myMenu" : { - "art" : "MENUART", - "mci" : { // lack of "form": direct to form:0 {} - "VM1" : { - "items" : [ "Hello", "Goodbye" ], - "action" : "@method:someMethod" // implies { "submit" : true } - }, - "submit" : { - // alternate form with filters + { + "myMenu" : { + "art" : "MENUART", + "mci" : { // lack of "form": direct to form:0 {} + "VM1" : { + "items" : [ "Hello", "Goodbye" ], + "action" : "@method:someMethod" // implies { "submit" : true } + }, + "submit" : { + // alternate form with filters + } } } } - } - */ - /* - "demoEditTextView" : { - "art" : "demo_edit_text_view.ans", - "options" : { "cls" : true }, - "form" : { - "0" : { - "ET1ET2ET3ET5SM4TM6" : { - "mci" : { - "ET1" : { "maxLength" : 1 }, - "ET2" : { "maxLength" : 1 }, - "ET3" : { "maxLength" : 1 }, - "SM4" : { - "items" : [ "One", "Two", "Three", "Four" ] - }, - "ET5" : { - "password" : true, - "submit" : [ "esc" ], - "fillChar" : "#" - }, - "TM6" : { - "items" : [ "Yes", "No" ], - "hotkeys" : { "Y" : 0, "n" : 1 } + */ + /* + "demoEditTextView" : { + "art" : "demo_edit_text_view.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "ET1ET2ET3ET5SM4TM6" : { + "mci" : { + "ET1" : { "maxLength" : 1 }, + "ET2" : { "maxLength" : 1 }, + "ET3" : { "maxLength" : 1 }, + "SM4" : { + "items" : [ "One", "Two", "Three", "Four" ] + }, + "ET5" : { + "password" : true, + "submit" : [ "esc" ], + "fillChar" : "#" + }, + "TM6" : { + "items" : [ "Yes", "No" ], + "hotkeys" : { "Y" : 0, "n" : 1 } + } } } } } } + */ } - */ } \ No newline at end of file diff --git a/mods/prompt.json b/mods/prompt.json index 75fc4e78..0d634322 100644 --- a/mods/prompt.json +++ b/mods/prompt.json @@ -1,71 +1,72 @@ { - "userCredentials" : { + "prompts" : { "art" : "usercred", - "mci" : { - "ET1" : { - "argName" : "username", - "maxLength" : "@config:users.usernameMax" - }, - "ET2" : { - "submit" : true, - "argName" : "password", - "password" : true, - "maxLength" : "@config:users.passwordMax" + "userCredentials" : { + "mci" : { + "ET1" : { + "argName" : "username", + "maxLength" : "@config:users.usernameMax" + }, + "ET2" : { + "submit" : true, + "argName" : "password", + "password" : true, + "maxLength" : "@config:users.passwordMax" + } } - } - }, - "pause" : { - "art" : "pause" - /* - "mci" : { - // :TODO: Need special pause for a key MCI - // e.g. %PA -> themed prompt - } - - ...or maybe pause should just be special: - { - ... - "pause" true - // uses theme pause which can be art/inline/etc. - - } - - ... better, a special prompt - - GetKeyView - * showKey : false - - */ - }, - "standard" : { + }, "pause" : { "art" : "pause" + /* + "mci" : { + // :TODO: Need special pause for a key MCI + // e.g. %PA -> themed prompt + } + + ...or maybe pause should just be special: + { + ... + "pause" true + // uses theme pause which can be art/inline/etc. + + } + + ... better, a special prompt + + GetKeyView + * echoKey : false + + */ + }, + "standard" : { + "pause" : { + "art" : "pause" + } + }, + "custom" : { + } - }, - "custom" : { + /* + see notes in menu_module.js also + ...how to allow for this to come from the theme first??? + same as custom vc drawing/etc.? ... - } - /* - see notes in menu_module.js also - ...how to allow for this to come from the theme first??? - same as custom vc drawing/etc.? ... - - { - "theme" : { - "inlineArt" : { - "something" : "%MC and |01Pipe codes here" + { + "theme" : { + "inlineArt" : { + "something" : "%MC and |01Pipe codes here" + } } } - } - "pause" : { - "art" : "@inline:simplePrompt", - // support pipe codes & MCI - "simplePrompt" : "--------/ Pause /----------------", - "mci" : { + "pause" : { + "art" : "@inline:simplePrompt", + // support pipe codes & MCI + "simplePrompt" : "--------/ Pause /----------------", + "mci" : { + } } + */ } - */ - } \ No newline at end of file