Saturday, August 6, 2011

The Good, the Bad and the Ugly

It was the best of times, it was the worst of times, ... It has certainly been a mixed week.

The good

... was that I helped three OU developers submit their first bug fix through the new Moodle development process: MDL-27631, MDL-28517 and MDL-28620. Hopefully those fixes all get through integration review next week.

The Bad

... was that the time had finally come to deal with a hot potato that we have been tossing around for some months; and, to mix metaphors, when the buck stopped, I was in the the wrong place at the wrong time.

As part of some new question types we are developing, we want students to be able to type responses that include superscripts and subscripts. For example 3×108 ms-1 or SO42-. We have an old implementation of this, done six years ago for OpenMark (for example this or this), but that never worked in Safari, and is a bit dodgy generally. We want a new, reliable implementation that works in IE, Firefox, Chrome and Safari.

Plan A: back near the start of spring, I quickly knocked up a partial solution using the YUI 2 Rich Text Editor library. It mostly worked, but there were issues. It did not work consistently across browsers, and it lets you nest superscripts inside subscripts inside superscripts which just gets confusing, so we want to prevent that.

I had a sneaking suspicion how hard it would be to get from my quick partial solution to a robust implementation. Therefore I moved on to other things, and tried to unload this job onto three other people in turn. There were plenty of other more urgent tasks on our todo list.

Time passed, and many of the other things got done, so at the start of the week I realised that creating this input widget could not be put off any longer. I also felt it was unfair to expect other developers to deal with a crappy job that I was not prepared to do myself, so I decided to have another go.

The other thing that had changed is that while attempting to implement this, Colin and Wale had both eliminated some blind alleys, and suggested some promising ideas. Therefore, I was continuing from a far better place than where I left off. Even so, it was a long week.

The Ugly

Plan B: Although we had a partial implementation in YUI 2, I did not want to continue with that. Moodle is trying to move away from YUI 2 and to YUI 3 as soon as possible. So, my first attempt was to use the YUI 3(.3) Rich Text Editor. As the docs make clear. That is not finished yet. It is also not terribly well documented. With hindsight, I now realise that it provides only a very thin rapper around the native editing facilities provided by web browsers. Therefore it does not really help with browser inconsistencies.

Plan C: Since the Rich Text Editor is only beta, I decided to have a look at what was new in YUI 3.4, which is due for release soon. The answer is that they have made quite a lot of progress - in the sense that if you are trying to walk from London to Edinburgh, you have made quite at lot of progress by the time you reach Milton Keynes. Compounded with the fact that it is hard to find any documentation for pre-release version of YUI, this approach also failed.

At this point, I decided to do a bit of reading. I found two excellent articles from Opera that explained exactly how contentEditable works in web browsers. I also found a good cross-browser compatibility table. That made me realise that YUI was hardly doing anything to help with cross-browser differences.

Plan D: Now that I knew roughly what the browsers were doing, I briefly toyed with the idea of implementing the widget entirely myself in plain JavaScript. Once again, getting something basic working was not too hard, but I had not even started to tackle the cross-browser differences.

Then I realised that TinyMCE, which Moodle uses, tends to work really well across browsers. It is a bit slow to load, because it is a huge mass of code, but perhaps all that code is there for a reason. A quick play with superscript and subscript in the Moodle HTML editor in various browsers confirmed that TinyMCE must be working around most of the problems. So I dived into the TinyMCE code with the original idea of stealing the bits I needed to make Plan D work. It did not take much looking for me to develop a new-found respect for how hard TinyMCE is working to keep different web browsers in line. I did not want to have to replicate all that.

Plan E: So I finally concluded I should just use TinyMCE directly. That is using a very large sledge-hammer to crack a nut, but at least it should work. Indeed, it was mostly just a matter of setting the right configuration options. What made it particularly good is that there is an option to limit which tags can be nested inside other tags. That robustly prevents people from nesting superscript inside subscript, etc.

I was very nearly there, but there were two more requirements. We did not want pressing enter to insert a line-break, and because we were only dealing with a single line of input, we wanted to use the up and down arrow keys as shortcuts for superscript and subscript. The only way I could find to do that was to write a simple TinyMCE plugin. Fortunately, that is well documented.

The end

I got there eventually. The code needs to be cleaned up, tested some more, and integrated into the question types we are building, but I don't foresee any problems doing that.

I would like to thank the Moxie Code, who make TinyMCE, even though they have completely ignored the patch I sent them some time ago in relation to MDL-27890. I would also like to thank Olav Junker Kjær, who wrote the Oracle blog posts, which were the most useful thing I read. Also, the team behind Firebug. I can't imagine doing JavaScript development without that debugging tool. Finally Colin, Wale and Jamie, who I tried to dump this on, and who in return gave me helpful ideas.


  1. This is a superb description of the learning process necessary to create usefull code in the always more complex computer universe...

    Pierre Pichet

  2. How does TinyMCE do on mobile webkit or chromium devices?

  3. No idea about mobile devices. I'll test when I get the chance.

  4. At the moment, it does not seem to work at all on iOS, even after hacking the browser sniffing in two places. (supsub_texteditor::supported_by_browser - change last line to return true; and tiny_mce_src.js, comment out if (tinymce.isIDevice) return;)

    There is a comment just there in the TinyMCE code:

    "// Is a iPad/iPhone, then skip initialization. We need to sniff here since the
    // browser says it has contentEditable support but there is no visible caret
    // We will remove this check ones Apple implements full contentEditable support"

    That is exactly what we found. Also (on an iPad) the keyboard did not come up when you focussed the input area.

    However, once Apple gets their act together, it should be easy to implement this.

    I just moved the code to its own repository: and updated the link in the post.