Skip to content

New jQuery plugin, status

Updated 2015-01-19 to allow $(console).status(). Now at version 1.4.

Updated 2013-12-18 to use Promises. This necessitated removing the result option and adding the returnPromise.

Updated 2014-01-09 to add initialText option and to document the markup used.

For the Kavanot editor I wanted to have a way to display messages (like "Text saved successfully") that would show up on screen then fade out, and a way to enter text (like the title, or editing commands if I ever get that working). I packaged that all together into a plugin could handle both, with an anticipated use like:

<textarea id=editor></textarea>
<div id=notifications></div>

And then do something like $('#notifications').status('Text saved'); and that briefly shows the message in the <div> and then gradually fade out.

And for user input,

$('#notifications').status({
  prompt: 'File Name:',
  run: function (text) { 
    /* do something with text */
    if (successful) return 'success message';
    if (! successful) throw {message: 'failure message'};
  }
});

displays the prompt and an input box then runs the function and displays the result (it automatically catches the thrown exception).

See the code on github.

See a demo.

status(message [, classname [, opts]])

There are two ways to use status. The simpler is $(element).status(message [, classname [, opts]]) which displays the string message with $('<span>').text(message).addClass(classname).hide().prependTo(element), then calling opts.show, then opt.hide on that <span>, then removing it entirely.

Parameters

message {String}
The message to be displayed
classname {String}
The class to be applied to the message. Default: "" (empty string).
opts {Object}
The options, all optional:
show {Function}
Function to be called to show the message. Called as opts.show.call(jQuery_object_with_message);. Default: $.fn.show.
hide {Function}
Function to be called to hide the message. Called as opts.hide.call(jQuery_object_with_message);. Default: function() {return this.fadeOut(5000)}.

status(opts)

The other way to use status is to pass a complete set of options including a function to return the message for display.

Options

show {Function}
Function to display the message, as above.
hide {Function}
Function to hide the message, as above.
successClass {String}
Class to be applied to the displayed message if the run returns without throwing an exception (passed to classname as above). Default "success".
failureClass {String}
Class to be applied to the displayed message if the run throws an exception (passed to classname as above). Default "failure".
run {Function}
Function whose return result is used as the message for display as above. If it throws an exception, the exception message is displayed. The algorithm is effectively:
try{
  var message = opts.run();
  this.status (message, opts.successClass, opts);
}catch(ex){
  this.status (ex.message, opts.failureClass, opts);
}
Default: $.noop.
As of version 1.1, the run function can return a Promise, with success or failure defined as resolution or rejection of that Promise, as:
run().then (function(message){
  this.status (message, opts.successClass, opts);
}, function(ex){
  this.status (ex.message, opts.failureClass, opts);
});
(where this really means the jQuery object. Arrow notation and lexical this would be nice!)
prompt {String|false}
If not false (the test is ===false), then the run function expects a string as a parameter (the message line above would be var message = opts.run(text);. A text input box is appended to the element with something like $('<label>').text(opts.prompt).appendTo(this).append('<input>'); (see below for the actual markup). It is displayed with opts.show. When the user hits "enter" the text in the input box is passed to the opts.run function as above. If the user hits "esc" then opts.cancelMessage is displayed without running opts.run. In either case, the input box is hidden with opts.hide.
A simple history is implemented; the up arrow will display the previous string entered or "undefined" if there is no previous string.
Default false.
initialText {Boolean | String}
Only relevant if prompt is true. If a string, use it as the initial value of the input element. If true then use prompt as a placeholder rather than a separate element. if false, prompt is outside the input element as above, and the input element itself is blank.

cancelMessage {String}
The message to display if "esc" is hit in the input box. Default 'User Canceled'.
returnPromise {Boolean}
Internally, the code uses a Promise, so the actual algorithm is closer to:
var promise = new Promise(function(resolve){
  resolve(opts.run());
});

promise.then( function(message){
  this.status (message, opts.successClass, opts);
}, function(ex){
  this.status (ex.message, opts.failureClass, opts);
});
Set returnPromise to true to return that Promise rather than this. This plugin uses that for signalling rather than events. Default: false.

So a sample "Save As" to the server would be:


function saveas(name){
  return $.post('save.php', {name: name, text: $('#editor').val()}).then(
      function() { return name + ' saved' },
      function() { return new Error(name + ' not saved') })
}

$('#notification').status({
  prompt: 'Save As:',
  run: saveas
});

Note that jQuery $.Deferreds like $.post aren't real Promises but implement then(), so the Promise code can handle it.

Multiple elements

jQuery objects can contain zero or more elements. run will run exactly once, no matter how many elements are in the object (or even if there are none) and the output message will be displayed in each of them. Input (if the prompt option is set) will be taken from the first element in the jQuery object, or if it is empty, via window.prompt. Note that window.prompt is synchronous and will block the rest of the page until it is addressed. However, it is pumped through a Promise, so from the point of view of code execution it is asynchronous (is executed on the next tick).

$(console).status()

jQuery will happily wrap non-DOM elements, which allows me to use the console rather than an element on the page for messages. $(console).status(...) will work exactly as above, but messages are printed with console.log() and errors with console.error(). Input is with window.prompt as above (I can't figure out any way to get input from the console.

Under the hood, it just calls log() and error(), so you can create your own output mechanism:

var output = {
  log: function (text) { alert (text) },
  error: function (text) { alert ('Error: '+text) },
  prompt: function (promptText, initialText) { window.prompt(promptText, initialText) }
};
$(output).status({run: saveFunction});

For input, when the target is not a real DOM node, the plugin will call target-object.prompt (prompt, initialText) if it exists; otherwise it will use window.prompt. The target-object.prompt function can return a string for a result or null for cancel, the way window.prompt does, or it can return a Promise that resolves to the result string. For example, you could use jQuery UI dialogs (see the dialog object in the demo)

Markup

It would be nice, with prompted input, for the input line to fill the available space. That is possible, if the markup is in the right order, with the input element last in the containing element. That means that all messages, even ones that should appear after the input element, and floated to the right side.

So messages (that do not require user input) are prepended to the parent element, with markup

<span class=classname>Message Text</span>

and your stylesheet should include

span.classname {
  float: right;
}

The input element itself is appended to the parent element, and marked up as

<label><strong>Prompt</strong><span><input/></span></label>

And CSS should be

label strong {
  /* the prompt itself */
  float: left;
}
label span {
  /* wrapper for the input element */
  float: none;
  display: block;
  overflow: hidden;
}
label input {
  width: 100%;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
}

{ 1 } Trackback

  1. […] my status plugin, I'd generally like the input element to take up the whole line, or at least the part that's not […]

Post a Comment

Your email is never published nor shared. Required fields are marked *