JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
can delete empty blocks with backspace (rough)
authorJason Woofenden <jason@jasonwoof.com>
Sat, 26 Mar 2016 21:31:53 +0000 (17:31 -0400)
committerJason Woofenden <jason@jasonwoof.com>
Sat, 26 Mar 2016 21:31:53 +0000 (17:31 -0400)
editor.coffee

index f025506..636bf19 100644 (file)
@@ -664,8 +664,8 @@ class PeachHTML5Editor
        constructor: (in_el, options) ->
                @options = options ? {}
                @in_el = in_el
-               @tree = null
-               @tree_parent = null # top-level nodes in @tree should have this .parent
+               @tree = null # array of Nodes, all editable content
+               @tree_parent = null # @tree is this.children. .el might === @idoc.body
                @matting = []
                @init_1_called = false # when iframes have loaded
                @outer_iframe # iframe to hold editor
@@ -923,16 +923,7 @@ class PeachHTML5Editor
                                        @move_cursor new_cursor
                                return false
                        when KEY_BACKSPACE
-                               return false unless @cursor?
-                               return false unless @cursor.i > 0
-                               @remove_character @cursor.n, @cursor.i - 1
-                               @adjust_whitespace_style @cursor.n
-                               @changed()
-                               new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i - 1
-                               if new_cursor?
-                                       @move_cursor new_cursor
-                               else
-                                       @kill_cursor()
+                               @on_key_backspace e
                                return false
                        when KEY_DELETE
                                return false unless @cursor?
@@ -1019,6 +1010,60 @@ class PeachHTML5Editor
                throw 'bork bork' unless new_cursor?
                @move_cursor new_cursor
                # TODO move content past cursor into this new block
+       on_key_backspace: (e) ->
+               return false unless @cursor?
+               if @is_lone_space @cursor.n # false if it's not in a tag
+                       if @cursor.i is 1
+                               # don't delete the space, because then it would collapse
+                               # instead leave a space after the cursor
+                               new_cursor = new_cursor_position n: @cursor.n, i: 0
+                               if new_cursor?
+                                       @move_cursor new_cursor
+                               else
+                                       @kill_cursor()
+                       else
+                               parent = @cursor.n.parent
+                               new_cursor = find_prev_cursor_position @tree, @cursor
+                               if new_cursor?
+                                       if new_cursor.n is @cursor.n or new_cursor.n is parent
+                                               new_cursor = null
+                               tag = @cursor.n.parent
+                               if tag is @tree_parent
+                                       console.log "top-level text not supported" # FIXME
+                                       return false
+                               for n, i in tag.parent.children
+                                       if n is tag
+                                               tag.parent.el.removeChild tag.el
+                                               tag.parent.children.splice i, 1
+                                               break
+                               @changed()
+                               if new_cursor?
+                                       # re-check, in case it moved or is invalid now
+                                       new_cursor = new_cursor_position n: new_cursor.n, i: new_cursor.i
+                                       if new_cursor?
+                                               @move_cursor new_cursor
+                                               return
+                               new_cursor = first_cursor_position @tree
+                               if new_cursor?
+                                       @move_cursor new_cursor
+                               else
+                                       @kill_cursor
+                               return
+               else if @cursor.i is 0
+                       console.log 'not implemented yet'
+                       # TODO if block, merge parent into prev
+                       # TODO if inline, delete char from prev text node
+                       return false
+               else
+                       # TODO handle case of removing last char
+                       @remove_character @cursor.n, @cursor.i - 1
+                       @adjust_whitespace_style @cursor.n
+                       @changed()
+                       new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i - 1
+                       if new_cursor?
+                               @move_cursor new_cursor
+                       else
+                               @kill_cursor()
        clear_dom: -> # remove all the editable content (and cursor, overlays, etc)
                while @idoc.body.childNodes.length
                        @idoc.body.removeChild @idoc.body.childNodes[0]
@@ -1026,9 +1071,12 @@ class PeachHTML5Editor
                return
        load_html: (html) ->
                @tree = peach_parser.parse html, @parser_opts
+               if !@tree[0]?.parent
+                       @tree = peach_parser.parse '<p style="white-space: pre-wrap"> </p>', @parser_opts
                @tree_parent = @tree[0]?.parent
+               @tree_parent.el = @idoc.body
                @clear_dom()
-               instantiate_tree @tree, @idoc.body
+               instantiate_tree @tree, @tree_parent.el
                tree_dedup_space @tree
                @changed()
        changed: ->
@@ -1107,6 +1155,16 @@ class PeachHTML5Editor
                                if needle is n.attrs.style.substr n.attrs.style.length - needle
                                        n.attrs.style = n.attrs.style.substr 0, n.attrs.style.length - needle
                                        n.el.setAttribute n.attrs.style
+       # true if n is text node with just a space in it, and the only child of a tag
+       is_lone_space: (n, i) ->
+               return false unless n.type is 'text'
+               return false unless n.text is ' '
+               return false if n.parent is @tree_parent
+               if n.parent.children.length is 1
+                       if n.parent.children[0] is n
+                               # n is only child
+                               return true
+               return false
        # detect 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
        insert_should_replace: (n, i) ->
@@ -1120,7 +1178,7 @@ class PeachHTML5Editor
                return false
        # after calling this, you MUST call changed() and adjust_whitespace_style()
        insert_character: (n, i, char) ->
-               return if @cursor.n.parent is @tree_parent # top-level text not supported atm
+               return if @cursor.n.parent is @tree_parent # FIXME implement text nodes at top level
                parent = @cursor.n.parent
                # insert the character
                if @insert_should_replace n, i
@@ -1197,7 +1255,7 @@ class PeachHTML5Editor
                return unless n?
                prev_bounds = x: 0, y: 0, w: 0, h: 0
                alpha = 0.1
-               while n?.el?
+               while n?.el? and n isnt @tree_parent
                        if n.type is 'text'
                                n = n.parent
                                continue