*/
WCF.Attachment = {};
-if (COMPILER_TARGET_DEFAULT) {
+/**
+ * Attachment upload function
+ *
+ * @see WCF.Upload
+ */
+WCF.Attachment.Upload = WCF.Upload.extend({
/**
- * Attachment upload function
- *
- * @see WCF.Upload
+ * list of upload ids which should be automatically inserted
+ * @var array<integer>
*/
- WCF.Attachment.Upload = WCF.Upload.extend({
- /**
- * list of upload ids which should be automatically inserted
- * @var array<integer>
- */
- _autoInsert: [],
-
- /**
- * reference to 'Insert All' button
- * @var jQuery
- */
- _insertAllButton: null,
-
- /**
- * object type of the object the uploaded attachments belong to
- * @var string
- */
- _objectType: '',
-
- /**
- * id of the object the uploaded attachments belong to
- * @var string
- */
- _objectID: 0,
+ _autoInsert: [],
+
+ /**
+ * reference to 'Insert All' button
+ * @var jQuery
+ */
+ _insertAllButton: null,
+
+ /**
+ * object type of the object the uploaded attachments belong to
+ * @var string
+ */
+ _objectType: '',
+
+ /**
+ * id of the object the uploaded attachments belong to
+ * @var string
+ */
+ _objectID: 0,
+
+ /**
+ * temporary hash to identify uploaded attachments
+ * @var string
+ */
+ _tmpHash: '',
+
+ /**
+ * id of the parent object of the object the uploaded attachments belongs to
+ * @var string
+ */
+ _parentObjectID: 0,
+
+ /**
+ * editor id
+ * @var string
+ */
+ _editorId: '',
+
+ /**
+ * replace img element on load
+ * @var Object
+ */
+ _replaceOnLoad: {},
+
+ /**
+ * additional options
+ * @var Object
+ */
+ _options: {},
+
+ /**
+ * @see WCF.Upload.init()
+ */
+ init: function (buttonSelector, fileListSelector, objectType, objectID, tmpHash, parentObjectID, maxUploads, editorId, options) {
+ this._super(buttonSelector, fileListSelector, 'wcf\\data\\attachment\\AttachmentAction', {
+ multiple: true,
+ maxUploads: maxUploads
+ });
- /**
- * temporary hash to identify uploaded attachments
- * @var string
- */
- _tmpHash: '',
+ this._autoInsert = [];
+ this._objectType = objectType;
+ this._objectID = parseInt(objectID);
+ this._tmpHash = tmpHash;
+ this._parentObjectID = parseInt(parentObjectID);
+ this._editorId = editorId;
+ this._options = $.extend(true, this._options, options || {});
- /**
- * id of the parent object of the object the uploaded attachments belongs to
- * @var string
- */
- _parentObjectID: 0,
+ this._buttonSelector.children('p.button').click($.proxy(this._validateLimit, this));
+ this._fileListSelector.find('.jsButtonInsertAttachment').click($.proxy(this._insert, this));
+ this._fileListSelector.find('.jsButtonAttachmentInsertThumbnail').click($.proxy(this._insert, this));
+ this._fileListSelector.find('.jsButtonAttachmentInsertFull').click($.proxy(this._insert, this));
- /**
- * editor id
- * @var string
- */
- _editorId: '',
+ WCF.DOMNodeRemovedHandler.addCallback('WCF.Attachment.Upload', $.proxy(this._removeLimitError, this));
+ WCF.System.Event.addListener('com.woltlab.wcf.action.delete', 'attachment_' + this._editorId, $.proxy(this._removeLimitError, this));
- /**
- * replace img element on load
- * @var Object
- */
- _replaceOnLoad: {},
+ this._makeSortable();
- /**
- * additional options
- * @var Object
- */
- _options: {},
+ // for backwards compatibility, the object is still created but only inserted
+ // if an editor is used
+ this._insertAllButton = $('<p class="button jsButtonAttachmentInsertAll">' + WCF.Language.get('wcf.attachment.insertAll') + '</p>').hide();
- /**
- * @see WCF.Upload.init()
- */
- init: function (buttonSelector, fileListSelector, objectType, objectID, tmpHash, parentObjectID, maxUploads, editorId, options) {
- this._super(buttonSelector, fileListSelector, 'wcf\\data\\attachment\\AttachmentAction', {
- multiple: true,
- maxUploads: maxUploads
- });
-
- this._autoInsert = [];
- this._objectType = objectType;
- this._objectID = parseInt(objectID);
- this._tmpHash = tmpHash;
- this._parentObjectID = parseInt(parentObjectID);
- this._editorId = editorId;
- this._options = $.extend(true, this._options, options || {});
+ if (this._editorId) {
+ this._insertAllButton.appendTo(this._buttonSelector);
+ this._insertAllButton.click($.proxy(this._insertAll, this));
- this._buttonSelector.children('p.button').click($.proxy(this._validateLimit, this));
- this._fileListSelector.find('.jsButtonInsertAttachment').click($.proxy(this._insert, this));
- this._fileListSelector.find('.jsButtonAttachmentInsertThumbnail').click($.proxy(this._insert, this));
- this._fileListSelector.find('.jsButtonAttachmentInsertFull').click($.proxy(this._insert, this));
-
- WCF.DOMNodeRemovedHandler.addCallback('WCF.Attachment.Upload', $.proxy(this._removeLimitError, this));
- WCF.System.Event.addListener('com.woltlab.wcf.action.delete', 'attachment_' + this._editorId, $.proxy(this._removeLimitError, this));
-
- this._makeSortable();
+ if (this._fileListSelector.children('li:not(.uploadFailed)').length) {
+ this._insertAllButton.show();
+ }
- // for backwards compatibility, the object is still created but only inserted
- // if an editor is used
- this._insertAllButton = $('<p class="button jsButtonAttachmentInsertAll">' + WCF.Language.get('wcf.attachment.insertAll') + '</p>').hide();
+ 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));
- if (this._editorId) {
- this._insertAllButton.appendTo(this._buttonSelector);
- this._insertAllButton.click($.proxy(this._insertAll, this));
-
- if (this._fileListSelector.children('li:not(.uploadFailed)').length) {
- this._insertAllButton.show();
+ WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._editorId, (function (data) {
+ if (!data.tmpHashes || !Array.isArray(data.tmpHashes)) {
+ data.tmpHashes = [];
}
- 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 index = data.tmpHashes.indexOf(tmpHash);
- WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._editorId, (function (data) {
- if (!data.tmpHashes || !Array.isArray(data.tmpHashes)) {
- data.tmpHashes = [];
+ var count = this._fileListSelector.children('li:not(.uploadFailed)').length;
+ if (count > 0) {
+ if (index === -1) {
+ data.tmpHashes.push(tmpHash);
}
+ }
+ else if (index !== -1) {
+ data.tmpHashes.splice(index);
+ }
+ }).bind(this));
+
+ var metacodeAttachUuid = WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'metacode_attach_' + this._editorId, (function (data) {
+ var images = this._getImageAttachments();
+ var attachmentId = data.attributes[0] || 0;
+ if (images.hasOwnProperty(attachmentId)) {
+ var thumbnail = data.attributes[2];
+ thumbnail = (thumbnail === true || thumbnail === 'true' || ~~thumbnail > 0);
- var index = data.tmpHashes.indexOf(tmpHash);
+ var image = elCreate('img');
+ image.className = 'woltlabAttachment';
+ image.src = images[attachmentId][(thumbnail ? 'thumbnailUrl' : 'url')];
+ elData(image, 'attachment-id', attachmentId);
- var count = this._fileListSelector.children('li:not(.uploadFailed)').length;
- if (count > 0) {
- if (index === -1) {
- data.tmpHashes.push(tmpHash);
- }
- }
- else if (index !== -1) {
- data.tmpHashes.splice(index);
- }
- }).bind(this));
-
- var metacodeAttachUuid = WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'metacode_attach_' + this._editorId, (function (data) {
- var images = this._getImageAttachments();
- var attachmentId = data.attributes[0] || 0;
- if (images.hasOwnProperty(attachmentId)) {
- var thumbnail = data.attributes[2];
- thumbnail = (thumbnail === true || thumbnail === 'true' || ~~thumbnail > 0);
-
- var image = elCreate('img');
- image.className = 'woltlabAttachment';
- image.src = images[attachmentId][(thumbnail ? 'thumbnailUrl' : 'url')];
- elData(image, 'attachment-id', attachmentId);
-
- var float = data.attributes[1] || 'none';
- if (float === 'left') image.classList.add('messageFloatObjectLeft');
- else if (float === 'right') image.classList.add('messageFloatObjectRight');
-
- var metacode = data.metacode;
- metacode.parentNode.insertBefore(image, metacode);
- elRemove(metacode);
-
- data.cancel = true;
- }
- }).bind(this));
-
- WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'destroy_' + this._editorId, (function () {
- 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.removeAllListeners('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this._editorId);
- WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._editorId);
+ var float = data.attributes[1] || 'none';
+ if (float === 'left') image.classList.add('messageFloatObjectLeft');
+ else if (float === 'right') image.classList.add('messageFloatObjectRight');
- WCF.System.Event.removeListener('com.woltlab.wcf.redactor2', 'metacode_attach_' + this._editorId, metacodeAttachUuid);
- }).bind(this));
- }
- },
-
- /**
- * Handles drag & drop uploads and clipboard paste.
- *
- * @param object data
- */
- _editorUpload: function (data) {
- var $uploadID, replace = null;
-
- // show tab
- this._fileListSelector.closest('.messageTabMenu').messageTabMenu('showTab', 'attachments', true);
-
- var callbackUploadId = (function(uploadId) {
- if (replace === null) {
- this._autoInsert.push(uploadId);
- }
- else {
- this._replaceOnLoad[uploadId] = replace;
+ var metacode = data.metacode;
+ metacode.parentNode.insertBefore(image, metacode);
+ elRemove(metacode);
+
+ data.cancel = true;
}
-
- data.uploadID = uploadId;
- }).bind(this);
+ }).bind(this));
- if (data.file) {
- $uploadID = this._upload(undefined, data.file, undefined, callbackUploadId);
+ WCF.System.Event.addListener('com.woltlab.wcf.redactor2', 'destroy_' + this._editorId, (function () {
+ 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.removeAllListeners('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + this._editorId);
+ WCF.System.Event.removeAllListeners('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._editorId);
+
+ WCF.System.Event.removeListener('com.woltlab.wcf.redactor2', 'metacode_attach_' + this._editorId, metacodeAttachUuid);
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Handles drag & drop uploads and clipboard paste.
+ *
+ * @param object data
+ */
+ _editorUpload: function (data) {
+ var $uploadID, replace = null;
+
+ // show tab
+ this._fileListSelector.closest('.messageTabMenu').messageTabMenu('showTab', 'attachments', true);
+
+ var callbackUploadId = (function(uploadId) {
+ if (replace === null) {
+ this._autoInsert.push(uploadId);
}
else {
- $uploadID = this._upload(undefined, undefined, data.blob, callbackUploadId);
- replace = data.replace || null;
+ this._replaceOnLoad[uploadId] = replace;
}
- },
+
+ data.uploadID = uploadId;
+ }).bind(this);
- /**
- * Sets the attachments representing an image.
- *
- * @return {Object}
- */
- _getImageAttachments: function () {
- var images = {};
-
- this._fileListSelector.children('li').each(function (index, attachment) {
- var $attachment = $(attachment);
- if ($attachment.data('isImage')) {
- images[~~$attachment.data('objectID')] = {
- thumbnailUrl: $attachment.find('.jsButtonAttachmentInsertThumbnail').data('url'),
- url: $attachment.find('.jsButtonAttachmentInsertFull').data('url')
- };
- }
- });
-
- return images;
- },
+ if (data.file) {
+ $uploadID = this._upload(undefined, data.file, undefined, callbackUploadId);
+ }
+ else {
+ $uploadID = this._upload(undefined, undefined, data.blob, callbackUploadId);
+ replace = data.replace || null;
+ }
+ },
+
+ /**
+ * Sets the attachments representing an image.
+ *
+ * @return {Object}
+ */
+ _getImageAttachments: function () {
+ var images = {};
- /**
- * Adds parameters for the inline editor.
- *
- * @param object data
- */
- _submitInline: function (data) {
- if (this._tmpHash) {
- data.tmpHash = this._tmpHash;
-
- var metaData = {};
- WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'getMetaData_' + this._editorId, metaData);
- if (metaData.tmpHashes && Array.isArray(metaData.tmpHashes) && metaData.tmpHashes.length > 0) {
- data.tmpHash += ',' + metaData.tmpHashes.join(',');
- }
+ this._fileListSelector.children('li').each(function (index, attachment) {
+ var $attachment = $(attachment);
+ if ($attachment.data('isImage')) {
+ images[~~$attachment.data('objectID')] = {
+ thumbnailUrl: $attachment.find('.jsButtonAttachmentInsertThumbnail').data('url'),
+ url: $attachment.find('.jsButtonAttachmentInsertFull').data('url')
+ };
}
- },
+ });
- /**
- * Resets the attachment container.
- */
- _reset: function () {
- this._fileListSelector.hide().empty();
- this._insertAllButton.hide();
- this._validateLimit();
- },
-
- /**
- * Validates upload limits.
- *
- * @return boolean
- */
- _validateLimit: function () {
- var $innerError = this._buttonSelector.next('small.innerError');
+ return images;
+ },
+
+ /**
+ * Adds parameters for the inline editor.
+ *
+ * @param object data
+ */
+ _submitInline: function (data) {
+ if (this._tmpHash) {
+ data.tmpHash = this._tmpHash;
- // check maximum uploads
- var $max = this._options.maxUploads - this._fileListSelector.children('li:not(.uploadFailed)').length;
- var $filesLength = (this._fileUpload) ? this._fileUpload.prop('files').length : 0;
- if ($max <= 0 || $max < $filesLength) {
- // reached limit
- var $errorMessage = ($max <= 0) ? WCF.Language.get('wcf.attachment.upload.error.reachedLimit') : WCF.Language.get('wcf.attachment.upload.error.reachedRemainingLimit').replace(/#remaining#/, $max);
- if (!$innerError.length) {
- $innerError = $('<small class="innerError" />').insertAfter(this._buttonSelector);
- }
-
- $innerError.html($errorMessage);
-
- return false;
+ var metaData = {};
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'getMetaData_' + this._editorId, metaData);
+ if (metaData.tmpHashes && Array.isArray(metaData.tmpHashes) && metaData.tmpHashes.length > 0) {
+ data.tmpHash += ',' + metaData.tmpHashes.join(',');
+ }
+ }
+ },
+
+ /**
+ * Resets the attachment container.
+ */
+ _reset: function () {
+ this._fileListSelector.hide().empty();
+ this._insertAllButton.hide();
+ this._validateLimit();
+ },
+
+ /**
+ * Validates upload limits.
+ *
+ * @return boolean
+ */
+ _validateLimit: function () {
+ var $innerError = this._buttonSelector.next('small.innerError');
+
+ // check maximum uploads
+ var $max = this._options.maxUploads - this._fileListSelector.children('li:not(.uploadFailed)').length;
+ var $filesLength = (this._fileUpload) ? this._fileUpload.prop('files').length : 0;
+ if ($max <= 0 || $max < $filesLength) {
+ // reached limit
+ var $errorMessage = ($max <= 0) ? WCF.Language.get('wcf.attachment.upload.error.reachedLimit') : WCF.Language.get('wcf.attachment.upload.error.reachedRemainingLimit').replace(/#remaining#/, $max);
+ if (!$innerError.length) {
+ $innerError = $('<small class="innerError" />').insertAfter(this._buttonSelector);
}
- // remove previous errors
- $innerError.remove();
+ $innerError.html($errorMessage);
- return true;
- },
+ return false;
+ }
- /**
- * Removes the limit error message.
- *
- * @param object data
- */
- _removeLimitError: function (data) {
- var $listItems = this._fileListSelector.children('li');
- if (!$listItems.filter(':not(.uploadFailed)').length) {
- this._insertAllButton.hide();
- }
+ // remove previous errors
+ $innerError.remove();
+
+ return true;
+ },
+
+ /**
+ * Removes the limit error message.
+ *
+ * @param object data
+ */
+ _removeLimitError: function (data) {
+ var $listItems = this._fileListSelector.children('li');
+ if (!$listItems.filter(':not(.uploadFailed)').length) {
+ this._insertAllButton.hide();
+ }
+
+ if (!$listItems.length) {
+ this._fileListSelector.hide();
+ }
+
+ if (this._editorId && data.button) {
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'deleteAttachment_' + this._editorId, {
+ attachmentId: data.button.data('objectID')
+ });
+ }
+ },
+
+ /**
+ * @see WCF.Upload._upload()
+ */
+ _upload: function (event, file, blob, callbackUploadId) {
+ var _super = this._super.bind(this);
+
+ require([
+ 'WoltLabSuite/Core/FileUtil',
+ 'WoltLabSuite/Core/Image/ImageUtil',
+ 'WoltLabSuite/Core/Image/Resizer',
+ 'WoltLabSuite/Core/Ajax/Status'
+ ], (function (FileUtil, ImageUtil, ImageResizer, AjaxStatus) {
+ AjaxStatus.show();
- if (!$listItems.length) {
- this._fileListSelector.hide();
- }
+ var files = [];
- if (this._editorId && data.button) {
- WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'deleteAttachment_' + this._editorId, {
- attachmentId: data.button.data('objectID')
- });
+ if (file) {
+ files.push(file);
+ }
+ else if (blob) {
+ files.push(FileUtil.blobToFile(blob, 'pasted-from-clipboard'));
+ }
+ else {
+ files = this._fileUpload.prop('files');
}
- },
-
- /**
- * @see WCF.Upload._upload()
- */
- _upload: function (event, file, blob, callbackUploadId) {
- var _super = this._super.bind(this);
- require([
- 'WoltLabSuite/Core/FileUtil',
- 'WoltLabSuite/Core/Image/ImageUtil',
- 'WoltLabSuite/Core/Image/Resizer',
- 'WoltLabSuite/Core/Ajax/Status'
- ], (function (FileUtil, ImageUtil, ImageResizer, AjaxStatus) {
- AjaxStatus.show();
+ // We resolve with the unaltered list of files in case auto scaling is disabled.
+ var promise = Promise.resolve(files);
+
+ if (this._options.autoScale && this._options.autoScale.enable) {
+ var maxSize = this._buttonSelector.data('maxSize');
- var files = [];
+ var resizer = new ImageResizer();
- if (file) {
- files.push(file);
- }
- else if (blob) {
- files.push(FileUtil.blobToFile(blob, 'pasted-from-clipboard'));
- }
- else {
- files = this._fileUpload.prop('files');
+ // Resize the images in series.
+ // As our resizer is based on Pica it will use multiple workers per image if possible.
+ promise = Array.prototype.reduce.call(files, (function (acc, file) {
+ return acc.then((function (arr) {
+ var timeout = new Promise(function (resolve, reject) {
+ // We issue one timeout per image, thus multiple timeout
+ // handlers will run in parallel
+ setTimeout(function () {
+ resolve(file);
+ }, 10000);
+ });
+
+ var promise = resizer.loadFile(file)
+ .then((function (result) {
+ var exif = result.exif;
+ var maxWidth = this._options.autoScale.maxWidth;
+ var maxHeight = this._options.autoScale.maxHeight;
+ var quality = this._options.autoScale.quality;
+
+ if (window.devicePixelRatio >= 2) {
+ var realWidth = window.screen.width * window.devicePixelRatio;
+ var realHeight = window.screen.height * window.devicePixelRatio;
+ // Check whether the width of the image is roughly the width of the physical screen, and
+ // the height of the image is at least the height of the physical screen.
+ if (realWidth - 10 < result.image.width && result.image.width < realWidth + 10 && realHeight - 10 < result.image.height) {
+ // This appears to be a screenshot from a HiDPI device in portrait mode: Scale to logical size
+ maxWidth = Math.min(maxWidth, window.screen.width);
+ }
+ }
+
+ return resizer.resize(result.image, maxWidth, maxHeight, quality, file.size > maxSize, timeout)
+ .then((function (resizedImage) {
+ // Check whether the image actually was resized
+ if (resizedImage === undefined) {
+ return file;
+ }
+
+ var fileType = this._options.autoScale.fileType;
+
+ if (this._options.autoScale.fileType === 'keep' || ImageUtil.containsTransparentPixels(resizedImage)) {
+ fileType = file.type;
+ }
+
+ return resizer.saveFile({
+ exif: exif,
+ image: resizedImage
+ }, file.name, fileType, quality);
+ }).bind(this))
+ .then(function (resizedFile) {
+ if (resizedFile.size > file.size) {
+ console.debug('[WCF.Attachment] File size of "' + file.name + '" increased, uploading untouched image.');
+ return file;
+ }
+
+ return resizedFile;
+ });
+ }).bind(this))
+ .catch(function (error) {
+ console.debug('[WCF.Attachment] Failed to resize image "' + file.name + '":', error);
+ return file;
+ });
+
+ return Promise.race([timeout, promise])
+ .then(function (file) {
+ arr.push(file);
+ return arr;
+ });
+ }).bind(this));
+ }).bind(this), Promise.resolve([]));
+ }
+
+ promise.then((function (files) {
+ var uploadID = undefined;
+
+ if (this._validateLimit()) {
+ uploadID = _super(event, undefined, undefined, files);
}
- // We resolve with the unaltered list of files in case auto scaling is disabled.
- var promise = Promise.resolve(files);
+ if (this._fileUpload) {
+ // remove and re-create the upload button since the 'files' property
+ // of the input field is readonly thus it can't be reset
+ this._removeButton();
+ this._createButton();
+ }
- if (this._options.autoScale && this._options.autoScale.enable) {
- var maxSize = this._buttonSelector.data('maxSize');
-
- var resizer = new ImageResizer();
-
- // Resize the images in series.
- // As our resizer is based on Pica it will use multiple workers per image if possible.
- promise = Array.prototype.reduce.call(files, (function (acc, file) {
- return acc.then((function (arr) {
- var timeout = new Promise(function (resolve, reject) {
- // We issue one timeout per image, thus multiple timeout
- // handlers will run in parallel
- setTimeout(function () {
- resolve(file);
- }, 10000);
- });
-
- var promise = resizer.loadFile(file)
- .then((function (result) {
- var exif = result.exif;
- var maxWidth = this._options.autoScale.maxWidth;
- var maxHeight = this._options.autoScale.maxHeight;
- var quality = this._options.autoScale.quality;
-
- if (window.devicePixelRatio >= 2) {
- var realWidth = window.screen.width * window.devicePixelRatio;
- var realHeight = window.screen.height * window.devicePixelRatio;
- // Check whether the width of the image is roughly the width of the physical screen, and
- // the height of the image is at least the height of the physical screen.
- if (realWidth - 10 < result.image.width && result.image.width < realWidth + 10 && realHeight - 10 < result.image.height) {
- // This appears to be a screenshot from a HiDPI device in portrait mode: Scale to logical size
- maxWidth = Math.min(maxWidth, window.screen.width);
- }
- }
-
- return resizer.resize(result.image, maxWidth, maxHeight, quality, file.size > maxSize, timeout)
- .then((function (resizedImage) {
- // Check whether the image actually was resized
- if (resizedImage === undefined) {
- return file;
- }
-
- var fileType = this._options.autoScale.fileType;
-
- if (this._options.autoScale.fileType === 'keep' || ImageUtil.containsTransparentPixels(resizedImage)) {
- fileType = file.type;
- }
-
- return resizer.saveFile({
- exif: exif,
- image: resizedImage
- }, file.name, fileType, quality);
- }).bind(this))
- .then(function (resizedFile) {
- if (resizedFile.size > file.size) {
- console.debug('[WCF.Attachment] File size of "' + file.name + '" increased, uploading untouched image.');
- return file;
- }
-
- return resizedFile;
- });
- }).bind(this))
- .catch(function (error) {
- console.debug('[WCF.Attachment] Failed to resize image "' + file.name + '":', error);
- return file;
- });
-
- return Promise.race([timeout, promise])
- .then(function (file) {
- arr.push(file);
- return arr;
- });
- }).bind(this));
- }).bind(this), Promise.resolve([]));
+ if (typeof callbackUploadId === 'function') {
+ callbackUploadId(uploadID);
}
- promise.then((function (files) {
- var uploadID = undefined;
-
- if (this._validateLimit()) {
- uploadID = _super(event, undefined, undefined, files);
- }
-
- if (this._fileUpload) {
- // remove and re-create the upload button since the 'files' property
- // of the input field is readonly thus it can't be reset
- this._removeButton();
- this._createButton();
- }
-
- if (typeof callbackUploadId === 'function') {
- callbackUploadId(uploadID);
- }
-
- return uploadID;
- }).bind(this))
- .catch(function (error) {
- console.debug('[WCF.Attachment] Failed to upload attachments:', error);
- })
- .finally(AjaxStatus.hide);
- }).bind(this), function (error) {
- console.debug('[WCF.Attachment] Failed to load modules:', error);
- });
- },
+ return uploadID;
+ }).bind(this))
+ .catch(function (error) {
+ console.debug('[WCF.Attachment] Failed to upload attachments:', error);
+ })
+ .finally(AjaxStatus.hide);
+ }).bind(this), function (error) {
+ console.debug('[WCF.Attachment] Failed to load modules:', error);
+ });
+ },
+
+ /**
+ * @see WCF.Upload._createUploadMatrix()
+ */
+ _createUploadMatrix: function (files) {
+ // remove failed uploads
+ this._fileListSelector.children('li.uploadFailed').remove();
- /**
- * @see WCF.Upload._createUploadMatrix()
- */
- _createUploadMatrix: function (files) {
- // remove failed uploads
- this._fileListSelector.children('li.uploadFailed').remove();
-
- return this._super(files);
- },
+ return this._super(files);
+ },
+
+ /**
+ * @see WCF.Upload._getParameters()
+ */
+ _getParameters: function () {
+ return {
+ objectType: this._objectType,
+ objectID: this._objectID,
+ tmpHash: this._tmpHash,
+ parentObjectID: this._parentObjectID
+ };
+ },
+
+ /**
+ * @see WCF.Upload._initFile()
+ */
+ _initFile: function (file) {
+ var $li = $('<li class="box64"><span class="icon icon64 fa-spinner" /><div><div><p>' + file.name + '</p><small><progress max="100"></progress></small></div><ul></ul></div></li>').data('filename', file.name);
+ this._fileListSelector.append($li);
+ this._fileListSelector.show();
- /**
- * @see WCF.Upload._getParameters()
- */
- _getParameters: function () {
- return {
- objectType: this._objectType,
- objectID: this._objectID,
- tmpHash: this._tmpHash,
- parentObjectID: this._parentObjectID
- };
- },
+ // validate file size
+ if (this._buttonSelector.data('maxSize') < file.size) {
+ // remove progress bar
+ $li.find('progress').remove();
+
+ // upload icon
+ $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
+
+ // error message
+ $li.find('div > div').append($('<small class="innerError">' + WCF.Language.get('wcf.attachment.upload.error.tooLarge') + '</small>'));
+ $li.addClass('uploadFailed');
+ }
- /**
- * @see WCF.Upload._initFile()
- */
- _initFile: function (file) {
- var $li = $('<li class="box64"><span class="icon icon64 fa-spinner" /><div><div><p>' + file.name + '</p><small><progress max="100"></progress></small></div><ul></ul></div></li>').data('filename', file.name);
- this._fileListSelector.append($li);
- this._fileListSelector.show();
+ return $li;
+ },
+
+ /**
+ * Returns true if thumbnails are enabled and should be
+ * used instead of the original images.
+ *
+ * @return {boolean}
+ * @protected
+ */
+ _useThumbnail: function() {
+ return elDataBool(this._fileListSelector[0], 'enable-thumbnails');
+ },
+
+ /**
+ * @see WCF.Upload._success()
+ */
+ _success: function (uploadID, data) {
+ var attachmentData;
+ for (var $i in this._uploadMatrix[uploadID]) {
+ if (!this._uploadMatrix[uploadID].hasOwnProperty($i)) {
+ continue;
+ }
- // validate file size
- if (this._buttonSelector.data('maxSize') < file.size) {
- // remove progress bar
- $li.find('progress').remove();
+ // get li
+ var $li = this._uploadMatrix[uploadID][$i];
+
+ // remove progress bar
+ $li.find('progress').remove();
+
+ // get filename and check result
+ var $filename = $li.data('filename');
+ var $internalFileID = $li.data('internalFileID');
+ if (data.returnValues && data.returnValues.attachments[$internalFileID]) {
+ attachmentData = data.returnValues.attachments[$internalFileID];
- // upload icon
- $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
+ // show thumbnail
+ if (attachmentData.tinyURL) {
+ $li.children('.fa-spinner').replaceWith($('<img src="' + attachmentData.tinyURL + '" alt="" class="attachmentTinyThumbnail" />'));
+
+ $li.data('height', attachmentData.height);
+ $li.data('width', attachmentData.width);
+ elData($li[0], 'is-image', attachmentData.isImage);
+ }
+ // show file icon
+ else {
+ $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-' + attachmentData.iconName);
+ }
- // error message
- $li.find('div > div').append($('<small class="innerError">' + WCF.Language.get('wcf.attachment.upload.error.tooLarge') + '</small>'));
- $li.addClass('uploadFailed');
- }
-
- return $li;
- },
-
- /**
- * Returns true if thumbnails are enabled and should be
- * used instead of the original images.
- *
- * @return {boolean}
- * @protected
- */
- _useThumbnail: function() {
- return elDataBool(this._fileListSelector[0], 'enable-thumbnails');
- },
-
- /**
- * @see WCF.Upload._success()
- */
- _success: function (uploadID, data) {
- var attachmentData;
- for (var $i in this._uploadMatrix[uploadID]) {
- if (!this._uploadMatrix[uploadID].hasOwnProperty($i)) {
- continue;
+ // update attachment link
+ var $link = $('<a href=""></a>');
+ $link.text($filename).attr('href', attachmentData.url);
+ $link[0].target = '_blank';
+
+ if (attachmentData.isImage != 0) {
+ $link.addClass('jsImageViewer').attr('title', $filename);
}
+ $li.find('p').empty().append($link);
- // get li
- var $li = this._uploadMatrix[uploadID][$i];
+ // update file size
+ $li.find('small').append(attachmentData.formattedFilesize);
- // remove progress bar
- $li.find('progress').remove();
+ // init buttons
+ var $buttonList = $li.find('ul').addClass('buttonGroup');
+ var $deleteButton = $('<li><span class="button small jsDeleteButton" data-object-id="' + attachmentData.attachmentID + '" data-confirm-message="' + WCF.Language.get('wcf.attachment.delete.sure') + '" data-event-name="attachment_' + this._editorId + '">' + WCF.Language.get('wcf.global.button.delete') + '</span></li>');
+ $buttonList.append($deleteButton);
- // get filename and check result
- var $filename = $li.data('filename');
- var $internalFileID = $li.data('internalFileID');
- if (data.returnValues && data.returnValues.attachments[$internalFileID]) {
- attachmentData = data.returnValues.attachments[$internalFileID];
-
- // show thumbnail
- if (attachmentData.tinyURL) {
- $li.children('.fa-spinner').replaceWith($('<img src="' + attachmentData.tinyURL + '" alt="" class="attachmentTinyThumbnail" />'));
+ $li.data('objectID', attachmentData.attachmentID);
+
+ if (this._editorId) {
+ if (attachmentData.tinyURL || (!this._useThumbnail() && attachmentData.isImage)) {
+ if (attachmentData.thumbnailURL) {
+ var $insertThumbnail = $('<li><span class="button small jsButtonAttachmentInsertThumbnail" data-object-id="' + attachmentData.attachmentID + '" data-url="' + WCF.String.escapeHTML(attachmentData.thumbnailURL) + '">' + WCF.Language.get('wcf.attachment.insertThumbnail') + '</span></li>').appendTo($buttonList);
+ $insertThumbnail.children('span.button').click($.proxy(this._insert, this));
+ }
- $li.data('height', attachmentData.height);
- $li.data('width', attachmentData.width);
- elData($li[0], 'is-image', attachmentData.isImage);
+ var $insertOriginal = $('<li><span class="button small jsButtonAttachmentInsertFull" data-object-id="' + attachmentData.attachmentID + '" data-url="' + WCF.String.escapeHTML(attachmentData.url) + '">' + WCF.Language.get('wcf.attachment.insertFull') + '</span></li>').appendTo($buttonList);
+ $insertOriginal.children('span.button').click($.proxy(this._insert, this));
}
- // show file icon
else {
- $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-' + attachmentData.iconName);
- }
-
- // update attachment link
- var $link = $('<a href=""></a>');
- $link.text($filename).attr('href', attachmentData.url);
- $link[0].target = '_blank';
-
- if (attachmentData.isImage != 0) {
- $link.addClass('jsImageViewer').attr('title', $filename);
- }
- $li.find('p').empty().append($link);
-
- // update file size
- $li.find('small').append(attachmentData.formattedFilesize);
-
- // init buttons
- var $buttonList = $li.find('ul').addClass('buttonGroup');
- var $deleteButton = $('<li><span class="button small jsDeleteButton" data-object-id="' + attachmentData.attachmentID + '" data-confirm-message="' + WCF.Language.get('wcf.attachment.delete.sure') + '" data-event-name="attachment_' + this._editorId + '">' + WCF.Language.get('wcf.global.button.delete') + '</span></li>');
- $buttonList.append($deleteButton);
-
- $li.data('objectID', attachmentData.attachmentID);
-
- if (this._editorId) {
- if (attachmentData.tinyURL || (!this._useThumbnail() && attachmentData.isImage)) {
- if (attachmentData.thumbnailURL) {
- var $insertThumbnail = $('<li><span class="button small jsButtonAttachmentInsertThumbnail" data-object-id="' + attachmentData.attachmentID + '" data-url="' + WCF.String.escapeHTML(attachmentData.thumbnailURL) + '">' + WCF.Language.get('wcf.attachment.insertThumbnail') + '</span></li>').appendTo($buttonList);
- $insertThumbnail.children('span.button').click($.proxy(this._insert, this));
- }
-
- var $insertOriginal = $('<li><span class="button small jsButtonAttachmentInsertFull" data-object-id="' + attachmentData.attachmentID + '" data-url="' + WCF.String.escapeHTML(attachmentData.url) + '">' + WCF.Language.get('wcf.attachment.insertFull') + '</span></li>').appendTo($buttonList);
- $insertOriginal.children('span.button').click($.proxy(this._insert, this));
- }
- else {
- var $insertPlain = $('<li><span class="button small jsButtonAttachmentInsertPlain" data-object-id="' + attachmentData.attachmentID + '">' + WCF.Language.get('wcf.attachment.insert') + '</span></li>');
- $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;
+ var $insertPlain = $('<li><span class="button small jsButtonAttachmentInsertPlain" data-object-id="' + attachmentData.attachmentID + '">' + WCF.Language.get('wcf.attachment.insert') + '</span></li>');
+ $insertPlain.appendTo($buttonList).children('span.button').click($.proxy(this._insert, this));
}
}
- else {
- // upload icon
- $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
- var $errorMessage = '';
-
- // error handling
- if (data.returnValues && data.returnValues.errors[$internalFileID]) {
- var errorData = data.returnValues.errors[$internalFileID];
- $errorMessage = errorData.errorType;
-
- if ($errorMessage === 'uploadFailed' && errorData.additionalData.phpLimitExceeded) {
- $errorMessage = 'uploadPhpLimit';
+
+ 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
+ });
}
}
- else {
- // unknown error
- $errorMessage = 'uploadFailed';
- }
- $li.find('div > div').append($('<small class="innerError">' + WCF.Language.get('wcf.attachment.upload.error.' + $errorMessage) + '</small>'));
- $li.addClass('uploadFailed');
+ this._replaceOnLoad[uploadID] = null;
}
+ }
+ else {
+ // upload icon
+ $li.children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
+ var $errorMessage = '';
- if (WCF.inArray(uploadID, this._autoInsert)) {
- this._autoInsert.splice(this._autoInsert.indexOf(uploadID), 1);
+ // error handling
+ if (data.returnValues && data.returnValues.errors[$internalFileID]) {
+ var errorData = data.returnValues.errors[$internalFileID];
+ $errorMessage = errorData.errorType;
- if (!$li.hasClass('uploadFailed')) {
- var btn = $li.find('.jsButtonAttachmentInsertThumbnail');
- if (!btn.length) btn = $li.find('.jsButtonAttachmentInsertFull');
-
- btn.trigger('click');
+ if ($errorMessage === 'uploadFailed' && errorData.additionalData.phpLimitExceeded) {
+ $errorMessage = 'uploadPhpLimit';
}
}
+ else {
+ // unknown error
+ $errorMessage = 'uploadFailed';
+ }
+
+ $li.find('div > div').append($('<small class="innerError">' + WCF.Language.get('wcf.attachment.upload.error.' + $errorMessage) + '</small>'));
+ $li.addClass('uploadFailed');
}
- this._makeSortable();
-
- if (this._fileListSelector.children('li:not(.uploadFailed)').length) {
- this._insertAllButton.show();
- }
- else {
- this._insertAllButton.hide();
+ if (WCF.inArray(uploadID, this._autoInsert)) {
+ this._autoInsert.splice(this._autoInsert.indexOf(uploadID), 1);
+
+ if (!$li.hasClass('uploadFailed')) {
+ var btn = $li.find('.jsButtonAttachmentInsertThumbnail');
+ if (!btn.length) btn = $li.find('.jsButtonAttachmentInsertFull');
+
+ btn.trigger('click');
+ }
}
-
- WCF.DOMNodeInsertedHandler.execute();
- },
+ }
- /**
- * Inserts an attachment into WYSIWYG editor contents.
- *
- * @param {Event} event
- */
- _insert: function (event) {
- WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertAttachment_' + this._editorId, {
- attachmentId: elData(event.currentTarget, 'object-id'),
- url: elData(event.currentTarget, 'url')
- });
- },
+ this._makeSortable();
- /**
- * Inserts all attachments at once.
- */
- _insertAll: function () {
- var attachment, button, preferThumbnail = this._useThumbnail();
- for (var i = 0, length = this._fileListSelector[0].childNodes.length; i < length; i++) {
- attachment = this._fileListSelector[0].childNodes[i];
- if (attachment.nodeName === 'LI' && !attachment.classList.contains('uploadFailed')) {
- button = null;
- if (preferThumbnail) {
- button = elBySel('.jsButtonAttachmentInsertThumbnail, .jsButtonAttachmentInsertPlain', attachment);
- }
-
- if (button === null) {
- button = elBySel('.jsButtonAttachmentInsertFull, .jsButtonAttachmentInsertPlain', attachment);
- }
+ if (this._fileListSelector.children('li:not(.uploadFailed)').length) {
+ this._insertAllButton.show();
+ }
+ else {
+ this._insertAllButton.hide();
+ }
+
+ WCF.DOMNodeInsertedHandler.execute();
+ },
+
+ /**
+ * Inserts an attachment into WYSIWYG editor contents.
+ *
+ * @param {Event} event
+ */
+ _insert: function (event) {
+ WCF.System.Event.fireEvent('com.woltlab.wcf.redactor2', 'insertAttachment_' + this._editorId, {
+ attachmentId: elData(event.currentTarget, 'object-id'),
+ url: elData(event.currentTarget, 'url')
+ });
+ },
+
+ /**
+ * Inserts all attachments at once.
+ */
+ _insertAll: function () {
+ var attachment, button, preferThumbnail = this._useThumbnail();
+ for (var i = 0, length = this._fileListSelector[0].childNodes.length; i < length; i++) {
+ attachment = this._fileListSelector[0].childNodes[i];
+ if (attachment.nodeName === 'LI' && !attachment.classList.contains('uploadFailed')) {
+ button = null;
+ if (preferThumbnail) {
+ button = elBySel('.jsButtonAttachmentInsertThumbnail, .jsButtonAttachmentInsertPlain', attachment);
+ }
- window.jQuery(button).trigger('click');
+ if (button === null) {
+ button = elBySel('.jsButtonAttachmentInsertFull, .jsButtonAttachmentInsertPlain', attachment);
}
+
+ window.jQuery(button).trigger('click');
}
- },
+ }
+ },
+
+ /**
+ * @see WCF.Upload._error()
+ */
+ _error: function (data) {
+ // mark uploads as failed
+ this._fileListSelector.find('li').each(function (index, listItem) {
+ var $listItem = $(listItem);
+ if ($listItem.children('.fa-spinner').length) {
+ // upload icon
+ $listItem.addClass('uploadFailed').children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
+ $listItem.find('div > div').append($('<small class="innerError">' + (data.responseJSON && data.responseJSON.message ? data.responseJSON.message : WCF.Language.get('wcf.attachment.upload.error.uploadFailed')) + '</small>'));
+ }
+ });
+ },
+
+ /**
+ * Initializes sorting for uploaded attachments.
+ */
+ _makeSortable: function () {
+ var $attachments = this._fileListSelector.children('li:not(.uploadFailed)');
+ if (!$attachments.length) {
+ return;
+ }
- /**
- * @see WCF.Upload._error()
- */
- _error: function (data) {
- // mark uploads as failed
- this._fileListSelector.find('li').each(function (index, listItem) {
- var $listItem = $(listItem);
- if ($listItem.children('.fa-spinner').length) {
- // upload icon
- $listItem.addClass('uploadFailed').children('.fa-spinner').removeClass('fa-spinner').addClass('fa-ban');
- $listItem.find('div > div').append($('<small class="innerError">' + (data.responseJSON && data.responseJSON.message ? data.responseJSON.message : WCF.Language.get('wcf.attachment.upload.error.uploadFailed')) + '</small>'));
- }
- });
- },
+ $attachments.addClass('sortableAttachment').children('img').addClass('sortableNode');
- /**
- * Initializes sorting for uploaded attachments.
- */
- _makeSortable: function () {
- var $attachments = this._fileListSelector.children('li:not(.uploadFailed)');
- if (!$attachments.length) {
- return;
- }
+ if (!this._fileListSelector.hasClass('sortableList')) {
+ this._fileListSelector.addClass('sortableList');
- $attachments.addClass('sortableAttachment').children('img').addClass('sortableNode');
-
- if (!this._fileListSelector.hasClass('sortableList')) {
- this._fileListSelector.addClass('sortableList');
-
- require(['Environment'], (function (Environment) {
- if (Environment.platform() === 'desktop') {
- new WCF.Sortable.List(this._fileListSelector.parent().wcfIdentify(), '', 0, {
- axis: false,
- items: 'li.sortableAttachment',
- toleranceElement: null,
- start: function (event, ui) {
- ui.placeholder[0].style.setProperty('height', ui.helper[0].offsetHeight + 'px', '');
- },
- update: (function () {
- var $attachmentIDs = [];
- this._fileListSelector.children('li:not(.uploadFailed)').each(function (index, listItem) {
- $attachmentIDs.push($(listItem).data('objectID'));
- });
-
- if ($attachmentIDs.length) {
- new WCF.Action.Proxy({
- autoSend: true,
- data: {
- actionName: 'updatePosition',
- className: 'wcf\\data\\attachment\\AttachmentAction',
- parameters: {
- attachmentIDs: $attachmentIDs,
- objectID: this._objectID,
- objectType: this._objectType,
- tmpHash: this._tmpHash
- }
+ require(['Environment'], (function (Environment) {
+ if (Environment.platform() === 'desktop') {
+ new WCF.Sortable.List(this._fileListSelector.parent().wcfIdentify(), '', 0, {
+ axis: false,
+ items: 'li.sortableAttachment',
+ toleranceElement: null,
+ start: function (event, ui) {
+ ui.placeholder[0].style.setProperty('height', ui.helper[0].offsetHeight + 'px', '');
+ },
+ update: (function () {
+ var $attachmentIDs = [];
+ this._fileListSelector.children('li:not(.uploadFailed)').each(function (index, listItem) {
+ $attachmentIDs.push($(listItem).data('objectID'));
+ });
+
+ if ($attachmentIDs.length) {
+ new WCF.Action.Proxy({
+ autoSend: true,
+ data: {
+ actionName: 'updatePosition',
+ className: 'wcf\\data\\attachment\\AttachmentAction',
+ parameters: {
+ attachmentIDs: $attachmentIDs,
+ objectID: this._objectID,
+ objectType: this._objectType,
+ tmpHash: this._tmpHash
}
- });
- }
- }).bind(this)
- }, true);
- }
- }).bind(this));
- }
+ }
+ });
+ }
+ }).bind(this)
+ }, true);
+ }
+ }).bind(this));
}
- });
-}
-else {
- WCF.Attachment.Upload = WCF.Upload.extend({
- _autoInsert: {},
- _insertAllButton: {},
- _objectType: "",
- _objectID: 0,
- _tmpHash: "",
- _parentObjectID: 0,
- _editorId: "",
- _replaceOnLoad: {},
- init: function() {},
- _editorUpload: function() {},
- _getImageAttachments: function() {},
- _submitInline: function() {},
- _reset: function() {},
- _validateLimit: function() {},
- _removeLimitError: function() {},
- _upload: function() {},
- _createUploadMatrix: function() {},
- _getParameters: function() {},
- _initFile: function() {},
- _success: function() {},
- _insert: function() {},
- _insertAll: function() {},
- _error: function() {},
- _makeSortable: function() {},
- _name: "",
- _buttonSelector: {},
- _fileListSelector: {},
- _fileUpload: {},
- _className: "",
- _iframe: {},
- _internalFileID: 0,
- _options: {},
- _uploadMatrix: {},
- _supportsAJAXUpload: true,
- _overlay: {},
- _createButton: function() {},
- _insertButton: function() {},
- _removeButton: function() {},
- _progress: function() {},
- _showOverlay: function() {},
- _evaluateResponse: function() {},
- _getFilename: function() {}
- });
-}
+ }
+});
}
});
-if (COMPILER_TARGET_DEFAULT) {
+/**
+ * Basic implementation for AJAXProxy-based deletion.
+ *
+ * @param string className
+ * @param string containerSelector
+ * @param string buttonSelector
+ */
+WCF.Action.Delete = Class.extend({
+ /**
+ * delete button selector
+ * @var string
+ */
+ _buttonSelector: '',
+
+ /**
+ * callback function called prior to triggering the delete effect
+ * @var function
+ */
+ _callback: null,
+
+ /**
+ * action class name
+ * @var string
+ */
+ _className: '',
+
/**
- * Basic implementation for AJAXProxy-based deletion.
+ * container selector
+ * @var string
+ */
+ _containerSelector: '',
+
+ /**
+ * list of known container ids
+ * @var array<string>
+ */
+ _containers: [],
+
+ /**
+ * Initializes 'delete'-Proxy.
*
* @param string className
* @param string containerSelector
* @param string buttonSelector
*/
- WCF.Action.Delete = Class.extend({
- /**
- * delete button selector
- * @var string
- */
- _buttonSelector: '',
-
- /**
- * callback function called prior to triggering the delete effect
- * @var function
- */
- _callback: null,
-
- /**
- * action class name
- * @var string
- */
- _className: '',
+ init: function (className, containerSelector, buttonSelector) {
+ this._containerSelector = containerSelector;
+ this._className = className;
+ this._buttonSelector = (buttonSelector) ? buttonSelector : '.jsDeleteButton';
+ this._callback = null;
- /**
- * container selector
- * @var string
- */
- _containerSelector: '',
+ this.proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
- /**
- * list of known container ids
- * @var array<string>
- */
- _containers: [],
+ this._initElements();
- /**
- * Initializes 'delete'-Proxy.
- *
- * @param string className
- * @param string containerSelector
- * @param string buttonSelector
- */
- init: function (className, containerSelector, buttonSelector) {
- this._containerSelector = containerSelector;
- this._className = className;
- this._buttonSelector = (buttonSelector) ? buttonSelector : '.jsDeleteButton';
- this._callback = null;
-
- this.proxy = new WCF.Action.Proxy({
- success: $.proxy(this._success, this)
- });
-
- this._initElements();
+ WCF.DOMNodeInsertedHandler.addCallback('WCF.Action.Delete' + this._className.hashCode(), $.proxy(this._initElements, this));
+ },
+
+ /**
+ * Initializes available element containers.
+ */
+ _initElements: function () {
+ $(this._containerSelector).each((function (index, container) {
+ var $container = $(container);
+ var $containerID = $container.wcfIdentify();
- WCF.DOMNodeInsertedHandler.addCallback('WCF.Action.Delete' + this._className.hashCode(), $.proxy(this._initElements, this));
- },
-
- /**
- * Initializes available element containers.
- */
- _initElements: function () {
- $(this._containerSelector).each((function (index, container) {
- var $container = $(container);
- var $containerID = $container.wcfIdentify();
+ if (!WCF.inArray($containerID, this._containers)) {
+ var $deleteButton = $container.find(this._buttonSelector);
- if (!WCF.inArray($containerID, this._containers)) {
- var $deleteButton = $container.find(this._buttonSelector);
-
- if ($deleteButton.length) {
- this._containers.push($containerID);
- $deleteButton.click($.proxy(this._click, this));
- }
+ if ($deleteButton.length) {
+ this._containers.push($containerID);
+ $deleteButton.click($.proxy(this._click, this));
}
- }).bind(this));
- },
-
- /**
- * Sends AJAX request.
- *
- * @param object event
- */
- _click: function (event) {
- var $target = $(event.currentTarget);
- event.preventDefault();
-
- if ($target.data('confirmMessageHtml') || $target.data('confirmMessage')) {
- WCF.System.Confirmation.show($target.data('confirmMessageHtml') ? $target.data('confirmMessageHtml') : $target.data('confirmMessage'), $.proxy(this._execute, this), {target: $target}, undefined, $target.data('confirmMessageHtml') ? true : false);
- }
- else {
- WCF.LoadingOverlayHandler.updateIcon($target);
- this._sendRequest($target);
}
- },
-
- /**
- * Is called if the delete effect has been triggered on the given element.
- *
- * @param jQuery element
- */
- _didTriggerEffect: function (element) {
- // does nothing
- },
+ }).bind(this));
+ },
+
+ /**
+ * Sends AJAX request.
+ *
+ * @param object event
+ */
+ _click: function (event) {
+ var $target = $(event.currentTarget);
+ event.preventDefault();
- /**
- * Executes deletion.
- *
- * @param string action
- * @param object parameters
- */
- _execute: function (action, parameters) {
- if (action === 'cancel') {
- return;
- }
-
- WCF.LoadingOverlayHandler.updateIcon(parameters.target);
- this._sendRequest(parameters.target);
- },
+ if ($target.data('confirmMessageHtml') || $target.data('confirmMessage')) {
+ WCF.System.Confirmation.show($target.data('confirmMessageHtml') ? $target.data('confirmMessageHtml') : $target.data('confirmMessage'), $.proxy(this._execute, this), {target: $target}, undefined, $target.data('confirmMessageHtml') ? true : false);
+ }
+ else {
+ WCF.LoadingOverlayHandler.updateIcon($target);
+ this._sendRequest($target);
+ }
+ },
+
+ /**
+ * Is called if the delete effect has been triggered on the given element.
+ *
+ * @param jQuery element
+ */
+ _didTriggerEffect: function (element) {
+ // does nothing
+ },
+
+ /**
+ * Executes deletion.
+ *
+ * @param string action
+ * @param object parameters
+ */
+ _execute: function (action, parameters) {
+ if (action === 'cancel') {
+ return;
+ }
- /**
- * Sends the request
- *
- * @param jQuery object
- */
- _sendRequest: function (object) {
- this.proxy.setOption('data', {
- actionName: 'delete',
- className: this._className,
- interfaceName: 'wcf\\data\\IDeleteAction',
- objectIDs: [$(object).data('objectID')]
- });
-
- this.proxy.sendRequest();
- },
+ WCF.LoadingOverlayHandler.updateIcon(parameters.target);
+ this._sendRequest(parameters.target);
+ },
+
+ /**
+ * Sends the request
+ *
+ * @param jQuery object
+ */
+ _sendRequest: function (object) {
+ this.proxy.setOption('data', {
+ actionName: 'delete',
+ className: this._className,
+ interfaceName: 'wcf\\data\\IDeleteAction',
+ objectIDs: [$(object).data('objectID')]
+ });
- /**
- * Deletes items from containers.
- *
- * @param object data
- * @param string textStatus
- * @param object jqXHR
- */
- _success: function (data, textStatus, jqXHR) {
- if (this._callback) {
- this._callback(data.objectIDs);
- }
-
- this.triggerEffect(data.objectIDs);
- },
+ this.proxy.sendRequest();
+ },
+
+ /**
+ * Deletes items from containers.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param object jqXHR
+ */
+ _success: function (data, textStatus, jqXHR) {
+ if (this._callback) {
+ this._callback(data.objectIDs);
+ }
- /**
- * Sets a callback function called prior to triggering the delete effect.
- *
- * @param {function} callback
- */
- setCallback: function (callback) {
- if (typeof callback !== 'function') {
- throw new TypeError("[WCF.Action.Delete] Expected a valid callback for '" + this._className + "'.");
- }
-
- this._callback = callback;
- },
+ this.triggerEffect(data.objectIDs);
+ },
+
+ /**
+ * Sets a callback function called prior to triggering the delete effect.
+ *
+ * @param {function} callback
+ */
+ setCallback: function (callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("[WCF.Action.Delete] Expected a valid callback for '" + this._className + "'.");
+ }
- /**
- * Triggers the delete effect for the objects with the given ids.
- *
- * @param array objectIDs
- */
- triggerEffect: function (objectIDs) {
- for (var $index in this._containers) {
- var $container = $('#' + this._containers[$index]);
- var $button = $container.find(this._buttonSelector);
- if (WCF.inArray($button.data('objectID'), objectIDs)) {
- var self = this;
- $container.wcfBlindOut('up', function () {
- var $container = $(this).remove();
- self._containers.splice(self._containers.indexOf($container.wcfIdentify()), 1);
- self._didTriggerEffect($container);
-
- if ($button.data('eventName')) {
- WCF.System.Event.fireEvent('com.woltlab.wcf.action.delete', $button.data('eventName'), {
- button: $button,
- container: $container
- });
- }
- });
- }
+ this._callback = callback;
+ },
+
+ /**
+ * Triggers the delete effect for the objects with the given ids.
+ *
+ * @param array objectIDs
+ */
+ triggerEffect: function (objectIDs) {
+ for (var $index in this._containers) {
+ var $container = $('#' + this._containers[$index]);
+ var $button = $container.find(this._buttonSelector);
+ if (WCF.inArray($button.data('objectID'), objectIDs)) {
+ var self = this;
+ $container.wcfBlindOut('up', function () {
+ var $container = $(this).remove();
+ self._containers.splice(self._containers.indexOf($container.wcfIdentify()), 1);
+ self._didTriggerEffect($container);
+
+ if ($button.data('eventName')) {
+ WCF.System.Event.fireEvent('com.woltlab.wcf.action.delete', $button.data('eventName'), {
+ button: $button,
+ container: $container
+ });
+ }
+ });
}
}
- });
-
+ }
+});
+
+if (COMPILER_TARGET_DEFAULT) {
/**
* Basic implementation for deletion of nested elements.
*
});
}
else {
- WCF.Action.Delete = Class.extend({
- _buttonSelector: "",
- _callback: {},
- _className: "",
- _containerSelector: "",
- _containers: {},
- init: function() {},
- _initElements: function() {},
- _click: function() {},
- _didTriggerEffect: function() {},
- _execute: function() {},
- _sendRequest: function() {},
- _success: function() {},
- setCallback: function() {},
- triggerEffect: function() {}
- });
-
WCF.Action.NestedDelete = WCF.Action.Delete.extend({
triggerEffect: function() {},
_buttonSelector: "",
}
// update progress
- this._dialog.find('progress').attr('value', data.returnValues.progress).text(data.returnValues.progress + '%').next('span').text(data.returnValues.progress + '%');
+ this._dialog.find('progress').attr(
+ 'value',
+ data.returnValues.progress
+ ).text(data.returnValues.progress + '%').next('span').text(data.returnValues.progress + '%');
// worker is still busy with its business, carry on
if (data.returnValues.progress < 100) {
else {
// exchange icon
this._dialog.find('.fa-spinner').removeClass('fa-spinner').addClass('fa-check green');
- this._dialog.find('.contentHeader h1').text(WCF.Language.get('wcf.global.worker.completed'));
+ this._dialog.find('.contentHeader h1').text(WCF.Language.get(
+ 'wcf.global.worker.completed'));
// display continue button
var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
- $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($formSubmit).focus().click(function () {
+ $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo(
+ $formSubmit).focus().click(function () {
if (data.returnValues.redirectURL) {
window.location = data.returnValues.redirectURL;
}
this.rebuild();
- WCF.DOMNodeInsertedHandler.addCallback('WCF.InlineEditor' + this._elementSelector.hashCode(), $.proxy(this.rebuild, this));
+ WCF.DOMNodeInsertedHandler.addCallback(
+ 'WCF.InlineEditor' + this._elementSelector.hashCode(),
+ $.proxy(this.rebuild, this)
+ );
this._proxy = new WCF.Action.Proxy({
success: $.proxy(this._success, this)
WCF.CloseOverlayHandler.addCallback('WCF.InlineEditor', $.proxy(this._closeAll, this));
- this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'), 'success');
+ this._notification = new WCF.System.Notification(
+ WCF.Language.get('wcf.global.success'),
+ 'success'
+ );
},
/**
* Identify new elements and adds the event listeners to them.
*/
- rebuild: function() {
+ rebuild: function () {
var $elements = $(this._elementSelector);
var self = this;
$elements.each(function (index, element) {
return;
}
- $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data('elementID', $elementID);
+ $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data(
+ 'elementID',
+ $elementID
+ );
if (self._quickOption) {
// simulate click on target action
- $trigger.disableSelection().data('optionName', self._quickOption).dblclick($.proxy(self._click, self));
+ $trigger.disableSelection().data(
+ 'optionName',
+ self._quickOption
+ ).dblclick($.proxy(self._click, self));
}
// store reference
// build dropdown
var $trigger = null;
if (!this._dropdowns[$elementID]) {
- this._triggerElements[$elementID] = $trigger = this._getTriggerElement(this._elements[$elementID]).addClass('dropdownToggle');
+ this._triggerElements[$elementID] = $trigger = this._getTriggerElement(this._elements[$elementID]).addClass(
+ 'dropdownToggle');
var parent = $trigger[0].parentNode;
if (parent && parent.nodeName === 'LI' && parent.childElementCount === 1) {
// do not add a wrapper element if the trigger is the only child
$lastElementType = $option.optionName;
}
}
- else if (this._validate($elementID, $option.optionName) || this._validateCallbacks($elementID, $option.optionName)) {
+ else if (this._validate($elementID, $option.optionName) || this._validateCallbacks(
+ $elementID,
+ $option.optionName
+ )) {
var $listItem = $('<li><span>' + $option.label + '</span></li>').appendTo(this._dropdowns[$elementID]);
- $listItem.data('elementID', $elementID).data('optionName', $option.optionName).data('isQuickOption', ($option.isQuickOption ? true : false)).click($.proxy(this._click, this));
+ $listItem.data('elementID', $elementID).data(
+ 'optionName',
+ $option.optionName
+ ).data('isQuickOption', ($option.isQuickOption ? true : false)).click($.proxy(
+ this._click,
+ this
+ ));
$hasOptions = true;
$lastElementType = $option.optionName;
}
}
});
+}
+else {
+ WCF.System.Worker = Class.extend({
+ _aborted: false,
+ _actionName: "",
+ _callback: {},
+ _className: "",
+ _dialog: {},
+ _proxy: {},
+ _title: "",
+ init: function() {},
+ _success: function() {}
+ });
- /**
- * Default implementation for ajax file uploads.
- *
- * @deprecated Use WoltLabSuite/Core/Upload
- *
- * @param jquery buttonSelector
- * @param jquery fileListSelector
- * @param string className
- * @param jquery options
- */
- WCF.Upload = Class.extend({
- /**
- * name of the upload field
- * @var string
- */
- _name: '__files[]',
-
- /**
- * button selector
- * @var jQuery
- */
- _buttonSelector: null,
-
- /**
- * file list selector
- * @var jQuery
- */
- _fileListSelector: null,
-
- /**
- * upload file
- * @var jQuery
- */
- _fileUpload: null,
-
- /**
- * class name
- * @var string
- */
- _className: '',
-
- /**
- * iframe for IE<10 fallback
- * @var jQuery
- */
- _iframe: null,
-
- /**
- * internal file id
- * @var integer
- */
- _internalFileID: 0,
-
- /**
- * additional options
- * @var jQuery
- */
+ WCF.InlineEditor = Class.extend({
+ _callbacks: {},
+ _dropdowns: {},
+ _elements: {},
+ _notification: {},
_options: {},
+ _proxy: {},
+ _triggerElements: {},
+ _updateData: {},
+ init: function() {},
+ _closeAll: function() {},
+ _setOptions: function() {},
+ registerCallback: function() {},
+ _getTriggerElement: function() {},
+ _show: function() {},
+ _validate: function() {},
+ _validateCallbacks: function() {},
+ _success: function() {},
+ _updateState: function() {},
+ _click: function() {},
+ _execute: function() {},
+ _executeCallback: function() {},
+ _hide: function() {}
+ });
+}
+
+/**
+ * Default implementation for ajax file uploads.
+ *
+ * @deprecated Use WoltLabSuite/Core/Upload
+ *
+ * @param jquery buttonSelector
+ * @param jquery fileListSelector
+ * @param string className
+ * @param jquery options
+ */
+WCF.Upload = Class.extend({
+ /**
+ * name of the upload field
+ * @var string
+ */
+ _name: '__files[]',
+
+ /**
+ * button selector
+ * @var jQuery
+ */
+ _buttonSelector: null,
+
+ /**
+ * file list selector
+ * @var jQuery
+ */
+ _fileListSelector: null,
+
+ /**
+ * upload file
+ * @var jQuery
+ */
+ _fileUpload: null,
+
+ /**
+ * class name
+ * @var string
+ */
+ _className: '',
+
+ /**
+ * iframe for IE<10 fallback
+ * @var jQuery
+ */
+ _iframe: null,
+
+ /**
+ * internal file id
+ * @var integer
+ */
+ _internalFileID: 0,
+
+ /**
+ * additional options
+ * @var jQuery
+ */
+ _options: {},
+
+ /**
+ * upload matrix
+ * @var array
+ */
+ _uploadMatrix: [],
+
+ /**
+ * true, if the active user's browser supports ajax file uploads
+ * @var boolean
+ */
+ _supportsAJAXUpload: true,
+
+ /**
+ * fallback overlay for stupid browsers
+ * @var jquery
+ */
+ _overlay: null,
+
+ /**
+ * Initializes a new upload handler.
+ *
+ * @param string buttonSelector
+ * @param string fileListSelector
+ * @param string className
+ * @param object options
+ */
+ init: function (buttonSelector, fileListSelector, className, options) {
+ this._buttonSelector = buttonSelector;
+ this._fileListSelector = fileListSelector;
+ this._className = className;
+ this._internalFileID = 0;
+ this._options = $.extend(true, {
+ action: 'upload',
+ multiple: false,
+ url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
+ }, options || {});
- /**
- * upload matrix
- * @var array
- */
- _uploadMatrix: [],
-
- /**
- * true, if the active user's browser supports ajax file uploads
- * @var boolean
- */
- _supportsAJAXUpload: true,
-
- /**
- * fallback overlay for stupid browsers
- * @var jquery
- */
- _overlay: null,
-
- /**
- * Initializes a new upload handler.
- *
- * @param string buttonSelector
- * @param string fileListSelector
- * @param string className
- * @param object options
- */
- init: function (buttonSelector, fileListSelector, className, options) {
- this._buttonSelector = buttonSelector;
- this._fileListSelector = fileListSelector;
- this._className = className;
- this._internalFileID = 0;
- this._options = $.extend(true, {
- action: 'upload',
- multiple: false,
- url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
- }, options || {});
-
- this._options.url = WCF.convertLegacyURL(this._options.url);
- if (this._options.url.indexOf('index.php') === 0) {
- this._options.url = WSC_API_URL + this._options.url;
- }
-
- // check for ajax upload support
- var $xhr = new XMLHttpRequest();
- this._supportsAJAXUpload = ($xhr && ('upload' in $xhr) && ('onprogress' in $xhr.upload));
-
- // create upload button
- this._createButton();
- },
-
- /**
- * Creates the upload button.
- */
- _createButton: function () {
- if (this._supportsAJAXUpload) {
- this._fileUpload = $('<input type="file" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/>');
- this._fileUpload.change($.proxy(this._upload, this));
- var $button = $('<p class="button uploadButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
- elAttr($button[0], 'role', 'button');
- $button.prepend(this._fileUpload);
-
- this._fileUpload[0].addEventListener('focus', function() {
- if (this.classList.contains('focus-visible')) {
- $button[0].classList.add('active');
- }
- });
- this._fileUpload[0].addEventListener('blur', function() { $button[0].classList.remove('active'); });
- }
- else {
- var $button = $('<p class="button uploadFallbackButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
- elAttr($button[0], 'role', 'button');
- elAttr($button[0], 'tabindex', '0');
- $button.click($.proxy(this._showOverlay, this));
- }
-
- this._insertButton($button);
- },
-
- /**
- * Inserts the upload button.
- *
- * @param jQuery button
- */
- _insertButton: function (button) {
- this._buttonSelector.prepend(button);
- },
-
- /**
- * Removes the upload button.
- */
- _removeButton: function () {
- var $selector = '.uploadButton';
- if (!this._supportsAJAXUpload) {
- $selector = '.uploadFallbackButton';
- }
-
- this._buttonSelector.find($selector).remove();
- },
-
- /**
- * Callback for file uploads.
- *
- * @param object event
- * @param File file
- * @param Blob blob
- * @param Array|FileList list of files
- * @return integer
- */
- _upload: function (event, file, blob, files) {
- var $uploadID = null;
- var $files = [];
-
- if (typeof files !== 'undefined') {
- $files = files;
- }
- else {
- if (file) {
- $files.push(file);
- }
- else if (blob) {
- var $ext = '';
- switch (blob.type) {
- case 'image/png':
- $ext = '.png';
- break;
-
- case 'image/jpeg':
- $ext = '.jpg';
- break;
-
- case 'image/gif':
- $ext = '.gif';
- break;
- }
-
- $files.push({
- name: 'pasted-from-clipboard' + $ext
- });
- }
- else {
- $files = this._fileUpload.prop('files');
- }
- }
-
- if ($files.length) {
- var $fd = new FormData();
- $uploadID = this._createUploadMatrix($files);
-
- // no more files left, abort
- if (!this._uploadMatrix[$uploadID].length) {
- return null;
- }
-
- for (var $i = 0, $length = $files.length; $i < $length; $i++) {
- if (this._uploadMatrix[$uploadID][$i]) {
- var $internalFileID = this._uploadMatrix[$uploadID][$i].data('internalFileID');
-
- if (blob) {
- $fd.append('__files[' + $internalFileID + ']', blob, $files[$i].name);
- }
- else {
- $fd.append('__files[' + $internalFileID + ']', $files[$i], $files[$i].name);
- }
- }
- }
-
- $fd.append('actionName', this._options.action);
- $fd.append('className', this._className);
- var $additionalParameters = this._getParameters();
- for (var $name in $additionalParameters) {
- $fd.append('parameters[' + $name + ']', $additionalParameters[$name]);
- }
-
- var self = this;
- $.ajax({
- type: 'POST',
- url: this._options.url,
- enctype: 'multipart/form-data',
- data: $fd,
- contentType: false,
- processData: false,
- success: function (data, textStatus, jqXHR) {
- self._success($uploadID, data);
- },
- error: $.proxy(this._error, this),
- xhr: function () {
- var $xhr = $.ajaxSettings.xhr();
- if ($xhr) {
- $xhr.upload.addEventListener('progress', function (event) {
- self._progress($uploadID, event);
- }, false);
- }
- return $xhr;
- },
- xhrFields: {
- withCredentials: true
- }
- });
- }
-
- return $uploadID;
- },
-
- /**
- * Creates upload matrix for provided files.
- *
- * @param array<object> files
- * @return integer
- */
- _createUploadMatrix: function (files) {
- if (files.length) {
- var $uploadID = this._uploadMatrix.length;
- this._uploadMatrix[$uploadID] = [];
-
- for (var $i = 0, $length = files.length; $i < $length; $i++) {
- var $file = files[$i];
- var $li = this._initFile($file);
-
- if (!$li.hasClass('uploadFailed')) {
- $li.data('filename', $file.name).data('internalFileID', this._internalFileID++);
- this._uploadMatrix[$uploadID][$i] = $li;
- }
- }
-
- return $uploadID;
- }
-
- return null;
- },
-
- /**
- * Callback for success event.
- *
- * @param integer uploadID
- * @param object data
- */
- _success: function (uploadID, data) {
- },
-
- /**
- * Callback for error event.
- *
- * @param jQuery jqXHR
- * @param string textStatus
- * @param string errorThrown
- */
- _error: function (jqXHR, textStatus, errorThrown) {
- },
-
- /**
- * Callback for progress event.
- *
- * @param integer uploadID
- * @param object event
- */
- _progress: function (uploadID, event) {
- var $percentComplete = Math.round(event.loaded * 100 / event.total);
-
- for (var $i in this._uploadMatrix[uploadID]) {
- this._uploadMatrix[uploadID][$i].find('progress').attr('value', $percentComplete);
- }
- },
-
- /**
- * Returns additional parameters.
- *
- * @return object
- */
- _getParameters: function () {
- return {};
- },
+ this._options.url = WCF.convertLegacyURL(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
- /**
- * Initializes list item for uploaded file.
- *
- * @return jQuery
- */
- _initFile: function (file) {
- return $('<li>' + file.name + ' (' + file.size + ')<progress max="100" /></li>').appendTo(this._fileListSelector);
- },
+ // check for ajax upload support
+ var $xhr = new XMLHttpRequest();
+ this._supportsAJAXUpload = ($xhr && ('upload' in $xhr) && ('onprogress' in $xhr.upload));
- /**
- * Shows the fallback overlay (work in progress)
- */
- _showOverlay: function () {
- // create iframe
- if (this._iframe === null) {
- this._iframe = $('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body);
- }
+ // create upload button
+ this._createButton();
+ },
+
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function () {
+ if (this._supportsAJAXUpload) {
+ this._fileUpload = $('<input type="file" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/>');
+ this._fileUpload.change($.proxy(this._upload, this));
+ var $button = $('<p class="button uploadButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
+ elAttr($button[0], 'role', 'button');
+ $button.prepend(this._fileUpload);
- // create overlay
- if (!this._overlay) {
- this._overlay = $('<div><form enctype="multipart/form-data" method="post" action="' + this._options.url + '" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);
-
- var $form = this._overlay.find('form');
- $('<dl class="wide"><dd><input type="file" id="__fileUpload" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/></dd></dl>').appendTo($form);
- $('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo($form);
-
- $('<input type="hidden" name="isFallback" value="1" />').appendTo($form);
- $('<input type="hidden" name="actionName" value="' + this._options.action + '" />').appendTo($form);
- $('<input type="hidden" name="className" value="' + this._className + '" />').appendTo($form);
- var $additionalParameters = this._getParameters();
- for (var $name in $additionalParameters) {
- $('<input type="hidden" name="' + $name + '" value="' + $additionalParameters[$name] + '" />').appendTo($form);
+ this._fileUpload[0].addEventListener('focus', function() {
+ if (this.classList.contains('focus-visible')) {
+ $button[0].classList.add('active');
}
-
- $form.submit($.proxy(function () {
- var $file = {
- name: this._getFilename(),
- size: ''
- };
-
- var $uploadID = this._createUploadMatrix([$file]);
- var self = this;
- this._iframe.data('loading', true).off('load').load(function () {
- self._evaluateResponse($uploadID);
- });
- this._overlay.wcfDialog('close');
- }, this));
- }
-
- this._overlay.wcfDialog({
- title: WCF.Language.get('wcf.global.button.upload')
});
- },
-
- /**
- * Evaluates iframe response.
- *
- * @param integer uploadID
- */
- _evaluateResponse: function (uploadID) {
- var $returnValues = $.parseJSON(this._iframe.contents().find('pre').html());
- this._success(uploadID, $returnValues);
- },
+ this._fileUpload[0].addEventListener('blur', function() { $button[0].classList.remove('active'); });
+ }
+ else {
+ var $button = $('<p class="button uploadFallbackButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
+ elAttr($button[0], 'role', 'button');
+ elAttr($button[0], 'tabindex', '0');
+ $button.click($.proxy(this._showOverlay, this));
+ }
- /**
- * Returns name of selected file.
- *
- * @return string
- */
- _getFilename: function () {
- return $('#__fileUpload').val().split('\\').pop();
+ this._insertButton($button);
+ },
+
+ /**
+ * Inserts the upload button.
+ *
+ * @param jQuery button
+ */
+ _insertButton: function (button) {
+ this._buttonSelector.prepend(button);
+ },
+
+ /**
+ * Removes the upload button.
+ */
+ _removeButton: function () {
+ var $selector = '.uploadButton';
+ if (!this._supportsAJAXUpload) {
+ $selector = '.uploadFallbackButton';
}
- });
+
+ this._buttonSelector.find($selector).remove();
+ },
/**
- * Default implementation for parallel AJAX file uploads.
+ * Callback for file uploads.
*
- * @deprecated Use WoltLabSuite/Core/Upload
+ * @param object event
+ * @param File file
+ * @param Blob blob
+ * @param Array|FileList list of files
+ * @return integer
*/
- WCF.Upload.Parallel = WCF.Upload.extend({
- /**
- * @see WCF.Upload.init()
- */
- init: function (buttonSelector, fileListSelector, className, options) {
- // force multiple uploads
- options = $.extend(true, options || {}, {
- multiple: true
- });
-
- this._super(buttonSelector, fileListSelector, className, options);
- },
+ _upload: function (event, file, blob, files) {
+ var $uploadID = null;
+ var $files = [];
- /**
- * @see WCF.Upload._upload()
- */
- _upload: function () {
- var $files = this._fileUpload.prop('files');
- for (var $i = 0, $length = $files.length; $i < $length; $i++) {
- var $file = $files[$i];
- var $formData = new FormData();
- var $internalFileID = this._createUploadMatrix($file);
-
- if (!this._uploadMatrix[$internalFileID].length) {
- continue;
- }
-
- $formData.append('__files[' + $internalFileID + ']', $file);
- $formData.append('actionName', this._options.action);
- $formData.append('className', this._className);
- var $additionalParameters = this._getParameters();
- for (var $name in $additionalParameters) {
- $formData.append('parameters[' + $name + ']', $additionalParameters[$name]);
+ if (typeof files !== 'undefined') {
+ $files = files;
+ }
+ else {
+ if (file) {
+ $files.push(file);
+ }
+ else if (blob) {
+ var $ext = '';
+ switch (blob.type) {
+ case 'image/png':
+ $ext = '.png';
+ break;
+
+ case 'image/jpeg':
+ $ext = '.jpg';
+ break;
+
+ case 'image/gif':
+ $ext = '.gif';
+ break;
}
- this._sendRequest($internalFileID, $formData);
+ $files.push({
+ name: 'pasted-from-clipboard' + $ext
+ });
}
- },
+ else {
+ $files = this._fileUpload.prop('files');
+ }
+ }
- /**
- * Sends an AJAX request to upload a file.
- *
- * @param integer internalFileID
- * @param FormData formData
- * @return jqXHR
- */
- _sendRequest: function (internalFileID, formData) {
- var self = this;
+ if ($files.length) {
+ var $fd = new FormData();
+ $uploadID = this._createUploadMatrix($files);
+
+ // no more files left, abort
+ if (!this._uploadMatrix[$uploadID].length) {
+ return null;
+ }
+
+ for (var $i = 0, $length = $files.length; $i < $length; $i++) {
+ if (this._uploadMatrix[$uploadID][$i]) {
+ var $internalFileID = this._uploadMatrix[$uploadID][$i].data('internalFileID');
+
+ if (blob) {
+ $fd.append('__files[' + $internalFileID + ']', blob, $files[$i].name);
+ }
+ else {
+ $fd.append('__files[' + $internalFileID + ']', $files[$i], $files[$i].name);
+ }
+ }
+ }
+
+ $fd.append('actionName', this._options.action);
+ $fd.append('className', this._className);
+ var $additionalParameters = this._getParameters();
+ for (var $name in $additionalParameters) {
+ $fd.append('parameters[' + $name + ']', $additionalParameters[$name]);
+ }
- return $.ajax({
+ var self = this;
+ $.ajax({
type: 'POST',
url: this._options.url,
enctype: 'multipart/form-data',
- data: formData,
+ data: $fd,
contentType: false,
processData: false,
success: function (data, textStatus, jqXHR) {
- self._success(internalFileID, data);
+ self._success($uploadID, data);
},
error: $.proxy(this._error, this),
xhr: function () {
var $xhr = $.ajaxSettings.xhr();
if ($xhr) {
$xhr.upload.addEventListener('progress', function (event) {
- self._progress(internalFileID, event);
+ self._progress($uploadID, event);
}, false);
}
return $xhr;
+ },
+ xhrFields: {
+ withCredentials: true
}
});
- },
+ }
- /**
- * Creates upload matrix for provided file and returns its internal file id.
- *
- * @param object file
- * @return integer
- */
- _createUploadMatrix: function (file) {
- var $li = this._initFile(file);
- if (!$li.hasClass('uploadFailed')) {
- $li.data('filename', file.name).data('internalFileID', this._internalFileID);
- this._uploadMatrix[this._internalFileID++] = $li;
+ return $uploadID;
+ },
+
+ /**
+ * Creates upload matrix for provided files.
+ *
+ * @param array<object> files
+ * @return integer
+ */
+ _createUploadMatrix: function (files) {
+ if (files.length) {
+ var $uploadID = this._uploadMatrix.length;
+ this._uploadMatrix[$uploadID] = [];
+
+ for (var $i = 0, $length = files.length; $i < $length; $i++) {
+ var $file = files[$i];
+ var $li = this._initFile($file);
- return this._internalFileID - 1;
+ if (!$li.hasClass('uploadFailed')) {
+ $li.data('filename', $file.name).data('internalFileID', this._internalFileID++);
+ this._uploadMatrix[$uploadID][$i] = $li;
+ }
}
- return null;
- },
+ return $uploadID;
+ }
- /**
- * Callback for success event.
- *
- * @param integer internalFileID
- * @param object data
- */
- _success: function (internalFileID, data) {
- },
+ return null;
+ },
+
+ /**
+ * Callback for success event.
+ *
+ * @param integer uploadID
+ * @param object data
+ */
+ _success: function (uploadID, data) {
+ },
+
+ /**
+ * Callback for error event.
+ *
+ * @param jQuery jqXHR
+ * @param string textStatus
+ * @param string errorThrown
+ */
+ _error: function (jqXHR, textStatus, errorThrown) {
+ },
+
+ /**
+ * Callback for progress event.
+ *
+ * @param integer uploadID
+ * @param object event
+ */
+ _progress: function (uploadID, event) {
+ var $percentComplete = Math.round(event.loaded * 100 / event.total);
- /**
- * Callback for progress event.
- *
- * @param integer internalFileID
- * @param object event
- */
- _progress: function (internalFileID, event) {
- var $percentComplete = Math.round(event.loaded * 100 / event.total);
-
- this._uploadMatrix[internalFileID].find('progress').attr('value', $percentComplete);
- },
+ for (var $i in this._uploadMatrix[uploadID]) {
+ this._uploadMatrix[uploadID][$i].find('progress').attr('value', $percentComplete);
+ }
+ },
+
+ /**
+ * Returns additional parameters.
+ *
+ * @return object
+ */
+ _getParameters: function () {
+ return {};
+ },
+
+ /**
+ * Initializes list item for uploaded file.
+ *
+ * @return jQuery
+ */
+ _initFile: function (file) {
+ return $('<li>' + file.name + ' (' + file.size + ')<progress max="100" /></li>').appendTo(this._fileListSelector);
+ },
+
+ /**
+ * Shows the fallback overlay (work in progress)
+ */
+ _showOverlay: function () {
+ // create iframe
+ if (this._iframe === null) {
+ this._iframe = $('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body);
+ }
- /**
- * @see WCF.Upload._showOverlay()
- */
- _showOverlay: function () {
- // create iframe
- if (this._iframe === null) {
- this._iframe = $('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body);
+ // create overlay
+ if (!this._overlay) {
+ this._overlay = $('<div><form enctype="multipart/form-data" method="post" action="' + this._options.url + '" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);
+
+ var $form = this._overlay.find('form');
+ $('<dl class="wide"><dd><input type="file" id="__fileUpload" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/></dd></dl>').appendTo($form);
+ $('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo($form);
+
+ $('<input type="hidden" name="isFallback" value="1" />').appendTo($form);
+ $('<input type="hidden" name="actionName" value="' + this._options.action + '" />').appendTo($form);
+ $('<input type="hidden" name="className" value="' + this._className + '" />').appendTo($form);
+ var $additionalParameters = this._getParameters();
+ for (var $name in $additionalParameters) {
+ $('<input type="hidden" name="' + $name + '" value="' + $additionalParameters[$name] + '" />').appendTo($form);
}
- // create overlay
- if (!this._overlay) {
- this._overlay = $('<div><form enctype="multipart/form-data" method="post" action="' + this._options.url + '" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);
-
- var $form = this._overlay.find('form');
- $('<dl class="wide"><dd><input type="file" id="__fileUpload" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/></dd></dl>').appendTo($form);
- $('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo($form);
-
- $('<input type="hidden" name="isFallback" value="1" />').appendTo($form);
- $('<input type="hidden" name="actionName" value="' + this._options.action + '" />').appendTo($form);
- $('<input type="hidden" name="className" value="' + this._className + '" />').appendTo($form);
- var $additionalParameters = this._getParameters();
- for (var $name in $additionalParameters) {
- $('<input type="hidden" name="' + $name + '" value="' + $additionalParameters[$name] + '" />').appendTo($form);
- }
+ $form.submit($.proxy(function () {
+ var $file = {
+ name: this._getFilename(),
+ size: ''
+ };
- $form.submit($.proxy(function () {
- var $file = {
- name: this._getFilename(),
- size: ''
- };
-
- var $internalFileID = this._createUploadMatrix($file);
- var self = this;
- this._iframe.data('loading', true).off('load').load(function () {
- self._evaluateResponse($internalFileID);
- });
- this._overlay.wcfDialog('close');
- }, this));
+ var $uploadID = this._createUploadMatrix([$file]);
+ var self = this;
+ this._iframe.data('loading', true).off('load').load(function () {
+ self._evaluateResponse($uploadID);
+ });
+ this._overlay.wcfDialog('close');
+ }, this));
+ }
+
+ this._overlay.wcfDialog({
+ title: WCF.Language.get('wcf.global.button.upload')
+ });
+ },
+
+ /**
+ * Evaluates iframe response.
+ *
+ * @param integer uploadID
+ */
+ _evaluateResponse: function (uploadID) {
+ var $returnValues = $.parseJSON(this._iframe.contents().find('pre').html());
+ this._success(uploadID, $returnValues);
+ },
+
+ /**
+ * Returns name of selected file.
+ *
+ * @return string
+ */
+ _getFilename: function () {
+ return $('#__fileUpload').val().split('\\').pop();
+ }
+});
+
+/**
+ * Default implementation for parallel AJAX file uploads.
+ *
+ * @deprecated Use WoltLabSuite/Core/Upload
+ */
+WCF.Upload.Parallel = WCF.Upload.extend({
+ /**
+ * @see WCF.Upload.init()
+ */
+ init: function (buttonSelector, fileListSelector, className, options) {
+ // force multiple uploads
+ options = $.extend(true, options || {}, {
+ multiple: true
+ });
+
+ this._super(buttonSelector, fileListSelector, className, options);
+ },
+
+ /**
+ * @see WCF.Upload._upload()
+ */
+ _upload: function () {
+ var $files = this._fileUpload.prop('files');
+ for (var $i = 0, $length = $files.length; $i < $length; $i++) {
+ var $file = $files[$i];
+ var $formData = new FormData();
+ var $internalFileID = this._createUploadMatrix($file);
+
+ if (!this._uploadMatrix[$internalFileID].length) {
+ continue;
}
- this._overlay.wcfDialog({
- title: WCF.Language.get('wcf.global.button.upload')
- });
- },
+ $formData.append('__files[' + $internalFileID + ']', $file);
+ $formData.append('actionName', this._options.action);
+ $formData.append('className', this._className);
+ var $additionalParameters = this._getParameters();
+ for (var $name in $additionalParameters) {
+ $formData.append('parameters[' + $name + ']', $additionalParameters[$name]);
+ }
+
+ this._sendRequest($internalFileID, $formData);
+ }
+ },
+
+ /**
+ * Sends an AJAX request to upload a file.
+ *
+ * @param integer internalFileID
+ * @param FormData formData
+ * @return jqXHR
+ */
+ _sendRequest: function (internalFileID, formData) {
+ var self = this;
- /**
- * Evaluates iframe response.
- *
- * @param integer internalFileID
- */
- _evaluateResponse: function (internalFileID) {
- var $returnValues = $.parseJSON(this._iframe.contents().find('pre').html());
- this._success(internalFileID, $returnValues);
+ return $.ajax({
+ type: 'POST',
+ url: this._options.url,
+ enctype: 'multipart/form-data',
+ data: formData,
+ contentType: false,
+ processData: false,
+ success: function (data, textStatus, jqXHR) {
+ self._success(internalFileID, data);
+ },
+ error: $.proxy(this._error, this),
+ xhr: function () {
+ var $xhr = $.ajaxSettings.xhr();
+ if ($xhr) {
+ $xhr.upload.addEventListener('progress', function (event) {
+ self._progress(internalFileID, event);
+ }, false);
+ }
+ return $xhr;
+ }
+ });
+ },
+
+ /**
+ * Creates upload matrix for provided file and returns its internal file id.
+ *
+ * @param object file
+ * @return integer
+ */
+ _createUploadMatrix: function (file) {
+ var $li = this._initFile(file);
+ if (!$li.hasClass('uploadFailed')) {
+ $li.data('filename', file.name).data('internalFileID', this._internalFileID);
+ this._uploadMatrix[this._internalFileID++] = $li;
+
+ return this._internalFileID - 1;
}
- });
-}
-else {
- WCF.System.Worker = Class.extend({
- _aborted: false,
- _actionName: "",
- _callback: {},
- _className: "",
- _dialog: {},
- _proxy: {},
- _title: "",
- init: function() {},
- _success: function() {}
- });
+
+ return null;
+ },
- WCF.InlineEditor = Class.extend({
- _callbacks: {},
- _dropdowns: {},
- _elements: {},
- _notification: {},
- _options: {},
- _proxy: {},
- _triggerElements: {},
- _updateData: {},
- init: function() {},
- _closeAll: function() {},
- _setOptions: function() {},
- registerCallback: function() {},
- _getTriggerElement: function() {},
- _show: function() {},
- _validate: function() {},
- _validateCallbacks: function() {},
- _success: function() {},
- _updateState: function() {},
- _click: function() {},
- _execute: function() {},
- _executeCallback: function() {},
- _hide: function() {}
- });
+ /**
+ * Callback for success event.
+ *
+ * @param integer internalFileID
+ * @param object data
+ */
+ _success: function (internalFileID, data) {
+ },
- WCF.Upload = Class.extend({
- _name: "",
- _buttonSelector: {},
- _fileListSelector: {},
- _fileUpload: {},
- _className: "",
- _iframe: {},
- _internalFileID: 0,
- _options: {},
- _uploadMatrix: {},
- _supportsAJAXUpload: true,
- _overlay: {},
- init: function() {},
- _createButton: function() {},
- _insertButton: function() {},
- _removeButton: function() {},
- _upload: function() {},
- _createUploadMatrix: function() {},
- _success: function() {},
- _error: function() {},
- _progress: function() {},
- _getParameters: function() {},
- _initFile: function() {},
- _showOverlay: function() {},
- _evaluateResponse: function() {},
- _getFilename: function() {}
- });
+ /**
+ * Callback for progress event.
+ *
+ * @param integer internalFileID
+ * @param object event
+ */
+ _progress: function (internalFileID, event) {
+ var $percentComplete = Math.round(event.loaded * 100 / event.total);
+
+ this._uploadMatrix[internalFileID].find('progress').attr('value', $percentComplete);
+ },
- WCF.Upload.Parallel = WCF.Upload.extend({
- init: function() {},
- _upload: function() {},
- _sendRequest: function() {},
- _createUploadMatrix: function() {},
- _success: function() {},
- _progress: function() {},
- _showOverlay: function() {},
- _evaluateResponse: function() {},
- _name: "",
- _buttonSelector: {},
- _fileListSelector: {},
- _fileUpload: {},
- _className: "",
- _iframe: {},
- _internalFileID: 0,
- _options: {},
- _uploadMatrix: {},
- _supportsAJAXUpload: true,
- _overlay: {},
- _createButton: function() {},
- _insertButton: function() {},
- _removeButton: function() {},
- _error: function() {},
- _getParameters: function() {},
- _initFile: function() {},
- _getFilename: function() {}
- });
-}
+ /**
+ * @see WCF.Upload._showOverlay()
+ */
+ _showOverlay: function () {
+ // create iframe
+ if (this._iframe === null) {
+ this._iframe = $('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body);
+ }
+
+ // create overlay
+ if (!this._overlay) {
+ this._overlay = $('<div><form enctype="multipart/form-data" method="post" action="' + this._options.url + '" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);
+
+ var $form = this._overlay.find('form');
+ $('<dl class="wide"><dd><input type="file" id="__fileUpload" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/></dd></dl>').appendTo($form);
+ $('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo($form);
+
+ $('<input type="hidden" name="isFallback" value="1" />').appendTo($form);
+ $('<input type="hidden" name="actionName" value="' + this._options.action + '" />').appendTo($form);
+ $('<input type="hidden" name="className" value="' + this._className + '" />').appendTo($form);
+ var $additionalParameters = this._getParameters();
+ for (var $name in $additionalParameters) {
+ $('<input type="hidden" name="' + $name + '" value="' + $additionalParameters[$name] + '" />').appendTo($form);
+ }
+
+ $form.submit($.proxy(function () {
+ var $file = {
+ name: this._getFilename(),
+ size: ''
+ };
+
+ var $internalFileID = this._createUploadMatrix($file);
+ var self = this;
+ this._iframe.data('loading', true).off('load').load(function () {
+ self._evaluateResponse($internalFileID);
+ });
+ this._overlay.wcfDialog('close');
+ }, this));
+ }
+
+ this._overlay.wcfDialog({
+ title: WCF.Language.get('wcf.global.button.upload')
+ });
+ },
+
+ /**
+ * Evaluates iframe response.
+ *
+ * @param integer internalFileID
+ */
+ _evaluateResponse: function (internalFileID) {
+ var $returnValues = $.parseJSON(this._iframe.contents().find('pre').html());
+ this._success(internalFileID, $returnValues);
+ }
+});
/**
* Namespace for sortables.