From 57b00d36f6efe5e9133ffb4346086c81eb9d89c0 Mon Sep 17 00:00:00 2001 From: Jason Woofenden Date: Sat, 19 Mar 2016 21:39:47 -0400 Subject: [PATCH] auto set/clear whitespace css on delete/backspace too --- editor.coffee | 146 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/editor.coffee b/editor.coffee index 98c199c..b6f28f0 100644 --- a/editor.coffee +++ b/editor.coffee @@ -882,26 +882,26 @@ class PeachHTML5Editor when KEY_BACKSPACE return false unless @cursor? return false unless @cursor.i > 0 - @cursor.n.text = @cursor.n.text.substr(0, @cursor.i - 1) + @cursor.n.text.substr(@cursor.i) - @cursor.n.el.nodeValue = @cursor.n.text + @remove_character @cursor.n, @cursor.i - 1 + @adjust_whitespace_style @cursor.n + @changed() new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i - 1 if new_cursor? @move_cursor new_cursor else @kill_cursor() - @changed() return false when KEY_DELETE return false unless @cursor? return false unless @cursor.i < @cursor.n.text.length - @cursor.n.text = @cursor.n.text.substr(0, @cursor.i) + @cursor.n.text.substr(@cursor.i + 1) - @cursor.n.el.nodeValue = @cursor.n.text + @remove_character @cursor.n, @cursor.i + @adjust_whitespace_style @cursor.n + @changed() new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i if new_cursor? @move_cursor new_cursor else @kill_cursor() - @changed() return false when KEY_ENTER return false @@ -924,13 +924,14 @@ class PeachHTML5Editor if char and @cursor? char = String.fromCharCode char @insert_character @cursor.n, @cursor.i, char + @adjust_whitespace_style @cursor.n + @changed() new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i + 1 if new_cursor @move_cursor new_cursor else console.log "ERROR: couldn't find cursor position after insert" @kill_cursor() - @changed() return false clear_dom: -> # remove all the editable content (and cursor, overlays, etc) while @idoc.body.childNodes.length @@ -957,72 +958,72 @@ class PeachHTML5Editor @iframe.style.height = "0" @iframe.style.height = "#{h}px" @wrap2.scrollTop = s - # Warning: this does not call changed() for you + # 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() insert_character: (n, i, char) -> - # TODO handle newlines, tabs, etc parent = @cursor.n.parent return unless parent return unless parent.el? - style = @iframe.contentWindow.getComputedStyle parent.el, null - ws = style.getPropertyValue 'white-space' - if char is ' ' - unless ws_props[ws].space - change = false - if i is 0 - # TODO check if a space at the beginning would actually get collapsed - change = true - else if i is n.text.length - change = true - # TODO check if a space at the end would actually get collapsed - else - if n.text.charAt(i - 1) is ' ' or n.text.charAt(i) is ' ' - change = true - if change - rule = "white-space: #{ws_props[ws].to_preserve}" - if parent.attrs[style]? - parent.attrs.style += "; #{rule}" - else - parent.attrs.style = rule - parent.el.setAttribute 'style', parent.attrs.style - else - # TODO test this - # inserting a visible (non-space) character - if ws_props[ws].space - if parent.el.style?['white-space'] - # This node has a "white-space" property on it - # probably created automatically by this editor - # when the user pressed space. - # Check if that's no longer needed. - need = false - for ti in [0...n.text.length] - code = n.text.charCodeAt ti - if code isnt 32 and is_space_code code - # tab, return - need = true - break - # check for double spaces that don't surround insert location - continue if ti is i - continue if ti is 0 - if n.text.substr(ti - 1, 2) is ' ' - need = true - break - if i > 0 - if 32 is n.text.charCodeAt 0 - need = true - if i < n.text.length - if 32 is n.text.charCodeAt n.text.length - 1 - need = true - unless need - # TODO don't assume whitespace is just so - if parent.attrs.style is "white-space: #{ws}" - delete parent.attrs.style - parent.el.removeAttribute 'style' - else - # FIXME find it in the middle and at the start - needle = "; white-space: #{ws}" - if needle is parent.attrs.style.substr parent.attrs.style.length - needle - parent.attrs.style = parent.attrs.style.substr 0, parent.attrs.style.length - needle - parent.el.setAttribute parent.attrs.style # insert the character if i is 0 n.text = char + n.text @@ -1034,11 +1035,10 @@ class PeachHTML5Editor char + n.text.substr(i) n.el.nodeValue = n.text - # TODO call this when the user types - # TODO detect when typing produces a collapsing space + # after calling this, you MUST call changed() and adjust_whitespace_style() remove_character: (n, i) -> - # TODO call this from delete and backspace key handlers - # TODO detect if this would result in collapsing space + n.text = n.text.substr(0, i) + n.text.substr(i + 1) + n.el.nodeValue = n.text kill_cursor: -> # remove it, forget where it was if @cursor_visible @cursor_el.parentNode.removeChild @cursor_el -- 1.7.10.4