Implemented issue #707 - Edge looses text formatting on reply (may also on other browsers).

This commit is contained in:
Martin Edenhofer 2017-06-21 17:49:35 +02:00
parent ef282e187b
commit 62e5be1ed2
4 changed files with 200 additions and 63 deletions

View file

@ -391,19 +391,19 @@ class App.TicketZoomArticleActions extends App.Controller
body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html() || ''
# check if quote need to be added
selectedText = App.ClipBoard.getSelected()
if selectedText
# clean selection
selectedText = App.Utils.textCleanup(selectedText)
# convert to html
selectedText = App.Utils.text2html(selectedText)
if selectedText
selectedText = "<div><br><br/></div><div><blockquote type=\"cite\">#{selectedText}</blockquote></div><div><br></div>"
selected = App.ClipBoard.getSelected('html')
if selected
selected = App.Utils.htmlCleanup(selected).html()
if !selected
selected = App.ClipBoard.getSelected('text')
if selected
selected = App.Utils.textCleanup(selected)
selected = App.Utils.text2html(selected)
if selected
selected = "<div><br><br/></div><div><blockquote type=\"cite\">#{selected}</blockquote></div><div><br></div>"
# add selected text to body
body = selectedText + body
body = selected + body
articleNew.body = body

View file

@ -6,25 +6,25 @@ class App.ClipBoard
_instance ?= new _Singleton
_instance.bind(el)
@getSelected: ->
@getSelected: (type) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.getSelected()
_instance.getSelected(type)
@getSelectedLast: ->
@getSelectedLast: (type) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.getSelectedLast()
_instance.getSelectedLast(type)
@getPosition: (el) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.getPosition(el)
@setPosition: ( el, pos ) ->
@setPosition: (el, pos) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.setPosition( el, pos )
_instance.setPosition(el, pos)
@keycode: (code) ->
if _instance == undefined
@ -33,54 +33,68 @@ class App.ClipBoard
class _Singleton
constructor: ->
@selection = ''
@selectionLast = ''
@selection =
html: ''
text: ''
@selectionLast =
html: ''
text: ''
# bind to fill selected text into
bind: (el) ->
$(el).bind('mouseup', =>
# check selection on mouse up
@selection = @_getSelected()
if @selection
@selectionLast = @selection
$(el).bind('mouseup', =>
@_updateSelection()
)
$(el).bind('keyup', (e) =>
# check selection on sonder key
if e.keyCode == 91
@selection = @_getSelected()
if @selection
@selectionLast = @selection
@_updateSelection()
# check selection of arrow keys
if e.keyCode == 37 || e.keyCode == 38 || e.keyCode == 39 || e.keyCode == 40
@selection = @_getSelected()
if @selection
@selectionLast = @selection
@_updateSelection()
)
_updateSelection: =>
for key in ['html', 'text']
@selection[key] = @_getSelected(key)
if @selection[key]
@selectionLast[key] = @selection[key]
# get cross browser selected string
_getSelected: ->
_getSelected: (type) ->
text = ''
html = ''
if window.getSelection
text = window.getSelection()
sel = window.getSelection()
text = sel.toString()
else if document.getSelection
text = document.getSelection()
sel = document.getSelection()
text = sel.toString()
else if document.selection
text = document.selection.createRange().text
if text
# text = text.toString().trim()
text = $.trim( text.toString() )
text
sel = document.selection.createRange()
text = sel.text
if type is 'text'
return $.trim(text.toString()) if text
return ''
if sel && sel.rangeCount
container = document.createElement('div')
for i in [1..sel.rangeCount]
container.appendChild(sel.getRangeAt(i-1).cloneContents())
html = container.innerHTML
html
# get current selection
getSelected: ->
@selection
getSelected: (type) ->
@selection[type]
# get latest selection
getSelectedLast: ->
@selectionLast
getSelectedLast: (type) ->
@selectionLast[type]
getPosition: (el) ->
pos = 0
@ -104,13 +118,13 @@ class _Singleton
# IE Support
if el.setSelectionRange
el.focus()
el.setSelectionRange( pos, pos )
el.setSelectionRange(pos, pos)
# Firefox support
else if el.createTextRange
range = el.createTextRange()
range.collapse(true)
range.moveEnd( 'character', pos )
range.moveEnd('character', pos)
range.moveStart('character', pos)
range.select()

View file

@ -1,5 +1,80 @@
# coffeelint: disable=no_unnecessary_double_quotes
class App.Utils
@mapAttributes:
'TABLE': ['align', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'frame', 'rules', 'sortable', 'summary', 'width', 'style']
'TD': ['abbr', 'align', 'axis', 'colspan', 'headers', 'rowspan', 'valign', 'width', 'style']
'TH': ['abbr', 'align', 'axis', 'colspan', 'headers', 'rowspan', 'scope', 'sorted', 'valign', 'width', 'style']
'TR': ['width', 'style']
@mapCss:
'TABLE': [
'background', 'background-color', 'color', 'font-size', 'vertical-align',
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'text-align',
'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-collapse', 'border-style', 'border-spacing',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
]
'TH': [
'background', 'background-color', 'color', 'font-size', 'vertical-align',
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'text-align',
'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-collapse', 'border-style', 'border-spacing',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
]
'TR': [
'background', 'background-color', 'color', 'font-size', 'vertical-align',
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'text-align',
'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-collapse', 'border-style', 'border-spacing',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
]
'TD': [
'background', 'background-color', 'color', 'font-size', 'vertical-align',
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'text-align',
'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-collapse', 'border-style', 'border-spacing',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
]
# textCleand = App.Utils.textCleanup(rawText)
@textCleanup: (ascii) ->
@ -252,26 +327,45 @@ class App.Utils
catch err
return $("<div>#{item}</div>")
@_removeAttribute: (element) ->
return if !element
if @mapAttributes[element.nodeName]
atts = element.attributes
for att in atts
if att && att.name && !_.contains(@mapAttributes[element.nodeName], att.name)
element.removeAttributeNode(att)
else
$element = $(element)
$element.removeAttr('style')
$element.removeAttr('class')
$element.removeAttr('title')
$element.removeAttr('lang')
$element.removeAttr('type')
$element.removeAttr('id')
$element.removeAttr('wrap')
$element.removeAttrs(/data-/)
if @mapCss[element.nodeName]
style = element.getAttributeNode('style')
if style && style.nodeValue && style.nodeValue.split
styleNew = ''
for local_pear in style.nodeValue.split(';')
prop = local_pear.split(':')
if prop[0] && prop[0].trim
key = prop[0].trim()
if _.contains(@mapCss[element.nodeName], key)
styleNew += "#{local_pear};"
if styleNew isnt ''
style.nodeValue = styleNew
element.setAttributeNode(style)
else
element.removeAttributeNode(style)
@_removeAttributes: (html, parent = true) ->
if parent
html.find('*')
.removeAttr('style')
.removeAttr('class')
.removeAttr('title')
.removeAttr('lang')
.removeAttr('type')
.removeAttr('id')
.removeAttr('wrap')
.removeAttrs(/data-/)
html
.removeAttr('style')
.removeAttr('class')
.removeAttr('title')
.removeAttr('lang')
.removeAttr('type')
.removeAttr('id')
.removeAttr('wrap')
.removeAttrs(/data-/)
html.each((index, element) => @_removeAttribute(element) )
html.find('*').each((index, element) => @_removeAttribute(element) )
html
@_removeComments: (html) ->

View file

@ -582,6 +582,35 @@ test("htmlCleanup", function() {
result = App.Utils.htmlCleanup(source)
equal(result.html(), should, source)
source = "<table bgcolor=\"green\" aaa=\"1\"><thead><tr><th colspan=\"2\" abc=\"a\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
should = "<table bgcolor=\"green\"><thead><tr><th colspan=\"2\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
result = App.Utils.htmlCleanup(source)
equal(result.get(0).outerHTML, should, source)
source = "<table bgcolor=\"green\" aaa=\"1\" style=\"color: red\"><thead><tr style=\"margin-top: 10px\"><th colspan=\"2\" abc=\"a\" style=\"margin-top: 12px\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
should = "<table bgcolor=\"green\" style=\"color: red;\"><thead><tr style=\"margin-top: 10px;\"><th colspan=\"2\" style=\"margin-top: 12px;\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
result = App.Utils.htmlCleanup(source)
//equal(result.get(0).outerHTML, should, source) / string order is different on browsers
equal(result.first().attr('bgcolor'), 'green')
equal(result.first().attr('style'), 'color: red;')
equal(result.first().attr('aaa'), undefined)
equal(result.find('tr').first().attr('style'), 'margin-top: 10px;')
equal(result.find('th').first().attr('colspan'), '2')
equal(result.find('th').first().attr('abc'), undefined)
equal(result.find('th').first().attr('style'), 'margin-top: 12px;')
source = "<table bgcolor=\"green\" aaa=\"1\" style=\"color:red; display: none;\"><thead><tr><th colspan=\"2\" abc=\"a\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
should = "<table bgcolor=\"green\" style=\"color:red;\"><thead><tr><th colspan=\"2\">aaa</th></tr></thead><tbody><tr><td>value</td></tr></tbody></table>"
result = App.Utils.htmlCleanup(source)
//equal(result.get(0).outerHTML, should, source) / string order is different on browsers
equal(result.first().attr('bgcolor'), 'green')
equal(result.first().attr('style'), 'color:red;')
equal(result.first().attr('aaa'), undefined)
equal(result.find('tr').first().attr('style'), undefined)
equal(result.find('th').first().attr('colspan'), '2')
equal(result.find('th').first().attr('abc'), undefined)
equal(result.find('th').first().attr('style'), undefined)
});
// wrap