function smf_fileUpload(oOptions) { var previewNode = document.querySelector('#au-template'); previewNode.id = ''; var tmp = document.createElement('div'); tmp.appendChild(previewNode.cloneNode(true)); previewTemplate = tmp.innerHTML; previewNode.parentNode.removeChild(previewNode); var isNewTemplate = !!document.getElementById('post_attachments_area'); if (typeof current_board == 'undefined') current_board = false; // Default values in case oOptions isn't defined. var dOptions = { url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id + (current_board ? ';board=' + current_board : ''), parallelUploads: 1, filesizeBase: 1024, paramName: 'attachment', uploadMultiple: true, previewsContainer: '#attachment_previews', previewTemplate: previewTemplate, acceptedFiles: '.doc,.gif,.jpg,.pdf,.png,.txt,.zip', thumbnailWidth: 100, thumbnailHeight: null, autoQueue: isNewTemplate, clickable: isNewTemplate ? ['.attachment_spacer', '#drop_zone_ui'] : '.fileinput-button', currentUsedSize: 0, timeout: null, smf_insertBBC: function (file, w, h) { var mime_type = typeof file.type !== "undefined" ? file.type : (typeof file.mime_type !== "undefined" ? file.mime_type : ''), bbcOptionalParams = { width: mime_type.indexOf('image') == 0 && + w > 0 ? (' width=' + w) : '', height: mime_type.indexOf('image') == 0 && + h > 0 ? (' height=' + h) : '', }; return '[attach id=' + file.attachID + bbcOptionalParams.width + bbcOptionalParams.height + ']' + (typeof file.name !== "undefined" ? decodeURIComponent(file.name.replace(/\+/g,' ')) : '') + '[/attach]'; }, createMaxSizeBar: function () { // Update the MaxSize bar to reflect the new size percentage. var currentSize = Math.round(myDropzone.options.currentUsedSize / 1024), maxSize = myDropzone.options.maxTotalSize, usedPercentage = Math.round($.fn.percentToRange($.fn.rangeToPercent(currentSize, 0, maxSize), 0, 100)); if (isNewTemplate && maxSize > 1024) { maxSize = Math.round(((maxSize / 1024) + Number.EPSILON) * 100) / 100; currentSize = Math.round(((currentSize / 1024) + Number.EPSILON) * 10) / 10; } // 3 basic colors. if (usedPercentage <= 33) percentage_class = 'green'; else if (usedPercentage >= 34 && usedPercentage <= 66) percentage_class = 'yellow'; else percentage_class = 'red'; $('#max_files_progress').removeClass().addClass('progress_bar progress_' + percentage_class).show(); $('#max_files_progress_text').show(); $('#max_files_progress .bar').width(usedPercentage + '%'); // Show or update the text. $('#max_files_progress_text').text(myDropzone.options.text_max_size_progress.replace('{currentTotal}', maxSize).replace('{currentRemain}', currentSize)); if (maxSize == 0) { $('#max_files_progress').hide(); $('#max_files_progress_text').hide(); } }, accept: function (file, done) { var currentlyUsedKB = myDropzone.options.currentUsedSize / 1024, totalKB = myDropzone.options.maxTotalSize, fileKB = myDropzone.options.maxFilesize, uploadedFileKB = file.size / 1024; // Check against the max amount of files setting. if ((myDropzone.options.maxFileAmount != null) && (myDropzone.getAcceptedFiles().length) >= myDropzone.options.maxFileAmount) { $('.attach_drop_zone_label').text(myDropzone.options.text_attachLimitNag); done(this.options.dictMaxFilesExceeded); } else $('.attach_drop_zone_label').text(myDropzone.options.text_attachDropzoneLabel); // Need to check if the added file doesn't surpass the total max size setting. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize + file.size; // This file has reached the max total size per post. if (totalKB > 0 && currentlyUsedKB > totalKB) { done(myDropzone.options.text_totalMaxSize.replace('{currentTotal}', totalKB).replace('{currentRemain}', currentlyUsedKB)); // Remove the file size from the total myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; // File is cancel. file.status = Dropzone.CANCELED; } else if (fileKB > 0 && uploadedFileKB > fileKB) { done(myDropzone.options.dictFileTooBig); // File is cancel. file.status = Dropzone.CANCELED; // File wasn't accepted so remove its size. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; } else { myDropzone.options.createMaxSizeBar(); // All done! done(); } }, hideFileProgressAndAllButtonsIfNeeded: function () { // Hide the cancel and upload all buttons if there is nothing to cancel/upload anymore. if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 0) { $('div#attachment_upload').find('#attach_cancel_all, #attach_upload_all').hide(); } if (myDropzone.getAcceptedFiles().length == 0) { $('#max_files_progress').hide(); $('#max_files_progress_text').hide(); } }, }; if (oOptions.thumbnailHeight && oOptions.thumbnailWidth) { if (oOptions.thumbnailHeight > oOptions.thumbnailWidth) { oOptions.thumbnailWidth = null; } else { oOptions.thumbnailHeight = null; } } $.extend(true, dOptions, oOptions); var myDropzone = new Dropzone('div#attachment_upload', dOptions); // Highlight the dropzone target as soon as a file is dragged onto the window. if (isNewTemplate) { var dragTimer; $(document).on('dragover', function(e) { var dt = e.originalEvent.dataTransfer; if (dt.types && (dt.types.indexOf ? dt.types.indexOf('Files') != -1 : dt.types.contains('Files'))) { $("#attachment_upload").addClass('dz-drag-hover'); window.clearTimeout(dragTimer); } }); $(document).on('dragleave dragend', function(e) { dragTimer = window.setTimeout(function() { $("#attachment_upload").removeClass('dz-drag-hover'); }, 25); }); } myDropzone.on('addedfile', function (file) { _thisElement = $(file.previewElement); // If the attachment is an image and has a thumbnail, show it. Otherwise fallback to the generic thumbfile. if (!file.type.match(/image.*/)) { myDropzone.emit('thumbnail', file, smf_images_url + '/generic_attach.png'); } // If the file is too small, it won't have a thumbnail, show the regular file. else if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined") { myDropzone.emit('thumbnail', file, smf_prepareScriptUrl(smf_scripturl) + 'action=dlattach;attach=' + (file.thumbID > 0 ? file.thumbID : file.attachID) + ';type=preview'); } file.name = file.name.php_to8bit().php_urlencode(); // Show the file info. _thisElement.find('.attach-ui').show(); // Show the progress bar $('#max_files_progress').show(); // Create a function to insert the BBC attach tag. file.insertAttachment = function (_innerElement, response) { // Backward compatibility for themes based on the pre-2.1.4 templates. if (!isNewTemplate) { insertButton = $('') .addClass('button') .addClass('insertBBC') .prop('disabled', false) .text(myDropzone.options.text_insertBBC) .on('click', function (e) { e.preventDefault(); w = _innerElement.find('input[name="attached_BBC_width"]').val(); h = _innerElement.find('input[name="attached_BBC_height"]').val(); // Get the editor stuff. var e = $('#' + oEditorID).get(0); var oEditor = sceditor.instance(e); oEditor.insert(myDropzone.options.smf_insertBBC(response, w, h), ' '); }) .appendTo(_innerElement.find('.attach-ui')); } // Insert as an image. else if (file.type.match(/image.*/)) { let attached_BBC_width_height = _innerElement.find('.attached_BBC_width_height'); insertPanelButton = $('') .addClass('main_icons') .addClass('select_above') .addClass('floatright') .addClass('insertBBC') .prop('disabled', false) .prop('title', myDropzone.options.text_insertBBC) .on('click', function (e) { attached_BBC_width_height.toggle(); }) .insertBefore(attached_BBC_width_height); insertButton = $('') .addClass('button') .addClass('insertBBC') .addClass('floatright') .prop('disabled', false) .text(myDropzone.options.text_insertBBC) .on('click', function (e) { e.preventDefault(); w = _innerElement.find('input[name="attached_BBC_width"]').val(); h = _innerElement.find('input[name="attached_BBC_height"]').val(); // Get the editor stuff. var e = $('#' + oEditorID).get(0); var oEditor = sceditor.instance(e); oEditor.insert(myDropzone.options.smf_insertBBC(response, w, h), ''); attached_BBC_width_height.hide(); }) .appendTo(attached_BBC_width_height); } // Insert as a plain link. else { insertButton = $('') .addClass('main_icons') .addClass('select_above') .addClass('floatright') .addClass('insertBBC') .prop('disabled', false) .prop('title', myDropzone.options.text_insertBBC) .on('click', function (e) { e.preventDefault(); // Get the editor stuff. var e = $('#' + oEditorID).get(0); var oEditor = sceditor.instance(e); oEditor.insert(myDropzone.options.smf_insertBBC(response, null, null), ' '); }) .appendTo(_innerElement.find('.attach-ui')); } }; // Replace the file with a message when the attachment has been deleted. file.deleteAttachment = function (_innerElement, attachmentId, file) { deleteButton = $('') .addClass(!isNewTemplate ? 'button' : 'main_icons delete floatright') .prop('disabled', false) .prop('title', myDropzone.options.text_deleteAttach) .text(!isNewTemplate ? myDropzone.options.text_deleteAttach : '') .one('click', function (e) { $this = $(this); // Perform the action only after receiving the confirmation. if (!confirm(smf_you_sure)) { return; } // Let the server know you want to delete the file you just recently uploaded... $.ajax({ url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach=' + attachmentId + ';' + smf_session_var + '=' + smf_session_id + (current_board ? ';board=' + current_board : ''), type: 'GET', headers: { "X-SMF-AJAX": 1 }, xhrFields: { withCredentials: typeof allow_xhjr_credentials !== "undefined" ? allow_xhjr_credentials : false }, dataType: 'json', beforeSend: function () { ajax_indicator(true); }, complete: function (jqXHR, textStatus) { ajax_indicator(false); // Delete the button. if (!isNewTemplate) $this.fadeOutAndRemove(); }, success: function (data, textStatus, xhr) { if (!isNewTemplate) { // For dramatic purposes only! _innerElement.removeClass('infobox').addClass(data.type + 'box'); // Remove the text fields and insert button. _innerElement.find('.attached_BBC').fadeOut(); _innerElement.find('.attachment_info a.insertBBC').fadeOut(); } // Do stuff only if the file was actually accepted and it doesn't have an error status. if (file.accepted && file.status != Dropzone.ERROR) { // Need to remove the file size to make sure theres plenty of room for another one. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; // Re-count! myDropzone.options.createMaxSizeBar(); file.accepted = false; // Show the current amount of remaining files $('.attach_remaining').html(Math.max(myDropzone.options.maxFileAmount - myDropzone.getAcceptedFiles().length, 0)); // Check against the max amount of files setting. if (myDropzone.getAcceptedFiles().length >= myDropzone.options.maxFileAmount) { $('.attach_drop_zone_label').text(myDropzone.options.text_attachLimitNag); } else $('.attach_drop_zone_label').text(myDropzone.options.text_attachDropzoneLabel); myDropzone.options.hideFileProgressAndAllButtonsIfNeeded(); if (isNewTemplate) _innerElement.remove(); } }, error: function (xhr, textStatus, errorThrown) { // Tell the user something horrible happen! _innerElement.find('span.error').append(textStatus.error.join('
')).css({ 'text-decoration': 'none' }); // For dramatic purposes only! _innerElement.removeClass('infobox').addClass('errorbox'); } }); // Remove BBC from the post text, if present. var attachBbcRegex = new RegExp('\\[attach[^\\]]+id=' + attachmentId + '[^\\]]*\\][^\\[\\]]*\\[/attach\\]', 'g'); var e = $('#' + oEditorID).get(0); var oEditor = sceditor.instance(e); var newEditorVal = oEditor.val().replace(attachBbcRegex, ''); oEditor.val(newEditorVal); }); if (!isNewTemplate) deleteButton.appendTo(_innerElement.find('.attach-ui')); else deleteButton.prependTo(_innerElement.find('.attach-ui')); // Check against the max amount of files setting. if (myDropzone.getAcceptedFiles().length >= myDropzone.options.maxFileAmount) { $('.attach_drop_zone_label').text(myDropzone.options.text_attachLimitNag); } else $('.attach_drop_zone_label').text(myDropzone.options.text_attachDropzoneLabel); // Show the current amount of remaining files $('.attach_remaining').html(Math.max(myDropzone.options.maxFileAmount - myDropzone.getAcceptedFiles().length, 0)); }; // The editor needs this to know how to handle embedded attachements file.addToCurrentAttachmentsList = function (file, response) { current_attachments.push({ name: file.name, size: file.size, attachID: response.attachID, type: file.type, thumbID: (response.thumbID > 0 ? response.thumbID : response.attachID) }); } // Hookup the upload button. _thisElement.find('.upload').on('click', function () { myDropzone.enqueueFile(file); }); // Show the main stuff! _thisElement.addClass('descbox'); // Show the upload and cancel all buttons only if there is something to cancel/upload. if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 1) { $('div#attachment_upload').find('#attach_cancel_all, #attach_upload_all').css('display', 'inline-block'); } }); // Stuff to do when a file gets cancel. myDropzone.on('removedfile', function (file) { // Do stuff only if the file was actually accepted and it doesn't have an error status. if (file.accepted && file.status != Dropzone.ERROR) { // Need to remove the file size to make sure theres plenty of room for another one. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; // Re-count! myDropzone.options.createMaxSizeBar(); } myDropzone.options.hideFileProgressAndAllButtonsIfNeeded(); }); // Event for when a file has been canceled myDropzone.on("canceled", function(file) { // Need to remove the file size to make sure theres plenty of room for another one. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; // Re-count! myDropzone.options.createMaxSizeBar(); this.removeFile(file); }); // Event for when the total amount of files exceeds the maxFiles option myDropzone.on("maxfilesexceeded", function(file) { // Need to remove the file size to make sure there is plenty of room for another one. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize - file.size; // Re-count! myDropzone.options.createMaxSizeBar(); this.removeFile(file); }); // Update the total progress bar. myDropzone.on('totaluploadprogress', function (progress) { $('#total_progress span').width(progress + '%'); }); myDropzone.on('error', function (file, errorMessage, xhr) { _thisElement = $(file.previewElement); // Remove the 'upload' button. _thisElement.find('.upload').fadeOutAndRemove(); // Set a nice css class to make it more obvious there is an error. _thisElement.addClass('errorbox').removeClass('descbox'); }); myDropzone.on('success', function (file, responseText, e) { _thisElement = $(file.previewElement); // Remove the 'upload' button. _thisElement.find('.upload').fadeOutAndRemove(); // Don't do anything if there is no response from server. if (!responseText) { return; } // There is a general error. if (responseText.generalErrors) { _thisElement.find('span.error').append(responseText.generalErrors.join('
')); return; } // Server returns an array. response = responseText.files[0]; // Show the input field and insert button. _thisElement.find('.attachment_info div.attached_BBC').fadeIn(); _thisElement.find('.attachment_info a.insertBBC').fadeIn(); if (typeof response.mime_type == "undefined" || response.mime_type.indexOf('image') != 0) { _thisElement.find('.attachment_info .attached_BBC_width_height').hide(); } // The request was complete but the server returned an error. if (typeof response.errors !== 'undefined' && response.errors.length > 0) { _thisElement.addClass('errorbox').removeClass('descbox'); // Show the server error. _thisElement.find('span.error').append(response.errors.join('
')); return; } // If there wasn't any error, change the current cover. _thisElement.removeClass('descbox'); if (!isNewTemplate) _thisElement.addClass('infobox'); // You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one. _thisElement.find('a.cancel').fadeOutAndRemove(); // Fire up the delete button. file.deleteAttachment(_thisElement, response.attachID, file); // Append the BBC. w = _thisElement.find('input[name="attached_BBC_width"]').val(); h = _thisElement.find('input[name="attached_BBC_height"]').val(); _thisElement.find('input[name="attachBBC"]').val(myDropzone.options.smf_insertBBC(response, w, h)); file.insertAttachment(_thisElement, response); // Let the editor know about this attachment so it can handle the BBC correctly file.addToCurrentAttachmentsList(file, response); }); myDropzone.on('uploadprogress', function (file, progress, bytesSent) { _thisElement = $(file.previewElement); // Get the current file box progress bar, set its inner span's width accordingly. _thisElement.find('.progress_bar .bar').width(progress + '%'); }); myDropzone.on('complete', function (file, progress, bytesSent) { _thisElement = $(file.previewElement); // Hide the progress bar. _thisElement.find('.progress_bar').fadeOut(); myDropzone.options.hideFileProgressAndAllButtonsIfNeeded(); // Finishing up mocking! if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined") { // Show the input field. _thisElement.find('.attachment_info div.attached_BBC').fadeIn(); _thisElement.find('.attachment_info a.insertBBC').fadeIn(); if (typeof file.type == "undefined" || file.type.indexOf('image') != 0) { _thisElement.find('.attachment_info .attached_BBC_width_height').hide(); } // If there wasn't any error, change the current cover. _thisElement.removeClass('descbox'); if (!isNewTemplate) _thisElement.addClass('infobox'); // Remove the 'upload' button. _thisElement.find('.upload').fadeOutAndRemove(); // You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one. _thisElement.find('a.cancel').remove(); // Fire up the delete button. file.deleteAttachment(_thisElement, file.attachID, file); // Append the BBC. w = _thisElement.find('input[name="attached_BBC_width"]').val(); h = _thisElement.find('input[name="attached_BBC_height"]').val(); _thisElement.find('input[name="attachBBC"]').val(myDropzone.options.smf_insertBBC(file, w, h)); file.insertAttachment(_thisElement, file); // Need to count this towards the max limit. myDropzone.options.currentUsedSize = myDropzone.options.currentUsedSize + file.size; // Re-count and display the bar. myDropzone.options.createMaxSizeBar(); } }); // Show each individual's progress bar. myDropzone.on('sending', function (file, xhr, formData) { _thisElement = $(file.previewElement); // Show the progress bar when upload starts. _thisElement.find('.progress_bar').fadeIn(); // Show the total progress bar when upload starts. $("#total_progress").fadeIn(); }); // Update the total progress bar. myDropzone.on("totaluploadprogress", function (progress) { $("#total_progress span").width(progress + '%'); }); // Hide the total progress bar when nothing's uploading anymore. myDropzone.on("queuecomplete", function (progress) { $("#total_progress").fadeOut(); }); // Add an event for uploading and cancelling all files. $('a#attach_cancel_all').on('click', function () { if (!confirm(smf_you_sure)) return; myDropzone.getAddedFiles().forEach(function(file){ myDropzone.removeFile(file) }); myDropzone.getFilesWithStatus(Dropzone.ERROR).forEach(function(file){ myDropzone.removeFile(file) }); myDropzone.options.createMaxSizeBar(); myDropzone.options.hideFileProgressAndAllButtonsIfNeeded(); }); $('a#attach_upload_all').on('click', function () { if (!confirm(smf_you_sure)) { return; } myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED)); myDropzone.options.createMaxSizeBar(); myDropzone.options.hideFileProgressAndAllButtonsIfNeeded(); }); // Need to tell the user they cannot post until all files are either uploaded or canceled. $("input[name ='post']").on('click', function (e) { attachAdded = myDropzone.getFilesWithStatus(Dropzone.ADDED).length; attachQueued = myDropzone.getFilesWithStatus(Dropzone.QUEUED).length; if (attachAdded > 0 || attachQueued > 0) { alert(myDropzone.options.text_attachLeft); e.preventDefault(); e.stopPropagation(); return false; } }); // Hide the default way to show already attached files. $('#postAttachment').remove(); $('#attachment_previews').css('display', !isNewTemplate ? 'block' : 'flex'); // Hide this, too. The progress bar does a better job. $('.attach_available').remove(); // Show the drag-and-drop instructions and buttons $('#drop_zone_ui').css('display', !isNewTemplate ? 'block' : 'flex'); // Show any attachments already uploaded. if (typeof current_attachments !== "undefined") { $.each(current_attachments, function (key, mock) { // Tell the world this is a mock file! mock.isMock = true; // Tell everyone this file was accepted. mock.status = Dropzone.ADDED; mock.accepted = true; myDropzone.emit("addedfile", mock); // Add to the files list mock.status = Dropzone.SUCCESS; myDropzone.files.push(mock); // This file is "completed". myDropzone.emit("complete", mock); }); } }