X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=editor.coffee;h=2ad0acc7bc866ec2c8750294f8d5bf2b598df6c2;hb=9b0a8adcd553683463cc338e6ad4d95523e7939f;hp=c78151a90cfa2dfd08487d75d63ae427b67e4ce2;hpb=1a3115b9b75ed1f9a380b59c80d51fabdc811a2a;p=peach-html5-editor.git diff --git a/editor.coffee b/editor.coffee index c78151a..2ad0acc 100644 --- a/editor.coffee +++ b/editor.coffee @@ -559,21 +559,32 @@ tree_dedup_space = (tree) -> throw "how is this possible?" next_i -= 1 return 1 - whitespace_to_space = (undo) -> + replace_with_space = (undo) -> if undo cur.text = (cur.text.substr 0, cur_i) + removed_char + (cur.text.substr cur_i + 1) cur.el.textContent = cur.text else removed_char = cur.text.charAt(cur_i) - cur.text = (cur.text.substr 0, cur_i) + ' ' + (cur.text.substr cur_i + 1) - cur.el.textContent = cur.text + if removed_char isnt ' ' + cur.text = (cur.text.substr 0, cur_i) + ' ' + (cur.text.substr cur_i + 1) + cur.el.textContent = cur.text return 0 # return true if cur was removed from the dom (ie re-use same prev) operate = -> # cur definitately set # prev and/or next might be null, indicating the start/end of a display:block return false unless is_space_code cur.text.charCodeAt cur_i - fixers = [remove, whitespace_to_space] + fixers = [remove, replace_with_space] + # check for common case: single whitespace surrounded by non-whitespace chars + if prev? and next? + unless (is_space_code prev.text.charCodeAt prev_i) or (is_space_code next.text.charCodeAt next_i) + dbg = cur.text.charCodeAt cur_i + if cur.text.charAt(cur_i) is ' ' # perens required + # single space can't collapse, doesn't need fixin' + return false + else + # tab, newline, etc, can't collapse, but maybe should be replaced + fixers = [replace_with_space] bounds = text_range_bounds cur.el, cur_i, cur_i + 1 # consistent cases: # 1. zero rects returned by getClientRects() means collapsed space @@ -581,7 +592,8 @@ tree_dedup_space = (tree) -> return remove() # 2. width greater than zero means visible space if bounds.w > 0 - fixers.shift() # don't try removing + # has bounds, don't try removing + fixers = [replace_with_space] # now the weird edge cases... # # firefox and chromium both report zero width for characters at the end @@ -590,6 +602,11 @@ tree_dedup_space = (tree) -> # collapsed spaces via the range/bounds api, so... # # remove it from the dom, and if prev or next moves, put it back. + # + # this block (try changing it, put it back if something moves) is also + # used on collapsable whitespace characters besides space. In this case + # the character is replaced with a normal space character instead of + # removed if prev? and not prev_px? prev_px = new_cursor_position n: prev, i: prev_i if next? and not next_px? @@ -1025,6 +1042,7 @@ class PeachHTML5Editor else @kill_cursor() else + # cursor at the begining of an element that contains only a space parent = @cursor.n.parent new_cursor = find_prev_cursor_position @tree, @cursor if new_cursor? @@ -1052,11 +1070,41 @@ class PeachHTML5Editor else @kill_cursor return - else if @cursor.i is 0 - console.log 'unimplemented: backspace at start of non-empty tag' - # TODO if block, merge parent into prev - # TODO if inline, delete char from prev text node - return false + else if @cursor.i is 0 # start of non-empty tag + # TODO factor out function for moving children around + # find containing block + containing_block = @cursor.n + loop + containing_block = containing_block.parent + return unless containing_block? + return if containing_block is @tree_parent + break if is_display_block containing_block + # FIXME only continue if first child, else inline merge here + # find contaning_block's previous sibling + prev = null + for n, contaning_block_i in contaning_block.parent.children + break if n is containing_block + prev = n + containing_block.el.parent.removeChild containing_block.el + containing_block.parent.children.splice contaning_block_i, 1 + if prev is null + console.log "inimplemented: backspace at start of nested blocks" # FIXME + return + if is_display_block prev + # insert contents of containing_block into prev + ws_cleanup = false + if prev.children.length > 0 + prev_last = prev.children[prev.children.length - 1] + if prev_last.type is 'text' and containing_block.children[0].type is 'text' + prev_last.text = prev_last.el.textContent = prev_last.text + containing_block.children[0].text + ws_cleanup = true + containing_block.children.shift() + for n in containing_block.children + # TODO insert into prev + # TODO adjust whitespace property of prev_last if ws_cleanup + else + # TODO insert contents of containing_block into prev.parent after prev + return else # TODO handle case of removing last char # CONTINUE @@ -1077,6 +1125,13 @@ class PeachHTML5Editor @kill_cursor() return on_page_up_key: (e) -> + if @wrap2.scrollTop is 0 + return unless @cursor? + new_cursor = first_cursor_position @tree + if new_cursor? + if new_cursor.n isnt @cursor.n or new_cursor.i isnt @cursor.i + @move_cursor new_cursor + return if @cursor? screen_y = @cursor.y - @wrap2.scrollTop scroll_amount = @wrap2_height - breathing_room @@ -1084,10 +1139,17 @@ class PeachHTML5Editor if @cursor? @move_cursor_into_view screen_y + @wrap2.scrollTop on_page_down_key: (e) -> + lowest_scrollpos = @wrap2.scrollHeight - @wrap2_height + if @wrap2.scrollTop is lowest_scrollpos + return unless @cursor? + new_cursor = last_cursor_position @tree + if new_cursor? + if new_cursor.n isnt @cursor.n or new_cursor.i isnt @cursor.i + @move_cursor new_cursor + return if @cursor? screen_y = @cursor.y - @wrap2.scrollTop scroll_amount = @wrap2_height - breathing_room - lowest_scrollpos = @wrap2.scrollHeight - @wrap2_height @wrap2.scrollTop = Math.min lowest_scrollpos, @wrap2.scrollTop + scroll_amount if @cursor? @move_cursor_into_view screen_y + @wrap2.scrollTop @@ -1095,32 +1157,36 @@ class PeachHTML5Editor move_cursor_into_view: (y_target) -> return if y_target is @cursor.y was = @cursor - y_min = @wrap2.scrollTop + breathing_room - y_max = @wrap2.scrollTop + @wrap2_height - 2 * breathing_room + y_min = @wrap2.scrollTop + unless @wrap2.scrollTop is 0 + y_min += breathing_room + y_max = @wrap2.scrollTop + @wrap2_height + unless @wrap2.scrollTop is @wrap2.scrollHeight - @wrap2_height # downmost + y_max -= breathing_room y_target = Math.min y_target, y_max y_target = Math.max y_target, y_min if y_target < @cursor.y finder = find_up_cursor_position - far_enough = (a, b) -> - return a <= b + far_enough = (cur, target_y) -> + return cur.y + cur.h <= target_y else finder = find_down_cursor_position - far_enough = (a, b) -> - return a >= b + far_enough = (cur, y_target) -> + return cur.y >= y_target loop cur = finder @tree, was, @cursor_ideal_x break unless cur? - break if far_enough cur.y, y_target + break if far_enough cur, y_target was = cur if was is @cursor was = null if was? - if was.y > y_max + if was.y + was.h > y_max was = null else if was.y < y_min was = null if cur? - if cur.y > y_max + if cur.y + cur.h > y_max cur = null else if cur.y < y_min cur = null @@ -1128,7 +1194,7 @@ class PeachHTML5Editor # both valid, pick best if cur.y < y_min new_cursor = was - else if was.y > y_max + else if was.y + was.h > y_max new_cursor = cur else if cur.y - y_target < y_target - was.y new_cursor = cur