From: Jason Woofenden Date: Sun, 20 Mar 2016 00:38:26 +0000 (-0400) Subject: preserve spaces as needed while typing X-Git-Url: https://jasonwoof.com/gitweb/?p=peach-html5-editor.git;a=commitdiff_plain;h=c5c3490bada2ec19ebc300e28e1b180e5f4979de preserve spaces as needed while typing --- diff --git a/editor.coffee b/editor.coffee index 699134a..e32d662 100644 --- a/editor.coffee +++ b/editor.coffee @@ -37,6 +37,32 @@ this_url_sans_path = -> ret = ret.substr 0, clip + 1 return ret +# table too look up the properties of various values for css's white-space +ws_props = + normal: + space: false # spaces are not preserved/rendered + newline: false # newlines are not preserved/rendered + wrap: true # text is word-wrapped + to_preserve: 'pre-wrap' # to preservespaces, change white-space to this + nowrap: + space: false + newline: false + wrap: false + to_preserve: 'pre' + 'pre-line': + space: false + newline: true + wrap: true + to_preserve: 'pre-wrap' + pre: + space: true + newline: true + wrap: false + 'pre-wrap': + space: true + newline: true + wrap: true + # xml 1.0 spec, chromium and firefox accept these, plus lots of unicode chars valid_attr_regex = new RegExp '^[a-zA-Z_:][-a-zA-Z0-9_:.]*$' # html5 spec is much more lax, but chromium won't let me make at attribute with the name "4" @@ -888,31 +914,16 @@ class PeachHTML5Editor onkeypress: (e) -> return if e.ctrlKey return false if ignore_key_codes[e.keyCode]? - # return false if control_key_codes[e.keyCode]? # handled in keydown char = e.charCode ? e.keyCode if char and @cursor? char = String.fromCharCode char - if @cursor.i is 0 - @cursor.n.text = char + @cursor.n.text - else if @cursor.i is @cursor.n.text.length - 1 - @cursor.n.text += char - else - @cursor.n.text = - @cursor.n.text.substr(0, @cursor.i) + - char + - @cursor.n.text.substr(@cursor.i) - @cursor.n.el.nodeValue = @cursor.n.text + @insert_character @cursor.n, @cursor.i, char new_cursor = new_cursor_position n: @cursor.n, i: @cursor.i + 1 - unless new_cursor - # probably pressed space, and browser isn't displaying it - # FIXME insert   instead, rip it out later if possible, etc. - # for now, remove it - @cursor.n.text = - @cursor.n.text.substr(0, @cursor.i) + - @cursor.n.text.substr(@cursor.i + 1) - @cursor.n.el.nodeValue = @cursor.n.text - return false - @move_cursor new_cursor + 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) @@ -940,6 +951,88 @@ class PeachHTML5Editor @iframe.style.height = "0" @iframe.style.height = "#{h}px" @wrap2.scrollTop = s + # Warning: this does not call changed() for you + 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 + # TODO insert the character now + if i is 0 + n.text = char + n.text + else if i is n.text.length - 1 + n.text += char + else + n.text = + n.text.substr(0, i) + + 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 + remove_character: (n, i) -> + # TODO call this from delete and backspace key handlers + # TODO detect if this would result in collapsing space kill_cursor: -> # remove it, forget where it was if @cursor_visible @cursor_el.parentNode.removeChild @cursor_el