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
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
# 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?
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?
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
@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
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
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
# 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