+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
+ return [n, i + 1]
+ 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
+ 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.type is TYPE_TEXT
+ # click is within bounding box that contains all text.
+ return [c, 0] if c.text.length is 0
+ before_i = 0
+ before = cursor_to_xyh c, before_i
+ after_i = c.text.length
+ after = cursor_to_xyh c, after_i
+ if loc.y < before.y + before.h and loc.x < before.x
+ continue # before first char on first line
+ if loc.y > after.y and loc.x > after.x
+ continue # after last char on last line
+ while after_i - before_i > 1
+ cur_i = Math.round((before_i + after_i) / 2)
+ cur = cursor_to_xyh c, cur_i
+ if loc.y < cur.y or (loc.y <= cur.y + cur.h and loc.x < cur.x)
+ after_i = cur_i
+ after = cur
+ else
+ before_i = cur_i
+ before = cur
+ # which one is closest?
+ if Math.abs(before.x - loc.x) < Math.abs(after.x - loc.x)
+ return [c, before_i]
+ else
+ return [c, after_i]
+ if c.children.length
+ ret = find_loc_cursor_position c.children, loc