JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
auto-drop spaces created by enter key
[peach-html5-editor.git] / editor.coffee
index 73d863e..9e21544 100644 (file)
@@ -371,6 +371,71 @@ find_prev_cursor_position = (tree, cursor) ->
                return false
        return found # maybe null
 
+find_up_cursor_position = (tree, cursor, ideal_x) ->
+       new_cursor = cursor
+       # go prev until we're higher on y axis
+       while new_cursor.y >= cursor.y
+               new_cursor = find_prev_cursor_position tree, new_cursor
+               return null unless new_cursor?
+       # done early if we're already left of old cursor position
+       if new_cursor.x <= ideal_x
+               return new_cursor
+       target_y = new_cursor.y
+       # search leftward, until we find the closest position
+       # new_cursor is the prev-most position we've checked
+       # prev_cursor is the older value, so it's not as prev as new_cursor
+       while new_cursor.x > ideal_x and new_cursor.y is target_y
+               prev_cursor = new_cursor
+               new_cursor = find_prev_cursor_position tree, new_cursor
+               break unless new_cursor?
+       # move cursor to prev_cursor or new_cursor
+       if new_cursor?
+               if new_cursor.y is target_y
+                       # both valid, and on the same line, use closest
+                       if (ideal_x - new_cursor.x) < (prev_cursor.x - ideal_x)
+                               return new_cursor
+                       else
+                               return prev_cursor
+               else
+                       # new_cursor on wrong line, use prev_cursor
+                       return prev_cursor
+       else
+               # can't go any further prev, use prev_cursor
+               return prev_cursor
+
+find_down_cursor_position = (tree, cursor, ideal_x) ->
+       new_cursor = cursor
+       # go next until we move on the y axis
+       while new_cursor.y <= cursor.y
+               new_cursor = find_next_cursor_position tree, new_cursor
+               return null unless new_cursor?
+       # done early if we're already right of old cursor position
+       if new_cursor.x >= ideal_x
+               # this would be strange, but could happen due to runaround
+               return new_cursor
+       target_y = new_cursor.y
+       # search rightward, until we find the closest position
+       # new_cursor is the next-most position we've checked
+       # prev_cursor is the older value, so it's not as next as new_cursor
+       while new_cursor.x < ideal_x and new_cursor.y is target_y
+               prev_cursor = new_cursor
+               new_cursor = find_next_cursor_position tree, new_cursor
+               break unless new_cursor?
+       # move cursor to prev_cursor or new_cursor
+       if new_cursor?
+               if new_cursor.y is target_y
+                       # both valid, and on the same line, use closest
+                       if (new_cursor.x - ideal_x) < (ideal_x - prev_cursor.x)
+                               return new_cursor
+                       else
+                               return prev_cursor
+               else
+                       # new_cursor on wrong line, use prev_cursor
+                       return prev_cursor
+       else
+               # can't go any further prev, use prev_cursor
+               return prev_cursor
+
 xy_to_cursor = (tree, xy) ->
        for n in tree
                if n.type is 'tag' or n.type is 'text'
@@ -612,6 +677,7 @@ class PeachHTML5Editor
                @cursor = null
                @cursor_el = null
                @cursor_visible = false
+               @cursor_ideal_x = null
                @poll_for_blur_timeout = null
                opt_fragment = @options.fragment ? true
                @parser_opts = {}
@@ -826,37 +892,11 @@ class PeachHTML5Editor
                                return false
                        when KEY_UP
                                if @cursor?
-                                       new_cursor = @cursor
-                                       # go prev until we're higher on y axis
-                                       while new_cursor.y >= @cursor.y
-                                               new_cursor = find_prev_cursor_position @tree, new_cursor
-                                               return false unless new_cursor?
-                                       # done early if we're already left of old cursor position
-                                       if new_cursor.x <= @cursor.x
-                                               @move_cursor new_cursor
-                                               return false
-                                       target_y = new_cursor.y
-                                       # search leftward, until we find the closest position
-                                       # new_cursor is the prev-most position we've checked
-                                       # prev_cursor is the older value, so it's not as prev as new_cursor
-                                       while new_cursor.x > @cursor.x and new_cursor.y is target_y
-                                               prev_cursor = new_cursor
-                                               new_cursor = find_prev_cursor_position @tree, new_cursor
-                                               break unless new_cursor?
-                                       # move cursor to prev_cursor or new_cursor
+                                       new_cursor = find_up_cursor_position @tree, @cursor, @cursor_ideal_x
                                        if new_cursor?
-                                               if new_cursor.y is target_y
-                                                       # both valid, and on the same line, use closest
-                                                       if (@cursor.x - new_cursor.x) < (prev_cursor.x - @cursor.x)
-                                                               @move_cursor new_cursor
-                                                       else
-                                                               @move_cursor prev_cursor
-                                               else
-                                                       # new_cursor on wrong line, use prev_cursor
-                                                       @move_cursor prev_cursor
-                                       else
-                                               # can't go any further prev, use prev_cursor
-                                               @move_cursor prev_cursor
+                                               saved_ideal_x = @cursor_ideal_x
+                                               @move_cursor new_cursor
+                                               @cursor_ideal_x = saved_ideal_x
                                else
                                        # move cursor to first position in document
                                        new_cursor = first_cursor_position @tree
@@ -865,38 +905,11 @@ class PeachHTML5Editor
                                return false
                        when KEY_DOWN
                                if @cursor?
-                                       new_cursor = @cursor
-                                       # go next until we move on the y axis
-                                       while new_cursor.y <= @cursor.y
-                                               new_cursor = find_next_cursor_position @tree, new_cursor
-                                               return false unless new_cursor?
-                                       # done early if we're already right of old cursor position
-                                       if new_cursor.x >= @cursor.x
-                                               # this would be strange, but could happen due to runaround
-                                               @move_cursor new_cursor
-                                               return false
-                                       target_y = new_cursor.y
-                                       # search rightward, until we find the closest position
-                                       # new_cursor is the next-most position we've checked
-                                       # prev_cursor is the older value, so it's not as next as new_cursor
-                                       while new_cursor.x < @cursor.x and new_cursor.y is target_y
-                                               prev_cursor = new_cursor
-                                               new_cursor = find_next_cursor_position @tree, new_cursor
-                                               break unless new_cursor?
-                                       # move cursor to prev_cursor or new_cursor
+                                       new_cursor = find_down_cursor_position @tree, @cursor, @cursor_ideal_x
                                        if new_cursor?
-                                               if new_cursor.y is target_y
-                                                       # both valid, and on the same line, use closest
-                                                       if (new_cursor.x - @cursor.x) < (@cursor.x - prev_cursor.x)
-                                                               @move_cursor new_cursor
-                                                       else
-                                                               @move_cursor prev_cursor
-                                               else
-                                                       # new_cursor on wrong line, use prev_cursor
-                                                       @move_cursor prev_cursor
-                                       else
-                                               # can't go any further prev, use prev_cursor
-                                               @move_cursor prev_cursor
+                                               saved_ideal_x = @cursor_ideal_x
+                                               @move_cursor new_cursor
+                                               @cursor_ideal_x = saved_ideal_x
                                else
                                        # move cursor to first position in document
                                        new_cursor = last_cursor_position @tree
@@ -904,6 +917,9 @@ class PeachHTML5Editor
                                                @move_cursor new_cursor
                                return false
                        when KEY_END
+                               new_cursor = last_cursor_position @tree
+                               if new_cursor?
+                                       @move_cursor new_cursor
                                return false
                        when KEY_BACKSPACE
                                return false unless @cursor?
@@ -930,10 +946,15 @@ class PeachHTML5Editor
                                        @kill_cursor()
                                return false
                        when KEY_ENTER
+                               @on_key_enter e
                                return false
                        when KEY_ESCAPE
+                               @kill_cursor()
                                return false
                        when KEY_HOME
+                               new_cursor = first_cursor_position @tree
+                               if new_cursor?
+                                       @move_cursor new_cursor
                                return false
                        when KEY_INSERT
                                return false
@@ -959,6 +980,43 @@ class PeachHTML5Editor
                                console.log "ERROR: couldn't find cursor position after insert"
                                @kill_cursor()
                return false
+       on_key_enter: (e) -> # enter key pressed
+               return unless @cursor_visible
+               cur_block = @cursor.n
+               loop
+                       if cur_block.type is 'tag'
+                               if is_display_block cur_block.el
+                                       break
+                       return unless cur_block.parent?
+                       cur_block = cur_block.parent
+               # find array to insert new element into
+               if cur_block.parent?.el?
+                       parent_el = cur_block.parent.el
+                       pc = cur_block.parent.children
+               else
+                       parent_el = @idoc.body
+                       pc = @tree
+               # find index of current block in its parent
+               for n, i in pc
+                       break if n is cur_block
+               i += 1 # we want to be after it
+               if i < pc.length
+                       before = pc[i].el
+               else
+                       before = null
+               # TODO if content after cursor
+               #       TODO new block is empty
+               new_text = new peach_parser.Node 'text', text: ' '
+               new_node = new peach_parser.Node 'tag', name: 'p', parent: cur_block.parent, attrs: {style: 'white-space: pre-wrap'}, children: [new_text]
+               new_text.parent = new_node
+               new_text.el = domify @idoc, text: ' '
+               new_node.el = domify @idoc, p: style: 'white-space: pre-wrap', children: [new_text.el]
+               pc.splice i, 0, new_node
+               parent_el.insertBefore new_node.el, before
+               new_cursor = new_cursor_position n: new_text, i: 0
+               throw 'bork bork' unless new_cursor?
+               @move_cursor new_cursor
+               # TODO move content past cursor into this new block
        clear_dom: -> # remove all the editable content (and cursor, overlays, etc)
                while @idoc.body.childNodes.length
                        @idoc.body.removeChild @idoc.body.childNodes[0]
@@ -1053,8 +1111,22 @@ class PeachHTML5Editor
                return unless parent.el?
                # insert the character
                if i is 0
-                       n.text = char + n.text
+                       # special case: typing before a space that's the only thing in a block/doc
+                       # reason: enter key creates blocks with just a space in them
+                       special_case = false
+                       if n.text is ' '
+                               if n.parent?.el?
+                                       if n.parent.children.length is 1
+                                               if n.parent.children[0] is n
+                                                       special_case = true
+                               else
+                                       special_case = true
+                       if special_case
+                               n.text = char
+                       else
+                               n.text = char + n.text
                else if i is n.text.length
+                       # replace the space
                        n.text += char
                else
                        n.text =
@@ -1073,6 +1145,7 @@ class PeachHTML5Editor
                @cursor = null
                @annotate null
        move_cursor: (cursor) ->
+               @cursor_ideal_x = cursor.x
                @cursor = cursor
                unless @cursor_visible
                        @cursor_el = domify @outer_idoc, div: id: 'cursor'