Skip to content

Javascript Syntax-Highlighting Code Editor

I suppose everyone knows about this, but I just discovered CodeMirror, a javascript, drop-in (not dependent on any other library) text editor that does syntax highlighting. It's smooth and fast. I'm still going to be playing with my editor (as part of the bililiteRange package), since it's been a useful learning experience, but if you want a professional-level editor, go there. I won't be insulted.

New bililiteRange plugin, ex

There have been lots of times that I've wanted to be able to keep my hand on the keyboard when editing, rather than running off to the mouse all the time. There's an implementation of VIM in Javascript but I figured I would learn something by doing it myself. My goal is vi, not vim, since I don't need anything that sophisticated.

The first step is implementing the line-oriented part of vi, called ex, based on the manual from the sourceforge project. My version is based on bililiteRange, and depends on the bililiteRange utilities and undo plugin.

Use it simply as bililiteRange(textarea).ex('%s/foo/bar/');, passing the ex command to the ex() function. The biggest difference from real ex is that this uses javascript regular expressions, rather than the original ex ones. Thus s/\w/x/ rather than s/[:class:]/x/, and use ?/.../ rather than ?...? to search backwards (the question mark is used in Javascript regular expressions so I don't want to use it as a delimiter).

See a demo.

See the code on github.

Continue reading ›

IE11 bug with Ranges

Try the demo in Internet Explorer 11 and in a real browser.

IE now implements Range objects, representing a range of text that may span several elements. It's the basis for the standards-based part of bililiteRange. And it almost works. It's a little flaky: inserting text that contains '\n' without '\r', what I consider the "normal" way, automatically inserts the '\r'. Then using node.nodeValue returns a string without the '\r', but Range.toString() includes them. But that I can hack around.

The real bug is that ranges cannot end with a '\n'. If you try, the range is truncated. I can't find any way around that, so I had to change the way bililiteRange returned text: I went from range.toString() to element.textContent.slice(). Since that works with no other changes, I suspect that the error is in IE's toString() function itself rather than reflecting an underlying error in the Range.

Now to report that to Microsoft.

Better Javascript testing

Evidently I'm doing test-driven development wrong. Or at least it could be easier. I will have to look at Google's karma to automate the testing (rather than running the test suite in each browser individually). That of course means I need to start using Node's npm package manager (which I probably should anyway, since all the cool kids are). I've been using chocolatey for installing programs, but it explicitly is designed to not overlap with Node and its package manager (though it will install Node itself).

This post will have to be my reminder to start hacking with all of this sooner rather than later. Now I have to see patients...

IE 11 bugs

Turns out Internet Explorer is even worse than imagined. It's right at the bottom of the Uncanny Valley--close enough to a real browser to make it look like it works, but lots of near-impossible-to-track-down bugs that make life miserable. Turns out that Node.normalize is broken (see the bug reports and a workaround), so I had to add a test in bililiteRange to check for that, and not bother normalizing text if it's broken. Normalization just means merging adjacent text nodes, so losing it makes things less efficient but it should still work. I'm not going to lose sleep over Internet Explorer's inefficiencies.

Internet Explorer 11 Mysteries

Trying to debug bililiteRange in IE11; some of the problems were easily fixed with .replace(/\r/g, '') liberally scattered about, since IE really likes its return characters in <div>s (anything that is not a <textarea> or an <input> and will add them even if I am just inserting a newline.

I'm still getting a WrongDocumentError on some tests, but when I run them individually they pass; there must be some subtle global interaction that I am just not getting. I will consider those passed for now, though it is annoying seeing the red when I run the tests.

That leaves one error in find, one error in sendkeys, and whole bunch in ex, which isn't officially working yet anyway. Progress!

bililiteRange and Internet Explorer 11

Well, I ran the test code for bililiteRange and got "132 assertions of 151 passed, 19 failed." Better than none passed, I suppose, but it means I've got some work ahead of me. Or I could just give up on IE, but IE11 is supposed to be standards-compliant, so the errors might actually reflect a problem.

Some of the results are weird: the expected and actual results look identical, so I imagine there's some '\r's rearing their ugly heads, even in IE11. The selection is not being retained on losing focus in input elements; that might be a real problem. And then some tests are failing with a "WrongDocumentError". Never seen that before.

I'll add this to my list of things to get to eventually.

Windows 8

Finally got a new machine (Toshiba Satellite C75), with Windows 8.1, and despite all the negative hype, it doesn't suck. My wife has had a Windows 8 computer for a while now, and I had explained that teh way to think about it was as two separate operating systems: the old, mouse-oriented one; and the new, touch-oriented one. She's adapted well to that.

But playing on my own machine made me realize that's the wrong mindset. I now treat the Start screen as a big Start menu, organized with all the programs I want the way I want them. The first thing was to remove all the junk that was on there (I kept the weather app and the news but that's it) and start pinning my programs (Notepad++, Chrome, Git Bash, etc). Now it's Windows key or mouse lower-left, then type or click what I want. I had to write a few .bat files and pin the shortcuts to get websites to open in Chrome, but that works as well.

Now to see what all the fuss with IE 11 is about...

flexcal with European date formatting

The question came up about using "European" dates (day/month/year) rather than "American" dates (month/day/year) in flexcal. The biggest problem is that the built-in Date.parse() (which is used by new Date(string)) uses the American format, at least with my browsers and settings.

The code in flexcal that does the formatting are in the following methods:

format({Date})
Converts a Date into a String. Used for external formatting: it determines what string is put in the input element after the user selects a date.
_date2string({Date})
Converts a Date into a String. Used for internal formatting: the rel attribute of each day link in the calendar is set with this.
_createDate(d, oldd)
Attempts to coerce d into a Date. If it is a valid Date, just returns that. Otherwise returns new Date(d). If that does not create a valid Date, returns oldd.
this._createDate(this._date2string(d)) must equal d in order for the calendar to work, and this._createDate(this.format(d)) must equal d for the calendar to be able to parse the string in the input element.

So to use European dates, we have to replace each of those methods. I'll use the "dd.mm.yyyy" format.

<input id="eurodate" value="01.02.2014" /> Should start as February first, not January second.

function euroformat (d){
  return d.getDate()+"."+(d.getMonth()+1)+"."+d.getFullYear();
}
$.ui.flexcal.subclass('ui.euroflexcal',{
  format: euroformat,
  _date2string: euroformat,
  _createDate: function (d, oldd){
    // converts d into a valid date
    oldd = oldd || new Date;
    if (!d) return oldd;
    if (typeof d == 'string' && d.match(/(\d+)\.(\d+)\.(\d+)/)){
      d = RegExp.$2 + '/' + RegExp.$1 + '/' + RegExp.$3; // convert to American style
    }
    if (!(d instanceof Date)) d = new Date(d);
    if (isNaN(d.getTime())) return oldd;
    return d;
  }
});
$('#eurodate').euroflexcal();

bililiteRange data

There have been a lot of times I have needed some information for a bililiteRange plugin that was associated with the underlying element, rather than a specific range. For instance, in bililiteRange(element).text('foo') then bililiteRange(element).undo() the undo needs to know about the previous text change. jQuery has a data() method that attaches an object to the element and you can add fields to that object. Actually, it only attaches an index that points to the actual object, since at least in some browsers the garbage collector had trouble with Javascript objects attached to DOM elements and you ended up with memory leaks. I'm not sure if that is still a problem, but it's an easy enough pattern to implement so I used it.

I didn't want to be jQuery-dependent and I wanted to be able to some more sophisticated things with my data, so I implemented my own. At its simplest, just use:

var data = bililiteRange(element).data();
data.foo = 'bar';
assert(bililiteRange(element).data().foo == 'bar');

bililiteRange(element).data() returns an object that you can add fields to and they will be saved across multiple calls to bililiteRange.

Continue reading ›