JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
backspace fixes: removing emptied parents, etc
authorJason Woofenden <jason@jasonwoof.com>
Wed, 25 May 2016 04:33:30 +0000 (00:33 -0400)
committerJason Woofenden <jason@jasonwoof.com>
Wed, 25 May 2016 04:33:30 +0000 (00:33 -0400)
editor.coffee

index 0b76863..36c6660 100644 (file)
@@ -1071,7 +1071,13 @@ class PeachHTML5Editor
                                                ret.push n
                        return false # not done traversing
                return ret
-       _merge_left: (state) -> # helper for on_key_backspace
+       node_is_decendant: (young, old) ->
+               while young? and young != @tree_parent
+                       return true if young is old
+                       young = young.parent
+               return false
+       # helper for on_key_backspace
+       _merge_left: (state) ->
                # the node prev to n was not prev to it a moment ago, merge with it if reasonable
                pi = state.n.parent.children.indexOf(state.n)
                if pi > 0
@@ -1082,18 +1088,30 @@ class PeachHTML5Editor
                                @remove_node state.n
                                state.n = prev
                                state.changed = true
+                               state.moved_cursor = true
                # else # TODO merge possible consecutive matching inline tags at @cursor
                return state
-       _remove_node_and_inline_parents: (n) -> # helper for on_key_backspace
+       # helper for on_key_backspace
+       # remove n from the dom, also remove its inline parents that are emptied by removing n
+       _backspace_node_helper: (n, run = @get_text_run(n), run_i = run.indexOf(n)) ->
                block = @find_block_parent n
                # delete text node
                @remove_node n
                # delete any inline parents
                n = n.parent
                while n? and n isnt block
+                       # bail if the previous node in this run is also inside the same parent
+                       if run_i > 0
+                               break if @node_is_decendant run[run_i - 1], n
+                       # bail if the next node in this run is also inside the same parent
+                       if run_i + 1 < run.length
+                               break if @node_is_decendant run[run_i + 1], n
+                       # move any sibling nodes to parent. These nodes are not in the text run
                        while n.children.length > 0
                                @move_node n.children[0], n.parent, n
+                       # remove (now completely empty) inline parent
                        @remove_node n
+                       # proceed to outer parent
                        n = n.parent
                return
        on_key_backspace: (e) ->
@@ -1124,7 +1142,7 @@ class PeachHTML5Editor
                                prev = run[run_i - 1]
                                if prev.type is 'text' # if previous in text run is text
                                        if prev.text.length is 1 # if emptying prev (in text run)
-                                               @_remove_node_and_inline_parents prev
+                                               @_backspace_node_helper prev, run, run_i
                                                merge_state = n: @cursor.n, i: @cursor.i
                                                @_merge_left merge_state
                                                @text_cleanup merge_state.n
@@ -1136,7 +1154,7 @@ class PeachHTML5Editor
                                                @text_cleanup @cursor.n
                                                new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i
                                else if prev.name is 'br' or prev.name is 'hr'
-                                       @_remove_node_and_inline_parents prev
+                                       @_backspace_node_helper prev, run, run_i
                                        merge_state = n: @cursor.n, i: @cursor.i
                                        @_merge_left merge_state
                                        @text_cleanup merge_state.n
@@ -1162,6 +1180,10 @@ class PeachHTML5Editor
                                        block = @find_block_parent @cursor.n
                                        changed = false
                                        n = @cursor.n.parent
+                                       # note: this doesn't use _backspace_node_helper because:
+                                       # 1. we don't want to delete the target node (we're replacing it's contents)
+                                       # 2. we want to track whether anything was removed
+                                       # 3. we know already know there's no other text from this run anywhere
                                        while n and n isnt block
                                                changed = true
                                                while n.children.length > 0
@@ -1183,19 +1205,25 @@ class PeachHTML5Editor
                                        ncb = @find_block_parent new_cursor.n
                                        if ncb isnt block
                                                new_cursor = find_next_cursor_position @tree, n: @cursor.n, i: 1
-                                       # delete text node
-                                       @remove_node @cursor.n
-                                       # delete any inline parents
-                                       n = @cursor.n.parent
-                                       while n? and n isnt block
-                                               # FIXME break if anything else in the text run is in here too
-                                               while n.children.length > 0
-                                                       @move_node n.children[0], n.parent, n
-                                               @remove_node n
-                                               n = n.parent
-                                       # update cursor dest in case things moved around
+                                       # delete text node and cleanup emptied parents
+                                       run_i = run.indexOf @cursor.n
+                                       @_backspace_node_helper @cursor.n, run, run_i
+                                       # see if new adjacent siblings should merge
+                                       # TODO make smarter
+                                       if run_i > 0 and run_i + 1 < run.length
+                                               if run[run_i - 1].type is 'text' and run[run_i + 1].type is 'text'
+                                                       merge_state = n: run[run_i + 1]
+                                                       @_merge_left merge_state
+                                                       if merge_state.moved_cursor
+                                                               new_cursor = merge_state
+                                       # update whitespace preservation
+                                       @text_cleanup(block)
+                                       # update cursor x/y in case things moved around
                                        if new_cursor?
-                                               new_cursor = new_cursor_position n: new_cursor.n, i: new_cursor.i
+                                               if new_cursor.n.el.parentNode # still in dom after cleanup
+                                                       new_cursor = new_cursor_position n: new_cursor.n, i: new_cursor.i
+                                               else
+                                                       new_cursor = null
                        else # there's a char left of cursor that we can delete without emptying anything
                                # delete character
                                need_text_cleanup = true