From 58267a982cec8dfe51a22b817f80d082edac4e10 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 12 Sep 2016 13:17:23 +0200 Subject: [PATCH] Added image paste from clipboard --- com.woltlab.wcf/templates/wysiwyg.tpl | 5 +- .../redactor2/plugins/WoltLabAttachment.js | 17 +++- .../redactor2/plugins/WoltLabModal.js | 5 +- .../redactor2/plugins/WoltLabPaste.js | 91 +++++++++++++++++++ wcfsetup/install/files/js/WCF.Attachment.js | 46 ++++++++-- wcfsetup/install/files/js/WCF.Message.js | 14 --- wcfsetup/install/files/style/ui/redactor.scss | 5 + 7 files changed, 155 insertions(+), 28 deletions(-) diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index 3b1d51de44..181f88eb9f 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -113,9 +113,10 @@ var config = { buttons: buttons, - clipboardImageUpload: false, + clipboardImageUpload: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('img')}true{else}false{/if}, formatting: ['p', 'h2', 'h3', 'h4'], imageCaption: false, + imageUpload: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('img')}true{else}false{/if}, lang: 'wsc', // fake language to offload phrases langs: { wsc: { @@ -164,7 +165,7 @@ linkify: false, linkSize: 0xBADC0DED, // some random value to disable truncating minHeight: 200, - pasteImages: false, + pasteImages: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('img')}true{else}false{/if}, plugins: [ // Imperavi 'alignment', diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabAttachment.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabAttachment.js index 79fd1e700f..8930abf2b5 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabAttachment.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabAttachment.js @@ -4,8 +4,9 @@ $.Redactor.prototype.WoltLabAttachment = function() { return { init: function() { require(['EventHandler'], (function(EventHandler) { - EventHandler.add('com.woltlab.wcf.redactor2', 'insertAttachment_' + this.$element[0].id, this.WoltLabAttachment._insert.bind(this)) - EventHandler.add('com.woltlab.wcf.redactor2', 'deleteAttachment_' + this.$element[0].id, this.WoltLabAttachment._delete.bind(this)) + EventHandler.add('com.woltlab.wcf.redactor2', 'insertAttachment_' + this.$element[0].id, this.WoltLabAttachment._insert.bind(this)); + EventHandler.add('com.woltlab.wcf.redactor2', 'deleteAttachment_' + this.$element[0].id, this.WoltLabAttachment._delete.bind(this)); + EventHandler.add('com.woltlab.wcf.redactor2', 'replaceAttachment_' + this.$element[0].id, this.WoltLabAttachment._replaceAttachment.bind(this)); }).bind(this)); }, @@ -21,6 +22,18 @@ $.Redactor.prototype.WoltLabAttachment = function() { // non-image attachment this.insert.text('[attach=' + attachmentId + '][/attach]'); } + + this.buffer.set(); + }, + + _replaceAttachment: function (data) { + var img = elCreate('img'); + img.className = 'woltlabAttachment'; + img.src = data.src; + elData(img, 'attachment-id', data.attachmentId); + + data.img.parentNode.insertBefore(img, data.img); + elRemove(data.img); }, _delete: function(data) { diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabModal.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabModal.js index df78cd349b..db2f9b7971 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabModal.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabModal.js @@ -12,7 +12,10 @@ $.Redactor.prototype.WoltLabModal = function() { close: function() { this.selection.restore(); - _uiDialog.close(this); + // avoid calling `close()` without any dialogs opened before + if (_uiDialog.getDialog(this)) { + _uiDialog.close(this); + } }, load: function(templateName, title) { diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabPaste.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabPaste.js index c4027eae50..52285e2a28 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabPaste.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabPaste.js @@ -36,6 +36,97 @@ $.Redactor.prototype.WoltLabPaste = function() { // rebind paste event this.core.editor().off('paste.redactor').on('paste.redactor', this.paste.init.bind(this)); + + this.paste.detectClipboardUpload = (function (e) { + e = e.originalEvent || e; + + var clipboard = e.clipboardData; + + // WoltLab modification: allow Edge + if (this.detect.isIe() && (this.detect.isIe() !== 'edge' || document.documentMode)) + { + return true; + } + + if (this.detect.isFirefox()) + { + return false; + } + + // prevent safari fake url + var types = clipboard.types; + // WoltLab modification: `DataTransfer.types` is a `DOMStringList` in Edge + if (Array.isArray(types) && types.indexOf('public.tiff') !== -1) + { + e.preventDefault(); + return false; + } + + + if (!clipboard.items || !clipboard.items.length) + { + return; + } + + var file = clipboard.items[0].getAsFile(); + if (file === null) + { + return false; + } + + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = $.proxy(this.paste.insertFromClipboard, this); + + return true; + }).bind(this); + + this.paste.insertFromClipboard = (function (e) { + if (!window.FormData) { + return; + } + + this.buffer.set(); + + WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this.$element[0].id, { + blob: this.utils.dataURItoBlob(e.target.result) + }); + }).bind(this); + + this.paste.clipboardUpload = (function () { + elBySelAll('img', this.$editor[0], (function (img) { + if (!window.FormData || img.src.indexOf('data:image') !== 0) { + return; + } + + this.buffer.set(); + + elHide(img); + + WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this.$element[0].id, { + blob: this.utils.dataURItoBlob(img.src), + replace: img + }); + }).bind(this)); + }).bind(this); + + var mpInsert = this.paste.insert; + this.paste.insert = (function(html, data) { + if (!data.pre && !data.text) { + var div = elCreate('div'); + div.innerHTML = html; + + elBySelAll('img', this.$editor[0], function (img) { + if (img.src.indexOf('data:image') === 0) { + elHide(img); + } + }); + + html = div.innerHTML; + } + + mpInsert.call(this, html, data); + }).bind(this); } }; }; diff --git a/wcfsetup/install/files/js/WCF.Attachment.js b/wcfsetup/install/files/js/WCF.Attachment.js index 5d3ad7c120..63f14bc21c 100644 --- a/wcfsetup/install/files/js/WCF.Attachment.js +++ b/wcfsetup/install/files/js/WCF.Attachment.js @@ -53,6 +53,12 @@ WCF.Attachment.Upload = WCF.Upload.extend({ */ _editorId: '', + /** + * replace img element on load + * @var Object + */ + _replaceOnLoad: {}, + /** * @see WCF.Upload.init() */ @@ -87,6 +93,7 @@ WCF.Attachment.Upload = WCF.Upload.extend({ WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'submit_' + this._editorId, this._submitInline.bind(this)); WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'reset_' + this._editorId, this._reset.bind(this)); WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'dragAndDrop_' + this._editorId, this._editorUpload.bind(this)); + WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this._editorId, this._editorUpload.bind(this)); var metacodeAttachUuid = WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'metacode_attach', (function(data) { var images = this._getImageAttachments(); @@ -117,6 +124,7 @@ WCF.Attachment.Upload = WCF.Upload.extend({ WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'reset_' + this._editorId); WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'insertAttachment_' + this._editorId); WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'dragAndDrop_' + this._editorId); + WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this._editorId); WCF.System.Event.removeListener('com.woltlab.wcf.redactor2', 'metacode_attach', metacodeAttachUuid); }).bind(this)); @@ -129,7 +137,7 @@ WCF.Attachment.Upload = WCF.Upload.extend({ * @param object data */ _editorUpload: function(data) { - var $uploadID; + var $uploadID, replace = null; // show tab this._fileListSelector.closest('.messageTabMenu').messageTabMenu('showTab', 'attachments', true); @@ -139,9 +147,16 @@ WCF.Attachment.Upload = WCF.Upload.extend({ } else { $uploadID = this._upload(undefined, undefined, data.blob); + replace = data.replace || null; + } + + if (replace === null) { + this._autoInsert.push($uploadID); + } + else { + this._replaceOnLoad[$uploadID] = replace; } - this._autoInsert.push($uploadID); data.uploadID = $uploadID; }, @@ -227,9 +242,7 @@ WCF.Attachment.Upload = WCF.Upload.extend({ } if (!$listItems.length) { - setTimeout((function() { - this._fileListSelector.wcfBlindOut(); - }).bind(this), 250); + this._fileListSelector.hide(); } if (this._editorId && data.button) { @@ -374,6 +387,21 @@ WCF.Attachment.Upload = WCF.Upload.extend({ $insertPlain.appendTo($buttonList).children('span.button').click($.proxy(this._insert, this)); } } + + if (this._replaceOnLoad.hasOwnProperty(uploadID)) { + if (!$li.hasClass('uploadFailed')) { + var img = this._replaceOnLoad[uploadID]; + if (img && img.parentNode) { + WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'replaceAttachment_' + this._editorId, { + attachmentId: attachmentData.attachmentID, + img: img, + src: (attachmentData.thumbnailURL) ? attachmentData.thumbnailURL : attachmentData.url + }); + } + } + + this._replaceOnLoad[uploadID] = null; + } } else { // upload icon @@ -400,10 +428,10 @@ WCF.Attachment.Upload = WCF.Upload.extend({ this._autoInsert.splice(this._autoInsert.indexOf(uploadID), 1); if (!$li.hasClass('uploadFailed')) { - WCF.System.Event.fireEvent('com.woltlab.wcf.attachment', 'autoInsert_' + this._editorId, { - attachment: '[attach=' + data.returnValues.attachments[$internalFileID].attachmentID + '][/attach]', - uploadID: uploadID - }); + var btn = $li.find('.jsButtonAttachmentInsertThumbnail'); + if (!btn.length) btn = $li.find('.jsButtonAttachmentInsertFull'); + + btn.trigger('click'); } } } diff --git a/wcfsetup/install/files/js/WCF.Message.js b/wcfsetup/install/files/js/WCF.Message.js index 9077830e32..4a041c2cb7 100644 --- a/wcfsetup/install/files/js/WCF.Message.js +++ b/wcfsetup/install/files/js/WCF.Message.js @@ -2153,12 +2153,6 @@ WCF.Message.UserMention = Class.extend({ * Provides a specialized tab menu used for message options, integrates better into the editor. */ $.widget('wcf.messageTabMenu', { - /** - * pointer span - * @var jQuery - */ - _span: null, - /** * list of existing tabs and their containers * @var array @@ -2192,9 +2186,6 @@ $.widget('wcf.messageTabMenu', { return; } - // pointer span - this._span = $('').appendTo($nav); - var $preselect = this.element.data('preselect'); // check for tabs containing '.innerError' and select the first matching one instead @@ -2302,11 +2293,6 @@ $.widget('wcf.messageTabMenu', { }); } - this._span.css({ - transform: 'translateX(' + $target.tab[0].offsetLeft + 'px)', - width: $target.tab[0].clientWidth + 'px' - }); - $(window).trigger('resize'); }, diff --git a/wcfsetup/install/files/style/ui/redactor.scss b/wcfsetup/install/files/style/ui/redactor.scss index df7c19fe16..78750924ee 100644 --- a/wcfsetup/install/files/style/ui/redactor.scss +++ b/wcfsetup/install/files/style/ui/redactor.scss @@ -109,6 +109,11 @@ // show cursor to indicate editing capability excluding smilies cursor: pointer; } + + /* prevent flicker from pasted images */ + &[src^="data:image"] { + display: none !important; + } } // TODO: this is somewhat out of sync -- 2.20.1