Implemented issue #707 - Edge looses text formatting on reply (may also on other browsers).
This commit is contained in:
parent
ef282e187b
commit
62e5be1ed2
4 changed files with 200 additions and 63 deletions
|
@ -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
|
||||
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>"
|
||||
|
||||
# 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>"
|
||||
|
||||
# add selected text to body
|
||||
body = selectedText + body
|
||||
# add selected text to body
|
||||
body = selected + body
|
||||
|
||||
articleNew.body = body
|
||||
|
||||
|
|
|
@ -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
|
||||
# check selection on mouse up
|
||||
$(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()
|
||||
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue