Jump to content

prompt.js: DOS + JavaScript = awesome


Gio
 Share

Recommended Posts

prompt.js (download here) is a replacement for the Windows command prompt that understands JavaScript and HTML5 as well as DOS.

To me as a JavaScript programmer, this is the most useful thing ever... initially a little toy that I developed for my own convenience, it's eventually become incredibly useful and almost everyone who's seen it wanted it, so we've now opensourced it (GPL) and made it available to everyone.

The idea is that you use it as a normal DOS command prompt, but you can also type in any JavaScript and it will understand it. It supports, by default, all the core Node.js libraries, jQuery, Underscore, backbone.js and the WADE engine. But you can add support for any library that you like in a very easy way.

 

promptjs2_small.jpgpromptjs_small.jpg

It comes with A LOT of useful functions that are built-in. Some highlights that may be interesting to people here:

 

  • create a webserver linked to any local folder just by going to that folder and typing server [portNumber]
  • multi-line editor with syntax highlighting and autocomplete (press Shift+Enter to open it, Esc to close it)
  • edit any file, variable, object or function by typing edit <whatYouWantToEdit>
  • load any scripts dynamically (wade.loadScript() is easiest, but you can use jQuery AJAX if you prefer).
  • style it as you like by editing a simple .css stylesheet.
  • the whole console window is an HTML document, so you can add elements to it, listen for input events, etc.
  • you can save data URL's to local files using promptjs.saveDataURL()
  • the commandhistory command shows you an editable list of commands you've typed, so you can make scripts out of them
  • if you do use WADE, it's able to show you an interactive WADE window in the console itself (just type wade show)
  • it's integrated with Windows Explorer: click the background of any folder and select prompt.js here

 

There's a lot of stuff, it's impossible to list everything... but you can watch this video to get an idea (sorry for the voice, I had a cold when I recorded it).

 

promptjs_video.jpg

 

I'm going to set up a GitHub repository for it, so if you have anything that you'd like to contribute you can.

It's Windows only at the moment, but hey, it's open source, so if you do want to port it, feel free - it's based on node-webkit, so shouldn't be too hard to port.

I hope you find it as useful as I do :)
 

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 weeks later...
  • 1 month later...

I finally got around to having a play with this (bad memory, plus fickle, is not a good combo;) ).

The first thing I tried was running grunt and the output was ugly coz of escape codes or what ever their called.

So I found this page...

 http://en.wikipedia.org/wiki/ANSI_escape_code

...and wrote something to handle them.

Not sure how to integrate it properly so I thought Id just paste it here for you and you can do what ever with it.

Did integrate it in a hacky way and at least grunts output is coloured and what not now, even with gawd awful underlines ;)

Still haven't checked some things like intensity.

Anyway, really cool thing, look forward to your updates.

 

EDIT: fixed it (I tHiNk) and added some stuff (like the keyInput that I used with the edit save thing).  Plus made it dom based now so you dont have to replace < symbols coz they get added as a text node.  Plus the dom based stuff lends its self to some cool stuff ;)

(function() {  function ConsoleToHTML(output) {    function self(str, className, lineFragment) {      if (str instanceof DocumentFragment || str instanceof Element) {        self.output.appendChild(str);        return;      }      var lineFragment = lineFragment || document.createDocumentFragment();      // http://regex101.com/r/rC3vM7/3      // Best online regex editor, ever!  Helped SOOOOOOOO much.      var re = /([^\x1B]*)(?:\x1B[[]([^m]*)m)([^\x1B]*)/gm;; // I SUCK at regular expressions!  If anyone can improve, please say       var result = '';        var m;        if (str.match(re))          while ((m = re.exec(str)) != null) {            if (m.index === re.lastIndex) {              re.lastIndex++;            }            if (m[1] !== '') {              result += self.wrapString(m[1], className, lineFragment);            }            if (m[2] !== '') {              self.processCommands(m[2]);            }            if (m[3] !== '') {              result += self.wrapString(m[3], className, lineFragment);            }          } else self.wrapString(str, className, lineFragment);        self.output.appendChild(lineFragment);      return self;    }    // http://en.wikipedia.org/wiki/ANSI_escape_code    self.processCommands = function(commands) {      commands = commands.split(';');      for (var i = 0, end = commands.length; i < end; i++) {        var command = commands[i];        switch (true) {          case command >= 30 && command <= 37: // foreground            self.setColour('foreground', command - 30, false);            break;          case command >= 40 && command <= 47: // backgroiund            self.setColour('background', command - 40, false);            break;          case command >= 90 && command <= 97: // intense foreground            self.setColour('foreground', command - 90, true);            break;          case command >= 100 && command <= 107: // iontense background            self.setColour('background', command - 100, true);            break;          case command == 0: // reset            self.resetProperties();            break;          case command == 1: // bold or increased intensity....still need to check how windows does intensity..this way or through 100-107?            self.properties.bold = true;            break;          case command == 2: // Bold: off or Underline: Double...were going with bold off            self.properties.bold = false;            break;          case command == 3: // Italic: on            self.properties.italic = true;            break;          case command == 23: // Not italic, not Fraktur            self.properties.italic = false;            break;          case command == 4: // Underline: Single            self.properties.underline = true;            break;          case command == 24: // Underline: None            self.properties.underline = false;            break;          case command == 22: // Normal color or intensity            // if windows intense is switched on and off (not 100-107) this will need to be different            self.properties.foreground = self.defaultForeground;            self.properties.background = self.defaultBackground;            break;          case command == 39: // Default text color (foreground)            self.properties.foreground = self.defaultForeground;            break;          case command == 49: // Default background color            self.properties.background = self.defaultBackground;            break;          default:            break;;        }      }    }    self.wrapString = function(str, className, lineFragment) {      var el = document.createElement(self.wrapTag);      if (className) el.classList.add(className);      el.innerText = str;      self.applyStyle(el);      lineFragment.appendChild(el);    }    self.applyStyle = function(el) {      if (self.properties.bold) self.applyProperty(el, 'bold');      if (self.properties.italic) self.applyProperty(el, 'italic');      if (self.properties.underline && !self.ignoreUnderline) self.applyProperty(el, 'underline');      if (self.defaultForeground !== self.properties.foreground) self.applyColour(el, 'foreground');      if (self.defaultBackground !== self.properties.background) self.applyColour(el, 'background');    }    self.applyProperty = function(el, prop) {      if (self.useClass) el.classList.add(self.styles[prop].class);      else el.style[self.styles[prop].atr[0]] = self.styles[prop].atr[1];    }    self.applyColour = function(el, colour) {      var cols = {        foreground: 'color',        background: 'background-color'      }      if (self.useClass) el.classList.add(self.properties[colour]);      else el.style[cols[colour]] = self.colours[self.properties[colour]].value;    }    // Different log types    self.path = function(path, command) {      return self(path, 'path');    }    self.specialCommand = function(path, command) {      return self.path(path)(command + '\n', 'special_command');    }    self.jsCommand = function(path, command) {      return self.path(path)(command + '\n', 'js_command');    }    self.dosCommand = function(path, command) {      return self.path(path)(command + '\n', 'dos_command');    }    // Converts HTML to Elements and then attachs those elements to the output    // Could see people thinking this is for attaching Elements to the output so make it do that aswell...couldnt think of a clear name for it that wasnt long    self.HTML = function(html) {      if (html instanceof Element || html instanceof DocumentFragment) return self(html);      // http://stackoverflow.com/a/9285046/189093  // The EVER awesome Rob W       var frag = document.createDocumentFragment(),        t = document.createElement('body'),        c;      t.innerHTML = html;      while (c = t.firstChild) frag.appendChild(c);      return self(frag);    }    self.keyInput = function(question, selections, callback) {            var qLine = document.createElement('span');      self(question, 'question', qLine);      for (var i = 0, end = selections.length; i < end; i++) selections[i] = selections[i].toLowerCase();      return (function(selections, callback, qLine) {        var f = function(eventData) {          var code = eventData.keyCode;          var charStr = String.fromCharCode(code).toLowerCase();          if (code == 27) charStr = 'esc';          else {            eventData.stopPropagation()            eventData.preventDefault();          }          if (selections.indexOf(charStr) !== -1) {            if (charStr !== 'esc') self(' ' + charStr + self.lineBreak, '', qLine);            callback(charStr);            window.removeEventListener('keypress', f);          }          return false;        }        window.addEventListener('keypress', f);        var escapeFunc = function(e) {          if (e.keyCode == 27) {            window.removeEventListener('keypress', f);            window.removeEventListener('keydown', escapeFunc);            f({              keyCode: 27            });            e.stopPropagation()            e.preventDefault();            return false;          }        }        if (selections.indexOf('esc') !== -1) window.addEventListener('keydown', escapeFunc)        return qLine;      })(selections, callback, qLine);    }    self.setColour = function(colour, value, intense) {      var shift = intense ? 8 : 0;      self.properties[colour] = value + shift;    }    self.resetProperties = function() {      for (var i = 0, keys = Object.keys(self.defaultProperties), key; key = keys[i]; i++) {        self.properties[key] = self.defaultProperties[key];      }    }    self.buffer = '';    self.useClass = false;    self.wrapTag = 'span';    self.defaultForeground = 7;    self.defaultBackground = 0;    self.ignoreUnderline = true;    self.defaultProperties = {      italic: false,      bold: false,      underline: false,      foreground: self.defaultForeground,      background: self.defaultBackground    }    self.properties = {};    self.resetProperties();    self.lineBreak = '\r\n';    self.styles = {      bold: {        atr: ['fontWeight', 'bold'],        class: 'font_bold'      },      italic: {        atr: ['fontStyle', 'italic'],        class: 'font_italic'      },      underline: {        atr: ['textDecoration', 'underline'],        class: 'font_underline'      }    }    self.colours = {      '0': {        value: 'rgb(0, 0, 0)',        class: 'black'      },      '1': {        value: 'rgb(128, 0, 0)',        class: 'red'      },      '2': {        value: 'rgb(0, 128, 0)',        class: 'green'      },      '3': {        value: 'rgb(128, 128, 0)',        class: 'yellow'      },      '4': {        value: 'rgb(0, 0, 128)',        class: 'blue'      },      '5': {        value: 'rgb(128, 0, 128)',        class: 'magenta'      },      '6': {        value: 'rgb(0, 128, 128)',        class: 'cyan'      },      '7': {        value: 'rgb(192, 192, 192)',        class: 'white'      },      // Intense Colours      '8': {        value: 'rgb(128, 128, 128)',        class: 'black_intense' //Dark grey      },      '9': {        value: 'rgb(255, 0, 0)',        class: 'red_intense'      },      '10': {        value: 'rgb(0, 255, 0)',        class: 'green_intense'      },      '11': {        value: 'rgb(255, 255, 0)',        class: 'yellow_intense'      },      '12': {        value: 'rgb(0, 0, 255)',        class: 'blue_intense'      },      '13': {        value: 'rgb(255, 0, 255)',        class: 'magenta_intense'      },      '14': {        value: 'rgb(0, 255, 255)',        class: 'cyan_intense'      },      '15': {        value: 'rgb(255, 255, 255)',        class: 'white_intense'      }    }    if (typeof output == 'string') {      var el = document.body.querySelector(output);      if (!el) throw 'No output element';    } else {      el = output;      if (!el instanceof Element) throw 'No output element';    }    self.output = el;    return self;  }  // I couldnt figure out how to require this in prompt.js so just attach script the old way  window.ConsoleToHTML = ConsoleToHTML;

})();

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...