-# pass a node (from parser library, ie it should have .el and .text)
-remove_insignificant_whitespace = (n) ->
- changed = false
- if n.type is TYPE_TEXT
- i = 0
- while i < n.text.length
- if is_space_code n.text.charCodeAt i
- if is_space_significant n, i
- i += 1
- else
- n.el.textContent = n.text = (n.text.substr 0, i) + (n.text.substr i + 1)
- changed = true
+ iterate = (tree, cb) ->
+ for n in tree
+ if n.type is TYPE_TEXT
+ i = 0
+ while i < n.text.length # don't foreach, cb might remove chars
+ advance = cb n, i
+ if advance
+ i += 1
+ if n.type is TYPE_TAG
+ block = is_display_block n.el
+ if block
+ cb null
+ if n.children.length > 0
+ iterate n.children, cb
+ if block
+ cb null
+ # remove cur char
+ remove = ->
+ removed_char = cur.text.charAt(cur_i)
+ cur.el.textContent = cur.text = (cur.text.substr 0, cur_i) + (cur.text.substr cur_i + 1)
+ if next is cur # in same text node
+ if next_i is 0
+ throw "how is this possible?"
+ next_i -= 1
+ return true
+ # undo remove()
+ put_it_back = ->
+ cur.el.textContent = cur.text = (cur.text.substr 0, cur_i) + removed_char + (cur.text.substr cur_i)
+ if next is cur # in same text node
+ next_i += 1
+ return false
+ # return true if cur was removed from the dom (ie re-use same prev)
+ operate = ->
+ # cur definitately set
+ # prev and/or next might be null, indicating the start/end of a display:block
+ return false unless is_space_code cur.text.charCodeAt cur_i
+ bounds = text_range_bounds cur.el, cur_i, cur_i + 1
+ # consistent cases:
+ # 1. zero rects returned by getClientRects() means collapsed space
+ if bounds is null
+ return remove()
+ # 2. width greater than zero means visible space
+ if bounds.w > 0
+ return false
+ # now the weird edge cases...
+ #
+ # firefox and chromium both report zero width for characters at the end
+ # of a line where the text wraps (automatically, due to word-wrap) to
+ # the next line. These do not appear to be distinguishable from
+ # collapsed spaces via the range/bounds api, so...
+ #
+ # remove it from the dom, and if prev or next moves, put it back.
+ if prev? and not prev_px?
+ prev_px = cursor_to_xyh prev, prev_i
+ if next? and not next_px?
+ next_px = cursor_to_xyh next, next_i
+ #if prev is null and next is null
+ # parent_px = cur.parent.el.getBoundingClientRect()
+ remove()
+ if prev?
+ if prev_px?
+ new_prev_px = cursor_to_xyh prev, prev_i
+ if new_prev_px.x isnt prev_px.x or new_prev_px.y isnt prev_px.y
+ return put_it_back()