Archive for December, 2013

I've been looking at the DOM 3 event model and decided that the bililiteRange.text() andbililiteRange.select methods ought to implement those, even though they're not in browsers yet. Nobody implements beforeinput, or event.data in the input event handler, and select only works in <input> and <textarea> (not in contenteditable). And that's in modern browsers.

But that's no excuse for me. beforeinput is now dispatched before changing the text, and input after, with event.data set to the text to be inserted, and event.bounds set to the bounds array in the original text (this last is not part of the spec, but it's hard to work with changing text without it). This is an incompatible change from the way it used to work (with event.detail being set to {text: text, bounds: bounds}), but I don't think that many people are using that aspect of the library.

I also moved a few methods from the utilities library into the main code, since I used them so much.

It doesn't look like my workplace is ever going to get past IE8 (many of the machines are still on Windows XP!), so I really want to support those. There are polyfills for getting the event listeners to work, and I incorporated a quick and cheap version of that into the code, and event listenders to dispatch input events on keystrokes, drag and drop, and cut/paste.

bililiteRange is now at version 2.0.

I will be forever grateful when I can drop IE8.

Prism is fast, generally fast enough to use for a live syntax highlighter. But for large texts (multiple kilocharacters) having the text re-highlighted with every input bogs down, and I can type faster than the text can show up. I remember old word processors that were like that, back in the dark ages of computerdom. But it's unacceptable today.

What I need to do is "debounce" the input events, so they aren't piling up and creating a backlog of highlighting that is going to be redone with the next event anyway. John Hann has a nice little routine that I simplified into:

function debounce (func, threshold){
	if (!threshold) return func; // no debouncing
	var timeout;
	return function(){
		var self = this, args = arguments;
		clearTimeout(timeout);
		timeout = setTimeout(function(){
			func.apply(self, args);
		}, threshold);
	};
}

Basically, it uses setTimeout to delay the application of the function, and resets the timer every time it's called. Use it as var debouncedFunc = debounce(func, 100);

This is a plugin for bililiteRange that implements a simple undo/redo stack. It listens for input events (so to use it in IE<10 you need some kind of polyfill) and records the old text each time, so it's pretty memory-inefficient, but it works for what I want to do.

See the code on github.

See a demo with the Prism editor (control-z to undo, control-y to redo).

Usage is simple:

bililiteRange(element).undo(0); // initializes the event listener without trying to undo anything; safe to call multiple times
bililiteRange(element).undo(); // restores the text from the last undo point
bililiteRange(element).undo(-1); // redo--undoes the last undo

bililiteRange(element).undo(n) for any n works and will undo (for positive n) or redo (for negative n) that many times, though I'm not sure how useful that would be.

There are convenience methods to be used as event handlers:

bililiteRange.undo(event);
bililiteRange.redo(event);

So a simple keyboard shortcut handler would be:

		bililiteRange(editor).undo(0); // initialize
		editor.addEventListener ('keydown', function(evt){
			// control z
			if (evt.ctrlKey && evt.which == 90) bililiteRange.undo(evt);
			// control y
			if (evt.ctrlKey && evt.which == 89) bililiteRange.redo(evt);
		});

or, if you use my hotkeys jQuery plugin,


$(editor).on('keydown', {keys: '^z'}, bililiteRange.undo);
$(editor).on('keydown', {keys: '^y'}, bililiteRange.redo);

Hope that this is useful to someone.

It tries to be sophisticated about typing, and bunches all typing done in a row into one, so that undo undoes the entire line. Backspacing, moving the insertion point, or typing a newline starts a new undo point.

Restoring the insertion point isn't as sophisticated as in a real word processor yet; moving the insertion point then typing then undoing moves the insertion point back to where is was originally, not to the start of typing.

As luck would have it, right after I wrote about synchronous vs. asynchronous event handlers, I found exactly that problem in by bililiteRange code. It uses dispatchEvent to fire an input event when text is inserted, but that fires synchronously, so that the event handlers run before the bililiteRange.text() has fully run. I ended up having to wrap the dispatchEvent in a setTimeout to force it to be asynchronous.

The bililiteRange.js code is now at version 1.7.

As promised, I have updated the status plugin to use Promises. Unfortunately, that required a small change to the parameters. Since I doubt anyone is using it, that will not likely affect anyone but me. The version is now 1.1.

There's a subtlety in using Promises with event handlers or any code that's executed asynchronously.

Promises are supposed to be used for future values, with the reject handler acting like an exception handler, so that the Promise constructor actually catches real exceptions and turns them into rejects (from the polyfill; I assume the browser-implemented code works similarly):

try {
  resolver(resolvePromise, rejectPromise);
} catch(e) {
  rejectPromise(e);
}

But asynchronous code runs outside the execution unit that runs resolver(), so any exceptions there have to be caught explicitly:

<button id=one>Click Me</button>
<button id=two>Don't Click Me</button>
<script>
var promise = new Promise(function(resolve, reject){
	document.querySelector('#one').onclick = function(){
		resolve ('You Clicked!');
	};
	document.querySelector('#two').onclick = function(){
		throw new Error ('You Clicked!');
	};
});

promise.then(function(msg){
	alert('Button clicked: '+msg);
}, function(err){
	alert('Error caught:'+err.message);
});
</script>

Clicking button two gives an uncaught exception error, not the alert from the then(). You have to catch the exceptions:

	document.querySelector('#two').onclick = function(){
		try {
			throw new Error ('You Clicked!');
		} catch (e) {
			reject(e);
		}
	};

or resolve to a new promise that can catch the error:

	document.querySelector('#two').onclick = function(){
		resolve(new Promise(function (){
			throw new Error ('You Clicked!');
		}));
	};

The other subtlety (which is obvious from the definition of a Promise) is that Promises are once-only; in the above code, clicking either button settles the Promise and further clicking does nothing. Event handlers can be called multiple times, so you have to think about what you intend.

Just in time for me to start thinking about Javascript Promises, comes along an official API, along with a polyfill that basically re-implements rsvp.js with the "official" terminology.

Now for some experimenting (and rewriting jquery.status.js to use real Promises).

It seems from all the discussion that jQuery $.Deferred are not and will not be real Promises, but they can be Promise.cast($.Deferred()) into one. Note that to use that, you need to $.Deferred().resolve() on the jQuery object, but attach Promise.then()'s to the Promise.

Prism is a great Javascript syntax highlighter, and it's fast enough to be used on the fly for active editing; Lea Verou (the author) does this with Dabblet. Combined with bililiteRange to handle some of the subtleties of dealing with contenteditable elements, a syntax-highlighting editor is relatively easy.

Simplest usage: bililiteRange.fancyText(element, Prism.highlightelement);.

See the demo.

See the code on github.

There's a fair amount of flakiness; contenteditable elements are still handled inconsistently. Newlines are changed into actual elements (Firefox adds <br>s; Chrome adds a whole new <div>), even though a <pre> doesn't need that; it would interpret newlines the way we want. So we have to intercept those and just insert the newline. Firefox insists on changing newlines into <br>'s even in pasted text, and Chrome's clipboard implementation includes the carriage return character, so we have to intercept paste events and insert the text appropriately mangled.

Continue reading ‘Simple syntax-highlighting editor with Prism’ »

I've been looking at Promise/A+ as a way to abstract future values, or values that depend on something the user will do later, specifically Yehuda Katz's rsvp.js, and came across Domenic Denicola's You're Missing the Point of Promises, which got me looking at Oliver Steele's Minimizing Code Paths in Asychronous Code. Now I have another consideration in my code that I never thought about before.

Continue reading ‘Thinking about synchonicity’ »

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 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.

Continue reading ‘New jQuery plugin, status’ »