From 35349bdf9f6e58758071ed2f01ff1b65c0428288 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 24 Jul 2014 21:20:31 +0200 Subject: [PATCH] Added drag&drop ability for Redactor --- com.woltlab.wcf/templates/wysiwyg.tpl | 12 +- .../js/3rdParty/redactor/plugins/wupload.js | 147 ++++++++++++++++++ .../install/files/js/3rdParty/slimbox2.js | 6 +- .../install/files/js/3rdParty/slimbox2.min.js | 2 +- wcfsetup/install/files/js/WCF.Attachment.js | 13 +- wcfsetup/install/files/js/WCF.js | 7 +- wcfsetup/install/files/style/redactor.less | 18 ++- wcfsetup/install/lang/de.xml | 2 + wcfsetup/install/lang/en.xml | 2 + 9 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 wcfsetup/install/files/js/3rdParty/redactor/plugins/wupload.js diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index ded918779b..26e6abc364 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -25,7 +25,7 @@ $(function() { lang: '{@$__wcf->getLanguage()->getFixedLanguageCode()}', minHeight: 200, imageResizable: false, - plugins: [ 'wutil', 'wmonkeypatch', 'wbutton', 'wbbcode', 'wfontcolor', 'wfontfamily', 'wfontsize' ], + plugins: [ 'wutil', 'wmonkeypatch', 'wbutton', 'wbbcode', 'wfontcolor', 'wfontfamily', 'wfontsize', 'wupload' ], wautosave: { active: ($autosave) ? true : false, key: ($autosave) ? '{@$__wcf->getAutosavePrefix()}_' + $autosave : '', @@ -34,6 +34,15 @@ $(function() { wOriginalValue: $textarea.val() }; + {if MODULE_ATTACHMENT && !$attachmentHandler|empty && $attachmentHandler->canUpload()} + WCF.Language.addObject({ + 'wcf.attachment.dragAndDrop.dropHere': '{lang}wcf.attachment.dragAndDrop.dropHere{/lang}', + 'wcf.attachment.dragAndDrop.dropNow': '{lang}wcf.attachment.dragAndDrop.dropNow{/lang}' + }); + + $config.plugins.push('wupload'); + {/if} + {event name='javascriptInit'} $textarea.redactor($config); @@ -52,6 +61,7 @@ $(function() { '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wfontsize.js?v={@$__wcfVersion}', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wmonkeypatch.js?v={@$__wcfVersion}', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wutil.js?v={@$__wcfVersion}', + '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wupload.js?v={@$__wcfVersion}' {/if} {event name='javascriptFiles'} ], function() { diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wupload.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wupload.js new file mode 100644 index 0000000000..84b16cb976 --- /dev/null +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wupload.js @@ -0,0 +1,147 @@ +if (!RedactorPlugins) var RedactorPlugins = {}; + +/** + * Handles drag&drop upload using the attachment system for Redactor. + * + * @author Alexander Ebert + * @copyright 2001-2014 WoltLab GmbH + * @license GNU Lesser General Public License + */ +RedactorPlugins.wupload = { + _boundGlobalUploadEvents: false, + _wUploadDropArea: { }, + _timer: null, + _isDragging: false, + + /** + * Initializes the RedactorPlugins.wupload plugin. + */ + init: function() { + var $namespace = '.redactor_' + this.$source.wcfIdentify(); + $(document).on('dragover' + $namespace, $.proxy(this._dragOver, this)); + $(document).on('dragleave' + $namespace, $.proxy(this._dragLeave, this)); + $(document).on('drop' + $namespace, $.proxy(function(event) { + event.preventDefault(); + + this._revertDropArea(undefined, this.$source.wcfIdentify()); + }, this)); + + if (!this._boundGlobalUploadEvents) { + this._boundGlobalUploadEvents = true; + + $(document).on('dragend', function(event) { event.preventDefault(); }); + } + }, + + /** + * Handles an actively dragged object. + * + * @param object event + */ + _dragOver: function(event) { + event = event.originalEvent; + + if (!event.dataTransfer || !event.dataTransfer.types) { + return; + } + + // IE and WebKit set 'Files', Firefox sets 'application/x-moz-file' for files being dragged + if (event.dataTransfer.types[0] !== 'Files' && event.dataTransfer.types[0] !== 'application/x-moz-file') { + return; + } + + event.preventDefault(); + + if (!this._isDragging) { + var $containerID = this.$source.wcfIdentify(); + + if (this._wUploadDropArea[$containerID] === undefined) { + this._wUploadDropArea[$containerID] = $('
' + WCF.Language.get('wcf.attachment.dragAndDrop.dropHere') + '
').hide().appendTo(document.body); + this._wUploadDropArea[$containerID].on('dragover', $.proxy(this._hoverDropArea, this)).on('dragleave', $.proxy(this._revertDropArea, this)).on('drop', $.proxy(this._drop, this)); + } + + // adjust dimensions + var $dimensions = (this.inWysiwygMode()) ? this.$editor.getDimensions('outer') : this.$source.getDimensions('outer'); + var $position = (this.inWysiwygMode()) ? this.$editor.getOffsets('offset') : this.$source.getOffsets('offset'); + + this._wUploadDropArea[$containerID].css({ + height: $dimensions.height + 'px', + left: $position.left + 'px', + lineHeight: $dimensions.height + 'px', + top: $position.top + 'px', + width: $dimensions.width + 'px' + }).show(); + + this._isDragging = true; + } + + event.preventDefault(); + }, + + /** + * Visualizes the drop area being hovered. + * + * @param object event + */ + _hoverDropArea: function(event) { + this._wUploadDropArea[this.$source.wcfIdentify()].addClass('active').text(WCF.Language.get('wcf.attachment.dragAndDrop.dropNow')); + }, + + /** + * Reverts the drop area into the initial state. + * + * @param object event + * @param string containerID + */ + _revertDropArea: function(event, containerID) { + var $containerID = containerID || this.$source.wcfIdentify(); + this._wUploadDropArea[$containerID].removeClass('active').text(WCF.Language.get('wcf.attachment.dragAndDrop.dropHere')); + + if (containerID) { + this._wUploadDropArea[$containerID].hide(); + } + }, + + /** + * Handles the object no longer being dragged. + * + * This event can fires whenever an object is hovering over a different element, there is + * a delay of 100ms before the dragging will be checked again to prevent flicker. + */ + _dragLeave: function() { + if (this._timer === null) { + var self = this; + this._timer = new WCF.PeriodicalExecuter(function(pe) { + pe.stop(); + + if (!self._isDragging) { + self._wUploadDropArea[self.$source.wcfIdentify()].hide(); + } + }, 100); + } + else { + this._timer.resume(); + } + + this._isDragging = false; + }, + + /** + * Handles the drop of the dragged object. + * + * @param object event + */ + _drop: function(event) { + event = event.originalEvent || event; + + if (event.dataTransfer && event.dataTransfer.files.length == 1) { + event.preventDefault(); + + // reset overlay + var $containerID = this.$source.wcfIdentify(); + this._revertDropArea(undefined, $containerID); + + WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'upload_' + $containerID, { file: event.dataTransfer.files[0] }); + } + } +}; diff --git a/wcfsetup/install/files/js/3rdParty/slimbox2.js b/wcfsetup/install/files/js/3rdParty/slimbox2.js index 795892ba2e..d8cbe048cc 100644 --- a/wcfsetup/install/files/js/3rdParty/slimbox2.js +++ b/wcfsetup/install/files/js/3rdParty/slimbox2.js @@ -78,6 +78,8 @@ // WoltLab modifications BEGIN if (activeImage == -1) { + WCF.System.DisableScrolling.disable(); + middle = win.scrollTop() + (win.height() / 2); centerWidth = options.initialWidth; centerHeight = options.initialHeight; @@ -85,9 +87,7 @@ compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed")); if (compatibleOverlay) overlay.style.position = "absolute"; $(overlay).css("opacity", options.overlayOpacity).fadeIn(options.overlayFadeDuration); - - WCF.System.DisableScrolling.disable(); - + position(); setup(1); } diff --git a/wcfsetup/install/files/js/3rdParty/slimbox2.min.js b/wcfsetup/install/files/js/3rdParty/slimbox2.min.js index b6bb1574e3..2977d1bd18 100644 --- a/wcfsetup/install/files/js/3rdParty/slimbox2.min.js +++ b/wcfsetup/install/files/js/3rdParty/slimbox2.min.js @@ -5,4 +5,4 @@ Modified by WoltLab GmbH to support large images. */ -(function(e){function L(){var n=t.scrollLeft(),r=t.width();e([b,T]).css("left",n+r/2);if(a)e(y).css({left:n,top:t.scrollTop(),width:r,height:t.height()})}function A(n){if(n){e("object").add(h?"select":"embed").each(function(e,t){p[e]=[t,t.style.visibility];t.style.visibility="hidden"})}else{e.each(p,function(e,t){t[0].style.visibility=t[1]});p=[]}var r=n?"bind":"unbind";t[r]("scroll resize",L);e(document)[r]("keydown",O)}function O(t){var r=t.which,i=e.inArray;return i(r,n.closeKeys)>=0?j():i(r,n.nextKeys)>=0?_():i(r,n.previousKeys)>=0?M():null}function M(){return D(o)}function _(){return D(u)}function D(t){if(t>=0){i=t;s=r[i][0];o=(i||(n.loop?r.length:0))-1;u=(i+1)%r.length||(n.loop?0:-1);B();e('').appendTo(b);v=new Image;v.onload=P;v.src=s}return false}function P(){e(w).css({backgroundImage:"url("+s+")",visibility:"hidden",display:""});e(E).width(v.width);e([E,S,x]).height(v.height);e(C).html(r[i][1]||"");e(k).html((r.length>1&&n.counterText||"").replace(/{x}/,i+1).replace(/{y}/,r.length));if(o>=0)m.src=r[o][0];if(u>=0)g.src=r[u][0];l=w.offsetWidth;c=w.offsetHeight;var t=e(window).getDimensions();var a=Math.round(t.height*.8);var h=Math.round(t.width*.8);if(c>a||l>h){var p=a/c;var d=h/l;if(p=0)e(S).show();if(u>=0)e(x).show();e(N).css("marginTop",-N.offsetHeight).animate({marginTop:0},n.captionAnimationDuration);T.style.visibility=""}function B(){v.onload=null;v.src=m.src=g.src=s;e([b,w,N]).stop(true);e([S,x,w,T]).hide()}function j(){if(i>=0){B();i=o=u=-1;e(b).hide();e(y).stop().fadeOut(n.overlayFadeDuration,A);WCF.System.DisableScrolling.enable()}return false}var t=e(window),n,r,i=-1,s,o,u,a,f,l,c,h=!window.XMLHttpRequest,p=[],d=document.documentElement,v={},m=new Image,g=new Image,y,b,w,E,S,x,T,N,C,k;e(function(){e("body").append(e([y=e('
').click(j)[0],b=e('
')[0],T=e('
')[0]]).css("display","none"));w=e('
').appendTo(b).append(E=e('
').append([S=e('').click(M)[0],x=e('').click(_)[0]])[0])[0];N=e('
').appendTo(T).append([e('').click(j)[0],C=e('
')[0],k=e('
')[0],e('
')[0]])[0]});e.slimbox=function(i,s,o){n=e.extend({loop:false,overlayOpacity:.8,overlayFadeDuration:400,resizeDuration:400,resizeEasing:"swing",initialWidth:250,initialHeight:250,imageFadeDuration:400,captionAnimationDuration:400,counterText:"Image {x} of {y}",closeKeys:[27,88,67],previousKeys:[37,80],nextKeys:[39,78]},o);if(typeof i=="string"){i=[[i,s]];s=0}f=t.scrollTop()+t.height()/2;l=n.initialWidth;c=n.initialHeight;e(b).css({top:Math.max(0,f-c/2),width:l,height:c,marginLeft:-l/2}).show();a=h||y.currentStyle&&y.currentStyle.position!="fixed";if(a)y.style.position="absolute";e(y).css("opacity",n.overlayOpacity).fadeIn(n.overlayFadeDuration);WCF.System.DisableScrolling.disable();L();A(1);r=i;n.loop=n.loop&&r.length>1;return D(s)};e.fn.slimbox=function(t,n,r){n=n||function(e){return[e.href,e.title]};r=r||function(){return true};var i=this;return i.unbind("click").click(function(){var s=this,o=0,u,a=0,f;u=e.grep(i,function(e,t){return r.call(s,e,t)});for(f=u.length;a=0?j():i(r,n.nextKeys)>=0?_():i(r,n.previousKeys)>=0?M():null}function M(){return D(o)}function _(){return D(u)}function D(t){if(t>=0){i=t;s=r[i][0];o=(i||(n.loop?r.length:0))-1;u=(i+1)%r.length||(n.loop?0:-1);B();e('').appendTo(b);v=new Image;v.onload=P;v.src=s}return false}function P(){e(w).css({backgroundImage:"url("+s+")",visibility:"hidden",display:""});e(E).width(v.width);e([E,S,x]).height(v.height);e(C).html(r[i][1]||"");e(k).html((r.length>1&&n.counterText||"").replace(/{x}/,i+1).replace(/{y}/,r.length));if(o>=0)m.src=r[o][0];if(u>=0)g.src=r[u][0];l=w.offsetWidth;c=w.offsetHeight;var t=e(window).getDimensions();var a=Math.round(t.height*.8);var h=Math.round(t.width*.8);if(c>a||l>h){var p=a/c;var d=h/l;if(p=0)e(S).show();if(u>=0)e(x).show();e(N).css("marginTop",-N.offsetHeight).animate({marginTop:0},n.captionAnimationDuration);T.style.visibility=""}function B(){v.onload=null;v.src=m.src=g.src=s;e([b,w,N]).stop(true);e([S,x,w,T]).hide()}function j(){if(i>=0){B();i=o=u=-1;e(b).hide();e(y).stop().fadeOut(n.overlayFadeDuration,A);WCF.System.DisableScrolling.enable()}return false}var t=e(window),n,r,i=-1,s,o,u,a,f,l,c,h=!window.XMLHttpRequest,p=[],d=document.documentElement,v={},m=new Image,g=new Image,y,b,w,E,S,x,T,N,C,k;e(function(){e("body").append(e([y=e('
').click(j)[0],b=e('
')[0],T=e('
')[0]]).css("display","none"));w=e('
').appendTo(b).append(E=e('
').append([S=e('').click(M)[0],x=e('').click(_)[0]])[0])[0];N=e('
').appendTo(T).append([e('').click(j)[0],C=e('
')[0],k=e('
')[0],e('
')[0]])[0]});e.slimbox=function(s,o,u){n=e.extend({loop:false,overlayOpacity:.8,overlayFadeDuration:400,resizeDuration:400,resizeEasing:"swing",initialWidth:250,initialHeight:250,imageFadeDuration:400,captionAnimationDuration:400,counterText:"Image {x} of {y}",closeKeys:[27,88,67],previousKeys:[37,80],nextKeys:[39,78]},u);if(typeof s=="string"){s=[[s,o]];o=0}if(i==-1){WCF.System.DisableScrolling.disable();f=t.scrollTop()+t.height()/2;l=n.initialWidth;c=n.initialHeight;e(b).css({top:Math.max(0,f-c/2),width:l,height:c,marginLeft:-l/2}).show();a=h||y.currentStyle&&y.currentStyle.position!="fixed";if(a)y.style.position="absolute";e(y).css("opacity",n.overlayOpacity).fadeIn(n.overlayFadeDuration);L();A(1)}r=s;n.loop=n.loop&&r.length>1;return D(o)};e.fn.slimbox=function(t,n,r){n=n||function(e){return[e.href,e.title]};r=r||function(){return true};var i=this;return i.unbind("click").click(function(){var s=this,o=0,u,a=0,f;u=e.grep(i,function(e,t){return r.call(s,e,t)});for(f=u.length;a files */ - _upload: function() { - var $files = this._fileUpload.prop('files'); + _upload: function(event, files) { + var $files = files || this._fileUpload.prop('files'); if ($files.length) { var $fd = new FormData(); var $uploadID = this._createUploadMatrix($files); diff --git a/wcfsetup/install/files/style/redactor.less b/wcfsetup/install/files/style/redactor.less index 13987b62dd..74dc887af4 100644 --- a/wcfsetup/install/files/style/redactor.less +++ b/wcfsetup/install/files/style/redactor.less @@ -334,4 +334,20 @@ width: 20px; } } -} \ No newline at end of file +} + +.redactorDropArea { + background-color: rgba(255, 255, 204, 1); + border: 5px dashed rgba(255, 204, 0); + box-sizing: border-box; + font-size: 1.4rem; + position: absolute; + text-align: center; + vertical-align: middle; + z-index: 1000; + + &.active { + background-color: #CEF6CE; + border-color: #04B404; + } +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 117262b889..477dd37d9b 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -1662,6 +1662,8 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getFormattedAllowedExt + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index a8d5dfbe73..2182943b07 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -1629,6 +1629,8 @@ Allowed extensions: {', '|implode:$attachmentHandler->getFormattedAllowedExtensi + + -- 2.20.1