Skip to content

Using Josh for form data

While Josh.js was built primarily for creating bash-style shells in the browser, the same mechanisms would be nice to use for form input. Yes, it does break the expected user behavior, but if you are creating a UI for unix people this might just be the geeky edge to push usability over the top. Problem was, Josh.js originally bound itself to the document root and you had to activate/deactivate it to take over key interception. While you could manually trigger this, it was less than ideal for having multiple instances of Josh.Readline on one page each attached to an input field. With todays release of Josh.js (marked minor, since it's backwards compatible and incompatible breaking changes are on the horizon), readline can bind to an element and can even do so after the fact. In addition, key bindings of josh can be disabled or remapped to better match your use case. Finally, because taking over the UX of form input is unfortunately not quite as simple as just binding readline to an input element, Josh.js now includes Josh.Input to simplify the binding.

Josh.Input

There are two ways to create a josh form element. Either you attach Josh.Input to an <input> field or a <span>. The former preserves the standard look and feel of an input field, but has to play some tricks to properly handle cursor positioning. The latter can be styled any way you like and uses the underscore cursor behavior also used in Josh.Shell.

The former uses html like this:

<input id="input1" type="text" />

and binds it to Josh.Input with:

var cmd1 = new Josh.Input({id: "input1"});

to produce:

Input:

And the latter will generally use a span like this:

<span id="input2"></span>

Adorned with whatever css you like to create the input look and feel and then is bound to Josh.Input the same way with:

var cmd2 = new Josh.Input({id: "input2"});

creating an input like this:

Span:

Note that the two inputs share history and killring, which in addition to line editing behavior makes them so much more powerful than just plain old input boxes. Also note that the only reason we capture the object created by new Josh.Input is so that we could access its members, such as the Josh.Readline instance bound to the element. As I said, I wouldn't recommend using this in a regular form, since form input behavior on the web is rather well established, but a custom app that evokes a unix input mentality could certainly benefit from it.

Changes to Josh.js for Input

Adding Josh.Input revv'ed Josh.js to 0.2.9 (i.e. no breaking changes), which allows Josh.Readline and by extension Josh.Shell to be bound to an element. When Josh.Shell is bound to an element, it will now activate/deactivate on focus (for this it will add a tabindex to the shell element if one isn't already there). Binding to an input or input mimicking span did illustrate that certain key bindings just don't make sense. Rather than hardcode a couple of binding exceptions for input, 0.2.9 also introduces the ability to bind and unbind the existing Readline commands to any key (modifiers work, but only a single modifier is recognized per key combo right now). This could be used to change the emacs-style bindings, but really is primarily intended to unbind commands that don't make sense. The bindable commands are:

  • complete - invoke command completion
  • done - invoke the command handler
  • noop - capture the key but don't do anything (bound to caps-lock, pause and insert)
  • history_top - go to and display the history top
  • history_end - go to and display the history end
  • history_next - go to and display the next item in the history
  • history_previous - go to and display the previous item in the history
  • end - cursor to end of line
  • home - cursor to beginning of line
  • left - cursor left
  • right - cursor right
  • cancel - interrupt the current command input
  • delete - delete character under cursor
  • backspace - delete character to the left of cursor
  • clear - clear the shell screen
  • search - start reverse search mode
  • wordback - cursor to previous word
  • wordforward - cursor to next word
  • kill_eof - kill text to end of line
  • kill_wordback - kill the previous word
  • kill_wordforward - kill the next word
  • yank - yank the current head of the killring to the cursor position
  • yank_rotate - if following yank, replace previously yanked text with next text in killring

Binding commands to keys is done with:

readline.bind(key,cmd);

and unbinding is done with:

readline.unbind(key);

where key is:

{
 keyCode:  120
 // or
 char: 'x',
 // plus one of these:
 crtlKey: true,
 metaKey: true
}

Josh also provides a simply lookup for special key keycodes:

Josh.Keys = {
    Special: {
      Backspace: 8,
      Tab: 9,
      Enter: 13,
      Pause: 19,
      CapsLock: 20,
      Escape: 27,
      Space: 32,
      PageUp: 33,
      PageDown: 34,
      End: 35,
      Home: 36,
      Left: 37,
      Up: 38,
      Right: 39,
      Down: 40,
      Insert: 45,
      Delete: 46
    }
  };

Josh.Input automatically unbinds Tab and Ctrl-R.

All these changes do not affect existing usages of Josh.js, however 0.3 is coming up soon and it may have some breaking changes (will try not to, but can't determine yet if that's possible), but I'll talk about those plans in a future post