JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
cursor left/right and alnum typing work
authorJason Woofenden <jason@jasonwoof.com>
Tue, 2 Feb 2016 03:46:41 +0000 (22:46 -0500)
committerJason Woofenden <jason@jasonwoof.com>
Tue, 2 Feb 2016 03:46:41 +0000 (22:46 -0500)
editor.coffee

index e0cae20..5ec9434 100644 (file)
@@ -34,7 +34,7 @@ get_el_offset = (el) ->
 # implementation: insert a span tag where we want the cursor, and ask the
 # browser where it put that span
 cursor_to_loc = (n, i) ->
-       span = domify span: style: "height: 1em"
+       span = domify span: style: "height: 1em; width: 0", children: [domify text: "|"]
        parent = n.el.parentNode
        if i is 0
                # cursor at start of text
@@ -189,19 +189,46 @@ instantiate_tree = (tree, parent) ->
                                if c.children.length
                                        instantiate_tree c.children, c.el
 
-find_next_cursor_position = (n, i) ->
-       if n.type is TYPE_TEXT
-               if n.text.length > i
-                       return [n, i + 1]
-               return null
-       if n.type is TYPE_TAG
-               if n.children.length
-                       for c in n.children
-                               ret = find_next_cursor_position c, -1
-                               return ret if ret?
-                       return null
-               else
-                       # FIXME create an empty text node if needed depending on tag name
+traverse_tree = (tree, state, cb) ->
+       for c in tree
+               cb c, state
+               break if state.done?
+               if c.children.length
+                       traverse_tree c.children, state, cb
+                       break if state.done?
+       return state
+# find the next element in top (and decendants) that is after n and can contain text
+# TODO make it so cursor can go places that don't have text but could
+find_next_cursor_position = (top, n, i) ->
+       if n? and n.type is TYPE_TEXT and n.text.length > i
+               return [n, i + 1]
+       found = traverse_tree top, before: n?, (node, state) ->
+               if node.type is TYPE_TEXT and state.before is false
+                       state.node = node
+                       state.done = true
+               if node is n
+                       state.before = false
+       if found.node?
+               return [found.node, 0]
+       return null
+
+# TODO make it so cursor can go places that don't have text but could
+find_prev_cursor_position = (top, n, i) ->
+       if n? and n.type is TYPE_TEXT and i > 0
+               return [n, i - 1]
+       found = traverse_tree top, before: n?, (node, state) ->
+               if node.type is TYPE_TEXT
+                       unless n?
+                               state.node = node
+                               state.done = true
+                       if node is n
+                               if state.prev?
+                                       state.node = state.prev
+                               state.done = true
+                       if node
+                               state.prev = node
+       if found.node?
+               return [found.node, found.node.text.length]
        return null
 
 class PeachHTML5Editor
@@ -252,83 +279,27 @@ class PeachHTML5Editor
                        #return false if control_key_codes[e.keyCode]?
                        switch e.keyCode
                                when KEY_LEFT
-                                       return false
-                               when KEY_UP
-                                       return false
-                               when KEY_RIGHT
-                                       window.ce = @cursor_el
                                        if @cursor?
-                                               new_cursor = find_next_cursor_position @cursor...
+                                               new_cursor = find_prev_cursor_position @tree, @cursor...
                                                if new_cursor?
                                                        @move_cursor new_cursor
-                                               else
-                                                       after = false
-                                                       for c in @cursor[0].parent.children
-                                                               if after
-                                                                       new_cursor = find_next_cursor_position c, -1
-                                                                       if new_cursor?
-                                                                               @move_cursor new_cursor
-                                                                               break
-                                                               if c is @cursor[0]
-                                                                       after = true
                                        else
                                                for c in @tree
-                                                       new_cursor = find_next_cursor_position c, -1
+                                                       new_cursor = find_next_cursor_position @tree, c, -1
                                                        if new_cursor?
                                                                @move_cursor new_cursor
                                                                break
                                        return false
-                               when KEY_DOWN
-                                       return false
-                               when KEY_END
-                                       return false
-                               when KEY_BACKSPACE
-                                       return false
-                               when KEY_DELETE
-                                       return false
-                               when KEY_ENTER
-                                       return false
-                               when KEY_ESCAPE
-                                       return false
-                               when KEY_HOME
-                                       return false
-                               when KEY_INSERT
-                                       return false
-                               when KEY_PAGE_UP
-                                       return false
-                               when KEY_PAGE_DOWN
-                                       return false
-                               when KEY_TAB
-                                       return false
-               @idoc.body.onkeypress = (e) =>
-                       return if e.ctrlKey
-                       return false if ignore_key_codes[e.keyCode]?
-                       # in firefox, keyCode is only set for non-typing keys
-                       switch e.keyCode
-                               when KEY_LEFT
-                                       return false
                                when KEY_UP
                                        return false
                                when KEY_RIGHT
-                                       console.log "hi"
-                                       console.log @cursor
                                        if @cursor?
-                                               new_cursor = find_next_cursor_position @cursor...
+                                               new_cursor = find_next_cursor_position @tree, @cursor...
                                                if new_cursor?
                                                        @move_cursor new_cursor
-                                               else
-                                                       after = false
-                                                       for c in @cursor[0].parent.children
-                                                               if after
-                                                                       new_cursor = find_next_cursor_position c, -1
-                                                                       if new_cursor?
-                                                                               @move_cursor new_cursor
-                                                                               break
-                                                               if c is @cursor[0]
-                                                                       after = true
                                        else
                                                for c in @tree
-                                                       new_cursor = find_next_cursor_position c, -1
+                                                       new_cursor = find_prev_cursor_position @tree, c, -1
                                                        if new_cursor?
                                                                @move_cursor new_cursor
                                                                break
@@ -355,9 +326,24 @@ class PeachHTML5Editor
                                        return false
                                when KEY_TAB
                                        return false
+               @idoc.body.onkeypress = (e) =>
+                       return if e.ctrlKey
+                       return false if ignore_key_codes[e.keyCode]?
+                       return false if control_key_codes[e.keyCode]? # handled in keydown
                        char = e.charCode ? e.keyCode
-                       @in_el.value += String.fromCharCode char
-                       @load_html @in_el.value
+                       if char and @cursor?
+                               char = String.fromCharCode char
+                               if @cursor[1] is 0
+                                       @cursor[0].text = char + @cursor[0].text
+                               else if @cursor[1] is @cursor[0].text.length - 1
+                                       @cursor[0].text += char
+                               else
+                                       @cursor[0].text =
+                                               @cursor[0].text.substr(0, @cursor[1]) +
+                                               char +
+                                               @cursor[0].text.substr(@cursor[1])
+                               @cursor[0].el.nodeValue = @cursor[0].text
+                               @move_cursor [@cursor[0], @cursor[1] + 1]
                        return false
                if options.stylesheet # TODO test this
                        istyle = @idoc.createElement 'style'
@@ -380,12 +366,14 @@ class PeachHTML5Editor
                @clear_dom()
                instantiate_tree @tree, @idoc.body
        move_cursor: (cursor) ->
+               return if @cursor? and cursor? and @cursor[0] is cursor[0] and @cursor[1] is cursor[1]
                @cursor = cursor
-               unless @cursor_el?
-                       @cursor_el = domify div: id: 'peach_html5_editor_cursor'
-               unless @cursor_visible
-                       @idoc.body.appendChild @cursor_el
-                       @cursor_visible = true
+               # replace cursor, to reset blink animation
+               if @cursor_visible
+                       @cursor_el.parentNode.removeChild @cursor_el
+               @cursor_el = domify div: id: 'peach_html5_editor_cursor'
+               @idoc.body.appendChild @cursor_el
+               @cursor_visible = true
                # TODO figure out top/left coords for cursor
                loc = cursor_to_loc cursor[0], cursor[1]
                @cursor_el.style.top = "#{loc.top}px"