From: Jason Woofenden Date: Wed, 25 May 2016 04:33:30 +0000 (-0400) Subject: backspace fixes: removing emptied parents, etc X-Git-Url: https://jasonwoof.com/gitweb/?p=peach-html5-editor.git;a=commitdiff_plain;h=d27bf60d438b9cf3f595923e9a5c80b2f8d480fb backspace fixes: removing emptied parents, etc --- diff --git a/editor.coffee b/editor.coffee index 0b76863..36c6660 100644 --- a/editor.coffee +++ b/editor.coffee @@ -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