+ @iframe.style.height = "#{h}px"
+ @wrap2.scrollTop = s
+ # does this node have whitespace that would be collapsed by white-space: normal?
+ # note: this checks direct text children, and does _not_ recurse into child tags
+ # tag is a node with type:"tag"
+ has_collapsable_space: (tag) ->
+ for n in tag.children
+ if n.type is 'text'
+ for i in [0...n.text.length]
+ code = n.text.charCodeAt i
+ if code isnt 32 and is_space_code code
+ # tab, return
+ return true
+ # check for double spaces that don't surround insert location
+ continue if i is 0
+ if n.text.substr(i - 1, 2) is ' '
+ return true
+ if n.text.length > 0
+ if is_space_code n.text.charCodeAt 0
+ return true
+ if is_space_code n.text.charCodeAt n.text.length - 1
+ return true
+ # add/remove "white-space: pre[-wrap]" to/from style="" on tags with direct
+ # child text nodes with multiple spaces in a row, or spaces at the
+ # start/end.
+ #
+ # text inside child tags are not consulted. Child tags are expected to have
+ # this function applied to them when their content changes.
+ adjust_whitespace_style: (n) ->
+ if n.type is 'text'
+ n = n.parent
+ return unless n?.el?
+ # which css rule should be used to preserve spaces (should we need to)
+ style = @iframe.contentWindow.getComputedStyle n.el, null
+ ws = style.getPropertyValue 'white-space'
+ if ws_props[ws].space
+ preserve_rule = ws
+ else
+ preserve_rule = ws_props[ws].to_preserve
+ preserve_rule = "white-space: #{preserve_rule}"
+ if @has_collapsable_space n
+ # make sure preserve_rule exists
+ if n.el.style['white-space']
+ # FIXME check that it matches
+ return
+ if n.attrs[style]?
+ n.attrs.style += "; #{preserve_rule}"
+ else
+ n.attrs.style = preserve_rule
+ n.el.setAttribute 'style', n.attrs.style
+ else
+ # remove preserve_rule if it exists
+ return unless n.attrs.style?
+ # FIXME don't assume whitespace is just so
+ if n.attrs.style is "white-space: #{ws}"
+ delete n.attrs.style
+ n.el.removeAttribute 'style'
+ else
+ # FIXME find it in the middle and at the start
+ needle = "; white-space: #{ws}"
+ 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
+ # after calling this, you MUST call changed() and adjust_whitespace_style()