Protocol‎ > ‎Design Documents‎ > ‎

Editor

    (Under construction)

    This document details the design & philosophy of Wave's rich text editor.


    Background knowledge

    The following information is useful, but not strictly necessary, for understanding this document.
    • Wave document model
    • Operations
    • High level knowledge of GWT (e.g. how JavaScriptObject works).


    Goals


    Features

    • Support arbitrary separation of document model from rendering.
      • Currently, the model is wave's XML+annotations, but in the future it would be relatively straight forward to be made to be abstract.
    • All features including standard rich text paragraphs/headings/bullets/styles/tables, as well as custom widgets, forms, gadgets, spelling annotations, search results, and so forth, are all definable based on the same core document and editor API.
      • Provide useful editing and selection events / interfaces to the code implementing above editing features.
    • Allow untrusted 3rd parties to define, extend and customise editor functionality in the same way that trusted code can.
    • Support for maintaining and rendering additional "local" content not persisted to the server, as required by the host application. For example, diff highlighting, inline avatars, anchors for other private documents, and so forth.
    • Support receiving and releasing editing control of a document, i.e. a wave document may exist independently of an editor, and an editor is attached to it only as needed. Additionally, a document may be rendered, or may have its rendering torn down when not needed, for efficiency.
    • Rich Text Editing is an obvious example capability that must be possible from the above.

    Properties

    • Extract user modifications with high granularity and low latency.
    • Concurrently modifiable by the user against incoming operations, with high granularity and low latency.
    • Work on IE7, Safari 3.0, Firefox 3, all Chrome without pluging support.
    • Multilingual (RTL, IMEs, etc)
    • Robust & SuperFast™ (obviously)
    • Produce a reasonably sane HTML rendering
    • Standalone component usable separately of wave
    • Open source

    Non Goals

    • Define a custom layout model - the layout model is HTML+CSS, used as the custom editing components see fit.
      • (Note: the editor is designed in many layers. It would not be fundamentally hard to swap out the very lowest HTML handling layer for a different rendering surface, e.g. Swing (as the code is written in GWT Java), Curses, some custom JS based formatting engine, etc).
    • Support older browsers

    (Note, some of these pertain to the document model and rendering, which is strictly separate from the editor, but we describe them all together for convenience)

    <<insert example screenshots here illustrating features/properties>>


    General approach and Philosophy


    As per goals, the editor is designed for collaborative, semantic editing of arbitrary web-focused content. We want to render content to clean HTML, but support editing gestures corresponding to the semantic level of the content. At the same time, we want to avoid re-inventing the wheel, and take advantage of as much of the native functionality present in browsers as possible. As of this writing, browsers provide some level of support for the kinds of goals we desire, but the APIs are immature and incomplete. Maintaining an arbitrary mapping from our abstract data model to an HTML rendering is difficult because letting the browser do everything (e.g. with raw contentEditable) will result in undesirable effects which are hard to control. Nonetheless, many man years have gone into browsers providing all kinds of functionality. So the overarching philosophy we've adopted is to strike a balance: leverage as much of what the web platform provides as is possible, let the browser do its job where it does it well and where we can control and keep track of its actions; for the rest, implement the desired functionality explicitly. This forms the core of the editor, and on top of this stable and flexible base, we can build all our features.





    -------

    Document model

    <EXPLAIN VIEWS>

    Diff on open


    Doodads


    <INSERT IMAGE FROM PRESENTATION>

    Event handling


    This diagram is a summary of the layers of event handling related to editing documents. Details follow.

    <INSERT IMAGE FROM PRESENTATION>

    Event normalisation layer

    Normalize raw browser events using SignalEvent. See Signal Events document. We actually provide a custom factory for the SignalEvent implementation, so that we can create EditorEvent objects, which add editor-specific extensions to SignalEvent. Details are discussed below.

    Additionally, some doodads may choose to add direct event handlers on their HTML renderings. In this case, what happens is entirely under their control and has nothing to do with the editor

    Event routing layer

    The editor's event routing logic is contained in the EditorEventHandler class. This contains all the fiddly logic for dealing with the events that pass through the editor, after already being normalized as SignalEvents. The class handles the following built in logic:
    • Regular text editing events, including choosing when to batch and ignore them, and when to route them to the TypingExtractor. (See section on TypingExtractor for more details).
    • IME composition (for multilingual input) text editing events, keeping track of the composition state which affects how other events are treated too. Most of the lower level logic for this, incl. handling how composition events work cross-browser, is handled in CompositionEventHandler. (Handling IME events without composition events, for older versions of Webkit and IE, using tricks with regular keydown events, is still handled by the editor event handler, but logic could arguably be moved into the composition event handler).
    • Clipboard events. Most work is delegated to the PasteExtractor
    • Key combos - detecting useful, ignorable, and harmful ones (where the browser may munge the content editable's html)
    • Mutation events (for browsers that support them) - we normally try to avoid using them, because they tell us nothing about user intent, but they are a useful catch-all for unexpected events. Most of the actual handling is delegated to classes like DomMutationReverter.

    Furthermore, it handles routing editor events to custom, "3rd party" event handlers. This is part of the rich extensibility story of the editor. The above bullet point contains low-level editing concerns that require fiddly handling, and have not (at this point) been factored out. Regardless, most editing behaviors, from what happens when you press "Enter" in a paragraph or bullet point, to what happens when you hit "Tab" inside an image thumbnail caption (e.g. tabbing to the next caption, vs indenting for paragraphs), to what happens when you hit "Ctrl/Command+B", is funnelled to these other event handlers.

    Custom Event Handlers

    These handlers, in turn, mutate the document, which then reacts by updating its rendering, completing the cycle so the user's changes are reflected in the HTML. Currently, there are a few bits and pieces to custom event handling.
    • Classes that implement NodeEventHandler. They are registered against specific element types, and then they are called to handle events that happen to those elements. Example methods that may be implemented are "handleEnter", "handleLeftAfterNode", and so forth. Most useful editing customisations to specific element types can be implemented through these classes.
    • Through the use of KeyBindingsRegistry. This allows defining handling for different key combos on both collapsed and expanded selection ranges.
    • General changes can be listened to by calling addUpdateListener on the Editor. This receives EditorUpdateEvents which are sent in an asynchronous and rate-limited fashion, for performance reasons. It is a useful, but coarse granularity source of information. It can only be queried about the types of things that have changed in the editor since the last call, such as, whether the selection changed, whether the selection coordinates changed, whether the content changed, etc.
    • When all else fails, through the use of addKeySignalListener on the Editor. It permits previewing any event, overriding the editor's event handling. This should be avoided unless absolutely necessary, for performance and correctness reasons.

    EditorEvent is an extension of SignalEvent that is passed to custom event handlers. It contains the following additions:
    1. Ability to specify whether the event was "handled" - if not, default handling will occur.
    2. Ability to store a mutable "caret" location for where the event is taking place. This allows event handling to be delegated around the DOM in any direction.


    Typing extraction



    IME

    two approaches

    • composition events
    • FF & webkit
    • webkit requires special extra logic
    • allow more reliable extraction, and more concurrent mutations to happen around
    • for older browsers: various tricks
    • IE: special keycode for IME events
    • Older webkit: same keycode, and also mutation events




    Clipboard

    • Paste
      • Hidden div trick: on the paste event, put the cursor in a hidden div (div must be "visible" for this to work, so we just move it off-screen). let the paste happen. in a deferred command (unfortunately no synchronous "after paste" event) interpret pasted content (see details below), update document, put cursor back.
      • 2 layers, tokenizer & mutation builder
        • tokenizer maps whatever is at the layer below to a generic format of a stream of rich text tokens
        • mutation builder maps the generic rich text format to wave's XML.
        • tokenizer abstracts browser differences, e.g. IE8 & lower don't have a proper DOM, so we need to parse the string; FF has wierd whitespace issues;
        • same code used as a general HTML->waveXML utility e.g. for robots, and can run on the server. Simply provide the string-backed tokenizer for the server version.
    • Copy/Cut
      • Again use hidden div trick (see paste)
        • on the cut or copy event, put stuff to copy into hidden div, set selection over that stuff, and in a deferred command, set selection back. If it's cut, also delete the original content.
        • future: use native clipboard API when it gets implemented in browsers.
      • Nice html
        • render dom slightly differently for copy, to improve fidelity when pasting into external apps
      • Semantic
        • trick: stick attribute in an initial span as well. Everything goes into clipboard. If pasting back into wave, we'll find the special span with attribute holding the data, otherwise, other apps will just use the HTML.

    Selection



    Related
Comments