+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 tree (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 = (tree, n, i) ->
+ if n? and n.type is TYPE_TEXT and n.text.length > i
+ orig_xyh = cursor_to_xyh n, i
+ for next_i in [i+1 .. n.text.length] # inclusive is valid (after last char)
+ next_xyh = cursor_to_xyh n, next_i
+ if next_xyh.x > orig_xyh.x or next_xyh.y > orig_xyh.y
+ return [n, next_i]
+ found = traverse_tree tree, 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 = (tree, n, i) ->
+ if n? and n.type is TYPE_TEXT and i > 0
+ orig_xyh = cursor_to_xyh n, i
+ for prev_i in [i-1 .. 0]
+ prev_xyh = cursor_to_xyh n, prev_i
+ if prev_xyh.x < orig_xyh.x or prev_xyh.y < orig_xyh.y
+ return [n, prev_i]
+ return [n, i - 1]
+ found = traverse_tree tree, 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
+
+find_loc_cursor_position = (tree, loc) ->
+ for c in tree
+ if c.type is TYPE_TAG or c.type is TYPE_TEXT
+ bounds = get_el_bounds c.el
+ continue if loc.x < bounds.x
+ continue if loc.x > bounds.x + bounds.w
+ continue if loc.y < bounds.y
+ continue if loc.y > bounds.y + bounds.h
+ if c.children.length
+ ret = find_loc_cursor_position c.children, loc