{"id":3034,"date":"2013-12-12T22:15:57","date_gmt":"2013-12-13T04:15:57","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3034"},"modified":"2020-07-02T11:26:24","modified_gmt":"2020-07-02T17:26:24","slug":"new-jquery-plugin-status","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2013\/12\/12\/new-jquery-plugin-status\/","title":{"rendered":"New jQuery plugin, <code>status<\/code>"},"content":{"rendered":"<p><strong>This plugin is obsolete. See the new <code>status<\/code> at <a href=\"http:\/\/github.bililite.com\/status\">github.bililite.com\/status<\/a>.<\/strong>\r\n\r\n<p><strong>Updated 2015-01-19 to allow <code class=\"language-javascript\" >$(console).status()<\/code>. Now at version 1.4<\/code>.<\/strong><\/p>\r\n<p><strong>Updated 2013-12-18 to use <a href=\"http:\/\/promises-aplus.github.io\/promises-spec\/\"><code class=\"language-javascript\" >Promise<\/code>s<\/a>. This necessitated removing the <code class=\"language-javascript\" >result<\/code> option and adding the <code class=\"language-javascript\" >returnPromise<\/code>.<\/strong><\/p>\r\n<p><strong>Updated 2014-01-09 to add <code class=\"language-javascript\" >initialText<\/code> option and to document the markup used.<\/strong><\/p>\r\n<p>For the <a href=\"http:\/\/kavanot.me\/Home\/edit\">Kavanot editor<\/a> 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:\r\n<pre><code class=\"language-html\" >&lt;textarea id=editor&gt;&lt;\/textarea&gt;\r\n&lt;div id=notifications&gt;&lt;\/div&gt;<\/code><\/pre>\r\n<p>And then do something like <code class=\"language-javascript\" >$('#notifications').status('Text saved');<\/code> and that briefly shows the message in the <code class=\"language-html\" >&lt;div&gt;<\/code> and then gradually fade out.<\/p>\r\n<p>And for user input,<\/p>\r\n<pre><code class=\"language-javascript\" >$('#notifications').status({\r\n  prompt: 'File Name:',\r\n  run: function (text) { \r\n    \/* do something with text *\/\r\n    if (successful) return 'success message';\r\n    if (! successful) throw {message: 'failure message'};\r\n  }\r\n});<\/code><\/pre>\r\n<p>displays the prompt and an input box then runs the function and displays the result (it automatically <code class=\"language-javascript\" >catches<\/code> the <code class=\"language-javascript\" >throw<\/code>n exception).<\/p>\r\n<p>See the <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/jquery.status.js\">code on github<\/a>.<\/p>\r\n<p>See a <a href=\"\/blog\/blogfiles\/vi\/statusbar.html\">demo<\/a>.<\/p>\r\n<!--more-->\r\n<h3><code>status(message [, classname [, opts]])<\/code><\/h3>\r\n<p>There are two ways to use <code class=\"language-javascript\">status<\/code>. The simpler is <code class=\"language-javascript\">$(element).status(message [, classname [, opts]])<\/code> which displays the string <codeclass=\"language-javascript\">message<\/code> with <code class=\"language-javascript\" >$('&lt;span&gt;').text(message).addClass(classname).hide().prependTo(element)<\/code>, then calling <code class=\"language-javascript\" >opts.show<\/code>, then <code class=\"language-javascript\" >opt.hide<\/code> on that <code class=\"language-html\" >&lt;span&gt;<\/code>, then removing it entirely.<\/p>\r\n<h4>Parameters<\/h4>\r\n<dl>\r\n<dt><code>message<\/code> {String}<\/dt>\r\n<dd>The message to be displayed<\/dd>\r\n<dt><code>classname<\/code> {String}<\/dt>\r\n<dd>The class to be applied to the message. Default: <code class=\"language-javascript\" >\"\"<\/code> (empty string).<\/dd>\r\n<dt><code>opts<\/code> {Object}<\/dt>\r\n<dd>The options, all optional:\r\n  <dl>\r\n  <dt><code>show<\/code> {Function}<\/dt>\r\n  <dd>Function to be called to show the message. Called as <code class=\"language-javascript\" >opts.show.call(jQuery_object_with_message);<\/code>. Default: <code class=\"language-javascript\" >$.fn.show<\/code>.<\/dd>\r\n  <dt><code>hide<\/code> {Function}<\/dt>\r\n  <dd>Function to be called to hide the message. Called as <code class=\"language-javascript\" >opts.hide.call(jQuery_object_with_message);<\/code>. Default: <code class=\"language-javascript\" >function() {return this.fadeOut(5000)}<\/code>.<\/dd>\r\n  <\/dl>\r\n<\/dd>\r\n<\/dl>\r\n<h3><code>status(opts)<\/code><\/h3>\r\n<p>The other way to use <code class=\"language-javascript\">status<\/code> is to pass a complete set of options including a function to return the message for display.<\/p>\r\n<h4>Options<\/h4>\r\n<dl>\r\n<dt><code>show<\/code> {Function}<\/dt>\r\n<dd>Function to display the message, as above.<\/dd>\r\n<dt><code>hide<\/code> {Function}<\/dt>\r\n<dd>Function to hide the message, as above.<\/dd>\r\n<dt><code>successClass<\/code> {String}<\/dt>\r\n<dd>Class to be applied to the displayed message if the <code class=\"language-javascript\" >run<\/code> returns without throwing an exception (passed to <code class=\"language-javascript\" >classname<\/code> as above). Default <code class=\"language-javascript\" >\"success\"<\/code>.<\/dd>\r\n<dt><code>failureClass<\/code> {String}<\/dt>\r\n<dd>Class to be applied to the displayed message if the <code class=\"language-javascript\" >run<\/code> throws an exception (passed to <code class=\"language-javascript\" >classname<\/code> as above). Default <code class=\"language-javascript\" >\"failure\"<\/code>.<\/dd>\r\n<dt><code>run<\/code> {Function}<\/dt>\r\n<dd>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:\r\n<pre><code class=\"language-javascript\" >try{\r\n  var message = opts.run();\r\n  this.status (message, opts.successClass, opts);\r\n}catch(ex){\r\n  this.status (ex.message, opts.failureClass, opts);\r\n}<\/code><\/pre>\r\nDefault: <code class=\"language-javascript\" >$.noop<\/code>.<\/dd>\r\n<dd>As of version 1.1, the <code class=\"language-javascript\" >run<\/code> function can return a <code class=\"language-javascript\" >Promise<\/code>, with success or failure defined as resolution or rejection of that <code class=\"language-javascript\" >Promise<\/code>, as:\r\n<pre><code class=\"language-javascript\" >run().then (function(message){\r\n  this.status (message, opts.successClass, opts);\r\n}, function(ex){\r\n  this.status (ex.message, opts.failureClass, opts);\r\n});<\/code><\/pre>\r\n(where <code class=\"language-javascript\" >this<\/code> really means the jQuery object. Arrow notation and lexical <code class=\"language-javascript\" >this<\/code> would be nice!)<\/dd>\r\n<dt><code>prompt<\/code> {String|<code class=\"language-javascript\" >false<\/code>}<\/dt>\r\n<dd>If not <code class=\"language-javascript\" >false<\/code> (the test is <code class=\"language-javascript\" >===false<\/code>), then the <code class=\"language-javascript\" >run<\/code> function expects a string as a parameter (the <code class=\"language-javascript\" >message<\/code> line above would be <code class=\"language-javascript\" >var message = opts.run(text);<\/code>. A text input box is appended to the element with something like <code class=\"language-javascript\" >$('&lt;label&gt;').text(opts.prompt).appendTo(this).append('&lt;input&gt;');<\/code> (see below for the actual markup). It is displayed with <code class=\"language-javascript\" >opts.show<\/code>. When the user hits \"enter\" the text in the input box is passed to the <code class=\"language-javascript\" >opts.run<\/code> function as above. If the user hits \"esc\" then <code class=\"language-javascript\" >opts.cancelMessage<\/code> is displayed without running <code class=\"language-javascript\" >opts.run<\/code>. In either case, the input box is hidden with <code class=\"language-javascript\" >opts.hide<\/code>.<\/dd>\r\n<dd>A simple history is implemented; the up arrow will display the previous string entered or <code class=\"language-javascript\" >\"undefined\"<\/code> if there is no previous string.<\/dd>\r\n<dd>Default <code class=\"language-javascript\" >false<\/code>.<\/dd>\r\n<dt><code>initialText<\/code> {Boolean | String}<\/dt>\r\n<dd>Only relevant if <code class=\"language-javascript\" >prompt<\/code> is <code class=\"language-javascript\" >true<\/code>. If a string, use it as the initial value of the input element. If <code class=\"language-javascript\" >true<\/code> then use <code class=\"language-javascript\" >prompt<\/code> as a <a href=\"http:\/\/www.w3schools.com\/tags\/att_input_placeholder.asp\">placeholder<\/a> rather than a separate element. if <code class=\"language-javascript\" >false<\/code>, <code class=\"language-javascript\" >prompt<\/code> is outside the input element as above, and the input element itself is blank.<\/p>\r\n<dt><code>cancelMessage<\/code> {String}<\/dt>\r\n<dd>The message to display if \"esc\" is hit in the input box. Default <code class=\"language-javascript\" >'User Canceled'<\/code>.<\/dd>\r\n<dt><code>returnPromise<\/code> {Boolean}<\/dt>\r\n<dd>Internally, the code uses a <code class=\"language-javascript\" >Promise<\/code>, so the actual algorithm is closer to:\r\n<pre><code class=\"language-javascript\" >var promise = new Promise(function(resolve){\r\n  resolve(opts.run());\r\n});\r\n\r\npromise.then( function(message){\r\n  this.status (message, opts.successClass, opts);\r\n}, function(ex){\r\n  this.status (ex.message, opts.failureClass, opts);\r\n});<\/code><\/pre>\r\nSet <code class=\"language-javascript\" >returnPromise<\/code> to <code class=\"language-javascript\" >true<\/code> to return that <code class=\"language-javascript\" >Promise<\/code> rather than <code class=\"language-javascript\" >this<\/code>. This plugin uses that for signalling rather than events. Default: <code class=\"language-javascript\" >false<\/code>.<\/dd>\r\n<\/dl>\r\n\r\n<p>So a sample \"Save As\" to the server would be:<\/p>\r\n<pre><code class=\"language-javascript\" >\r\nfunction saveas(name){\r\n  return $.post('save.php', {name: name, text: $('#editor').val()}).then(\r\n      function() { return name + ' saved' },\r\n      function() { return new Error(name + ' not saved') })\r\n}\r\n\r\n$('#notification').status({\r\n  prompt: 'Save As:',\r\n  run: saveas\r\n});<\/code><\/pre>\r\n\r\n<p>Note that jQuery <code class=\"language-javascript\" >$.Deferred<\/code>s like <code class=\"language-javascript\" >$.post<\/code> <a href=\"https:\/\/github.com\/kriskowal\/q\/wiki\/Coming-from-jQuery\">aren't real <code class=\"language-javascript\" >Promise<\/code>s<\/a> but implement <code class=\"language-javascript\" >then()<\/code>, so the <code class=\"language-javascript\" >Promise<\/code> code can handle it.<\/p>\r\n\r\n<h3>Multiple elements<\/h3>\r\n<p>jQuery objects can contain zero or more elements. <code>run<\/code> 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 <code>prompt<\/code> option is set) will be taken from the first element in the jQuery object, or if it is empty, via <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window.prompt\"><code>window.prompt<\/code><\/a>. Note that <code>window.prompt<\/code> is <em>synchronous<\/em> and will block the rest of the page until it is addressed. However, it is pumped through a <code class=\"language-javascript\" >Promise<\/code>, so from the point of view of code execution it is <a href=\"http:\/\/stackoverflow.com\/questions\/24881345\/why-are-q-js-promises-asynchronous-after-they-have-been-resolved\">asynchronous (is executed on the next tick)<\/a>.<\/p>\r\n\r\n<h3><code>$(console).status()<\/code><\/h3>\r\n<p>jQuery will happily wrap non-DOM elements, which allows me to use the console rather than an element on the page for messages. <code class=\"language-javascript\" >$(console).status(...)<\/code> will work exactly as above, but messages are printed with <code class=\"language-javascript\" >console.log()<\/code> and errors with <code class=\"language-javascript\" >console.error()<\/code>. Input is with <code class=\"language-javascript\" >window.prompt<\/code> as above (I can't figure out any way to get input from the console.<\/p>\r\n\r\n<p>Under the hood, it just calls <code class=\"language-javascript\" >log()<\/code> and <code class=\"language-javascript\" >error()<\/code>, so you can create your own output mechanism:<\/p>\r\n<pre><code class=\"language-javascript\" >var output = {\r\n  log: function (text) { alert (text) },\r\n  error: function (text) { alert ('Error: '+text) },\r\n  prompt: function (promptText, initialText) { window.prompt(promptText, initialText) }\r\n};\r\n$(output).status({run: saveFunction});<\/code><\/pre>\r\n<p>For input, when the target is not a real DOM node, the plugin will call <code class=\"language-javascript\" >target-object.prompt (prompt, initialText)<\/code> if it exists; otherwise it will use <code class=\"language-javascript\" >window.prompt<\/code>. The <code class=\"language-javascript\" >target-object.prompt<\/code> function can return a string for a result or <code class=\"language-javascript\" >null<\/code> for cancel, the way <code class=\"language-javascript\" >window.prompt<\/code> does, or it can return a <code class=\"language-javascript\" >Promise<\/code> that resolves to the result string. For example, you could use jQuery UI dialogs (see the <code class=\"language-javascript\" >dialog<\/code> object in <a href=\"\/blog\/blogfiles\/vi\/statusbar.html\">the demo<\/a>)<\/p>\r\n\r\n<h3>Markup<\/h3>\r\n<p>It would be nice, with prompted input, for the input line to fill the available space. <a href=\"http:\/\/bililite.com\/blog\/2014\/01\/09\/input-elements-that-fill-the-available-space\/\" title=\"Input elements that fill the available space\">That is possible<\/a>, if the markup is in the right order, with the input element <em>last<\/em> in the containing element. That means that all messages, even ones that should appear <em>after<\/em> the input element, and <code class=\"language-css\" >float<\/code>ed to the right side.<\/p>\r\n<p>So messages (that do not require user input) are prepended to the parent element, with markup<\/p>\r\n<pre><code class=\"language-html\" >&lt;span class=classname&gt;Message Text&lt;\/span&gt;<\/code><\/pre>\r\n<p>and your stylesheet should include<\/p>\r\n<pre><code class=\"language-css\" >span.classname {\r\n  float: right;\r\n}<\/code><\/pre>\r\n<p>The input element itself is appended to the parent element, and marked up as<\/p>\r\n<pre><code class=\"language-html\" >&lt;label&gt;&lt;strong&gt;Prompt&lt;\/strong&gt;&lt;span&gt;&lt;input\/&gt;&lt;\/span&gt;&lt;\/label&gt;<\/code><\/pre>\r\n<p>And CSS should be<\/p>\r\n<pre><code class=\"language-css\" >label strong {\r\n  \/* the prompt itself *\/\r\n  float: left;\r\n}\r\nlabel span {\r\n  \/* wrapper for the input element *\/\r\n  float: none;\r\n  display: block;\r\n  overflow: hidden;\r\n}\r\nlabel input {\r\n  width: 100%;\r\n  box-sizing: border-box;\r\n  -moz-box-sizing: border-box;\r\n}\r\n<\/code><\/pre>","protected":false},"excerpt":{"rendered":"This plugin is obsolete. See the new status at github.bililite.com\/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 [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3034"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=3034"}],"version-history":[{"count":27,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3034\/revisions"}],"predecessor-version":[{"id":3654,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3034\/revisions\/3654"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}