Brian Peacock

SeaGlass: A JavaScript Word Processing Engine

June 2013
SeaGlass: A JavaScript Word Processing Engine

It was time to refactor–but this time for good. The FastFig document system was being held back by a couple of major limitations:

  1. The document format was totally linear. Headings could only contain text because elements could not be nested.
  2. Basic cursor behavior was buggy because each type of element (math, text, pictures, headings and graphs) handled the cursor individually.
  3. We needed a system that could be expanded more easily to include more elements and more formatting options.

I began my research by learning more about the different approaches used to write browser-based word processors. The simplest and most common way is to use the built-in ContentEditable feature in modern browsers. Basically this involves adding an attribute to an HTML element that causes its HTML contents to magically become editable. Unfortunately, the browser support on mobile is still spotty and the UI implementation between browsers is varied. This would be fine if it was just a rich text editor for comments or a CRM but this was the core of the application.

So I looked at more sophisticated browser implementations. Google Docs uses a combination of div and span elements to construct the document but then relies on an absolutely positioned cursor that moved across the document as users typed. This was closer to what I wanted in that the editing UI could be precisely defined, however, the idea of calculating a cursor position with varied-width fonts seemed dreadfully complex.

Finally, I decided to go with a hybrid architecture. I would create a base Element class that would handle be the superclass for all of the different types of elements. It would handle all of the basic interactions between elements (deletion, insertion, selection, etc.) much like a library like jQuery handles DOM interactions. For text, I would actually use ContentEditable (with fallbacks) so that cursor interactions within text would not be a big deal and rich text formatting would come standard. For everything else, I wrote classes that used the editors from the original code: Math used Mathquill, Pictures used a standard file upload system plus the HTML5 camera API and Graphs used SVGs piped from our compute system. For editing structural elements like headings and sections, I created an object called Cursor that moved a flashing zero-width element around the actual document and handled all of the keyboard interactions. For element types that had their own cursor, the Cursor would turn invisible and delegate all of the interactions back to the focused element. It was a clean way to get native performance while ensuring consistent cursor behavior between wildly different elements.

We named the system SeaGlass after a perfect piece of blue sea glass that I found at dawn after walking the beach all night dreaming it up. It was the first time I truly understood what good software looks like.