X-Git-Url: https://jasonwoof.com/gitweb/?p=peach-html5-editor.git;a=blobdiff_plain;f=editor.coffee;h=238625c0793b3c745b68e8665ff825ce7bb34e64;hp=8835a33be0b65a6adc34a24b7b7808a78af03cfd;hb=e3b396215f59cd45b7d7c510dcddea8354f756aa;hpb=ee09148f5480ccb4cc0f6d79b459651428097e6c diff --git a/editor.coffee b/editor.coffee index 8835a33..238625c 100644 --- a/editor.coffee +++ b/editor.coffee @@ -952,15 +952,22 @@ class PeachHTML5Editor return false when KEY_DELETE return false unless @cursor? - return false unless @cursor.i < @cursor.n.text.length - @remove_character @cursor.n, @cursor.i - @text_cleanup @cursor.n - @changed() - new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i + new_cursor = find_next_cursor_position @tree, n: @cursor.n, i: @cursor.i + # try moving cursor right and then running backspace code + # TODO replace this hack with a real implementation if new_cursor? - @move_cursor new_cursor - else - @kill_cursor() + # try to detect common case where cursor goes inside an block, + # but doesn't pass a character (and advance one more in that case) + if new_cursor.n isnt @cursor.n and new_cursor.i is 0 + if new_cursor.n.type is 'text' and new_cursor.n.text.length > 0 + if new_cursor.n.parent? + unless @is_display_block new_cursor.n.parent + # FIXME should test run sibling + new_cursor = new_cursor_position n: new_cursor.n, i: new_cursor.i + 1 + if new_cursor? + if new_cursor.n isnt @cursor.n or new_cursor.i isnt @cursor.i + @move_cursor new_cursor + @on_key_backspace e return false when KEY_ENTER @on_key_enter e @@ -1071,7 +1078,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 +1095,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) -> @@ -1104,11 +1129,6 @@ class PeachHTML5Editor if @cursor.i is 0 # cursor is at start of text node run ?= @get_text_run @cursor.n run_i = run.indexOf(@cursor.n) - if run_i is -1 - console.log 'run', run - console.log 'cursor', @cursor - throw 'fffffff' - return if run_i is 0 # if at start of text run block = @find_block_parent @cursor.n prev_cursor = find_prev_cursor_position @tree, n: @cursor.n, i: 0 @@ -1129,7 +1149,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 @@ -1141,12 +1161,12 @@ 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 new_cursor = new_cursor_position n: merge_state.n, i: merge_state.i - # FIXME CONTINUE + # FIXME implement this: # else # if prev (in run) is inline-block # if that inline-block has text in it # delete last char in prev inlineblock @@ -1164,42 +1184,53 @@ class PeachHTML5Editor if @cursor.n.text.length is 1 # if emptying text node if run.length is 1 # if emptying text run (of text/br/hr/inline-block) # remove inline-parents of @cursor.n - block = @find_block_parent n - changed = false - n = @cursor.n.parent - while n and n isnt block - changed = true - while n.children.length > 0 - @move_node n.children[0], n.parent, n - @remove_node n - n = n.parent - # replace @cursor.n with a single (preserved) space - if @cursor.n.text != ' ' - changed = true - @cursor.n.text = @cursor.n.el.textContent = ' ' - if changed - @text_cleanup @cursor.n - # place the cursor to the left of that space - new_cursor = new_cursor_position n: @cursor.n, i: 0 - else # emptying a text node (but not a whole text run) - # figure out where cursor should land - block = @find_block_parent @cursor.n - new_cursor = find_prev_cursor_position @tree, n: @cursor.n, i: 0 - ncb = @find_block_parent new_cursor - 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 - 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 - if new_cursor? + 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 + @move_node n.children[0], n.parent, n + @remove_node n + n = n.parent + # replace @cursor.n with a single (preserved) space + if @cursor.n.text != ' ' + changed = true + @cursor.n.text = @cursor.n.el.textContent = ' ' + if changed + @text_cleanup @cursor.n + # place the cursor to the left of that space + new_cursor = new_cursor_position n: @cursor.n, i: 0 + else # emptying a text node (but not a whole text run) + # figure out where cursor should land + block = @find_block_parent @cursor.n + new_cursor = find_prev_cursor_position @tree, n: @cursor.n, i: 0 + 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 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? + 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