Add Upload.js
authorMatthias Schmidt <gravatronics@live.com>
Sun, 27 Sep 2015 08:34:11 +0000 (10:34 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sun, 27 Sep 2015 08:34:11 +0000 (10:34 +0200)
wcfsetup/install/files/js/WCF.js
wcfsetup/install/files/js/WoltLab/WCF/Upload.js [new file with mode: 0644]

index 8cdb33fa34564d4f0490abbd71d92c7cf37772d8..e11c1725f0f3920e41fe84f5f2d1239894c11b6d 100755 (executable)
@@ -6514,7 +6514,9 @@ WCF.InlineEditor = Class.extend({
 });
 
 /**
- * Default implementation for ajax file uploads
+ * Default implementation for ajax file uploads.
+ * 
+ * @deprecated Use WoltLab/WCF/Upload
  * 
  * @param      jquery          buttonSelector
  * @param      jquery          fileListSelector
@@ -6891,6 +6893,8 @@ WCF.Upload = Class.extend({
 
 /**
  * Default implementation for parallel AJAX file uploads.
+ * 
+ * @deprecated Use WoltLab/WCF/Upload
  */
 WCF.Upload.Parallel = WCF.Upload.extend({
        /**
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Upload.js b/wcfsetup/install/files/js/WoltLab/WCF/Upload.js
new file mode 100644 (file)
index 0000000..d6123dd
--- /dev/null
@@ -0,0 +1,327 @@
+/**
+ * Uploads file via AJAX.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Upload
+ */
+define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], function(AjaxRequest, Core, DomChangeListener, Language, DomUtil) {
+       "use strict";
+       
+       /**
+        * @constructor
+        */
+       function Upload(buttonContainerId, targetId, options) {
+               options = options || {};
+               
+               if (options.className === undefined) {
+                       throw new Error("Missing class name.");
+               }
+               
+               // set default options
+               this._options = Core.extend({
+                       // name of the PHP action
+                       action: 'upload',
+                       // is true if multiple files can be uploaded at once
+                       multiple: false,
+                       // name if the upload field
+                       name: '__files[]',
+                       // is true if every file from a multi-file selection is uploaded in its own request
+                       singleFileRequests: false,
+                       // url for uploading file
+                       url: 'index.php/AJAXUpload/?t=' + SECURITY_TOKEN + SID_ARG_2ND
+               }, options);
+               
+               this._options.url = WCF.convertLegacyURL(this._options.url);
+               
+               this._buttonContainer = elById(buttonContainerId);
+               if (this._buttonContainer === null) {
+                       throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+               }
+               
+               this._target = elById(targetId);
+               if (targetId === null) {
+                       throw new Error("Element id '" + targetId + "' is unknown.");
+               }
+               if (options.multiple && this._target.nodeName !== 'UL' && this._target.nodeName !== 'OL') {
+                       throw new Error("Target element has to be list when allowing upload of multiple files.");
+               }
+               
+               this._fileElements = [];
+               this._internalFileId = 0;
+               
+               this._createButton();
+       };
+       Upload.prototype = {
+               /**
+                * Creates the upload button.
+                */
+               _createButton: function() {
+                       this._fileUpload = elCreate('input');
+                       elAttr(this._fileUpload, 'type', 'file');
+                       elAttr(this._fileUpload, 'name', this._options.name);
+                       if (this._options.multiple) {
+                               elAttr(this._fileUpload, 'multiple', 'true');
+                       }
+                       this._fileUpload.addEventListener('change', this._upload.bind(this));
+                       
+                       this._button = elCreate('p');
+                       this._button.classList.add('button');
+                       this._button.classList.add('uploadButton');
+                       
+                       var span = elCreate('span');
+                       span.textContent = Language.get('wcf.global.button.upload');
+                       this._button.appendChild(span);
+                       
+                       DomUtil.prepend(this._fileUpload, this._button);
+                       
+                       this._insertButton();
+                       
+                       DomChangeListener.trigger();
+               },
+               
+               /**
+                * Creates the document element for an uploaded file.
+                * 
+                * @param       {File}          file            uploaded file
+                */
+               _createFileElement: function(file) {
+                       var progress = elCreate('progress');
+                       elAttr(progress, 'max', 100);
+                       
+                       if (this._target.nodeName === 'OL' || this._target.nodeName === 'UL') {
+                               var li = elCreate('li');
+                               li.innerText = file.name;
+                               li.appendChild(progress);
+                               
+                               this._target.appendChild(li);
+                               
+                               return li;
+                       }
+                       else {
+                               var p = elCreate('p');
+                               p.appendChild(progress);
+                               
+                               this._target.appendChild(p);
+                               
+                               return p;
+                       }
+               },
+               
+               /**
+                * Creates the document elements for uploaded files.
+                * 
+                * @param       {(FileList|Array.<File>)}       files           uploaded files
+                */
+               _createFileElements: function(files) {
+                       if (files.length) {
+                               var uploadId = this._fileElements.length;
+                               this._fileElements[uploadId] = [];
+                               
+                               for (var i = 0, length = files.length; i < length; i++) {
+                                       var file = files[i];
+                                       var fileElement = this._createFileElement(file);
+                                       
+                                       if (!fileElement.classList.contains('uploadFailed')) {
+                                               elAttr(fileElement, 'data-filename', file.name);
+                                               elAttr(fileElement, 'data-internal-file-id', this._internalFileId++);
+                                               this._fileElements[uploadId][i] = fileElement;
+                                       }
+                               }
+                               
+                               DomChangeListener.trigger();
+                               
+                               return uploadId;
+                       }
+                       
+                       return null;
+               },
+               
+               /**
+                * Handles a failed file upload.
+                * 
+                * @param       {integer}               uploadId        identifier of a file upload
+                * @param       {object<string, *>}     data            response data
+                * @param       {string}                responseText    response
+                * @param       {XMLHttpRequest}        xhr             request object
+                * @param       {object<string, *>}     requestOptions  options used to send AJAX request
+                * @return      {boolean}       true if the error message should be shown
+                */
+               _failure: function(uploadId, data, responseText, xhr, requestOptions) {
+                       // does nothing
+                       return true;
+               },
+               
+               /**
+                * Return additional parameters for upload requests.
+                * 
+                * @return      {object<string, *>}     additional parameters
+                */
+               _getParameters: function() {
+                       return {};
+               },
+               
+               /**
+                * Inserts the created button to upload files into the button container.
+                */
+               _insertButton: function() {
+                       DomUtil.prepend(this._button, this._buttonContainer);
+               },
+               
+               /**
+                * Updates the progress of an upload.
+                * 
+                * @param       {integer}                       uploadId        internal upload identifier
+                * @param       {XMLHttpRequestProgressEvent}   event           progress event object
+                */
+               _progress: function(uploadId, event) {
+                       var percentComplete = Math.round(event.loaded / event.total * 100);
+                       
+                       for (var i in this._fileElements[uploadId]) {
+                               var progress = elByTag('PROGRESS', this._fileElements[uploadId][i]);
+                               if (progress.length === 1) {
+                                       elAttr(progress[0], 'value', percentComplete);
+                               }
+                       }
+               },
+               
+               /**
+                * Removes the button to upload files.
+                */
+               _removeButton: function() {
+                       this._button.parentNode.removeChild(this._button);
+                       
+                       DomChangeListener.trigger();
+               },
+               
+               /**
+                * Handles a successful file upload.
+                * 
+                * @param       {integer}               uploadId        identifier of a file upload
+                * @param       {object<string, *>}     data            response data
+                * @param       {string}                responseText    response
+                * @param       {XMLHttpRequest}        xhr             request object
+                * @param       {object<string, *>}     requestOptions  options used to send AJAX request
+                */
+               _success: function(uploadId, data, responseText, xhr, requestOptions) {
+                       // does nothing
+               },
+               
+               /**
+                * File input change callback to upload files.
+                * 
+                * @param       {Event}         event           input change event object
+                * @param       {File}          file            uploaded file
+                * @param       {Blob}          blob            file blob
+                * @return      {(integer|Array.<integer>|null)}        identifier(s) for the uploaded files
+                */
+               _upload: function(event, file, blob) {
+                       var uploadId = null;
+                       
+                       var files = [];
+                       if (file) {
+                               files.push(file);
+                       }
+                       else if (blob) {
+                               var fileExtension = '';
+                               switch (blob.type) {
+                                       case 'image/jpeg':
+                                               fileExtension = '.jpg';
+                                       break;
+                                       
+                                       case 'image/gif':
+                                               fileExtension = '.gif';
+                                       break;
+                                       
+                                       case 'image/png':
+                                               fileExtension = '.png';
+                                       break;
+                               }
+                               
+                               files.push({
+                                       name: 'pasted-from-clipboard' + fileExtension
+                               });
+                       }
+                       else {
+                               files = this._fileUpload.files;
+                       }
+                       
+                       if (files.length) {
+                               if (this._options.singleFileRequests) {
+                                       uploadId = [];
+                                       for (var i = 0, length = files.length; i < length; i++) {
+                                               uploadId.push(this._uploadFiles([ files[i] ], blob));
+                                       }
+                               }
+                               else {
+                                       uploadId = this._uploadFiles(files, blob);
+                               }
+                       }
+                       
+                       // re-create upload button to effectively reset the 'files'
+                       // property of the input element
+                       this._removeButton();
+                       this._createButton();
+                       
+                       return uploadId;
+               },
+               
+               /**
+                * Sends the request to upload files.
+                * 
+                * @param       {(FileList|Array.<File>)}       files           uploaded files
+                * @param       {Blob}                          blob            file blob
+                * @return      {(integer|null)}        identifier for the uploaded files
+                */
+               _uploadFiles: function(files, blob) {
+                       var uploadId = this._createFileElements(files);
+                       
+                       // no more files left, abort
+                       if (!this._fileElements[uploadId].length) {
+                               return null;
+                       }
+                       
+                       var formData = new FormData();
+                       for (var i = 0, length = files.length; i < length; i++) {
+                               if (this._fileElements[uploadId][i]) {
+                                       var internalFileId = elAttr(this._fileElements[uploadId][i], 'data-internal-file-id');
+                                       
+                                       if (blob) {
+                                               formData.append('__files[' + internalFileId + ']', blob, files[i].name);
+                                       }
+                                       else {
+                                               formData.append('__files[' + internalFileId + ']', files[i]);
+                                       }
+                               }
+                       }
+                       
+                       formData.append('actionName', this._options.action);
+                       formData.append('className', this._options.className);
+                       var additionalParameters = this._getParameters();
+                       for (var name in additionalParameters) {
+                               formData.append('parameters[' + name + ']', additionalParameters[name]);
+                       }
+                       
+                       var request = new AjaxRequest({
+                               data: formData,
+                               contentType: false,
+                               failure: function(data, responseText, xhr, requestOptions) {
+                                       this._failure(uploadId, data, responseText, xhr, options.data);
+                               }.bind(this),
+                               success: function(data, responseText, xhr, requestOptions) {
+                                       this._success(uploadId, data, responseText, xhr, requestOptions);
+                               }.bind(this),
+                               uploadProgress: (function(event) {
+                                       this._progress(uploadId, event);
+                               }).bind(this),
+                               url: this._options.url
+                       });
+                       request.sendRequest();
+                       
+                       return uploadId;
+               }
+       };
+       
+       return Upload;
+});