corinthia-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pmke...@apache.org
Subject [49/92] [abbrv] [partial] incubator-corinthia git commit: Add editing code from UX Write
Date Wed, 17 Dec 2014 13:28:59 GMT
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/Cursor.js
----------------------------------------------------------------------
diff --git a/Editor/src/Cursor.js b/Editor/src/Cursor.js
new file mode 100644
index 0000000..606c4bd
--- /dev/null
+++ b/Editor/src/Cursor.js
@@ -0,0 +1,934 @@
+// Copyright 2011-2014 UX Productivity Pty Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var Cursor_ensurePositionVisible;
+var Cursor_ensureCursorVisible;
+var Cursor_scrollDocumentForY;
+var Cursor_positionCursor;
+var Cursor_getCursorPosition;
+var Cursor_moveLeft;
+var Cursor_moveRight;
+var Cursor_moveToStartOfDocument;
+var Cursor_moveToEndOfDocument;
+var Cursor_updateBRAtEndOfParagraph;
+var Cursor_insertReference;
+var Cursor_insertLink;
+var Cursor_insertCharacter;
+var Cursor_deleteCharacter;
+var Cursor_enterPressed;
+var Cursor_getPrecedingWord;
+var Cursor_getAdjacentNodeWithType;
+var Cursor_getLinkProperties;
+var Cursor_setLinkProperties;
+var Cursor_setReferenceTarget;
+var Cursor_makeContainerInsertionPoint;
+var Cursor_set;
+var Cursor_insertFootnote;
+var Cursor_insertEndnote;
+
+(function() {
+
+    var cursorX = null;
+
+    Cursor_ensurePositionVisible = function(pos,center)
+    {
+        // If we can't find the cursor rect for some reason, just don't do anything.
+        // This is better than using an incorrect position or throwing an exception.
+        var rect = Position_displayRectAtPos(pos)
+        if (rect != null) {
+            var extraSpace = 4;
+
+            var cursorTop = rect.top + window.scrollY - extraSpace;
+            var cursorBottom = rect.top + rect.height + window.scrollY + extraSpace;
+
+            var windowTop = window.scrollY;
+            var windowBottom = window.scrollY + window.innerHeight;
+
+            if (center) {
+                var newY = Math.floor(cursorTop + rect.height/2 - window.innerHeight/2);
+                window.scrollTo(window.scrollX,newY);
+            }
+            else if (cursorTop < windowTop) {
+                window.scrollTo(window.scrollX,cursorTop);
+            }
+            else if (cursorBottom > windowBottom) {
+                window.scrollTo(window.scrollX,cursorBottom - window.innerHeight);
+            }
+        }
+    }
+
+    // public
+    Cursor_ensureCursorVisible = function(center)
+    {
+        var selRange = Selection_get();
+        if (selRange != null)
+            Cursor_ensurePositionVisible(selRange.end,center);
+    }
+
+    Cursor_scrollDocumentForY = function(y)
+    {
+        var absY = window.scrollY + y;
+        if (absY-44 < window.scrollY) {
+            window.scrollTo(window.scrollX,absY-44);
+            y = absY - window.scrollY;
+        }
+        else if (absY+44 >= window.scrollY + window.innerHeight) {
+            window.scrollTo(window.scrollX,absY+44 - window.innerHeight);
+            y = absY - window.scrollY;
+        }
+        return y;
+    }
+
+    // public
+    Cursor_positionCursor = function(x,y,wordBoundary)
+    {
+        if (UndoManager_groupType() != "Cursor movement")
+            UndoManager_newGroup("Cursor movement");
+
+        y = Cursor_scrollDocumentForY(y);
+
+        var result = null;
+        var position = Position_atPoint(x,y);
+        if (position == null)
+            return null;
+
+        var node = Position_closestActualNode(position);
+        for (; node != null; node = node.parentNode) {
+            var type = node._type;
+            if ((type == HTML_A) &&
+                (node.hasAttribute("href")) &&
+                (result == null)) {
+
+                var arange = new Range(node,0,node,node.childNodes.length);
+                var rects = Range_getClientRects(arange);
+                var insideLink = false;
+                for (var i = 0; i < rects.length; i++) {
+                    if (rectContainsPoint(rects[i],x,y))
+                        insideLink = true;
+                }
+
+                if (insideLink) {
+                    var href = node.getAttribute("href");
+                    if ((href != null) && (href.charAt(0) == "#")) {
+                        if (isInTOC(node))
+                            result = "intocreference-"+href.substring(1);
+                        else
+                            result = "inreference";
+                    }
+                    else {
+                        result = "inlink";
+                    }
+                }
+            }
+            else if ((type == HTML_IMG) && (result == null)) {
+                for (var anc = node; anc != null; anc = anc.parentNode) {
+                    if (anc._type == HTML_FIGURE) {
+                        result = "infigure";
+                        break;
+                    }
+                }
+            }
+            else if (isAutoCorrectNode(node) && (result == null)) {
+                result = "incorrection";
+            }
+            else if (isTOCNode(node)) {
+                var rect = node.getBoundingClientRect();
+                if (x >= rect.left + rect.width/2)
+                    position = new Position(node.parentNode,DOM_nodeOffset(node)+1);
+                else
+                    position = new Position(node.parentNode,DOM_nodeOffset(node));
+                break;
+            }
+        }
+
+        var position = Position_closestMatchForwards(position,Position_okForMovement);
+        if ((position != null) && isOpaqueNode(position.node))
+            position = Position_nextMatch(position,Position_okForMovement);
+        if (position == null)
+            return false;
+
+        var selectionRange = Selection_get();
+        var samePosition = ((selectionRange != null) && Range_isEmpty(selectionRange) &&
+                            (position.node == selectionRange.start.node) &&
+                            (position.offset == selectionRange.start.offset));
+        if (samePosition && (result == null))
+            result = "same";
+
+        if (wordBoundary) {
+            var startOfWord = Selection_posAtStartOfWord(position);
+            var endOfWord = Selection_posAtEndOfWord(position);
+            if ((startOfWord.node != position.node) || (startOfWord.node != position.node))
+                throw new Error("Word boundary in different node");
+            var distanceBefore = position.offset - startOfWord.offset;
+            var distanceAfter = endOfWord.offset - position.offset;
+            if (distanceBefore <= distanceAfter)
+                position = startOfWord;
+            else
+                position = endOfWord;
+        }
+
+        Cursor_set(position.node,position.offset);
+        return result;
+    }
+
+    // public
+    Cursor_getCursorPosition = function()
+    {
+        var selRange = Selection_get();
+        if (selRange == null)
+            return null;
+
+        // FIXME: in the cases where this is called from Objective C, test what happens if we
+        // return a null rect
+        var rect = Position_displayRectAtPos(selRange.end);
+        if (rect == null)
+            return null;
+
+        var left = rect.left + window.scrollX;
+        var top = rect.top + window.scrollY;
+        var height = rect.height;
+        return { x: left, y: top, width: 0, height: height };
+    }
+
+    // public
+    Cursor_moveLeft = function()
+    {
+        var range = Selection_get();
+        if (range == null)
+            return;
+
+        var pos = Position_prevMatch(range.start,Position_okForMovement);
+        if (pos != null)
+            Cursor_set(pos.node,pos.offset);
+        Cursor_ensureCursorVisible();
+    }
+
+    // public
+    Cursor_moveRight = function()
+    {
+        var range = Selection_get();
+        if (range == null)
+            return;
+
+        var pos = Position_nextMatch(range.start,Position_okForMovement);
+        if (pos != null)
+            Cursor_set(pos.node,pos.offset);
+        Cursor_ensureCursorVisible();
+    }
+
+    // public
+    Cursor_moveToStartOfDocument = function()
+    {
+        var pos = new Position(document.body,0);
+        pos = Position_closestMatchBackwards(pos,Position_okForMovement);
+        Cursor_set(pos.node,pos.offset);
+        Cursor_ensureCursorVisible();
+    }
+
+    // public
+    Cursor_moveToEndOfDocument = function()
+    {
+        var pos = new Position(document.body,document.body.childNodes.length);
+        pos = Position_closestMatchForwards(pos,Position_okForMovement);
+        Cursor_set(pos.node,pos.offset);
+        Cursor_ensureCursorVisible();
+    }
+
+    // An empty paragraph does not get shown and cannot be edited. We can fix this by adding
+    // a BR element as a child
+    // public
+    Cursor_updateBRAtEndOfParagraph = function(node)
+    {
+        var paragraph = node;
+        while ((paragraph != null) && !isParagraphNode(paragraph))
+            paragraph = paragraph.parentNode;
+        if (paragraph != null) {
+
+            var br = null;
+            var last = paragraph;
+            do {
+
+                var child = last;
+                while ((child != null) && isWhitespaceTextNode(child))
+                    child = child.previousSibling;
+
+                if ((child != null) && (child._type == HTML_BR))
+                    br = child;
+
+                last = last.lastChild;
+
+            } while ((last != null) && isInlineNode(last));
+
+            if (nodeHasContent(paragraph)) {
+                // Paragraph has content: don't want BR at end
+                if (br != null) {
+                    DOM_deleteNode(br);
+                }
+            }
+            else {
+                // Paragraph consists only of whitespace: must have BR at end
+                if (br == null) {
+                    br = DOM_createElement(document,"BR");
+                    DOM_appendChild(paragraph,br);
+                }
+            }
+        }
+    }
+
+    // public
+    Cursor_insertReference = function(itemId)
+    {
+        var a = DOM_createElement(document,"A");
+        DOM_setAttribute(a,"href","#"+itemId);
+        Clipboard_pasteNodes([a]);
+    }
+
+    // public
+    Cursor_insertLink = function(text,url)
+    {
+        var a = DOM_createElement(document,"A");
+        DOM_setAttribute(a,"href",url);
+        DOM_appendChild(a,DOM_createTextNode(document,text));
+        Clipboard_pasteNodes([a]);
+    }
+
+    var nbsp = String.fromCharCode(160);
+
+    function spaceToNbsp(pos)
+    {
+        var node = pos.node;
+        var offset = pos.offset;
+
+        if ((node.nodeType == Node.TEXT_NODE) && (offset > 0) &&
+            (isWhitespaceString(node.nodeValue.charAt(offset-1)))) {
+            // Insert first, to preserve any tracked positions
+            DOM_insertCharacters(node,offset-1,nbsp);
+            DOM_deleteCharacters(node,offset,offset+1);
+        }
+    }
+
+    function nbspToSpace(pos)
+    {
+        var node = pos.node;
+        var offset = pos.offset;
+
+        if ((node.nodeType == Node.TEXT_NODE) && (offset > 0) &&
+            (node.nodeValue.charAt(offset-1) == nbsp)) {
+            // Insert first, to preserve any tracked positions
+            DOM_insertCharacters(node,offset-1," ");
+            DOM_deleteCharacters(node,offset,offset+1);
+        }
+    }
+
+    function checkNbsp()
+    {
+        Selection_preserveWhileExecuting(function() {
+            var selRange = Selection_get();
+            if (selRange != null)
+                nbspToSpace(selRange.end);
+        });
+    }
+
+    function isPosAtStartOfParagraph(pos)
+    {
+        if ((pos.node.nodeType == Node.ELEMENT_NODE) && (pos.offset == 0) &&
+            !isInlineNode(pos.node)) {
+            return true;
+        }
+
+
+
+        while (pos != null) {
+            if (pos.node.nodeType == Node.ELEMENT_NODE) {
+                if ((pos.offset == 0) && !isInlineNode(pos.node))
+                    return true;
+                else
+                    pos = Position_prev(pos);
+            }
+            else if (pos.node.nodeType == Node.TEXT_NODE) {
+                if (pos.offset > 0)
+                    return false;
+                else
+                    pos = Position_prev(pos);
+            }
+            else {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+    // public
+    Cursor_insertCharacter = function(str,allowInvalidPos,allowNoParagraph)
+    {
+        var firstInsertion = (UndoManager_groupType() != "Insert text");
+
+        if (firstInsertion)
+            UndoManager_newGroup("Insert text",checkNbsp);
+
+        if (str == "-") {
+            var preceding = Cursor_getPrecedingWord();
+            if (preceding.match(/[0-9]\s*$/))
+                str = String.fromCharCode(0x2013); // en dash
+            else if (preceding.match(/\s+$/))
+                str = String.fromCharCode(0x2014); // em dash
+        }
+
+        var selRange = Selection_get();
+        if (selRange == null)
+            return;
+
+        if (!Range_isEmpty(selRange)) {
+            Selection_deleteContents(true);
+            selRange = Selection_get();
+        }
+        var pos = selRange.start;
+        pos = Position_preferTextPosition(pos);
+        if ((str == " ") && isPosAtStartOfParagraph(pos))
+            return;
+        if (!allowInvalidPos && !Position_okForInsertion(pos)) {
+            var elemPos = Position_preferElementPosition(pos);
+            if (Position_okForInsertion(elemPos)) {
+                pos = elemPos;
+            }
+            else {
+                var oldPos = pos;
+                pos = Position_closestMatchForwards(selRange.start,Position_okForInsertion);
+                var difference = new Range(oldPos.node,oldPos.offset,pos.node,pos.offset);
+                difference = Range_forwards(difference);
+                Position_trackWhileExecuting([pos],function() {
+                    if (!Range_hasContent(difference)) {
+                        Selection_deleteRangeContents(difference,true);
+                    }
+                });
+            }
+        }
+        var node = pos.node;
+        var offset = pos.offset;
+
+        if ((str == " ") &&
+            !firstInsertion &&
+            (node.nodeType == Node.TEXT_NODE) &&
+            (offset > 0) &&
+            (node.nodeValue.charAt(offset-1) == nbsp)) {
+
+            if (!node.nodeValue.substring(0,offset).match(/\.\s+$/)) {
+                DOM_deleteCharacters(node,offset-1,offset);
+                DOM_insertCharacters(node,offset-1,".");
+            }
+        }
+
+        if (isWhitespaceString(str) && (node.nodeType == Node.TEXT_NODE) && (offset > 0)) {
+            var prevChar = node.nodeValue.charAt(offset-1);
+            if (isWhitespaceString(prevChar) || (prevChar == nbsp)) {
+                Selection_update();
+                Cursor_ensureCursorVisible();
+                return;
+            }
+        }
+
+        nbspToSpace(pos);
+
+        // If the user enters two double quotes in succession (open and close), replace them with
+        // just one plain double quote character
+        if ((str == "”") && (node.nodeType == Node.TEXT_NODE) &&
+            (offset > 0) && (node.nodeValue.charAt(offset-1) == "“")) {
+            DOM_deleteCharacters(node,offset-1,offset);
+            offset--;
+            str = "\"";
+        }
+
+        if (node.nodeType == Node.ELEMENT_NODE) {
+            var emptyTextNode = DOM_createTextNode(document,"");
+            if (offset >= node.childNodes.length)
+                DOM_appendChild(node,emptyTextNode);
+            else
+                DOM_insertBefore(node,emptyTextNode,node.childNodes[offset]);
+            node = emptyTextNode;
+            offset = 0;
+        }
+
+        if (str == " ")
+            DOM_insertCharacters(node,offset,nbsp);
+        else
+            DOM_insertCharacters(node,offset,str);
+
+                // must be done *after* inserting the text
+        if (!allowNoParagraph) {
+            switch (node.parentNode._type) {
+            case HTML_CAPTION:
+            case HTML_FIGCAPTION:
+                // Do nothing
+                break;
+            default:
+                Hierarchy_ensureInlineNodesInParagraph(node,true);
+                break;
+            }
+        }
+
+        offset += str.length;
+
+        pos = new Position(node,offset);
+        Position_trackWhileExecuting([pos],function() {
+            Formatting_mergeWithNeighbours(pos.node,Formatting_MERGEABLE_INLINE);
+        });
+
+        Cursor_set(pos.node,pos.offset);
+        Range_trackWhileExecuting(Selection_get(),function() {
+            Cursor_updateBRAtEndOfParagraph(pos.node);
+        });
+
+        Selection_update();
+        Cursor_ensureCursorVisible();
+    }
+
+    // public
+    Cursor_deleteCharacter = function()
+    {
+        if (UndoManager_groupType() != "Delete text")
+            UndoManager_newGroup("Delete text",checkNbsp);
+
+        Selection_preferElementPositions();
+        var selRange = Selection_get();
+        if (selRange == null)
+            return;
+
+        if (!Range_isEmpty(selRange)) {
+            Selection_deleteContents(true);
+        }
+        else {
+            var currentPos = selRange.start;
+
+            // Special case of pressing backspace after a table, figure, or TOC
+            var back = Position_closestMatchBackwards(currentPos,Position_okForMovement);
+            if ((back != null) && (back.node.nodeType == Node.ELEMENT_NODE) && (back.offset > 0)) {
+                var prevNode = back.node.childNodes[back.offset-1];
+                if (isSpecialBlockNode(prevNode)) {
+                    var p = DOM_createElement(document,"P");
+                    DOM_insertBefore(prevNode.parentNode,p,prevNode);
+                    DOM_deleteNode(prevNode);
+                    Cursor_updateBRAtEndOfParagraph(p);
+                    Cursor_set(p,0);
+                    Cursor_ensureCursorVisible();
+                    return;
+                }
+                if (prevNode._type == HTML_A) {
+                    Cursor_set(back.node,back.offset-1);
+                    Selection_preserveWhileExecuting(function() {
+                        DOM_deleteNode(prevNode);
+                    });
+                    return;
+                }
+            }
+
+            currentPos = Position_preferTextPosition(currentPos);
+            var prevPos = Position_prevMatch(currentPos,Position_okForMovement);
+            if (prevPos != null) {
+                var startBlock = firstBlockAncestor(Position_closestActualNode(prevPos));
+                var endBlock = firstBlockAncestor(Position_closestActualNode(selRange.end));
+                if ((startBlock != endBlock) &&
+                    isParagraphNode(startBlock) && !nodeHasContent(startBlock)) {
+                    DOM_deleteNode(startBlock);
+                    Cursor_set(selRange.end.node,selRange.end.offset)
+                }
+                else {
+                    var range = new Range(prevPos.node,prevPos.offset,
+                                          selRange.end.node,selRange.end.offset);
+                    Selection_deleteRangeContents(range,true);
+                }
+            }
+        }
+
+        selRange = Selection_get();
+        if (selRange != null)
+            spaceToNbsp(selRange.end);
+        Selection_update();
+        Cursor_ensureCursorVisible();
+
+        function firstBlockAncestor(node)
+        {
+            while (isInlineNode(node))
+                node = node.parentNode;
+            return node;
+        }
+    }
+
+    // public
+    Cursor_enterPressed = function()
+    {
+        UndoManager_newGroup("New paragraph");
+
+        Selection_preferElementPositions();
+        var selRange = Selection_get();
+        if (selRange == null)
+            return;
+
+        Range_trackWhileExecuting(selRange,function() {
+            if (!Range_isEmpty(selRange))
+                Selection_deleteContents(true);
+        });
+
+        // Are we inside a figure or table caption? If so, put an empty paragraph directly after it
+        var inCaption = false;
+        var inFigCaption = false;
+        var closestNode = Position_closestActualNode(selRange.start);
+        for (var ancestor = closestNode; ancestor != null; ancestor = ancestor.parentNode) {
+            switch (ancestor._type) {
+            case HTML_CAPTION:
+                inCaption = true;
+                break;
+            case HTML_FIGCAPTION:
+                inFigCaption = true;
+                break;
+            case HTML_TABLE:
+            case HTML_FIGURE:
+                if ((inCaption && (ancestor._type == HTML_TABLE)) ||
+                    (inFigCaption && (ancestor._type == HTML_FIGURE))) {
+                    var p = DOM_createElement(document,"P");
+                    DOM_insertBefore(ancestor.parentNode,p,ancestor.nextSibling);
+                    Cursor_updateBRAtEndOfParagraph(p);
+                    Selection_set(p,0,p,0);
+                    return;
+                }
+                break;
+            }
+        }
+
+        var check = Position_preferElementPosition(selRange.start);
+        if (check.node.nodeType == Node.ELEMENT_NODE) {
+            var before = check.node.childNodes[check.offset-1];
+            var after = check.node.childNodes[check.offset];
+            if (((before != null) && isSpecialBlockNode(before)) ||
+                ((after != null) && isSpecialBlockNode(after))) {
+                var p = DOM_createElement(document,"P");
+                DOM_insertBefore(check.node,p,check.node.childNodes[check.offset]);
+                Cursor_updateBRAtEndOfParagraph(p);
+                Cursor_set(p,0);
+                Cursor_ensureCursorVisible();
+                return;
+            }
+        }
+
+        Range_trackWhileExecuting(selRange,function() {
+            Range_ensureInlineNodesInParagraph(selRange);
+            Range_ensureValidHierarchy(selRange);
+        });
+
+        var pos = selRange.start;
+
+        var detail = Range_detail(selRange);
+        switch (detail.startParent._type) {
+        case HTML_OL:
+        case HTML_UL: {
+            var li = DOM_createElement(document,"LI");
+            DOM_insertBefore(detail.startParent,li,detail.startChild);
+
+            Cursor_set(li,0);
+            Cursor_ensureCursorVisible();
+            return;
+        }
+        }
+
+        if (isAutoCorrectNode(pos.node)) {
+            pos = Position_preferTextPosition(pos);
+            selRange.start = selRange.end = pos;
+        }
+
+        Range_trackWhileExecuting(selRange,function() {
+
+            // If we're directly in a container node, add a paragraph, so we have something to
+            // split.
+            if (isContainerNode(pos.node) && (pos.node._type != HTML_LI)) {
+                var p = DOM_createElement(document,"P");
+                DOM_insertBefore(pos.node,p,pos.node.childNodes[pos.offset]);
+                pos = new Position(p,0);
+            }
+
+            var blockToSplit = getBlockToSplit(pos);
+            var stopAt = blockToSplit.parentNode;
+
+            if (positionAtStartOfHeading(pos)) {
+                var container = getContainerOrParagraph(pos.node);
+                pos = new Position(container,0);
+                pos = Formatting_movePreceding(pos,function(n) { return (n == stopAt); },true);
+            }
+            else if (pos.node.nodeType == Node.TEXT_NODE) {
+                pos = Formatting_splitTextAfter(pos,function(n) { return (n == stopAt); },true);
+            }
+            else {
+                pos = Formatting_moveFollowing(pos,function(n) { return (n == stopAt); },true);
+            }
+        });
+
+        Cursor_set(pos.node,pos.offset);
+        selRange = Selection_get();
+
+        Range_trackWhileExecuting(selRange,function() {
+            if ((pos.node.nodeType == Node.TEXT_NODE) && (pos.node.nodeValue.length == 0)) {
+                DOM_deleteNode(pos.node);
+            }
+
+            var detail = Range_detail(selRange);
+
+            // If a preceding paragraph has become empty as a result of enter being pressed
+            // while the cursor was in it, then update the BR at the end of the paragraph
+            var start = detail.startChild ? detail.startChild : detail.startParent;
+            for (var ancestor = start; ancestor != null; ancestor = ancestor.parentNode) {
+                var prev = ancestor.previousSibling;
+                if ((prev != null) && isParagraphNode(prev) && !nodeHasContent(prev)) {
+                    DOM_deleteAllChildren(prev);
+                    Cursor_updateBRAtEndOfParagraph(prev);
+                    break;
+                }
+                else if ((prev != null) && (prev._type == HTML_LI) && !nodeHasContent(prev)) {
+                    var next;
+                    for (var child = prev.firstChild; child != null; child = next) {
+                        next = child.nextSibling;
+                        if (isWhitespaceTextNode(child))
+                            DOM_deleteNode(child);
+                        else
+                            Cursor_updateBRAtEndOfParagraph(child);
+                    }
+                    break;
+                }
+            }
+
+            for (var ancestor = start; ancestor != null; ancestor = ancestor.parentNode) {
+
+                if (isParagraphNode(ancestor)) {
+                    var nextSelector = Styles_nextSelectorAfter(ancestor);
+                    if (nextSelector != null) {
+                        var nextElementName = null;
+                        var nextClassName = null;
+
+
+                        var dotIndex = nextSelector.indexOf(".");
+                        if (dotIndex >= 0) {
+                            nextElementName = nextSelector.substring(0,dotIndex);
+                            nextClassName = nextSelector.substring(dotIndex+1);
+                        }
+                        else {
+                            nextElementName = nextSelector;
+                        }
+
+                        ancestor = DOM_replaceElement(ancestor,nextElementName);
+                        DOM_removeAttribute(ancestor,"id");
+                        DOM_setAttribute(ancestor,"class",nextClassName);
+                    }
+                }
+
+                if (isParagraphNode(ancestor) && !nodeHasContent(ancestor)) {
+                    Cursor_updateBRAtEndOfParagraph(prev);
+                    break;
+                }
+                else if ((ancestor._type == HTML_LI) && !nodeHasContent(ancestor)) {
+                    DOM_deleteAllChildren(ancestor);
+                    break;
+                }
+            }
+
+            Cursor_updateBRAtEndOfParagraph(Range_singleNode(selRange));
+        });
+
+        Selection_set(selRange.start.node,selRange.start.offset,
+                      selRange.end.node,selRange.end.offset);
+        cursorX = null;
+        Cursor_ensureCursorVisible();
+
+        function getBlockToSplit(pos)
+        {
+            var blockToSplit = null;
+            for (var n = pos.node; n != null; n = n.parentNode) {
+                if (n._type == HTML_LI) {
+                    blockToSplit = n;
+                    break;
+                }
+            }
+            if (blockToSplit == null) {
+                blockToSplit = pos.node;
+                while (isInlineNode(blockToSplit))
+                    blockToSplit = blockToSplit.parentNode;
+            }
+            return blockToSplit;
+        }
+
+        function getContainerOrParagraph(node)
+        {
+            while ((node != null) && isInlineNode(node))
+                node = node.parentNode;
+            return node;
+        }
+
+        function positionAtStartOfHeading(pos)
+        {
+            var container = getContainerOrParagraph(pos.node);
+            if (isHeadingNode(container)) {
+                var startOffset = 0;
+                if (isOpaqueNode(container.firstChild))
+                    startOffset = 1;
+                var range = new Range(container,startOffset,pos.node,pos.offset);
+                return !Range_hasContent(range);
+            }
+            else
+                return false;
+        }
+    }
+
+    Cursor_getPrecedingWord = function() {
+        var selRange = Selection_get();
+        if ((selRange == null) && !Range_isEmpty(selRange))
+            return "";
+
+        var node = selRange.start.node;
+        var offset = selRange.start.offset;
+        if (node.nodeType != Node.TEXT_NODE)
+            return "";
+
+        return node.nodeValue.substring(0,offset);
+    }
+
+    Cursor_getAdjacentNodeWithType = function(type)
+    {
+        var selRange = Selection_get();
+        var position = selRange.start;
+        while (position != null) {
+            var node = Position_closestActualNode(position);
+            for (; node != null; node = node.parentNode) {
+                if (node._type == type)
+                    return node;
+            }
+            position = Position_prev(position);
+        }
+        return null;
+    }
+
+    Cursor_getLinkProperties = function()
+    {
+        var a = Cursor_getAdjacentNodeWithType(HTML_A);
+        if (a == null)
+            return null;
+
+        return { href: a.getAttribute("href"),
+                 text: getNodeText(a) };
+    }
+
+    Cursor_setLinkProperties = function(properties)
+    {
+        var a = Cursor_getAdjacentNodeWithType(HTML_A);
+        if (a == null)
+            return null;
+
+        Selection_preserveWhileExecuting(function() {
+            DOM_setAttribute(a,"href",properties.href);
+            DOM_deleteAllChildren(a);
+            DOM_appendChild(a,DOM_createTextNode(document,properties.text));
+        });
+    }
+
+    Cursor_setReferenceTarget = function(itemId)
+    {
+        var a = Cursor_getAdjacentNodeWithType(HTML_A);
+        if (a != null)
+            Outline_setReferenceTarget(a,itemId);
+    }
+
+    // Deletes the current selection contents and ensures that the cursor is located directly
+    // inside the nearest container element, i.e. not inside a paragraph or inline node. This
+    // is intended for preventing things like inserting a table of contants inside a heading
+    Cursor_makeContainerInsertionPoint = function()
+    {
+        var selRange = Selection_get();
+        if (selRange == null)
+            return;
+
+        if (!Range_isEmpty(selRange)) {
+            Selection_deleteContents();
+            selRange = Selection_get();
+        }
+
+        var parent;
+        var previousSibling;
+        var nextSibling;
+
+        if (selRange.start.node.nodeType == Node.ELEMENT_NODE) {
+            parent = selRange.start.node;
+            nextSibling = selRange.start.node.childNodes[selRange.start.offset];
+        }
+        else {
+            if (selRange.start.offset > 0)
+                Formatting_splitTextBefore(selRange.start);
+            parent = selRange.start.node.parentNode;
+            nextSibling = selRange.start.node;
+        }
+
+        var offset = DOM_nodeOffset(nextSibling,parent);
+
+        if (isContainerNode(parent)) {
+            Cursor_set(parent,offset);
+            return;
+        }
+
+        if ((offset > 0) && isItemNumber(parent.childNodes[offset-1]))
+            offset--;
+
+        Formatting_moveFollowing(new Position(parent,offset),isContainerNode);
+        Formatting_movePreceding(new Position(parent,offset),isContainerNode);
+
+        offset = 0;
+        while (!isContainerNode(parent)) {
+            var old = parent;
+            offset = DOM_nodeOffset(parent);
+            parent = parent.parentNode;
+            DOM_deleteNode(old);
+        }
+
+        Cursor_set(parent,offset);
+        cursorX = null;
+    }
+
+    Cursor_set = function(node,offset,keepCursorX)
+    {
+        Selection_set(node,offset,node,offset);
+        if (!keepCursorX)
+            cursorX = null;
+    }
+
+    function insertNote(className,content)
+    {
+        var footnote = DOM_createElement(document,"span");
+        DOM_setAttribute(footnote,"class",className);
+        DOM_appendChild(footnote,DOM_createTextNode(document,content));
+
+        var range = Selection_get();
+        Formatting_splitAroundSelection(range,false);
+
+        var pos = Position_preferElementPosition(range.start);
+
+        DOM_insertBefore(pos.node,footnote,pos.node.childNodes[pos.offset]);
+        Selection_set(footnote,0,footnote,footnote.childNodes.length);
+        Cursor_updateBRAtEndOfParagraph(footnote);
+    }
+
+    Cursor_insertFootnote = function(content)
+    {
+        insertNote("footnote",content);
+    }
+
+    Cursor_insertEndnote = function(content)
+    {
+        insertNote("endnote",content);
+    }
+
+})();

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/DOM.js
----------------------------------------------------------------------
diff --git a/Editor/src/DOM.js b/Editor/src/DOM.js
new file mode 100644
index 0000000..723f20e
--- /dev/null
+++ b/Editor/src/DOM.js
@@ -0,0 +1,963 @@
+// Copyright 2011-2014 UX Productivity Pty Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Helper functions
+var DOM_assignNodeIds;
+
+// Primitive node creation operations
+var DOM_createElement;
+var DOM_createElementNS;
+var DOM_createTextNode;
+var DOM_createComment;
+var DOM_cloneNode;
+
+// Primitive and high-level node mutation operations
+var DOM_appendChild;
+var DOM_insertBefore;
+var DOM_deleteNode;
+var DOM_setAttribute;
+var DOM_setAttributeNS;
+var DOM_removeAttribute;
+var DOM_removeAttributeNS;
+var DOM_setStyleProperties;
+var DOM_insertCharacters;
+var DOM_moveCharacters;
+var DOM_deleteCharacters;
+var DOM_setNodeValue;
+
+// High-level DOM operations
+var DOM_getAttribute;
+var DOM_getAttributeNS;
+var DOM_getStringAttribute;
+var DOM_getStringAttributeNS;
+var DOM_getStyleProperties;
+var DOM_deleteAllChildren;
+var DOM_shallowCopyElement;
+var DOM_replaceElement;
+var DOM_wrapNode;
+var DOM_wrapSiblings;
+var DOM_mergeWithNextSibling;
+var DOM_nodesMergeable;
+var DOM_replaceCharacters;
+var DOM_addTrackedPosition;
+var DOM_removeTrackedPosition;
+var DOM_removeAdjacentWhitespace;
+var DOM_documentHead;
+var DOM_ensureUniqueIds;
+var DOM_nodeOffset;
+var DOM_maxChildOffset;
+var DOM_ignoreMutationsWhileExecuting;
+var DOM_getIgnoreMutations;
+var DOM_addListener;
+var DOM_removeListener;
+var DOM_Listener;
+
+(function() {
+
+    var nextNodeId = 0;
+    var nodeData = new Object();
+    var ignoreMutations = 0;
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //                                                                                            //
+    //                                    DOM Helper Functions                                    //
+    //                                                                                            //
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+    function addUndoAction()
+    {
+        if (window.undoSupported)
+            UndoManager_addAction.apply(null,arrayCopy(arguments));
+    }
+
+    function assignNodeId(node)
+    {
+        if (node._nodeId != null)
+            throw new Error(node+" already has id");
+        node._nodeId = nextNodeId++;
+        node._type = ElementTypes[node.nodeName];
+        return node;
+    }
+
+    function checkNodeId(node)
+    {
+        if (node._nodeId == null)
+            throw new Error(node.nodeName+" lacks _nodeId");
+    }
+
+    // public
+    DOM_assignNodeIds = function(root)
+    {
+        if (root._nodeId != null)
+            throw new Error(root+" already has id");
+        recurse(root);
+        return;
+
+        function recurse(node) {
+            node._nodeId = nextNodeId++;
+            node._type = ElementTypes[node.nodeName];
+            for (var child = node.firstChild; child != null; child = child.nextSibling)
+                recurse(child);
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //                                                                                            //
+    //                                  Primitive DOM Operations                                  //
+    //                                                                                            //
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /*
+
+      The following functions are considered "primitive", in that they are the core functions
+      through which all manipulation of the DOM ultimately occurs. All other DOM functions call
+      these, either directly or indirectly, instead of making direct method calls on node objects.
+      These functions are divided into two categories: node creation and mode mutation.
+
+      The creation functions are as follows:
+
+      * createElement(document,elementName)
+      * createElementNS(document,namespaceURI,qualifiedName)
+      * createTextNode(document,data)
+      * createComment(document,data)
+      * cloneNode(original,deep,noIdAttr)
+
+      The purpose of these is to ensure that a unique _nodeId value is assigned to each node object,
+      which is needed for using the NodeSet and NodeMap classes. All nodes in a document must have
+      this set; we use our own functions for this because DOM provides no other way of uniquely
+      identifying nodes in a way that allows them to be stored in a hash table.
+
+      The mutation functions are as follows:
+
+      * insertBeforeInternal(parent,newChild,refChild)
+      * deleteNodeInternal(node,deleteDescendantData)
+      * setAttribute(element,name,value)
+      * setAttributeNS(element,namespaceURI,qualifiedName,value)
+      * setStyleProperties(element,properties)
+      * insertCharacters(textNode,offset,characters)
+      * deleteCharacters(textNode,startOffset,endOffset)
+      * moveCharacters(srcTextNode,srcStartOffset,srcEndOffset,destTextNode,destOffset)
+      * setNodeValue(textNode,value)
+
+      These functions exist to allow us to record undo information. We can't use DOM mutation events
+      for this purpose they're not fully supported in WebKit.
+
+      Every time a mutation operation is performed on a node, we add an action to the undo stack
+      corresponding to the inverse of that operaton, i.e. an action that undoes the operaton. It
+      is absolutely critical that all changes to a DOM node go through these functions, regardless
+      of whether or not the node currently resides in the tree. This ensures that the undo history
+      is able to correctly revert the tree to the same state that it was in at the relevant point
+      in time.
+
+      By routing all DOM modifications through these few functions, virtually all of the other
+      javascript code can be ignorant of the undo manager, provided the only state they change is
+      in the DOM. Parts of the code which maintain their own state about the document, such as the
+      style manager, must implement their own undo-compliant state manipulation logic.
+
+      *** IMPORTANT ***
+
+      Just in case it isn't already clear, you must *never* make direct calls to methods like
+      appendChild() and createElement() on the node objects themselves. Doing so will result in
+      subtle and probably hard-to-find bugs. As far as all javascript code for UX Write is
+      concerned, consider the public functions defined in this file to be the DOM API. You can use
+      check-dom-methods.sh to search for any cases where this rule has been violated.
+
+      */
+
+    // public
+    DOM_createElement = function(document,elementName)
+    {
+        return assignNodeId(document.createElement(elementName)); // check-ok
+    }
+
+    // public
+    DOM_createElementNS = function(document,namespaceURI,qualifiedName)
+    {
+        return assignNodeId(document.createElementNS(namespaceURI,qualifiedName)); // check-ok
+    }
+
+    // public
+    DOM_createTextNode = function(document,data)
+    {
+        return assignNodeId(document.createTextNode(data)); // check-ok
+    }
+
+    // public
+    DOM_createComment = function(document,data)
+    {
+        return assignNodeId(document.createComment(data)); // check-ok
+    }
+
+    // public
+    DOM_cloneNode = function(original,deep,noIdAttr)
+    {
+        var clone = original.cloneNode(deep); // check-ok
+        DOM_assignNodeIds(clone);
+        if (noIdAttr)
+            clone.removeAttribute("id"); // check-ok
+        return clone;
+    }
+
+    function insertBeforeInternal(parent,newChild,refChild)
+    {
+        if (newChild.parentNode == null) {
+            addUndoAction(deleteNodeInternal,newChild)
+        }
+        else {
+            var oldParent = newChild.parentNode;
+            var oldNext = newChild.nextSibling;
+            addUndoAction(insertBeforeInternal,oldParent,newChild,oldNext);
+        }
+
+        parent.insertBefore(newChild,refChild); // check-ok
+    }
+
+    function deleteNodeInternal(node,deleteDescendantData)
+    {
+        checkNodeId(node);
+
+        addUndoAction(insertBeforeInternal,node.parentNode,node,node.nextSibling);
+
+        if (node.parentNode == null)
+            throw new Error("Undo delete "+nodeString(node)+": parent is null");
+        node.parentNode.removeChild(node); // check-ok
+
+        // Delete all data associated with the node. This is not preserved across undo/redo;
+        // currently the only thing we are using this data for is tracked positions, and we
+        // are going to be recording undo information for the selection separately, so this is
+        // not a problem.
+        if (deleteDescendantData)
+            deleteNodeDataRecursive(node);
+        else
+            deleteNodeData(node);
+
+        return;
+
+        function deleteNodeData(current)
+        {
+            delete nodeData[current._nodeId];
+        }
+
+        function deleteNodeDataRecursive(current)
+        {
+            deleteNodeData(current);
+            for (var child = current.firstChild; child != null; child = child.nextSibling)
+                deleteNodeDataRecursive(child);
+        }
+    }
+
+    // public
+    DOM_setAttribute = function(element,name,value)
+    {
+        if (element.hasAttribute(name))
+            addUndoAction(DOM_setAttribute,element,name,element.getAttribute(name));
+        else
+            addUndoAction(DOM_setAttribute,element,name,null);
+
+        if (value == null)
+            element.removeAttribute(name); // check-ok
+        else
+            element.setAttribute(name,value); // check-ok
+    }
+
+    // public
+    DOM_setAttributeNS = function(element,namespaceURI,qualifiedName,value)
+    {
+        var localName = qualifiedName.replace(/^.*:/,"");
+        if (element.hasAttributeNS(namespaceURI,localName)) {
+            var oldValue = element.getAttributeNS(namespaceURI,localName);
+            var oldQName = element.getAttributeNodeNS(namespaceURI,localName).nodeName; // check-ok
+            addUndoAction(DOM_setAttributeNS,element,namespaceURI,oldQName,oldValue)
+        }
+        else {
+            addUndoAction(DOM_setAttributeNS,element,namespaceURI,localName,null);
+        }
+
+        if (value == null)
+            element.removeAttributeNS(namespaceURI,localName); // check-ok
+        else
+            element.setAttributeNS(namespaceURI,qualifiedName,value); // check-ok
+    }
+
+    // public
+    DOM_setStyleProperties = function(element,properties)
+    {
+        if (Object.getOwnPropertyNames(properties).length == 0)
+            return;
+
+        if (element.hasAttribute("style"))
+            addUndoAction(DOM_setAttribute,element,"style",element.getAttribute("style"));
+        else
+            addUndoAction(DOM_setAttribute,element,"style",null);
+
+        for (var name in properties)
+            element.style.setProperty(name,properties[name]); // check-ok
+
+        if (element.getAttribute("style") == "")
+            element.removeAttribute("style"); // check-ok
+    }
+
+    // public
+    DOM_insertCharacters = function(textNode,offset,characters)
+    {
+        if (textNode.nodeType != Node.TEXT_NODE)
+            throw new Error("DOM_insertCharacters called on non-text node");
+        if ((offset < 0) || (offset > textNode.nodeValue.length))
+            throw new Error("DOM_insertCharacters called with invalid offset");
+        trackedPositionsForNode(textNode).forEach(function (position) {
+            if (position.offset > offset)
+                position.offset += characters.length;
+        });
+        textNode.nodeValue = textNode.nodeValue.slice(0,offset) +
+                             characters +
+                             textNode.nodeValue.slice(offset);
+        var startOffset = offset;
+        var endOffset = offset + characters.length;
+        addUndoAction(DOM_deleteCharacters,textNode,startOffset,endOffset);
+    }
+
+    // public
+    DOM_deleteCharacters = function(textNode,startOffset,endOffset)
+    {
+        if (textNode.nodeType != Node.TEXT_NODE)
+            throw new Error("DOM_deleteCharacters called on non-text node "+nodeString(textNode));
+        if (endOffset == null)
+            endOffset = textNode.nodeValue.length;
+        if (endOffset < startOffset)
+            throw new Error("DOM_deleteCharacters called with invalid start/end offset");
+        trackedPositionsForNode(textNode).forEach(function (position) {
+            var deleteCount = endOffset - startOffset;
+            if ((position.offset > startOffset) && (position.offset < endOffset))
+                position.offset = startOffset;
+            else if (position.offset >= endOffset)
+                position.offset -= deleteCount;
+        });
+
+        var removed = textNode.nodeValue.slice(startOffset,endOffset);
+        addUndoAction(DOM_insertCharacters,textNode,startOffset,removed);
+
+        textNode.nodeValue = textNode.nodeValue.slice(0,startOffset) +
+                             textNode.nodeValue.slice(endOffset);
+    }
+
+    // public
+    DOM_moveCharacters = function(srcTextNode,srcStartOffset,srcEndOffset,destTextNode,destOffset,
+                                  excludeStartPos,excludeEndPos)
+    {
+        if (srcTextNode == destTextNode)
+            throw new Error("src and dest text nodes cannot be the same");
+        if (srcStartOffset > srcEndOffset)
+            throw new Error("Invalid src range "+srcStartOffset+" - "+srcEndOffset);
+        if (srcStartOffset < 0)
+            throw new Error("srcStartOffset < 0");
+        if (srcEndOffset > srcTextNode.nodeValue.length)
+            throw new Error("srcEndOffset beyond end of src length");
+        if (destOffset < 0)
+            throw new Error("destOffset < 0");
+        if (destOffset > destTextNode.nodeValue.length)
+            throw new Error("destOffset beyond end of dest length");
+
+        var length = srcEndOffset - srcStartOffset;
+
+        addUndoAction(DOM_moveCharacters,destTextNode,destOffset,destOffset+length,
+                      srcTextNode,srcStartOffset,excludeStartPos,excludeEndPos);
+
+        trackedPositionsForNode(destTextNode).forEach(function (pos) {
+            var startMatch = excludeStartPos ? (pos.offset > destOffset)
+                                             : (pos.offset >= destOffset);
+            if (startMatch)
+                pos.offset += length;
+        });
+        trackedPositionsForNode(srcTextNode).forEach(function (pos) {
+
+            var startMatch = excludeStartPos ? (pos.offset > srcStartOffset)
+                                             : (pos.offset >= srcStartOffset);
+            var endMatch = excludeEndPos ? (pos.offset < srcEndOffset)
+                                         : (pos.offset <= srcEndOffset);
+
+            if (startMatch && endMatch) {
+                pos.node = destTextNode;
+                pos.offset = destOffset + (pos.offset - srcStartOffset);
+            }
+            else if (pos.offset >= srcEndOffset) {
+                pos.offset -= length;
+            }
+        });
+        var extract = srcTextNode.nodeValue.substring(srcStartOffset,srcEndOffset);
+        srcTextNode.nodeValue = srcTextNode.nodeValue.slice(0,srcStartOffset) +
+                                srcTextNode.nodeValue.slice(srcEndOffset);
+        destTextNode.nodeValue = destTextNode.nodeValue.slice(0,destOffset) +
+                                 extract +
+                                 destTextNode.nodeValue.slice(destOffset);
+    }
+
+    // public
+    DOM_setNodeValue = function(textNode,value)
+    {
+        if (textNode.nodeType != Node.TEXT_NODE)
+            throw new Error("DOM_setNodeValue called on non-text node");
+        trackedPositionsForNode(textNode).forEach(function (position) {
+            position.offset = 0;
+        });
+        var oldValue = textNode.nodeValue;
+        addUndoAction(DOM_setNodeValue,textNode,oldValue);
+        textNode.nodeValue = value;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //                                                                                            //
+    //                                  High-level DOM Operations                                 //
+    //                                                                                            //
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+    function appendChildInternal(parent,newChild)
+    {
+        insertBeforeInternal(parent,newChild,null);
+    }
+
+    // public
+    DOM_appendChild = function(node,child)
+    {
+        return DOM_insertBefore(node,child,null);
+    }
+
+    // public
+    DOM_insertBefore = function(parent,child,nextSibling)
+    {
+        var newOffset;
+        if (nextSibling != null)
+            newOffset = DOM_nodeOffset(nextSibling);
+        else
+            newOffset = parent.childNodes.length;
+
+        var oldParent = child.parentNode;
+        if (oldParent != null) { // already in tree
+            var oldOffset = DOM_nodeOffset(child);
+
+            if ((oldParent == parent) && (newOffset > oldOffset))
+                newOffset--;
+
+            trackedPositionsForNode(oldParent).forEach(function (position) {
+                if (position.offset > oldOffset) {
+                    position.offset--;
+                }
+                else if (position.offset == oldOffset) {
+                    position.node = parent;
+                    position.offset = newOffset;
+                }
+            });
+        }
+
+        var result = insertBeforeInternal(parent,child,nextSibling);
+        trackedPositionsForNode(parent).forEach(function (position) {
+            if (position.offset > newOffset)
+                position.offset++;
+        });
+        return result;
+    }
+
+    // public
+    DOM_deleteNode = function(node)
+    {
+        if (node.parentNode == null) // already deleted
+            return;
+        adjustPositionsRecursive(node);
+        deleteNodeInternal(node,true);
+
+        function adjustPositionsRecursive(current)
+        {
+            for (var child = current.firstChild; child != null; child = child.nextSibling)
+                adjustPositionsRecursive(child);
+
+            trackedPositionsForNode(current.parentNode).forEach(function (position) {
+                var offset = DOM_nodeOffset(current);
+                if (offset < position.offset) {
+                    position.offset--;
+                }
+            });
+            trackedPositionsForNode(current).forEach(function (position) {
+                var offset = DOM_nodeOffset(current);
+                position.node = current.parentNode;
+                position.offset = offset;
+            });
+        }
+    }
+
+    // public
+    DOM_removeAttribute = function(element,name,value)
+    {
+        DOM_setAttribute(element,name,null);
+    }
+
+    // public
+    DOM_removeAttributeNS = function(element,namespaceURI,localName)
+    {
+        DOM_setAttributeNS(element,namespaceURI,localName,null)
+    }
+
+    // public
+    DOM_getAttribute = function(element,name)
+    {
+        if (element.hasAttribute(name))
+            return element.getAttribute(name);
+        else
+            return null;
+    }
+
+    // public
+    DOM_getAttributeNS = function(element,namespaceURI,localName)
+    {
+        if (element.hasAttributeNS(namespaceURI,localName))
+            return element.getAttributeNS(namespaceURI,localName);
+        else
+            return null;
+    }
+
+    // public
+    DOM_getStringAttribute = function(element,name)
+    {
+        var value = element.getAttribute(name);
+        return (value == null) ? "" : value;
+    }
+
+    // public
+    DOM_getStringAttributeNS = function(element,namespaceURI,localName)
+    {
+        var value = element.getAttributeNS(namespaceURI,localName);
+        return (value == null) ? "" : value;
+    }
+
+    // public
+    DOM_getStyleProperties = function(node)
+    {
+        var properties = new Object();
+        if (node.nodeType == Node.ELEMENT_NODE) {
+            for (var i = 0; i < node.style.length; i++) {
+                var name = node.style[i];
+                var value = node.style.getPropertyValue(name);
+                properties[name] = value;
+            }
+        }
+        return properties;
+    }
+
+    // public
+    DOM_deleteAllChildren = function(parent)
+    {
+        while (parent.firstChild != null)
+            DOM_deleteNode(parent.firstChild);
+    }
+
+    // public
+    DOM_shallowCopyElement = function(element)
+    {
+        return DOM_cloneNode(element,false,true);
+    }
+
+    // public
+    DOM_removeNodeButKeepChildren = function(node)
+    {
+        if (node.parentNode == null)
+            throw new Error("Node "+nodeString(node)+" has no parent");
+        var offset = DOM_nodeOffset(node);
+        var childCount = node.childNodes.length;
+
+        trackedPositionsForNode(node.parentNode).forEach(function (position) {
+            if (position.offset > offset)
+                position.offset += childCount-1;
+        });
+
+        trackedPositionsForNode(node).forEach(function (position) {
+            position.node = node.parentNode;
+            position.offset += offset;
+        });
+
+        var parent = node.parentNode;
+        var nextSibling = node.nextSibling;
+        deleteNodeInternal(node,false);
+
+        while (node.firstChild != null) {
+            var child = node.firstChild;
+            insertBeforeInternal(parent,child,nextSibling);
+        }
+    }
+
+    // public
+    DOM_replaceElement = function(oldElement,newName)
+    {
+        var listeners = listenersForNode(oldElement);
+        var newElement = DOM_createElement(document,newName);
+        for (var i = 0; i < oldElement.attributes.length; i++) {
+            var name = oldElement.attributes[i].nodeName; // check-ok
+            var value = oldElement.getAttribute(name);
+            DOM_setAttribute(newElement,name,value);
+        }
+
+        var positions = arrayCopy(trackedPositionsForNode(oldElement));
+        if (positions != null) {
+            for (var i = 0; i < positions.length; i++) {
+                if (positions[i].node != oldElement)
+                    throw new Error("replaceElement: position with wrong node");
+                positions[i].node = newElement;
+            }
+        }
+
+        var parent = oldElement.parentNode;
+        var nextSibling = oldElement.nextSibling;
+        while (oldElement.firstChild != null)
+            appendChildInternal(newElement,oldElement.firstChild);
+        // Deletion must be done first so if it's a heading, the outline code picks up the change
+        // correctly. Otherwise, there could be two elements in the document with the same id at
+        // the same time.
+        deleteNodeInternal(oldElement,false);
+        insertBeforeInternal(parent,newElement,nextSibling);
+
+        for (var i = 0; i < listeners.length; i++)
+            listeners[i].afterReplaceElement(oldElement,newElement);
+
+        return newElement;
+    }
+
+    // public
+    DOM_wrapNode = function(node,elementName)
+    {
+        return DOM_wrapSiblings(node,node,elementName);
+    }
+
+    DOM_wrapSiblings = function(first,last,elementName)
+    {
+        var parent = first.parentNode;
+        var wrapper = DOM_createElement(document,elementName);
+
+        if (first.parentNode != last.parentNode)
+            throw new Error("first and last are not siblings");
+
+        if (parent != null) {
+            var firstOffset = DOM_nodeOffset(first);
+            var lastOffset = DOM_nodeOffset(last);
+            var nodeCount = lastOffset - firstOffset + 1;
+            trackedPositionsForNode(parent).forEach(function (position) {
+                if ((position.offset >= firstOffset) && (position.offset <= lastOffset+1)) {
+                    position.node = wrapper;
+                    position.offset -= firstOffset;
+                }
+                else if (position.offset > lastOffset+1) {
+                    position.offset -= (nodeCount-1);
+                }
+            });
+
+            insertBeforeInternal(parent,wrapper,first);
+        }
+
+        var end = last.nextSibling;
+        var current = first;
+        while (current != end) {
+            var next = current.nextSibling;
+            appendChildInternal(wrapper,current);
+            current = next;
+        }
+        return wrapper;
+    }
+
+    // public
+    DOM_mergeWithNextSibling = function(current,whiteList)
+    {
+        var parent = current.parentNode;
+        var next = current.nextSibling;
+
+        if ((next == null) || !DOM_nodesMergeable(current,next,whiteList))
+            return;
+
+        var currentLength = DOM_maxChildOffset(current);
+        var nextOffset = DOM_nodeOffset(next);
+
+        var lastChild = null;
+
+        if (current.nodeType == Node.ELEMENT_NODE) {
+            lastChild = current.lastChild;
+            DOM_insertBefore(current,next,null);
+            DOM_removeNodeButKeepChildren(next);
+        }
+        else {
+            DOM_insertCharacters(current,current.nodeValue.length,next.nodeValue);
+
+            trackedPositionsForNode(next).forEach(function (position) {
+                position.node = current;
+                position.offset = position.offset+currentLength;
+            });
+
+            trackedPositionsForNode(current.parentNode).forEach(function (position) {
+                if (position.offset == nextOffset) {
+                    position.node = current;
+                    position.offset = currentLength;
+                }
+            });
+
+            DOM_deleteNode(next);
+        }
+
+        if (lastChild != null)
+            DOM_mergeWithNextSibling(lastChild,whiteList);
+    }
+
+    // public
+    DOM_nodesMergeable = function(a,b,whiteList)
+    {
+        if ((a.nodeType == Node.TEXT_NODE) && (b.nodeType == Node.TEXT_NODE))
+            return true;
+        else if ((a.nodeType == Node.ELEMENT_NODE) && (b.nodeType == Node.ELEMENT_NODE))
+            return elementsMergableTypes(a,b);
+        else
+            return false;
+
+        function elementsMergableTypes(a,b)
+        {
+            if (whiteList["force"] && isParagraphNode(a) && isParagraphNode(b))
+                return true;
+            if ((a._type == b._type) &&
+                whiteList[a._type] &&
+                (a.attributes.length == b.attributes.length)) {
+                for (var i = 0; i < a.attributes.length; i++) {
+                    var attrName = a.attributes[i].nodeName; // check-ok
+                    if (a.getAttribute(attrName) != b.getAttribute(attrName))
+                        return false;
+                }
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+    function getDataForNode(node,create)
+    {
+        if (node._nodeId == null)
+            throw new Error("getDataForNode: node "+node.nodeName+" has no _nodeId property");
+        if ((nodeData[node._nodeId] == null) && create)
+            nodeData[node._nodeId] = new Object();
+        return nodeData[node._nodeId];
+    }
+
+    function trackedPositionsForNode(node)
+    {
+        var data = getDataForNode(node,false);
+        if ((data != null) && (data.trackedPositions != null)) {
+            // Sanity check
+            for (var i = 0; i < data.trackedPositions.length; i++) {
+                if (data.trackedPositions[i].node != node)
+                    throw new Error("Position "+data.trackedPositions[i]+" has wrong node");
+            }
+            return arrayCopy(data.trackedPositions);
+        }
+        else {
+            return [];
+        }
+    }
+
+    function listenersForNode(node)
+    {
+        var data = getDataForNode(node,false);
+        if ((data != null) && (data.listeners != null))
+            return data.listeners;
+        else
+            return [];
+    }
+
+    // public
+    DOM_replaceCharacters = function(textNode,startOffset,endOffset,replacement)
+    {
+        // Note that we do the insertion *before* the deletion so that the position is properly
+        // maintained, and ends up at the end of the replacement (unless it was previously at
+        // startOffset, in which case it will stay the same)
+        DOM_insertCharacters(textNode,startOffset,replacement);
+        DOM_deleteCharacters(textNode,startOffset+replacement.length,endOffset+replacement.length);
+    }
+
+    // public
+    DOM_addTrackedPosition = function(position)
+    {
+        var data = getDataForNode(position.node,true);
+        if (data.trackedPositions == null)
+            data.trackedPositions = new Array();
+        data.trackedPositions.push(position);
+    }
+
+    // public
+    DOM_removeTrackedPosition = function(position)
+    {
+        var data = getDataForNode(position.node,false);
+        if ((data == null) || (data.trackedPositions == null))
+            throw new Error("DOM_removeTrackedPosition: no registered positions for this node "+
+                            "("+position.node.nodeName+")");
+        for (var i = 0; i < data.trackedPositions.length; i++) {
+            if (data.trackedPositions[i] == position) {
+                data.trackedPositions.splice(i,1);
+                return;
+            }
+        }
+        throw new Error("DOM_removeTrackedPosition: position is not registered ("+
+                        data.trackedPositions.length+" others)");
+    }
+
+    // public
+    DOM_removeAdjacentWhitespace = function(node)
+    {
+        while ((node.previousSibling != null) && (isWhitespaceTextNode(node.previousSibling)))
+            DOM_deleteNode(node.previousSibling);
+        while ((node.nextSibling != null) && (isWhitespaceTextNode(node.nextSibling)))
+            DOM_deleteNode(node.nextSibling);
+    }
+
+    // public
+    DOM_documentHead = function(document)
+    {
+        var html = document.documentElement;
+        for (var child = html.firstChild; child != null; child = child.nextSibling) {
+            if (child._type == HTML_HEAD)
+                return child;
+        }
+        throw new Error("Document contains no HEAD element");
+    }
+
+    // public
+    DOM_ensureUniqueIds = function(root)
+    {
+        var ids = new Object();
+        var duplicates = new Array();
+
+        discoverDuplicates(root);
+        renameDuplicates();
+
+        return;
+
+        function discoverDuplicates(node)
+        {
+            if (node.nodeType != Node.ELEMENT_NODE)
+                return;
+
+            var id = node.getAttribute("id");
+            if ((id != null) && (id != "")) {
+                if (ids[id])
+                    duplicates.push(node);
+                else
+                    ids[id] = true;
+            }
+            for (var child = node.firstChild; child != null; child = child.nextSibling)
+                discoverDuplicates(child);
+        }
+
+        function renameDuplicates()
+        {
+            var nextNumberForPrefix = new Object();
+            for (var i = 0; i < duplicates.length; i++) {
+                var id = duplicates[i].getAttribute("id");
+                var prefix = id.replace(/[0-9]+$/,"");
+                var num = nextNumberForPrefix[prefix] ? nextNumberForPrefix[prefix] : 1;
+
+                var candidate;
+                do {
+                    candidate = prefix + num;
+                    num++;
+                } while (ids[candidate]);
+
+                DOM_setAttribute(duplicates[i],"id",candidate);
+                ids[candidate] = true;
+                nextNumberForPrefix[prefix] = num;
+            }
+        }
+    }
+
+    // public
+    DOM_nodeOffset = function(node,parent)
+    {
+        if ((node == null) && (parent != null))
+            return DOM_maxChildOffset(parent);
+        var offset = 0;
+        for (var n = node.parentNode.firstChild; n != node; n = n.nextSibling)
+            offset++;
+        return offset;
+    }
+
+    // public
+    DOM_maxChildOffset = function(node)
+    {
+        if (node.nodeType == Node.TEXT_NODE)
+            return node.nodeValue.length;
+        else if (node.nodeType == Node.ELEMENT_NODE)
+            return node.childNodes.length;
+        else
+            throw new Error("maxOffset: invalid node type ("+node.nodeType+")");
+    }
+
+    function incIgnoreMutations()
+    {
+        UndoManager_addAction(decIgnoreMutations);
+        ignoreMutations++;
+    }
+
+    function decIgnoreMutations()
+    {
+        UndoManager_addAction(incIgnoreMutations);
+        ignoreMutations--;
+        if (ignoreMutations < 0)
+            throw new Error("ignoreMutations is now negative");
+    }
+
+    // public
+    DOM_ignoreMutationsWhileExecuting = function(fun)
+    {
+        incIgnoreMutations();
+        try {
+            return fun();
+        }
+        finally {
+            decIgnoreMutations();
+        }
+    }
+
+    // public
+    DOM_getIgnoreMutations = function()
+    {
+        return ignoreMutations;
+    }
+
+    // public
+    DOM_addListener = function(node,listener)
+    {
+        var data = getDataForNode(node,true);
+        if (data.listeners == null)
+            data.listeners = [listener];
+        else
+            data.listeners.push(listener);
+    }
+
+    // public
+    DOM_removeListener = function(node,listener)
+    {
+        var list = listenersForNode(node);
+        var index = list.indexOf(listener);
+        if (index >= 0)
+            list.splice(index,1);
+    }
+
+    // public
+    function Listener()
+    {
+    }
+
+    Listener.prototype.afterReplaceElement = function(oldElement,newElement) {}
+
+    DOM_Listener = Listener;
+
+})();

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/Editor.js
----------------------------------------------------------------------
diff --git a/Editor/src/Editor.js b/Editor/src/Editor.js
new file mode 100644
index 0000000..88a6ce2
--- /dev/null
+++ b/Editor/src/Editor.js
@@ -0,0 +1,110 @@
+// Copyright 2011-2014 UX Productivity Pty Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var Editor_getBackMessages;
+var Editor_debug;
+var Editor_addOutlineItem;
+var Editor_updateOutlineItem;
+var Editor_removeOutlineItem;
+var Editor_outlineUpdated;
+var Editor_setCursor;
+var Editor_setSelectionHandles;
+var Editor_clearSelectionHandlesAndCursor;
+var Editor_setSelectionBounds;
+var Editor_updateAutoCorrect;
+var Editor_error;
+var debug;
+
+(function(){
+
+    var backMessages = new Array();
+
+    function addBackMessage()
+    {
+        backMessages.push(arrayCopy(arguments));
+        return null;
+    }
+
+    Editor_getBackMessages = function()
+    {
+        var result = JSON.stringify(backMessages);
+        backMessages = new Array();
+        return result;
+    };
+
+    Editor_debug = function(str)
+    {
+        addBackMessage("debug",str);
+    };
+
+    Editor_error = function(error,type)
+    {
+        if (type == null)
+            type = "";
+        addBackMessage("error",error.toString(),type);
+    };
+
+    Editor_addOutlineItem = function(itemId,type,title)
+    {
+        addBackMessage("addOutlineItem",itemId,type,title);
+    };
+
+    Editor_updateOutlineItem = function(itemId,title)
+    {
+        addBackMessage("updateOutlineItem",itemId,title);
+    };
+
+    Editor_removeOutlineItem = function(itemId)
+    {
+        addBackMessage("removeOutlineItem",itemId);
+    };
+
+    Editor_outlineUpdated = function()
+    {
+        addBackMessage("outlineUpdated");
+    };
+
+    Editor_setCursor = function(x,y,width,height)
+    {
+        addBackMessage("setCursor",x,y,width,height);
+    };
+
+    Editor_setSelectionHandles = function(x1,y1,height1,x2,y2,height2)
+    {
+        addBackMessage("setSelectionHandles",x1,y1,height1,x2,y2,height2);
+    };
+
+    Editor_setTableSelection = function(x,y,width,height)
+    {
+        addBackMessage("setTableSelection",x,y,width,height);
+    };
+
+    Editor_setSelectionBounds = function(left,top,right,bottom)
+    {
+        addBackMessage("setSelectionBounds",left,top,right,bottom);
+    };
+
+    Editor_clearSelectionHandlesAndCursor = function()
+    {
+        addBackMessage("clearSelectionHandlesAndCursor");
+    };
+
+    Editor_updateAutoCorrect = function()
+    {
+        addBackMessage("updateAutoCorrect");
+    };
+
+    debug = Editor_debug;
+
+})();

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/ElementTypes.js
----------------------------------------------------------------------
diff --git a/Editor/src/ElementTypes.js b/Editor/src/ElementTypes.js
new file mode 100644
index 0000000..06702a2
--- /dev/null
+++ b/Editor/src/ElementTypes.js
@@ -0,0 +1,344 @@
+// Automatically generated from elements.txt
+ElementTypes = {
+  "#DOCUMENT": 1,
+  "#document": 1,
+  "#TEXT": 2,
+  "#text": 2,
+  "#COMMENT": 3,
+  "#comment": 3,
+  "A": 4,
+  "a": 4,
+  "ABBR": 5,
+  "abbr": 5,
+  "ADDRESS": 6,
+  "address": 6,
+  "AREA": 7,
+  "area": 7,
+  "ARTICLE": 8,
+  "article": 8,
+  "ASIDE": 9,
+  "aside": 9,
+  "AUDIO": 10,
+  "audio": 10,
+  "B": 11,
+  "b": 11,
+  "BASE": 12,
+  "base": 12,
+  "BDI": 13,
+  "bdi": 13,
+  "BDO": 14,
+  "bdo": 14,
+  "BLOCKQUOTE": 15,
+  "blockquote": 15,
+  "BODY": 16,
+  "body": 16,
+  "BR": 17,
+  "br": 17,
+  "BUTTON": 18,
+  "button": 18,
+  "CANVAS": 19,
+  "canvas": 19,
+  "CAPTION": 20,
+  "caption": 20,
+  "CITE": 21,
+  "cite": 21,
+  "CODE": 22,
+  "code": 22,
+  "COL": 23,
+  "col": 23,
+  "COLGROUP": 24,
+  "colgroup": 24,
+  "COMMAND": 25,
+  "command": 25,
+  "DATA": 26,
+  "data": 26,
+  "DATALIST": 27,
+  "datalist": 27,
+  "DD": 28,
+  "dd": 28,
+  "DEL": 29,
+  "del": 29,
+  "DETAILS": 30,
+  "details": 30,
+  "DFN": 31,
+  "dfn": 31,
+  "DIALOG": 32,
+  "dialog": 32,
+  "DIV": 33,
+  "div": 33,
+  "DL": 34,
+  "dl": 34,
+  "DT": 35,
+  "dt": 35,
+  "EM": 36,
+  "em": 36,
+  "EMBED": 37,
+  "embed": 37,
+  "FIELDSET": 38,
+  "fieldset": 38,
+  "FIGCAPTION": 39,
+  "figcaption": 39,
+  "FIGURE": 40,
+  "figure": 40,
+  "FOOTER": 41,
+  "footer": 41,
+  "FORM": 42,
+  "form": 42,
+  "H1": 43,
+  "h1": 43,
+  "H2": 44,
+  "h2": 44,
+  "H3": 45,
+  "h3": 45,
+  "H4": 46,
+  "h4": 46,
+  "H5": 47,
+  "h5": 47,
+  "H6": 48,
+  "h6": 48,
+  "HEAD": 49,
+  "head": 49,
+  "HEADER": 50,
+  "header": 50,
+  "HGROUP": 51,
+  "hgroup": 51,
+  "HR": 52,
+  "hr": 52,
+  "HTML": 53,
+  "html": 53,
+  "I": 54,
+  "i": 54,
+  "IFRAME": 55,
+  "iframe": 55,
+  "IMG": 56,
+  "img": 56,
+  "INPUT": 57,
+  "input": 57,
+  "INS": 58,
+  "ins": 58,
+  "KBD": 59,
+  "kbd": 59,
+  "KEYGEN": 60,
+  "keygen": 60,
+  "LABEL": 61,
+  "label": 61,
+  "LEGEND": 62,
+  "legend": 62,
+  "LI": 63,
+  "li": 63,
+  "LINK": 64,
+  "link": 64,
+  "MAP": 65,
+  "map": 65,
+  "MARK": 66,
+  "mark": 66,
+  "MENU": 67,
+  "menu": 67,
+  "META": 68,
+  "meta": 68,
+  "METER": 69,
+  "meter": 69,
+  "NAV": 70,
+  "nav": 70,
+  "NOSCRIPT": 71,
+  "noscript": 71,
+  "OBJECT": 72,
+  "object": 72,
+  "OL": 73,
+  "ol": 73,
+  "OPTGROUP": 74,
+  "optgroup": 74,
+  "OPTION": 75,
+  "option": 75,
+  "OUTPUT": 76,
+  "output": 76,
+  "P": 77,
+  "p": 77,
+  "PARAM": 78,
+  "param": 78,
+  "PRE": 79,
+  "pre": 79,
+  "PROGRESS": 80,
+  "progress": 80,
+  "Q": 81,
+  "q": 81,
+  "RP": 82,
+  "rp": 82,
+  "RT": 83,
+  "rt": 83,
+  "RUBY": 84,
+  "ruby": 84,
+  "S": 85,
+  "s": 85,
+  "SAMP": 86,
+  "samp": 86,
+  "SCRIPT": 87,
+  "script": 87,
+  "SECTION": 88,
+  "section": 88,
+  "SELECT": 89,
+  "select": 89,
+  "SMALL": 90,
+  "small": 90,
+  "SOURCE": 91,
+  "source": 91,
+  "SPAN": 92,
+  "span": 92,
+  "STRONG": 93,
+  "strong": 93,
+  "STYLE": 94,
+  "style": 94,
+  "SUB": 95,
+  "sub": 95,
+  "SUMMARY": 96,
+  "summary": 96,
+  "SUP": 97,
+  "sup": 97,
+  "TABLE": 98,
+  "table": 98,
+  "TBODY": 99,
+  "tbody": 99,
+  "TD": 100,
+  "td": 100,
+  "TEXTAREA": 101,
+  "textarea": 101,
+  "TFOOT": 102,
+  "tfoot": 102,
+  "TH": 103,
+  "th": 103,
+  "THEAD": 104,
+  "thead": 104,
+  "TIME": 105,
+  "time": 105,
+  "TITLE": 106,
+  "title": 106,
+  "TR": 107,
+  "tr": 107,
+  "TRACK": 108,
+  "track": 108,
+  "U": 109,
+  "u": 109,
+  "UL": 110,
+  "ul": 110,
+  "VAR": 111,
+  "var": 111,
+  "VIDEO": 112,
+  "video": 112,
+  "WBR": 113,
+  "wbr": 113,
+};
+
+HTML_DOCUMENT = 1;
+HTML_TEXT = 2;
+HTML_COMMENT = 3;
+HTML_A = 4;
+HTML_ABBR = 5;
+HTML_ADDRESS = 6;
+HTML_AREA = 7;
+HTML_ARTICLE = 8;
+HTML_ASIDE = 9;
+HTML_AUDIO = 10;
+HTML_B = 11;
+HTML_BASE = 12;
+HTML_BDI = 13;
+HTML_BDO = 14;
+HTML_BLOCKQUOTE = 15;
+HTML_BODY = 16;
+HTML_BR = 17;
+HTML_BUTTON = 18;
+HTML_CANVAS = 19;
+HTML_CAPTION = 20;
+HTML_CITE = 21;
+HTML_CODE = 22;
+HTML_COL = 23;
+HTML_COLGROUP = 24;
+HTML_COMMAND = 25;
+HTML_DATA = 26;
+HTML_DATALIST = 27;
+HTML_DD = 28;
+HTML_DEL = 29;
+HTML_DETAILS = 30;
+HTML_DFN = 31;
+HTML_DIALOG = 32;
+HTML_DIV = 33;
+HTML_DL = 34;
+HTML_DT = 35;
+HTML_EM = 36;
+HTML_EMBED = 37;
+HTML_FIELDSET = 38;
+HTML_FIGCAPTION = 39;
+HTML_FIGURE = 40;
+HTML_FOOTER = 41;
+HTML_FORM = 42;
+HTML_H1 = 43;
+HTML_H2 = 44;
+HTML_H3 = 45;
+HTML_H4 = 46;
+HTML_H5 = 47;
+HTML_H6 = 48;
+HTML_HEAD = 49;
+HTML_HEADER = 50;
+HTML_HGROUP = 51;
+HTML_HR = 52;
+HTML_HTML = 53;
+HTML_I = 54;
+HTML_IFRAME = 55;
+HTML_IMG = 56;
+HTML_INPUT = 57;
+HTML_INS = 58;
+HTML_KBD = 59;
+HTML_KEYGEN = 60;
+HTML_LABEL = 61;
+HTML_LEGEND = 62;
+HTML_LI = 63;
+HTML_LINK = 64;
+HTML_MAP = 65;
+HTML_MARK = 66;
+HTML_MENU = 67;
+HTML_META = 68;
+HTML_METER = 69;
+HTML_NAV = 70;
+HTML_NOSCRIPT = 71;
+HTML_OBJECT = 72;
+HTML_OL = 73;
+HTML_OPTGROUP = 74;
+HTML_OPTION = 75;
+HTML_OUTPUT = 76;
+HTML_P = 77;
+HTML_PARAM = 78;
+HTML_PRE = 79;
+HTML_PROGRESS = 80;
+HTML_Q = 81;
+HTML_RP = 82;
+HTML_RT = 83;
+HTML_RUBY = 84;
+HTML_S = 85;
+HTML_SAMP = 86;
+HTML_SCRIPT = 87;
+HTML_SECTION = 88;
+HTML_SELECT = 89;
+HTML_SMALL = 90;
+HTML_SOURCE = 91;
+HTML_SPAN = 92;
+HTML_STRONG = 93;
+HTML_STYLE = 94;
+HTML_SUB = 95;
+HTML_SUMMARY = 96;
+HTML_SUP = 97;
+HTML_TABLE = 98;
+HTML_TBODY = 99;
+HTML_TD = 100;
+HTML_TEXTAREA = 101;
+HTML_TFOOT = 102;
+HTML_TH = 103;
+HTML_THEAD = 104;
+HTML_TIME = 105;
+HTML_TITLE = 106;
+HTML_TR = 107;
+HTML_TRACK = 108;
+HTML_U = 109;
+HTML_UL = 110;
+HTML_VAR = 111;
+HTML_VIDEO = 112;
+HTML_WBR = 113;
+HTML_COUNT = 114;

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/Equations.js
----------------------------------------------------------------------
diff --git a/Editor/src/Equations.js b/Editor/src/Equations.js
new file mode 100644
index 0000000..d18d21d
--- /dev/null
+++ b/Editor/src/Equations.js
@@ -0,0 +1,52 @@
+// Copyright 2011-2014 UX Productivity Pty Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var Equations_insertEquation;
+
+(function() {
+
+    Equations_insertEquation = function()
+    {
+        var math = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","math");
+        var mrow = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mrow");
+        var msup = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","msup");
+        var mi = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mi");
+        var mn = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mn");
+        var mfrac = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mfrac");
+        var mrow1 = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mrow");
+        var mrow2 = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mrow");
+        var mi1 = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mi");
+        var mi2 = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mi");
+        var mo = DOM_createElementNS(document,"http://www.w3.org/1998/Math/MathML","mo");
+
+        DOM_appendChild(mi,DOM_createTextNode(document,"x"));
+        DOM_appendChild(mn,DOM_createTextNode(document,"2"));
+        DOM_appendChild(mo,DOM_createTextNode(document,"+"));
+        DOM_appendChild(mi1,DOM_createTextNode(document,"a"));
+        DOM_appendChild(mi2,DOM_createTextNode(document,"b"));
+        DOM_appendChild(mrow1,mi1);
+        DOM_appendChild(mrow2,mi2);
+        DOM_appendChild(mfrac,mrow1);
+        DOM_appendChild(mfrac,mrow2);
+        DOM_appendChild(msup,mi);
+        DOM_appendChild(msup,mn);
+        DOM_appendChild(mrow,msup);
+        DOM_appendChild(mrow,mo);
+        DOM_appendChild(mrow,mfrac);
+        DOM_appendChild(math,mrow);
+
+        Clipboard_pasteNodes([math]);
+    }
+
+})();

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/03bd5af0/Editor/src/Figures.js
----------------------------------------------------------------------
diff --git a/Editor/src/Figures.js b/Editor/src/Figures.js
new file mode 100644
index 0000000..21c8511
--- /dev/null
+++ b/Editor/src/Figures.js
@@ -0,0 +1,122 @@
+// Copyright 2011-2014 UX Productivity Pty Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var Figures_insertFigure;
+var Figures_getSelectedFigureId;
+var Figures_getProperties;
+var Figures_setProperties;
+var Figures_getGeometry;
+
+(function() {
+
+    // public
+    Figures_insertFigure = function(filename,width,numbered,caption)
+    {
+        UndoManager_newGroup("Insert figure");
+
+        var figure = DOM_createElement(document,"FIGURE");
+        var img = DOM_createElement(document,"IMG");
+        DOM_setAttribute(img,"src",encodeURI(filename));
+        DOM_setStyleProperties(img,{"width": width});
+        DOM_appendChild(figure,img);
+
+        if ((caption != null) && (caption != "")) {
+            var figcaption = DOM_createElement(document,"FIGCAPTION");
+            DOM_appendChild(figcaption,DOM_createTextNode(document,caption));
+            DOM_appendChild(figure,figcaption);
+        }
+
+        Clipboard_pasteNodes([figure]);
+
+        // Now that the figure has been inserted into the DOM tree, the outline code will
+        // have noticed it and added an id attribute, as well as a caption giving the
+        // table number.
+        Outline_setNumbered(figure.getAttribute("id"),numbered);
+
+        // Place the cursor directly after the figure
+        var offset = DOM_nodeOffset(figure);
+        var pos = new Position(figure.parentNode,offset);
+        pos = Position_closestMatchForwards(pos,Position_okForMovement);
+        Selection_set(pos.node,pos.offset,pos.node,pos.offset);
+
+        PostponedActions_add(UndoManager_newGroup);
+    }
+
+    Figures_getSelectedFigureId = function()
+    {
+        var element = Cursor_getAdjacentNodeWithType(HTML_FIGURE);
+        return element ? element.getAttribute("id") : null;
+    }
+
+    // public
+    Figures_getProperties = function(itemId)
+    {
+        var figure = document.getElementById(itemId);
+        if (figure == null)
+            return null;
+        var rect = figure.getBoundingClientRect();
+        var result = { width: null, src: null };
+
+        var img = firstDescendantOfType(figure,HTML_IMG);
+        if (img != null) {
+            result.src = decodeURI(img.getAttribute("src"));
+            result.width = img.style.width;
+
+            if ((result.width == null) || (result.width == ""))
+                result.width = DOM_getAttribute(img,"width");
+        }
+        return result;
+    }
+
+    // public
+    Figures_setProperties = function(itemId,width,src)
+    {
+        var figure = document.getElementById(itemId);
+        if (figure == null)
+            return null;
+        var img = firstDescendantOfType(figure,HTML_IMG);
+        if (img != null) {
+            if (src == null)
+                DOM_removeAttribute(img,"src");
+            else
+                DOM_setAttribute(img,"src",encodeURI(src));
+
+            DOM_setStyleProperties(img,{"width": width});
+            if (img.getAttribute("style") == "")
+                DOM_removeAttribute(img,"style");
+            Selection_update();
+        }
+    }
+
+    // public
+    Figures_getGeometry = function(itemId)
+    {
+        var figure = document.getElementById(itemId);
+        if ((figure == null) || (figure.parentNode == null))
+            return null;
+        var img = firstDescendantOfType(figure,HTML_IMG);
+        if (img == null)
+            return null;
+
+        var figcaption = firstChildOfType(figure,HTML_FIGCAPTION);
+
+        var result = new Object();
+        result.contentRect = xywhAbsElementRect(img);
+        result.fullRect = xywhAbsElementRect(figure);
+        result.parentRect = xywhAbsElementRect(figure.parentNode);
+        result.hasCaption = (figcaption != null);
+        return result;
+    }
+
+})();


Mime
View raw message