// jQuery plugin to display a custom jQuery File Uploader interface. // (C) 2019 CubicleSoft. All Rights Reserved. (function($) { var EscapeHTML = function(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } var FormatStr = function(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function(match, number) { return (typeof args[number] != 'undefined' ? args[number] : match); }); }; var GetDisplayFilesize = function(numbytes, adjustprecision, units) { if (numbytes == 0) return '0 Bytes'; if (numbytes == 1) return '1 Byte'; numbytes = Math.abs(numbytes); var magnitude, abbreviations; if (units && units.toLowerCase() === 'iec_formal') { magnitude = Math.pow(2, 10); abbreviations = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; } else if (units && units.toLowerCase() === 'si') { magnitude = Math.pow(10, 3); abbreviations = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; } else { magnitude = Math.pow(2, 10); abbreviations = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; } var pos = Math.floor(Math.log(numbytes) / Math.log(magnitude)); var result = (numbytes / Math.pow(magnitude, pos)); return (pos == 0 || (adjustprecision && result >= 99.995) ? result.toFixed(0) : result.toFixed(2)) + ' ' + abbreviations[pos]; }; var DisplayPreviewDialog = function(preview, endelem, inforow, data, settings) { var previewbackground = $('
').addClass('ff_fileupload_dialog_background'); var previewclone = preview.clone(true, true).click(function(e) { e.stopPropagation(); }); var previewdialog = $('
').addClass('ff_fileupload_dialog_main').append(previewclone); var HidePreviewDialog = function() { $(document).off('keyup.fancy_fileupload'); previewbackground.remove(); endelem.focus(); if (settings.hidepreview) settings.hidepreview.call(inforow, data, preview, previewclone); }; $(document).on('keyup.fancy_fileupload', function(e) { if (e.keyCode == 27) { HidePreviewDialog(); } }); previewbackground.append(previewdialog).click(function() { HidePreviewDialog(); }); $('body').append(previewbackground); previewclone.focus(); if (settings.showpreview) settings.showpreview.call(inforow, data, preview, previewclone); }; var InitShowAriaLabelInfo = function(inforow) { inforow.find('button').hover(function() { var val = $(this).attr('aria-label'); if (val) { inforow.find('.ff_fileupload_buttoninfo').text(val).removeClass('ff_fileupload_hidden'); inforow.find('.ff_fileupload_fileinfo').addClass('ff_fileupload_hidden'); } }, function() { inforow.find('.ff_fileupload_fileinfo').removeClass('ff_fileupload_hidden'); inforow.find('.ff_fileupload_buttoninfo').addClass('ff_fileupload_hidden'); }); }; $.fn.FancyFileUpload = function(options) { this.each(function() { var $this = $(this); // Remove the previous file uploader. if ($this.data('fancy-fileupload') && typeof($this.data('fancy-fileupload')) === 'object') { $this.removeClass('ff_fileupload_hidden'); var data = $this.data('fancy-fileupload'); data.form.find('input[type=file]').fileupload('destroy'); data.form.remove(); data.fileuploadwrap.remove(); $this.removeData('fancy-fileupload'); } }); if (!$('.ff_fileupload_hidden').length) { $(document).off('drop.fancy_fileupload dragover.fancy_fileupload'); $(window).off('beforeunload.fancy_fileupload'); } if (typeof(options) === 'string' && options === 'destroy') return this; var settings = $.extend({}, $.fn.FancyFileUpload.defaults, options); // Let custom callbacks make last second changes to the finalized settings. if (settings.preinit) settings.preinit(settings); // Prevent default file drag-and-drop operations. $(document).off('drop.fancy_fileupload dragover.fancy_fileupload'); $(document).on('drop.fancy_fileupload dragover.fancy_fileupload', function (e) { e.preventDefault(); }); // Some useful functions. var Translate = function(str) { return (settings.langmap[str] ? settings.langmap[str] : str); }; // Prevent the user from leaving the page if there is an active upload. // Most browsers won't show the custom message. So make the relevant UI elements bounce using CSS. $(window).on('beforeunload.fancy_fileupload', function(e) { var active = $('.ff_fileupload_uploading, .ff_fileupload_starting'); var queued = $('.ff_fileupload_queued'); if (active.length || queued.length) { active.removeClass('ff_fileupload_bounce'); setTimeout(function() { active.addClass('ff_fileupload_bounce') }, 250); queued.removeClass('ff_fileupload_bounce'); setTimeout(function() { queued.addClass('ff_fileupload_bounce') }, 250); if (active.length) return Translate('There is a file upload still in progress. Leaving the page will cancel the upload.\n\nAre you sure you want to leave this page?'); if (queued.length) return Translate('There is a file that was added to the queue but the upload has not been started. Leaving the page will clear the queue and not upload the file.\n\nAre you sure you want to leave this page?'); } }); // Create some extra DOM nodes for preview checking. var audioelem = document.createElement('audio'); var videoelem = document.createElement('video'); var AddFile = function(uploads, e, data) { var inforow = $('
'); var pos = data.files[0].name.lastIndexOf('.'); var filename = (pos > -1 ? data.files[0].name.substring(0, pos) : data.files[0].name); var fileext = (pos > -1 ? data.files[0].name.substring(pos + 1).toLowerCase() : ''); var alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789'; pos = (fileext == '' ? -1 : alphanum.indexOf(fileext.charAt(0))); var fileextclass = alphanum.charAt((pos > -1 ? pos : Math.floor(Math.random() * alphanum.length))); // Initialize necessary callback options. data.ff_info = {}; data.ff_info.errors = []; data.ff_info.retries = 0; data.ff_info.retrydelay = settings.retrydelay; data.ff_info.removewidget = false; data.ff_info.inforow = inforow; data.ff_info.displayfilesize = GetDisplayFilesize(data.files[0].size, settings.adjustprecision, settings.displayunits); data.context = inforow; // A couple of functions for handling actions. var StartUpload = function(e) { e.preventDefault(); // Set filename. if (settings.edit && !data.ff_info.errors.length) { var fileinput = inforow.find('.ff_fileupload_filename input'); if (fileinput.length) { var newfilename = fileinput.val(); if (fileext != '') newfilename += '.' + fileext; inforow.find('.ff_fileupload_filename').text(newfilename); data.files[0].uploadName = newfilename; } } // Remove start upload buttons. inforow.find('button.ff_fileupload_start_upload').remove(); // Reset hover status. inforow.find('.ff_fileupload_fileinfo').removeClass('ff_fileupload_hidden'); inforow.find('.ff_fileupload_buttoninfo').addClass('ff_fileupload_hidden'); // Set the status. inforow.find('.ff_fileupload_fileinfo').text(data.ff_info.displayfilesize + ' | ' + Translate('Starting upload...')); // Display progress bar. inforow.find('.ff_fileupload_progress_background').removeClass('ff_fileupload_hidden'); // Alter remove buttons. inforow.find('button.ff_fileupload_remove_file').attr('aria-label', Translate('Cancel upload and remove from list')); // Begin the actual upload. inforow.removeClass('ff_fileupload_queued'); inforow.addClass('ff_fileupload_starting'); var SubmitUpload = function() { inforow.removeClass('ff_fileupload_starting'); inforow.addClass('ff_fileupload_uploading'); data.submit(); }; if (settings.startupload) settings.startupload.call(inforow, SubmitUpload, e, data); else SubmitUpload(); }; var RemoveFile = function(e) { e.preventDefault(); if (inforow.hasClass('ff_fileupload_uploading')) { if (!confirm(Translate('This file is currently being uploaded.\n\nStop the upload and remove the file from the list?'))) return; data.ff_info.removewidget = true; data.abort(); } else { if (inforow.hasClass('ff_fileupload_starting')) { if (!confirm(Translate('This file is waiting to start.\n\nCancel the operation and remove the file from the list?'))) return; if (settings.uploadcancelled) settings.uploadcancelled.call(data.ff_info.inforow, e, data); } inforow.remove(); delete data.ff_info; } }; data.ff_info.RemoveFile = function() { if (inforow.hasClass('ff_fileupload_uploading')) { data.ff_info.removewidget = true; data.abort(); } else { if (inforow.hasClass('ff_fileupload_starting')) { if (settings.uploadcancelled) settings.uploadcancelled.call(data.ff_info.inforow, e, data); } inforow.remove(); delete data.ff_info; } }; // Thumbnail preview. var haspreview = false; var preview; var hasimage = false; if (URL && URL.createObjectURL) { var url = URL.createObjectURL(data.files[0]); if (url) { if (data.files[0].type === 'image/gif' || data.files[0].type === 'image/jpeg' || data.files[0].type === 'image/png') { inforow.find('.ff_fileupload_preview_image').css('background-image', 'url("' + url + '")'); haspreview = true; preview = $('').attr('src', url); hasimage = true; } else if (data.files[0].type.lastIndexOf('audio/', 0) > -1 && audioelem.canPlayType && audioelem.canPlayType(data.files[0].type)) { haspreview = true; preview = $('