[evolution/wip/mcrha/webkit-jsc-api: 212/292] Implement EvoEditor::SetAlignment()



commit d28eee74c24303dc50f4496c8194d44a5ad69fbe
Author: Milan Crha <mcrha redhat com>
Date:   Fri Oct 25 11:38:12 2019 +0200

    Implement EvoEditor::SetAlignment()

 data/webkit/e-editor.js                            | 303 +++++++++++++++++++-
 data/webkit/e-selection.js                         |  26 +-
 data/webkit/e-undo-redo.js                         | 317 ++++++++-------------
 src/e-util/e-util-enums.h                          |   6 +-
 .../web-extension/e-editor-web-extension.c         |   2 +
 5 files changed, 443 insertions(+), 211 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 6e9e9785e9..8378341a7a 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -21,9 +21,11 @@
    public functions start with upper-case letter. */
 
 var EvoEditor = {
+       E_CONTENT_EDITOR_ALIGNMENT_NONE : -1,
        E_CONTENT_EDITOR_ALIGNMENT_LEFT : 0,
        E_CONTENT_EDITOR_ALIGNMENT_CENTER : 1,
        E_CONTENT_EDITOR_ALIGNMENT_RIGHT : 2,
+       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY : 3,
 
        E_CONTENT_EDITOR_BLOCK_FORMAT_NONE : 0,
        E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH : 1,
@@ -44,6 +46,233 @@ var EvoEditor = {
        storedSelection : null
 };
 
+EvoEditor.IsBlockNode = function(node)
+{
+       if (!node || !node.tagName) {
+               return false;
+       }
+
+       return node.tagName == "BLOCKQUOTE" ||
+               node.tagName == "DIV" ||
+               node.tagName == "P" ||
+               node.tagName == "PRE" ||
+               node.tagName == "ADDRESS" ||
+               node.tagName == "H1" ||
+               node.tagName == "H2" ||
+               node.tagName == "H3" ||
+               node.tagName == "H4" ||
+               node.tagName == "H5" ||
+               node.tagName == "H6" ||
+               node.tagName == "TD" ||
+               node.tagName == "TH" ||
+               node.tagName == "UL" ||
+               node.tagName == "OL";
+}
+
+EvoEditor.foreachChildRecur = function(topParent, parent, firstChildIndex, lastChildIndex, traversar)
+{
+       if (!parent) {
+               return false;
+       }
+
+       var ii;
+
+       for (ii = firstChildIndex; ii < parent.children.length && ii <= lastChildIndex; ii++) {
+               var child = parent.children.item(ii);
+
+               if (!traversar.onlyBlockElements || EvoEditor.IsBlockNode(child)) {
+                       if (!traversar.exec(topParent, child)) {
+                               return false;
+                       }
+               }
+
+               if (child.children.length > 0 &&
+                   !traversar.flat &&
+                   !EvoEditor.foreachChildRecur(topParent, child, 0, child.children.length - 1, traversar)) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/*
+   Traverses children of the 'parent', between the 'firstChildIndex' and
+   the 'lastChildIndex', where both indexes are meant inclusive.
+
+   The 'traversar' is an object, which should contain at least function:
+
+      bool exec(parent, element);
+
+   which does its work in the 'element' and returns true, when the traversar
+   should continue. The 'parent' is the one with which the funcion had been
+   called with. The 'traversar' can also contain properties:
+
+      bool flat;
+      bool onlyBlockElements;
+
+   the 'flat', if set to true, traverses only direct children of the parent,
+   otherwise it dives into the hierarchy;
+
+   the 'onlyBlockElements', if set to true, calls exec() only on elements,
+   which are block elements (as of EvoEditor.IsBlockNode()), otherwise it
+   is called for each element on the way.
+*/
+EvoEditor.ForeachChild = function(parent, firstChildIndex, lastChildIndex, traversar)
+{
+       return EvoEditor.foreachChildRecur(parent, parent, firstChildIndex, lastChildIndex, traversar);
+}
+
+EvoEditor.GetCommonParent = function(firstNode, secondNode)
+{
+       if (!firstNode || !secondNode) {
+               return null;
+       }
+
+       if (firstNode.nodeType == firstNode.TEXT_NODE) {
+               firstNode = firstNode.parentElement;
+       }
+
+       if (secondNode.nodeType == secondNode.TEXT_NODE) {
+               secondNode = secondNode.parentElement;
+       }
+
+       if (!firstNode || !secondNode) {
+               return null;
+       }
+
+       var commonParent, secondParent;
+
+       if (secondParent === document.body) {
+               return document.body;
+       }
+
+       for (commonParent = firstNode.parentElement; commonParent; commonParent = commonParent.parentElement) 
{
+               if (commonParent === document.body) {
+                       break;
+               }
+
+               for (secondParent = secondNode.parentElement; secondNode; secondNode = 
secondNode.parentElement) {
+                       if (secondParent === document.body) {
+                               break;
+                       }
+
+                       if (secondParent === commonParent) {
+                               return commonParent;
+                       }
+               }
+       }
+
+       return document.body;
+}
+
+EvoEditor.GetDirectChild = function(parent, child)
+{
+       if (!parent || !child || parent === child) {
+               return null;
+       }
+
+       while (child && !(child.parentElement === parent)) {
+               child = child.parentElement;
+       }
+
+       return child;
+}
+
+EvoEditor.ClaimAffectedContent = function(startNode, endNode, useParentBlockNode, withHtml)
+{
+       var commonParent, startChild, endChild;
+       var firstChildIndex = -1, html = "", ii;
+
+       if (!startNode) {
+               startNode = document.getSelection().baseNode;
+               endNode = document.getSelection().extentNode;
+
+               if (!startNode) {
+                       startNode = document.body;
+               }
+       }
+
+       if (!endNode) {
+               endNode = startNode;
+       }
+
+       if (useParentBlockNode) {
+               while (startNode && !(startNode === document.body)) {
+                       if (EvoEditor.IsBlockNode(startNode)) {
+                               break;
+                       }
+
+                       startNode = startNode.parentElement;
+               }
+       }
+
+       commonParent = EvoEditor.GetCommonParent(startNode, endNode);
+       startChild = EvoEditor.GetDirectChild(commonParent, startNode);
+       endChild = EvoEditor.GetDirectChild(commonParent, endNode);
+
+       for (ii = 0 ; ii < commonParent.children.length; ii++) {
+               var child = commonParent.children.item(ii);
+
+               if (firstChildIndex == -1) {
+                       /* The selection can be made both from the top to the bottom and
+                          from the bottom to the top, thus cover both cases. */
+                       if (child === startChild) {
+                               firstChildIndex = ii;
+                       } else if (child === endChild) {
+                               endChild = startChild;
+                               startChild = child;
+                               firstChildIndex = ii;
+                       }
+               }
+
+               if (firstChildIndex != -1) {
+                       if (withHtml) {
+                               html += child.outerHTML;
+                       }
+
+                       if (child === endChild) {
+                               ii++;
+                               break;
+                       }
+               }
+       }
+
+       var affected = {};
+
+       affected.path = EvoSelection.GetChildPath(document.body, commonParent);
+       affected.firstChildIndex = firstChildIndex;
+       affected.restChildrenCount = commonParent.children.length - ii;
+
+       if (withHtml) {
+               affected.html = html;
+       }
+
+       return affected;
+}
+
+/* Calls EvoEditor.ForeachChild() on a content described by 'affected',
+   which is result of EvoEditor.ClaimAffectedContent(). */
+EvoEditor.ForeachChildInAffectedContent = function(affected, traversar)
+{
+       if (!affected || !traversar) {
+               throw "EvoEditor.ForeachChildInAffectedContent: No 'affected' or 'traversar'";
+       }
+
+       var parent, firstChildIndex, lastChildIndex;
+
+       parent = EvoSelection.FindElementByPath(document.body, affected.path);
+       if (!parent) {
+               throw "EvoEditor.ForeachChildInAffectedContent: Cannot find parent";
+       }
+
+       firstChildIndex = affected.firstChildIndex;
+       /* Cannot subtract one, when none left, because the child index is inclusive */
+       lastChildIndex = parent.children.length - affected.restChildrenCount + (affected.restChildrenCount ? 
-1 : 0);
+
+       return EvoEditor.ForeachChild(parent, firstChildIndex, lastChildIndex, traversar);
+}
+
 EvoEditor.StoreSelection = function()
 {
        EvoEditor.storedSelection = EvoSelection.Store(document);
@@ -57,11 +286,83 @@ EvoEditor.RestoreSelection = function()
        }
 }
 
+EvoEditor.applySetAlignment = function(record, isUndo)
+{
+       if (record.changes) {
+               var ii, parent, child;
+
+               parent = EvoSelection.FindElementByPath(document.body, record.path);
+               if (!parent) {
+                       throw "EvoEditor.applySetAlignment: Cannot find parent at path " + record.path;
+               }
+
+               for (ii = 0; ii < record.changes.length; ii++) {
+                       var change = record.changes[ii];
+
+                       child = EvoSelection.FindElementByPath(parent, change.path);
+                       if (!child) {
+                               throw "EvoEditor.applySetAlignment: Cannot find child";
+                       }
+
+                       child.style.textAlign = isUndo ? change.before : record.applyValueAfter;
+               }
+       }
+}
+
 EvoEditor.SetAlignment = function(alignment)
 {
-       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setAlignment");
+       var traversar = {
+               record : null,
+               toSet : null,
+
+               flat : false,
+               onlyBlockElements : true,
+
+               exec : function(parent, element) {
+                       if (window.getComputedStyle(element, null).textAlign != traversar.toSet) {
+                               if (traversar.record) {
+                                       if (!traversar.record.changes)
+                                               traversar.record.changes = [];
+
+                                       var change = {};
+
+                                       change.path = EvoSelection.GetChildPath(parent, element);
+                                       change.before = element.style.textAlign;
+
+                                       traversar.record.changes[traversar.record.changes.length] = change;
+                               }
+
+                               element.style.textAlign = traversar.toSet;
+
+                               return true;
+                       }
+               }
+       };
+
+       var affected = EvoEditor.ClaimAffectedContent(null, null, true, false);
+
+       if (alignment == EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_NONE)
+               traversar.toSet = "";
+       else if (alignment == EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_LEFT)
+               traversar.toSet = "left";
+       else if (alignment == EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_CENTER)
+               traversar.toSet = "center";
+       else if (alignment == EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_RIGHT)
+               traversar.toSet = "right";
+       else if (alignment == EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY)
+               traversar.toSet = "justify";
+       else
+               throw "EvoEditor.SetAlignment: Unknown alignment value: '" + alignment + "'";
+
+       traversar.record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setAlignment", null, 
null, true, false);
 
        try {
+               EvoEditor.ForeachChildInAffectedContent(affected, traversar);
+
+               if (traversar.record) {
+                       traversar.record.applyValueAfter = traversar.toSet;
+                       traversar.record.apply = EvoEditor.applySetAlignment;
+               }
        } finally {
                EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setAlignment");
        }
diff --git a/data/webkit/e-selection.js b/data/webkit/e-selection.js
index 0b91b33970..32574667fd 100644
--- a/data/webkit/e-selection.js
+++ b/data/webkit/e-selection.js
@@ -128,12 +128,12 @@ EvoSelection.Store = function(doc)
 
        var selection = {}, sel = doc.getSelection();
 
-       selection["baseElem"] = sel.baseNode ? EvoSelection.GetChildPath(doc.body, sel.baseNode) : [];
-       selection["baseOffset"] = sel.baseOffset + EvoSelection.GetOverallTextOffset(sel.baseNode);
+       selection.baseElem = sel.baseNode ? EvoSelection.GetChildPath(doc.body, sel.baseNode) : [];
+       selection.baseOffset = sel.baseOffset + EvoSelection.GetOverallTextOffset(sel.baseNode);
 
        if (!sel.isCollapsed) {
-               selection["extentElem"] = EvoSelection.GetChildPath(doc.body, sel.extentNode);
-               selection["extentOffset"] = sel.extentOffset + 
EvoSelection.GetOverallTextOffset(sel.extentNode);
+               selection.extentElem = EvoSelection.GetChildPath(doc.body, sel.extentNode);
+               selection.extentOffset = sel.extentOffset + EvoSelection.GetOverallTextOffset(sel.extentNode);
        }
 
        return selection;
@@ -149,8 +149,8 @@ EvoSelection.Restore = function(doc, selection)
 
        var base_node, base_offset, extent_node, extent_offset;
 
-       base_node = EvoSelection.FindElementByPath(doc.body, selection["baseElem"]);
-       base_offset = selection["baseOffset"];
+       base_node = EvoSelection.FindElementByPath(doc.body, selection.baseElem);
+       base_offset = selection.baseOffset;
 
        if (!base_node) {
                return;
@@ -163,8 +163,8 @@ EvoSelection.Restore = function(doc, selection)
        base_node = EvoSelection.GetTextOffsetNode(base_node, base_offset);
        base_offset -= EvoSelection.GetOverallTextOffset(base_node);
 
-       extent_node = EvoSelection.FindElementByPath(doc.body, selection["extentElem"]);
-       extent_offset = selection["extentOffset"];
+       extent_node = EvoSelection.FindElementByPath(doc.body, selection.extentElem);
+       extent_offset = selection.extentOffset;
 
        if (extent_node) {
                extent_node = EvoSelection.GetTextOffsetNode(extent_node, extent_offset);
@@ -205,10 +205,10 @@ EvoSelection.ToString = function(selection)
 
        var str = "", base_elem, base_offset, extent_elem, extent_offset;
 
-       base_elem = selection["baseElem"];
-       base_offset = selection["baseOffset"];
-       extent_elem = selection["extentElem"];
-       extent_offset = selection["extentOffset"];
+       base_elem = selection.baseElem;
+       base_offset = selection.baseOffset;
+       extent_elem = selection.extentElem;
+       extent_offset = selection.extentOffset;
 
        str += "baseElem=" + utils.arrayToString(base_elem);
        str += " baseOffset=" + (base_offset ? base_offset : 0);
@@ -304,7 +304,7 @@ EvoSelection.FromString = function(str)
        }
 
        /* The "baseElem" is required, the rest is optional */
-       if (!selection["baseElem"])
+       if (!selection.baseElem)
                return null;
 
        return selection;
diff --git a/data/webkit/e-undo-redo.js b/data/webkit/e-undo-redo.js
index 086701790c..0f49336cbd 100644
--- a/data/webkit/e-undo-redo.js
+++ b/data/webkit/e-undo-redo.js
@@ -25,6 +25,11 @@ var EvoUndoRedo = {
        E_UNDO_REDO_STATE_CAN_UNDO : 1 << 0,
        E_UNDO_REDO_STATE_CAN_REDO : 1 << 1,
 
+       /* Flags for StartRecord() */
+       FLAG_NONE : 0,
+       FLAG_USE_PARENT_BLOCK_NODE : 1 << 0,
+       FLAG_SAVE_HTML : 1 << 1,
+
        stack : {
                // to not claim changes when none being made
                state : -1,
@@ -37,9 +42,9 @@ var EvoUndoRedo = {
                        undoRecord = EvoUndoRedo.stack.getCurrentUndoRecord();
                        redoRecord = EvoUndoRedo.stack.getCurrentRedoRecord();
                        undoAvailable = undoRecord != null;
-                       undoOpType = undoRecord ? undoRecord["opType"] : "";
+                       undoOpType = undoRecord ? undoRecord.opType : "";
                        redoAvailable = redoRecord != null;
-                       redoOpType = redoRecord ? redoRecord["opType"] : "";
+                       redoOpType = redoRecord ? redoRecord.opType : "";
 
                        var state = EvoUndoRedo.E_UNDO_REDO_STATE_NONE;
 
@@ -60,9 +65,9 @@ var EvoUndoRedo = {
 
                                var params = {};
 
-                               params["state"] = EvoUndoRedo.state;
-                               params["undoOpType"] = EvoUndoRedo.undoOpType;
-                               params["redoOpType"] = EvoUndoRedo.redoOpType;
+                               params.state = EvoUndoRedo.state;
+                               params.undoOpType = EvoUndoRedo.undoOpType;
+                               params.redoOpType = EvoUndoRedo.redoOpType;
 
                                window.webkit.messageHandlers.undoRedoStateChanged.postMessage(params);
                        }
@@ -265,7 +270,32 @@ EvoUndoRedo.before_input_cb = function(inputEvent)
                return;
        }
 
-       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, inputEvent.inputType, null, null);
+       var opType = inputEvent.inputType, useParentBlockNode = false;
+
+       if (opType == "" || // some WebKit-specific editing commands use this
+           opType.startsWith("format") ||
+           opType == "insertLineBreak" ||
+           opType == "insertParagraph") {
+               useParentBlockNode = true;
+               var startNode;
+
+               startNode = document.getSelection().baseNode;
+
+               if (!startNode) {
+                       startNode = document.body;
+               }
+
+               while (startNode && !(startNode === document.body)) {
+                       if (EvoEditor.IsBlockNode(startNode)) {
+                               break;
+                       }
+
+                       startNode = startNode.parentElement;
+               }
+       }
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType, startNode, null,
+               EvoUndoRedo.FLAG_SAVE_HTML | (useParentBlockNode ? EvoUndoRedo.FLAG_USE_PARENT_BLOCK_NODE : 
EvoUndoRedo.FLAG_NONE));
 }
 
 EvoUndoRedo.input_cb = function(inputEvent)
@@ -283,10 +313,12 @@ EvoUndoRedo.applyRecord = function(record, isUndo, withSelection)
                return;
        }
 
-       if (record["kind"] == EvoUndoRedo.RECORD_KIND_GROUP) {
+       var kind = record.kind;
+
+       if (kind == EvoUndoRedo.RECORD_KIND_GROUP) {
                var ii, records;
 
-               records = record["records"];
+               records = record.records;
 
                if (records && records.length) {
                        if (isUndo) {
@@ -302,9 +334,9 @@ EvoUndoRedo.applyRecord = function(record, isUndo, withSelection)
 
                if (withSelection) {
                        if (isUndo) {
-                               EvoSelection.Restore(document, record["selectionBefore"]);
+                               EvoSelection.Restore(document, record.selectionBefore);
                        } else {
-                               EvoSelection.Restore(document, record["selectionAfter"]);
+                               EvoSelection.Restore(document, record.selectionAfter);
                        }
                }
 
@@ -314,57 +346,67 @@ EvoUndoRedo.applyRecord = function(record, isUndo, withSelection)
        EvoUndoRedo.Disable();
 
        try {
-               var commonParent, first, last, ii;
+               if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+                       if (isUndo) {
+                               document.documentElement.outerHTML = record.htmlBefore;
+                       } else {
+                               document.documentElement.outerHTML = record.htmlAfter;
+                       }
+               } else if (kind == EvoUndoRedo.RECORD_KIND_CUSTOM && record.apply != null) {
+                       record.apply(record, isUndo);
+               } else {
+                       var commonParent, first, last, ii;
 
-               commonParent = EvoSelection.FindElementByPath(document.body, record["path"]);
-               if (!commonParent) {
-                       throw "EvoUndoRedo::applyRecord: Cannot find parent at path " + record["path"];
-               }
+                       commonParent = EvoSelection.FindElementByPath(document.body, record.path);
+                       if (!commonParent) {
+                               throw "EvoUndoRedo::applyRecord: Cannot find parent at path " + record.path;
+                       }
 
-               first = record["firstChildIndex"];
+                       first = record.firstChildIndex;
 
-               // it can equal to the children.length, when the node had been removed
-               if (first < 0 || first > commonParent.children.length) {
-                       throw "EvoUndoRedo::applyRecord: firstChildIndex (" + first + ") out of bounds (" + 
commonParent.children.length + ")";
-               }
+                       // it can equal to the children.length, when the node had been removed
+                       if (first < 0 || first > commonParent.children.length) {
+                               throw "EvoUndoRedo::applyRecord: firstChildIndex (" + first + ") out of 
bounds (" + commonParent.children.length + ")";
+                       }
 
-               last = commonParent.children.length - record["restChildrenCount"];
-               if (last < 0 || last < first) {
-                       throw "EvoUndoRedo::applyRecord: restChildrenCount (" + record["restChildrenCount"] + 
") out of bounds (length:" +
-                               commonParent.children.length + " first:" + first + " last:" + last + ")";
-               }
+                       last = commonParent.children.length - record.restChildrenCount;
+                       if (last < 0 || last < first) {
+                               throw "EvoUndoRedo::applyRecord: restChildrenCount (" + 
record.restChildrenCount + ") out of bounds (length:" +
+                                       commonParent.children.length + " first:" + first + " last:" + last + 
")";
+                       }
 
-               for (ii = last - 1; ii >= first; ii--) {
-                       if (ii >= 0 && ii < commonParent.children.length) {
-                               commonParent.removeChild(commonParent.children.item(ii));
+                       for (ii = last - 1; ii >= first; ii--) {
+                               if (ii >= 0 && ii < commonParent.children.length) {
+                                       commonParent.removeChild(commonParent.children.item(ii));
+                               }
                        }
-               }
 
-               var tmpNode = document.createElement("evo-tmp");
+                       var tmpNode = document.createElement("evo-tmp");
 
-               if (isUndo) {
-                       tmpNode.innerHTML = record["htmlBefore"];
-               } else {
-                       tmpNode.innerHTML = record["htmlAfter"];
-               }
+                       if (isUndo) {
+                               tmpNode.innerHTML = record.htmlBefore;
+                       } else {
+                               tmpNode.innerHTML = record.htmlAfter;
+                       }
 
-               if (first + 1 < commonParent.children.length) {
-                       first = commonParent.children.item(first + 1);
+                       if (first + 1 < commonParent.children.length) {
+                               first = commonParent.children.item(first + 1);
 
-                       for (ii = tmpNode.children.length - 1; ii >= 0; ii--) {
-                               commonParent.insertBefore(tmpNode.children.item(ii), first);
-                       }
-               } else {
-                       while(tmpNode.children.length) {
-                               commonParent.appendChild(tmpNode.children.item(0));
+                               for (ii = tmpNode.children.length - 1; ii >= 0; ii--) {
+                                       commonParent.insertBefore(tmpNode.children.item(ii), first);
+                               }
+                       } else {
+                               while(tmpNode.children.length) {
+                                       commonParent.appendChild(tmpNode.children.item(0));
+                               }
                        }
                }
 
                if (withSelection) {
                        if (isUndo) {
-                               EvoSelection.Restore(document, record["selectionBefore"]);
+                               EvoSelection.Restore(document, record.selectionBefore);
                        } else {
-                               EvoSelection.Restore(document, record["selectionAfter"]);
+                               EvoSelection.Restore(document, record.selectionAfter);
                        }
                }
        } finally {
@@ -372,157 +414,38 @@ EvoUndoRedo.applyRecord = function(record, isUndo, withSelection)
        }
 }
 
-EvoUndoRedo.getCommonParent = function(firstNode, secondNode)
-{
-       if (!firstNode || !secondNode) {
-               return null;
-       }
-
-       if (firstNode.nodeType == firstNode.TEXT_NODE) {
-               firstNode = firstNode.parentElement;
-       }
-
-       if (secondNode.nodeType == secondNode.TEXT_NODE) {
-               secondNode = secondNode.parentElement;
-       }
-
-       if (!firstNode || !secondNode) {
-               return null;
-       }
-
-       var commonParent, secondParent;
-
-       if (secondParent === document.body) {
-               return document.body;
-       }
-
-       for (commonParent = firstNode.parentElement; commonParent; commonParent = commonParent.parentElement) 
{
-               if (commonParent === document.body) {
-                       break;
-               }
-
-               for (secondParent = secondNode.parentElement; secondNode; secondNode = 
secondNode.parentElement) {
-                       if (secondParent === document.body) {
-                               break;
-                       }
-
-                       if (secondParent === commonParent) {
-                               return commonParent;
-                       }
-               }
-       }
-
-       return document.body;
-}
-
-EvoUndoRedo.getDirectChild = function(parent, child)
-{
-       if (!parent || !child || parent === child) {
-               return null;
-       }
-
-       while (child && !(child.parentElement === parent)) {
-               child = child.parentElement;
-       }
-
-       return child;
-}
-
-EvoUndoRedo.StartRecord = function(kind, opType, startNode, endNode)
+EvoUndoRedo.StartRecord = function(kind, opType, startNode, endNode, flags)
 {
        if (EvoUndoRedo.disabled) {
-               return;
+               return null;
        }
 
-       var record = {};
+       var record = {}, saveHTML;
 
-       record["kind"] = kind;
-       record["opType"] = opType;
-       record["selectionBefore"] = EvoSelection.Store(document);
+       saveHTML = (flags & EvoUndoRedo.FLAG_SAVE_HTML) != 0;
 
-       if (kind != EvoUndoRedo.RECORD_KIND_GROUP) {
-               var commonParent, startChild, endChild;
-               var firstChildIndex = -1, html = "", ii;
+       record.kind = kind;
+       record.opType = opType;
+       record.selectionBefore = EvoSelection.Store(document);
 
-               if (!startNode) {
-                       startNode = document.getSelection().baseNode;
-                       endNode = document.getSelection().extentNode;
+       if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+               record.htmlBefore = document.documentElement.outerHTML;
+       } else if (kind != EvoUndoRedo.RECORD_KIND_GROUP) {
+               var affected;
 
-                       if (!startNode) {
-                               startNode = document.body;
-                       }
-               }
+               affected = EvoEditor.ClaimAffectedContent(startNode, endNode, (flags & 
EvoUndoRedo.FLAG_USE_PARENT_BLOCK_NODE) != 0, saveHTML);
 
-               if (!endNode) {
-                       endNode = startNode;
-               }
+               record.path = affected.path;
+               record.firstChildIndex = affected.firstChildIndex;
+               record.restChildrenCount = affected.restChildrenCount;
 
-               /* Tweak what to save, because some events do not modify only selection, but also its parent 
elements */
-               if (kind == EvoUndoRedo.RECORD_KIND_EVENT) {
-                       if (opType == "" || // some WebKit-specific editing commands use this
-                           opType.startsWith("format") ||
-                           opType == "insertLineBreak" ||
-                           opType == "insertParagraph") {
-                               while (startNode && !(startNode === document.body)) {
-                                       if (startNode.tagName == "P" ||
-                                           startNode.tagName == "DIV" ||
-                                           startNode.tagName == "BLOCKQUOTE" ||
-                                           startNode.tagName == "UL" ||
-                                           startNode.tagName == "OL" ||
-                                           startNode.tagName == "PRE" ||
-                                           startNode.tagName == "H1" ||
-                                           startNode.tagName == "H2" ||
-                                           startNode.tagName == "H3" ||
-                                           startNode.tagName == "H4" ||
-                                           startNode.tagName == "H5" ||
-                                           startNode.tagName == "H6" ||
-                                           startNode.tagName == "ADDRESS" ||
-                                           startNode.tagName == "TD" ||
-                                           startNode.tagName == "TH") {
-                                               break;
-                                       }
-
-                                       startNode = startNode.parentElement;
-                               }
-                       }
-               }
-
-               commonParent = EvoUndoRedo.getCommonParent(startNode, endNode);
-               startChild = EvoUndoRedo.getDirectChild(commonParent, startNode);
-               endChild = EvoUndoRedo.getDirectChild(commonParent, endNode);
-
-               for (ii = 0 ; ii < commonParent.children.length; ii++) {
-                       var child = commonParent.children.item(ii);
-
-                       if (firstChildIndex == -1) {
-                               /* The selection can be made both from the top to the bottom and
-                                  from the bottom to the top, thus cover both cases. */
-                               if (child === startChild) {
-                                       firstChildIndex = ii;
-                               } else if (child === endChild) {
-                                       endChild = startChild;
-                                       startChild = child;
-                                       firstChildIndex = ii;
-                               }
-                       }
-
-                       if (firstChildIndex != -1) {
-                               html += child.outerHTML;
-
-                               if (child === endChild) {
-                                       ii++;
-                                       break;
-                               }
-                       }
-               }
-
-               record["path"] = EvoSelection.GetChildPath(document.body, commonParent);
-               record["firstChildIndex"] = firstChildIndex;
-               record["restChildrenCount"] = commonParent.children.length - ii;
-               record["htmlBefore"] = html;
+               if (saveHTML)
+                       record.htmlBefore = affected.html;
        }
 
        EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length] = record;
+
+       return record;
 }
 
 EvoUndoRedo.StopRecord = function(kind, opType)
@@ -537,37 +460,39 @@ EvoUndoRedo.StopRecord = function(kind, opType)
 
        var record = EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1];
 
-       if (record["kind"] != kind) {
-               throw "EvoUndoRedo:StopRecord: Mismatch in record kind, expected " + record["kind"] + ", but 
received " + kind;
+       if (record.kind != kind) {
+               throw "EvoUndoRedo:StopRecord: Mismatch in record kind, expected " + record.kind + ", but 
received " + kind;
        }
 
-       if (record["opType"] != opType) {
-               throw "EvoUndoRedo:StopRecord: Mismatch in record opType, expected '" + record["opType"] + 
"', but received '" + opType + "'";
+       if (record.opType != opType) {
+               throw "EvoUndoRedo:StopRecord: Mismatch in record opType, expected '" + record.opType + "', 
but received '" + opType + "'";
        }
 
        EvoUndoRedo.ongoingRecordings.length = EvoUndoRedo.ongoingRecordings.length - 1;
 
-       record["selectionAfter"] = EvoSelection.Store(document);
+       record.selectionAfter = EvoSelection.Store(document);
 
-       if (kind != EvoUndoRedo.RECORD_KIND_GROUP) {
+       if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+               record.htmlAfter = document.documentElement.outerHTML;
+       } else if (record.htmlBefore != window.undefined) {
                var commonParent, first, last, ii, html = "";
 
-               commonParent = EvoSelection.FindElementByPath(document.body, record["path"]);
+               commonParent = EvoSelection.FindElementByPath(document.body, record.path);
 
                if (!commonParent) {
                        throw "EvoUndoRedo.StopRecord:: Failed to stop '" + opType + "', cannot find common 
parent";
                }
 
-               first = record["firstChildIndex"];
+               first = record.firstChildIndex;
 
                // it can equal to the children.length, when the node had been removed
                if (first < 0 || first > commonParent.children.length) {
                        throw "EvoUndoRedo::StopRecord: firstChildIndex (" + first + ") out of bounds (" + 
commonParent.children.length + ")";
                }
 
-               last = commonParent.children.length - record["restChildrenCount"];
+               last = commonParent.children.length - record.restChildrenCount;
                if (last < 0 || last < first) {
-                       throw "EvoUndoRedo::StopRecord: restChildrenCount (" + record["restChildrenCount"] + 
") out of bounds (length:" +
+                       throw "EvoUndoRedo::StopRecord: restChildrenCount (" + record.restChildrenCount + ") 
out of bounds (length:" +
                                commonParent.children.length + " first:" + first + " last:" + last + ")";
                }
 
@@ -577,17 +502,17 @@ EvoUndoRedo.StopRecord = function(kind, opType)
                        }
                }
 
-               record["htmlAfter"] = html;
+               record.htmlAfter = html;
        }
 
        if (EvoUndoRedo.ongoingRecordings.length) {
                var parentRecord = EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1];
-               var records = parentRecord["records"];
+               var records = parentRecord.records;
                if (!records) {
                        records = [];
                }
                records[records.length] = record;
-               parentRecord["records"] = records;
+               parentRecord.records = records;
        } else {
                EvoUndoRedo.stack.push(record);
        }
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index 9bdb26d3af..4ea7165872 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -292,16 +292,20 @@ typedef enum {
 
 /**
  * EContentEditorAlignment:
+ * @E_CONTENT_EDITOR_ALIGNMENT_NONE:
  * @E_CONTENT_EDITOR_ALIGNMENT_LEFT:
  * @E_CONTENT_EDITOR_ALIGNMENT_CENTER:
  * @E_CONTENT_EDITOR_ALIGNMENT_RIGHT:
+ * @E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY:
  *
  * Since: 3.22
  **/
 typedef enum {
+       E_CONTENT_EDITOR_ALIGNMENT_NONE = -1,
        E_CONTENT_EDITOR_ALIGNMENT_LEFT = 0,
        E_CONTENT_EDITOR_ALIGNMENT_CENTER,
-       E_CONTENT_EDITOR_ALIGNMENT_RIGHT
+       E_CONTENT_EDITOR_ALIGNMENT_RIGHT,
+       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY
 } EContentEditorAlignment;
 
 /**
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c 
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
index de431baffd..7b02093190 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -2497,6 +2497,8 @@ load_javascript_file (JSCContext *jsc_context,
                        jsc_exception_get_line_number (exception),
                        jsc_exception_get_column_number (exception),
                        jsc_exception_get_message (exception));
+
+               jsc_context_clear_exception (jsc_context);
        }
 
        g_clear_object (&result);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]