Added drag & drop support
authorAlexander Ebert <ebert@woltlab.com>
Sun, 11 Sep 2016 10:01:00 +0000 (12:01 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 11 Sep 2016 10:01:07 +0000 (12:01 +0200)
com.woltlab.wcf/templates/wysiwyg.tpl
wcfsetup/install/files/acp/templates/wysiwyg.tpl
wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js [new file with mode: 0644]
wcfsetup/install/files/js/WCF.Attachment.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/DragAndDrop.js [new file with mode: 0644]
wcfsetup/install/files/style/ui/redactor.scss

index efbad3efaa9f20d4fd218456fe3a3ea5c8f34342..3b1d51de449d31803409aa401e72350d86330bc1 100644 (file)
@@ -17,6 +17,7 @@
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabClean.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabCode.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabColor.js?v={@LAST_UPDATE_TIME}',
+                       '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabDropdown.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabEvent.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabFont.js?v={@LAST_UPDATE_TIME}',
@@ -50,6 +51,9 @@
        ], function () {
                require(['Environment', 'Language', 'WoltLabSuite/Core/Ui/Redactor/Autosave', 'WoltLabSuite/Core/Ui/Redactor/Metacode'], function(Environment, Language, UiRedactorAutosave, UiRedactorMetacode) {
                        Language.addObject({
+                               'wcf.attachment.dragAndDrop.dropHere': '{lang}wcf.attachment.dragAndDrop.dropHere{/lang}',
+                               'wcf.attachment.dragAndDrop.dropNow': '{lang}wcf.attachment.dragAndDrop.dropNow{/lang}',
+                               
                                'wcf.editor.code.edit': '{lang}wcf.editor.code.edit{/lang}',
                                'wcf.editor.code.file': '{lang}wcf.editor.code.file{/lang}',
                                'wcf.editor.code.file.description': '{lang}wcf.editor.code.file.description{/lang}',
                                        'WoltLabClean',
                                        'WoltLabCode',
                                        'WoltLabColor',
+                                       'WoltLabDragAndDrop',
                                        'WoltLabDropdown',
                                        'WoltLabFont',
                                        'WoltLabImage',
index efbad3efaa9f20d4fd218456fe3a3ea5c8f34342..3b1d51de449d31803409aa401e72350d86330bc1 100644 (file)
@@ -17,6 +17,7 @@
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabClean.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabCode.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabColor.js?v={@LAST_UPDATE_TIME}',
+                       '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabDropdown.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabEvent.js?v={@LAST_UPDATE_TIME}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabFont.js?v={@LAST_UPDATE_TIME}',
@@ -50,6 +51,9 @@
        ], function () {
                require(['Environment', 'Language', 'WoltLabSuite/Core/Ui/Redactor/Autosave', 'WoltLabSuite/Core/Ui/Redactor/Metacode'], function(Environment, Language, UiRedactorAutosave, UiRedactorMetacode) {
                        Language.addObject({
+                               'wcf.attachment.dragAndDrop.dropHere': '{lang}wcf.attachment.dragAndDrop.dropHere{/lang}',
+                               'wcf.attachment.dragAndDrop.dropNow': '{lang}wcf.attachment.dragAndDrop.dropNow{/lang}',
+                               
                                'wcf.editor.code.edit': '{lang}wcf.editor.code.edit{/lang}',
                                'wcf.editor.code.file': '{lang}wcf.editor.code.file{/lang}',
                                'wcf.editor.code.file.description': '{lang}wcf.editor.code.file.description{/lang}',
                                        'WoltLabClean',
                                        'WoltLabCode',
                                        'WoltLabColor',
+                                       'WoltLabDragAndDrop',
                                        'WoltLabDropdown',
                                        'WoltLabFont',
                                        'WoltLabImage',
diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js
new file mode 100644 (file)
index 0000000..384923c
--- /dev/null
@@ -0,0 +1,11 @@
+$.Redactor.prototype.WoltLabDragAndDrop = function() {
+       "use strict";
+       
+       return {
+               init: function() {
+                       require(['WoltLabSuite/Core/Ui/Redactor/DragAndDrop'], (function (UiRedactorDragAndDrop) {
+                               UiRedactorDragAndDrop.init(this);
+                       }).bind(this));
+               }
+       };
+};
index 954227665d523abca39f87d9e95ef2df63641549..5d3ad7c120428da6ce8f5c7c9f8fb6abedf189cf 100644 (file)
@@ -86,6 +86,7 @@ WCF.Attachment.Upload = WCF.Upload.extend({
                if (this._editorId) {
                        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));
                        
                        var metacodeAttachUuid = WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'metacode_attach', (function(data) {
                                var images = this._getImageAttachments();
@@ -115,12 +116,10 @@ WCF.Attachment.Upload = WCF.Upload.extend({
                                WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'submit_' + this._editorId);
                                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.removeListener('com.woltlab.wcf.redactor2', 'metacode_attach', metacodeAttachUuid);
                        }).bind(this));
-                       
-                       // TODO
-                       //WCF.System.Event.addListener('com.woltlab.wcf.redactor', 'upload_' + this._editorId, $.proxy(this._editorUpload, this));
                }
        },
        
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/DragAndDrop.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/DragAndDrop.js
new file mode 100644 (file)
index 0000000..f57b806
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * Drag and Drop file uploads.
+ * 
+ * @author      Alexander Ebert
+ * @copyright   2001-2016 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module      WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+ */
+define(['Dictionary', 'EventHandler', 'Language'], function (Dictionary, EventHandler, Language) {
+       "use strict";
+       
+       var _didInit = false;
+       var _dragArea = new Dictionary();
+       var _isDragging = false;
+       var _isFile = false;
+       var _timerLeave = null;
+       
+       /**
+        * @exports     WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+        */
+       return {
+               /**
+                * Initializes drag and drop support for provided editor instance.
+                * 
+                * @param       {$.Redactor}    editor          editor instance
+                */
+               init: function (editor) {
+                       if (!_didInit) {
+                               this._setup();
+                       }
+                       
+                       _dragArea.set(editor.uuid, {
+                               editor: editor,
+                               element: null
+                       });
+               },
+               
+               /**
+                * Handles items dragged into the browser window.
+                * 
+                * @param       {Event}         event           drag event
+                */
+               _dragOver: function (event) {
+                       event.preventDefault();
+                       
+                       //noinspection JSUnresolvedVariable
+                       if (!event.dataTransfer || !event.dataTransfer.types) {
+                               return;
+                       }
+                       
+                       var isFirefox = false;
+                       //noinspection JSUnresolvedVariable
+                       for (var property in event.dataTransfer) {
+                               //noinspection JSUnresolvedVariable
+                               if (event.dataTransfer.hasOwnProperty(property) && property.match(/^moz/)) {
+                                       isFirefox = true;
+                                       break;
+                               }
+                       }
+                       
+                       // IE and WebKit set 'Files', Firefox sets 'application/x-moz-file' for files being dragged
+                       // and Safari just provides 'Files' along with a huge list of garbage
+                       _isFile = false;
+                       if (isFirefox) {
+                               // Firefox sets the 'Files' type even if the user is just dragging an on-page element
+                               //noinspection JSUnresolvedVariable
+                               if (event.dataTransfer.types[0] === 'application/x-moz-file') {
+                                       _isFile = true;
+                               }
+                       }
+                       else {
+                               //noinspection JSUnresolvedVariable
+                               for (var i = 0; i < event.dataTransfer.types.length; i++) {
+                                       //noinspection JSUnresolvedVariable
+                                       if (event.dataTransfer.types[i] === 'Files') {
+                                               _isFile = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       
+                       if (!_isFile) {
+                               // user is just dragging around some garbage, ignore it
+                               return;
+                       }
+                       
+                       if (_isDragging) {
+                               // user is still dragging the file around
+                               return;
+                       }
+                       
+                       _isDragging = true;
+                       
+                       _dragArea.forEach((function (data, uuid) {
+                               var editor = data.editor.$editor[0];
+                               if (!editor.parentNode) {
+                                       _dragArea.delete(uuid);
+                                       return;
+                               }
+                               
+                               var element = data.element;
+                               if (element === null) {
+                                       element = elCreate('div');
+                                       element.className = 'redactorDropArea';
+                                       elData(element, 'element-id', data.editor.$element[0].id);
+                                       elData(element, 'drop-here', Language.get('wcf.attachment.dragAndDrop.dropHere'));
+                                       elData(element, 'drop-now', Language.get('Drop now to upload'));
+                                       
+                                       element.addEventListener('dragover', function () { element.classList.add('active'); });
+                                       element.addEventListener('dragleave', function () { element.classList.remove('active'); });
+                                       element.addEventListener('drop', this._drop.bind(this));
+                                       
+                                       data.element = element;
+                               }
+                               
+                               editor.parentNode.insertBefore(element, editor);
+                               element.style.setProperty('top', editor.offsetTop + 'px', '');
+                       }).bind(this));
+               },
+               
+               /**
+                * Handles items dropped onto an editor's drop area
+                * 
+                * @param       {Event}         event           drop event
+                * @protected
+                */
+               _drop: function (event) {
+                       if (!_isFile) {
+                               return;
+                       }
+                       
+                       //noinspection JSUnresolvedVariable
+                       if (!event.dataTransfer || !event.dataTransfer.files.length) {
+                               return;
+                       }
+                       
+                       event.preventDefault();
+                       
+                       //noinspection JSCheckFunctionSignatures
+                       var elementId = elData(event.currentTarget, 'element-id');
+                       
+                       //noinspection JSUnresolvedVariable
+                       for (var i = 0, length = event.dataTransfer.files.length; i < length; i++) {
+                               //noinspection JSUnresolvedVariable
+                               EventHandler.fire('com.woltlab.wcf.redactor2', 'dragAndDrop_' + elementId, {
+                                       file: event.dataTransfer.files[i]
+                               });
+                       }
+                       
+                       // this will reset all drop areas
+                       this._dragLeave();
+               },
+               
+               /**
+                * Invoked whenever the item is no longer dragged or was dropped.
+                * 
+                * @protected
+                */
+               _dragLeave: function () {
+                       if (!_isDragging || !_isFile) {
+                               return;
+                       }
+                       
+                       if (_timerLeave !== null) {
+                               window.clearTimeout(_timerLeave);
+                       }
+                       
+                       _timerLeave = window.setTimeout(function () {
+                               if (!_isDragging) {
+                                       _dragArea.forEach(function (data) {
+                                               if (data.element && data.element.parentNode) {
+                                                       data.element.classList.remove('active');
+                                                       elRemove(data.element);
+                                               }
+                                       });
+                               }
+                               
+                               _timerLeave = null;
+                       }, 100);
+                       
+                       _isDragging = false;
+               },
+               
+               /**
+                * Binds listeners to global events.
+                * 
+                * @protected
+                */
+               _setup: function () {
+                       // discard garbage events
+                       window.addEventListener('dragstart', function (event) { event.preventDefault(); });
+                       window.addEventListener('dragend', function (event) { event.preventDefault(); });
+                       
+                       window.addEventListener('dragover', this._dragOver.bind(this));
+                       window.addEventListener('dragleave', this._dragLeave.bind(this));
+                       
+                       _didInit = true;
+               }
+       };
+});
index 10e4fc3cde2b8b6b68b8ad719e180246b59dd4c7..df7c19fe16945bce1324266be0758ecef5387c41 100644 (file)
 }
 
 .redactorDropArea {
-       background-color: rgba(255, 255, 204, 1);
-       border: 5px dashed rgba(255, 204, 0);
-       box-sizing: border-box;
-       font-size: 1.4rem; // TODO
+       align-items: center;
+       background-color: $wcfStatusInfoBackground;
+       border: 5px dashed currentColor;
+       bottom: 0;
+       color: $wcfStatusInfoText;
+       display: flex;
+       justify-content: center;
+       left: 0;
        position: absolute;
-       text-align: center;
-       vertical-align: middle;
+       right: 0;
        z-index: 360;
        
+       @include wcfFontSection;
+       
+       &::before {
+               content: attr(data-drop-here);
+       }
+       
        &.active {
-               background-color: #CEF6CE;
-               border-color: #04B404;
+               background-color: $wcfStatusSuccessBackground;
+               color: $wcfStatusSuccessText;
+               
+               &::before {
+                       content: attr(data-drop-now);
+               }
        }
 }