diff --git a/exec/dorkit/ansi_console.js b/exec/dorkit/ansi_console.js index 8a343d526fbc21dad31214b70c393708606a8a41..de01bd8f6b91599b01f310b46c8bdd3e3280aced 100644 --- a/exec/dorkit/ansi_console.js +++ b/exec/dorkit/ansi_console.js @@ -17,6 +17,28 @@ dk.console.remote_io = { this.print('\x1b[K'); }, + movex:function(pos) { + if (pos == 1) + return this.print("\x1b[C"); + if (pos > 1) + return this.print("\x1b["+pos+"C"); + if (pos == -1) + return this.print("\x1b[D"); + if (pos < -1) + return this.print("\x1b["+(0-pos)+"D"); + }, + + movey:function(pos) { + if (pos == 1) + return this.print("\x1b[B"); + if (pos > 1) + return this.print("\x1b["+pos+"B"); + if (pos == -1) + return this.print("\x1b[A"); + if (pos < -1) + return this.print("\x1b["+(0-pos)+"A"); + }, + /* * Moves the cursor to the specified position. * returns false on error. diff --git a/exec/dorkit/local_console.js b/exec/dorkit/local_console.js index 722c00a17fe00deb85bd48c70212dd8c9257131c..a8ce26afd7bfdb7cea2ed929a9078e21bf20b5fe 100644 --- a/exec/dorkit/local_console.js +++ b/exec/dorkit/local_console.js @@ -14,6 +14,10 @@ dk.console.local_io = { }, gotoxy:function(x,y) { }, + movex:function(pos) { + }, + movey:function(pos) { + }, print:function(string) { }, }; diff --git a/exec/dorkit/sbbs_console.js b/exec/dorkit/sbbs_console.js index 7119ef17c1077be40ba9cecd49ee96ed53edb9c5..0e31c8ec8dc11aa7306788862a0fe07c03e1183b 100644 --- a/exec/dorkit/sbbs_console.js +++ b/exec/dorkit/sbbs_console.js @@ -30,6 +30,20 @@ dk.console.remote_io = { console.gotoxy(x+1,y+1); }, + movex(pos) { + if (pos > 0) + console.right(pos); + if (pos < 0) + console.left(0-pos); + }, + + movey(pos) { + if (pos > 0) + console.down(pos); + if (pos < 0) + console.up(0-pos); + }, + /* * Writes a string unmodified. */ diff --git a/exec/load/dorkit.js b/exec/load/dorkit.js index 3c4f0fdfb7ef7b3f05a9f0cfc927ad87dbd12527..c46b95e2ff60879cd2f5a5e92d3605128ae32680 100644 --- a/exec/load/dorkit.js +++ b/exec/load/dorkit.js @@ -3,6 +3,50 @@ if (js.global.system !== undefined) js.load_path_list.unshift(system.exec_dir+"/dorkit/"); load("screen.js"); +// polyfill the String object with repeat method. +// Swiped from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Browser_compatibility +if (!String.prototype.repeat) { + String.prototype.repeat = function(count) { + 'use strict'; + if (this == null) { + throw new TypeError('can\'t convert ' + this + ' to object'); + } + var str = '' + this; + count = +count; + if (count != count) { + count = 0; + } + if (count < 0) { + throw new RangeError('repeat count must be non-negative'); + } + if (count == Infinity) { + throw new RangeError('repeat count must be less than infinity'); + } + count = Math.floor(count); + if (str.length == 0 || count == 0) { + return ''; + } + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) { + throw new RangeError('repeat count must not overflow maximum string size'); + } + var rpt = ''; + for (;;) { + if ((count & 1) == 1) { + rpt += str; + } + count >>>= 1; + if (count == 0) { + break; + } + str += str; + } + return rpt; + } +} + var dk = { console:{ last_pos:{x:1, y:1}, @@ -251,6 +295,20 @@ var dk = { this.remote_io.gotoxy(x,y); }, + movex:function(pos) { + if (this.local) + this.local_io.movex(pos); + if (this.remote) + this.remote_io.movex(pos); + }, + + movey:function(pos) { + if (this.local) + this.local_io.movex(pos); + if (this.remote) + this.remote_io.movex(pos); + }, + /* * Returns a Graphic object representing the specified block * or undefined on error (ie: invalid block specified). @@ -363,29 +421,34 @@ var dk = { return ret; }, getstr_defaults:{ + timeout:undefined, // Timeout, undefined for "wait forever" password:false, // Password field (echo password_char instead of string) password_char:'*', // Character to echo when entering passwords. upper_case:false, // Convert to upper-case during editing integer:false, // Input integer values only decimal:false, // Input decimal values only - ansi:false, // Allows ANSI input - ctrl_a:false, // Allows CTRL-A input - beep:false, // Allows beep input (WTF?) edit:'', // Edit this value rather than a new input crlf:true, // Print CRLF after input exascii:false, // Allow extended ASCII (Higher than 127) - echo:true, // Display input while typing input_box:false, // Draw an input "box" in a different background attr select:true, // Select all when editing... first character typed if not movement will erase attr:undefined, // Foreground attribute... used to draw the input box and for output... undefined means "use current" sel_attr:undefined, // Selected text attribute... used for edit value when select is true. Undefined uses inverse (swaps fg and bg, leaving bright and blink unswapped) - len:80 // Max length and length of input box + len:80, // Max length and length of input box }, getstr:function(in_opts) { var i; var opt={}; var str; + var newstr; + var key; + var pos=0; + var insmode=true; var orig_attr = new Attribute(this.attr); + var dispstr; + var decimal_re; + if (in_opts===undefined) + in_opts={}; // Set up option defaults for (i in this.getstr_defaults) { @@ -394,6 +457,8 @@ var dk = { else opt[i] = this.getstr_defaults[i]; } + if (opt.decimal) + decimal_re = /^([0-9]+(\.[0-9]*)?)?$/; if (opt.attr === undefined) opt.attr=new Attribute(this.attr); if (opt.sel_attr === undefined) { @@ -403,7 +468,140 @@ var dk = { opt.sel_attr.bg = i; } str = opt.edit; + if (opt.password) + dispstr = opt.password_char.repeat(str.length); + else + dispstr = str; + this.attr.value = opt.attr.value; // Draw the input box... + if (opt.input_box) { + // TODO: Verify that it fits and do the "right" thing. + for (i=0; i<opt.len; i++) + this.print(' '); + this.movex(-(opt.len)); + } + if (opt.select) + this.attr.value = opt.sel_attr.value; + this.print(dispstr); + if (opt.select) { + this.movex(-(str.length)); + this.attr.value = opt.attr.value; + } + else + pos = str.length; + while(1) { + if (!this.waitkey(opt.timeout === undefined ? 1000 : opt.timeout)) { + if (opt.timeout !== undefined) + return str; + } + key = this.getkey(); + switch(key) { + case 'KEY_HOME': + if (opt.select) { + opt.select = false; + this.movex(-pos); + this.print(dispstr); + this.movex(-str.length); + pos = 0; + } + this.movex(-pos); + pos=0; + break; + case 'KEY_END': + if (opt.select) { + opt.select = false; + this.movex(-pos); + this.print(dispstr); + this.pos = str.length; + } + this.movex(str.length - pos); + pos = str.length; + break; + case 'KEY_LEFT': + if (opt.select) { + opt.select = false; + this.movex(-pos); + this.print(dispstr); + this.movex(pos - str.length); + } + if (pos == 0) // Already at start... ignoe TODO: Beep? + break; + pos--; + this.movex(-1); + break; + case 'KEY_RIGHT': + if (opt.select) { + opt.select = false; + this.movex(-pos); + this.print(dispstr); + this.movex(pos - str.length); + } + if (pos >= str.length) // Already at end... ignore TODO: Beep? + break; + pos++; + this.movex(1); + break; + case '\x7f': + case '\b': + if (pos == 0) // Already at start... ignoe TODO: Beep? + break; + str = str.substr(0, pos - 1) + str.substr(pos); + if (opt.password) + dispstr = opt.password_char.repeat(str.length); + else + dispstr = str; + pos--; + this.movex(-1); + this.print(dispstr.substr(pos)+' '); + this.movex(-1-(str.length - pos)); + break; + case 'KEY_DEL': + if (pos >= str.length) // Already at end... ignore TODO: Beep? + break; + str = str.substr(0, pos) + str.substr(pos+1); + if (opt.password) + dispstr = opt.password_char.repeat(str.length); + else + dispstr = str; + this.movex(-1); + this.print(dispstr.substr(pos)+' '); + this.movex(-1-(str.length - pos)); + break; + case 'KEY_INS': + insmode = !insmode; + break; + case '\r': + if (opt.crlf) + this.println(''); + return str; + case undefined: + break; + default: + if (key.length > 1) // Unhandled extended key... ignore TODO: Beep? + break; + if (str.length >= opt.len) // String already too log... ignore TODO: Beep? + break; + if (opt.integer && (key < '0' || key > '9')) // Invalid integer... ignore TODO: Beep? + break; + if (opt.upper_case) + key = key.toUpperCase(); + if ((!opt.exascii) && key.charCodeAt(0) > 127) // Invalid EXASCII... ignore TODO: Beep? + break; + if (key.charCodeAt(0) < 32) // Control char... ignore TODO: Beep? + break; + newstr = str.substr(0, pos) + key + str.substr(insmode ? pos : pos+1); + if (opt.decimal && newstr.search(decimal_re) === -1) + break; + str = newstr; + if (opt.password) + dispstr = opt.password_char.repeat(str.length); + else + dispstr = str; + this.print(dispstr.substr(pos)); + pos++; + this.movex(-(str.length-pos)); + } + } }, }, connection:{