From ddb4bb5150ca22e454bd6ecf7e47626ae8f3e8d7 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 10 Feb 2020 09:06:39 +0100 Subject: [PATCH] Fixed issue #2924 - Unable to paste from clipboard in note field in ticket zoom. --- .../javascripts/app/lib/app_post/utils.coffee | 41 +++++ .../app/lib/base/jquery.contenteditable.js | 163 +++++++++--------- public/assets/tests/html_utils.js | 61 +++++++ 3 files changed, 180 insertions(+), 85 deletions(-) diff --git a/app/assets/javascripts/app/lib/app_post/utils.coffee b/app/assets/javascripts/app/lib/app_post/utils.coffee index 5a80d973c..956889672 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.coffee @@ -73,6 +73,8 @@ class App.Utils ascii = @textCleanup(ascii) #ascii = @htmlEscape(ascii) ascii = @linkify(ascii) + ascii = ascii.replace(/(\n\r|\r\n|\r)/g, "\n") + ascii = ascii.replace(/ /g, '  ') ascii = '
' + ascii.replace(/\n/g, '
') + '
' ascii.replace(/
<\/div>/g, '

') @@ -1269,3 +1271,42 @@ class App.Utils .filter (elem) -> elem? .join '/' + + @clipboardHtmlIsWithText: (html) -> + if !html + return false + + parsedHTML = jQuery(jQuery.parseHTML(html)) + + if !parsedHTML || !parsedHTML.text + return false + + if parsedHTML.text().trim().length is 0 + return false + + true + + @clipboardHtmlInsertPreperation: (htmlRaw, options) -> + if options.mode is 'textonly' + if !options.multiline + html = App.Utils.htmlRemoveTags(htmlRaw) + else + html = App.Utils.htmlRemoveRichtext(htmlRaw) + else + html = App.Utils.htmlCleanup(htmlRaw) + + htmlString = html.html() + + if !htmlString && html && html.text && html.text() + htmlString = App.Utils.text2html(html.text()) + + # as fallback, get text from htmlRaw + if !htmlString || htmlString == '' + parsedHTML = jQuery(jQuery.parseHTML(htmlRaw)) + if parsedHTML + text = parsedHTML.text().trim() + + if text + htmlString = App.Utils.text2html(text) + + htmlString diff --git a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js index 09cf6532f..985d3d4ef 100644 --- a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js +++ b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js @@ -263,11 +263,32 @@ } } - Plugin.prototype.onPaste = function (e) { - e.preventDefault() - this.log('paste') + Plugin.prototype.getHtmlFromClipboard = function(clipboardData) { + try { + return clipboardData.getData('text/html') + } + catch (e) { + console.log('Sorry, can\'t get html of clipboard because browser is not supporting it.') + return + } + } - // insert and in case, resize images + Plugin.prototype.getTextFromClipboard = function(clipboardData) { + var text + try { + text = clipboardData.getData('text/plain') + if (!text || text.length === 0) { + text = clipboardData.getData('text') + } + return text + } + catch (e) { + console.log('Sorry, can\'t get text of clipboard because browser is not supporting it.') + return + } + } + + Plugin.prototype.getClipboardData = function(e) { var clipboardData if (e.clipboardData) { // ie clipboardData = e.clipboardData @@ -281,30 +302,41 @@ else { throw "No clipboardData support" } + return clipboardData + } - if (clipboardData && clipboardData.items && clipboardData.items[0]) { - var imageInserted = false - var item + Plugin.prototype.getClipboardDataImage = function(clipboardData) { + if (!clipboardData.items || !clipboardData.items[0]) { + return + } + return $.grep(clipboardData.items, function(item){ + return item.kind == 'file' && (item.type == 'image/png' || item.type == 'image/jpeg') + })[0] + } - // look for image only if no HTML with textual content is available. - // E.g. Excel provides images of the spreadsheet along with HTML. - // While some browsers make images available in clipboard as HTML, - // sometimes wrapped in multiple nodes. + Plugin.prototype.onPaste = function (e) { + e.preventDefault() + var clipboardData, clipboardImage, text, htmlRaw, htmlString - var rawHTML = clipboardData.getData('text/html') - var parsedHTML = jQuery(jQuery.parseHTML(rawHTML)) + this.log('paste') - if(parsedHTML.text().trim().length == 0) { - item = jQuery.grep(clipboardData.items, function(item){ - return item.kind == 'file' && (item.type == 'image/png' || item.type == 'image/jpeg') - })[0] - } + clipboardData = this.getClipboardData(e) - if (item) { - this.log('paste image', item) - console.log(item) + // look for image only if no HTML with textual content is available. + // E.g. Excel provides images of the spreadsheet along with HTML. + // While some browsers make images available in clipboard as HTML, + // sometimes wrapped in multiple nodes. + htmlRaw = this.getHtmlFromClipboard(clipboardData) - var imageFile = item.getAsFile() + if (!App.Utils.clipboardHtmlIsWithText(htmlRaw)) { + + // insert and in case, resize images + clipboardImage = this.getClipboardDataImage(clipboardData) + if (clipboardImage) { + + this.log('paste image', clipboardImage) + + var imageFile = clipboardImage.getAsFile() var reader = new FileReader() reader.onload = $.proxy(function (e) { @@ -340,74 +372,35 @@ }, this) reader.readAsDataURL(imageFile) - imageInserted = true - } else { - item = clipboardData.items[0] + return true } } - if (imageInserted) { + + // insert html + if (htmlRaw) { + htmlString = App.Utils.clipboardHtmlInsertPreperation(htmlRaw, this.options) + if (htmlString) { + this.log('insert html from clipboard', htmlString) + this.paste(htmlString) + return true + } + } + + // insert text + text = this.getTextFromClipboard(clipboardData) + if (!text) { + return false + } + htmlString = App.Utils.text2html(text) + + // check length limit + if (!this.maxLengthOk(htmlString.length)) { return } - // check existing + paste text for limit - var text, docType - try { - text = clipboardData.getData('text/html') - docType = 'html' - if (!text || text.length === 0) { - docType = 'text' - text = clipboardData.getData('text/plain') - } - if (!text || text.length === 0) { - docType = 'text2' - text = clipboardData.getData('text') - } - } - catch (e) { - console.log('Sorry, can\'t insert markup because browser is not supporting it.') - docType = 'text3' - text = clipboardData.getData('text') - } - this.log('paste', docType, text) - - if (docType == 'html') { - if (this.options.mode === 'textonly') { - if (!this.options.multiline) { - text = App.Utils.htmlRemoveTags(text) - this.log('htmlRemoveTags', text) - } - else { - this.log('htmlRemoveRichtext', text) - text = App.Utils.htmlRemoveRichtext(text) - } - } - else { - this.log('htmlCleanup', text) - text = App.Utils.htmlCleanup(text) - } - text = text.html() - this.log('text.html()', text) - - // as fallback, take text - if (!text) { - text = App.Utils.text2html(text.text()) - this.log('text2html', text) - } - } - else { - text = App.Utils.text2html(text) - this.log('text2html', text) - } - - if (!this.maxLengthOk(text.length)) { - return - } - - // cleanup - text = App.Utils.removeEmptyLines(text) - this.log('insert', text) - - this.paste(text) + htmlString = App.Utils.removeEmptyLines(htmlString) + this.log('insert text from clipboard', htmlString) + this.paste(htmlString) return true } diff --git a/public/assets/tests/html_utils.js b/public/assets/tests/html_utils.js index a409a1674..37234a117 100644 --- a/public/assets/tests/html_utils.js +++ b/public/assets/tests/html_utils.js @@ -63,6 +63,35 @@ test("text2html", function() { result = App.Utils.text2html(source) equal(result, should, source) + source = "Some\nValue\n" + should = "
Some
Value
" + result = App.Utils.text2html(source) + equal(result, should, source) + + source = "Some\rValue\r" + should = "
Some
Value
" + result = App.Utils.text2html(source) + equal(result, should, source) + + source = "Some\n\rValue\n\r" + should = "
Some
Value
" + result = App.Utils.text2html(source) + equal(result, should, source) + + source = "Some\r\nValue\r\n" + should = "
Some
Value
" + result = App.Utils.text2html(source) + equal(result, should, source) + + source = "Some Value 123" + should = "
Some   Value 123
" + result = App.Utils.text2html(source) + equal(result, should, source) + + source = "Some\n Value\n 123" + should = "
Some
  Value
   123
" + result = App.Utils.text2html(source) + equal(result, should, source) }); // htmlStrip @@ -3306,3 +3335,35 @@ test('App.Utils.joinUrlComponents()', function() { // expect @joinUrlComponents() to filter them out of the results before joining the rest with slashes equal(App.Utils.joinUrlComponents('foo', undefined, 'bar', null, 'baz'), 'foo/bar/baz', 'with a list including null or undefined') }); + +test('App.Utils.clipboardHtmlIsWithText()', function() { + + // no content with text + equal(App.Utils.clipboardHtmlIsWithText('
'), false) + equal(App.Utils.clipboardHtmlIsWithText('
'), false) + equal(App.Utils.clipboardHtmlIsWithText('
'), false) + equal(App.Utils.clipboardHtmlIsWithText('
'), false) + equal(App.Utils.clipboardHtmlIsWithText('
'), false) + equal(App.Utils.clipboardHtmlIsWithText("
\n
"), false) + + // content with text + equal(App.Utils.clipboardHtmlIsWithText('test'), true) + equal(App.Utils.clipboardHtmlIsWithText('
test
'), true) + equal(App.Utils.clipboardHtmlIsWithText('sometext'), true) +}); + +test('App.Utils.clipboardHtmlInsertPreperation()', function() { + equal(App.Utils.clipboardHtmlInsertPreperation('
', {}), '') + equal(App.Utils.clipboardHtmlInsertPreperation('
', {}), ' ') + equal(App.Utils.clipboardHtmlInsertPreperation('
', {}), '') + equal(App.Utils.clipboardHtmlInsertPreperation('
', {}), '') + equal(App.Utils.clipboardHtmlInsertPreperation('
', {}), ' ') + equal(App.Utils.clipboardHtmlInsertPreperation("
\n
", {}), " \n ") + equal(App.Utils.clipboardHtmlInsertPreperation('test', {}), 'test') + equal(App.Utils.clipboardHtmlInsertPreperation('
test
', {}), 'test') + equal(App.Utils.clipboardHtmlInsertPreperation('sometext', {}), '
sometext
') + equal(App.Utils.clipboardHtmlInsertPreperation('
test 123
', { mode: 'textonly' }), 'test 123') + equal(App.Utils.clipboardHtmlInsertPreperation('
test
123
', { mode: 'textonly' }), 'test 123') + equal(App.Utils.clipboardHtmlInsertPreperation('
test
123
', { mode: 'textonly', multiline: true }), 'test
123') +}); +