jquery.upload.js 42.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
/*
UploadiFive 1.2.2
Copyright (c) 2012 Reactive Apps, Ronnie Garcia
Released under the UploadiFive Standard License <http://www.uploadify.com/uploadifive-standard-license>
*/

let jQuery = require('yoho-jquery');

(function($) {

    let methods = {

        init: function(options) {

            return this.each(function() {

                // Create a reference to the jQuery DOM object
                let $this = $(this);

                $this.data('uploadifive', {
                    inputs: {}, // The object that contains all the file inputs
                    inputCount: 0,  // The total number of file inputs created
                    fileID: 0,
                    queue: {
                        count: 0, // Total number of files in the queue
                        selected: 0, // Number of files selected in the last select operation
                        replaced: 0, // Number of files replaced in the last select operation
                        errors: 0, // Number of files that returned an error in the last select operation
                        queued: 0, // Number of files added to the queue in the last select operation
                        cancelled: 0  // Total number of files that have been cancelled or removed from the queue
                    },
                    uploads: {
                        current: 0, // Number of files currently being uploaded
                        attempts: 0, // Number of file uploads attempted in the last upload operation
                        successful: 0, // Number of files successfully uploaded in the last upload operation
                        errors: 0, // Number of files returning errors in the last upload operation
                        count: 0  // Total number of files uploaded successfully
                    }
                });
                let $data = $this.data('uploadifive');

                // Set the default options
                let settings = $data.settings = $.extend({
                    auto: true,               // Automatically upload a file when it's added to the queue
                    buttonClass: false,              // A class to add to the UploadiFive button
                    buttonText: 'Select Files',     // The text that appears on the UploadiFive button
                    checkScript: false,              // Path to the script that checks for existing file names
                    dnd: true,               // Allow drag and drop into the queue
                    dropTarget: false,              // Selector for the drop target
                    fileObjName: 'Filedata',         // The name of the file object to use in your server-side script
                    fileSizeLimit: 0,                  // Maximum allowed size of files to upload
                    fileType: false,              // Type of files allowed (image, etc), separate with a pipe character |
                    formData: {},                 // Additional data to send to the upload script
                    height: 30,                 // The height of the button
                    itemTemplate: false,              // The HTML markup for the item in the queue
                    method: 'post',             // The method to use when submitting the upload
                    multi: true,               // Set to true to allow multiple file selections
                    overrideEvents: [],                 // An array of events to override
                    queueID: false,              // The ID of the file queue
                    queueSizeLimit: 0,                  // The maximum number of files that can be in the queue
                    removeCompleted: false,              // Set to true to remove files that have completed uploading
                    simUploadLimit: 0,                  // The maximum number of files to upload at once
                    truncateLength: 0,                  // The length to truncate the file names to
                    uploadLimit: 0,                  // The maximum number of files you can upload
                    uploadScript: 'uploadifive.php',  // The path to the upload script
                    width: 100                 // The width of the button

                    /*
                    // Events
                    'onAddQueueItem'   : function(file) {},                        // Triggered for each file that is added to the queue
                    'onCancel'         : function(file) {},                        // Triggered when a file is cancelled or removed from the queue
                    'onCheck'          : function(file, exists) {},                // Triggered when the server is checked for an existing file
                    'onClearQueue'     : function(queue) {},                       // Triggered during the clearQueue function
                    'onDestroy'        : function() {}                             // Triggered during the destroy function
                    'onDrop'           : function(files, numberOfFilesDropped) {}, // Triggered when files are dropped into the file queue
                    'onError'          : function(file, fileType, data) {},        // Triggered when an error occurs
                    'onFallback'       : function() {},                            // Triggered if the HTML5 File API is not supported by the browser
                    'onInit'           : function() {},                            // Triggered when UploadiFive if initialized
                    'onQueueComplete'  : function() {},                            // Triggered once when an upload queue is done
                    'onProgress'       : function(file, event) {},                 // Triggered during each progress update of an upload
                    'onSelect'         : function() {},                            // Triggered once when files are selected from a dialog box
                    'onUpload'         : function(file) {},                        // Triggered when an upload queue is started
                    'onUploadComplete' : function(file, data) {},                  // Triggered when a file is successfully uploaded
                    'onUploadFile'     : function(file) {},                        // Triggered for each file being uploaded
                    */
                }, options);

                // Calculate the file size limit
                if (isNaN(settings.fileSizeLimit)) {
                    let fileSizeLimitBytes = parseInt(settings.fileSizeLimit) * 1.024;

                    if (settings.fileSizeLimit.indexOf('KB') > -1) {
                        settings.fileSizeLimit = fileSizeLimitBytes * 1000;
                    } else if (settings.fileSizeLimit.indexOf('MB') > -1) {
                        settings.fileSizeLimit = fileSizeLimitBytes * 1000000;
                    } else if (settings.fileSizeLimit.indexOf('GB') > -1) {
                        settings.fileSizeLimit = fileSizeLimitBytes * 1000000000;
                    }
                } else {
                    settings.fileSizeLimit = settings.fileSizeLimit * 1024;
                }

                // Create a template for a file input
                $data.inputTemplate = $('<input type="file">')
                    .css({
                        'font-size': settings.height + 'px',
                        opacity: 0,
                        position: 'absolute',
                        right: '-3px',
                        top: '-3px',
                        'z-index': 999
                    });

                // Create a new input
                $data.createInput = function() {

                    // Create a clone of the file input
                    let input = $data.inputTemplate.clone();

                    // Create a unique name for the input item
                    let inputName = input.name = 'input' + $data.inputCount++;

                    // Set the multiple attribute
                    if (settings.multi) {
                        input.attr('multiple', true);
                    }

                    // Set the accept attribute on the input
                    if (settings.fileType) {
                        input.attr('accept', settings.fileType);
                    }

                    // Set the onchange event for the input
                    input.bind('change', function() {
                        $data.queue.selected = 0;
                        $data.queue.replaced = 0;
                        $data.queue.errors = 0;
                        $data.queue.queued = 0;

                        // Add a queue item to the queue for each file
                        let limit = this.files.length;

                        $data.queue.selected = limit;
                        if (($data.queue.count + limit) > settings.queueSizeLimit && settings.queueSizeLimit !== 0) {
                            if ($.inArray('onError', settings.overrideEvents) < 0) {
                                alert('The maximum number of queue items has been reached (' + settings.queueSizeLimit + ').  Please select fewer files.');
                            }

                            // Trigger the error event
                            if (typeof settings.onError === 'function') {
                                settings.onError.call($this, 'QUEUE_LIMIT_EXCEEDED');
                            }
                        } else {
                            for (let n = 0; n < limit; n++) {
                                let file = this.files[n];

                                $data.addQueueItem(file);
                            }
                            $data.inputs[inputName] = this;
                            $data.createInput();
                        }

                        // Upload the file if auto-uploads are enabled
                        if (settings.auto) {
                            methods.upload.call($this);
                        }

                        // Trigger the select event
                        if (typeof settings.onSelect === 'function') {
                            settings.onSelect.call($this, $data.queue);
                        }
                    });

                    // Hide the existing current item and add the new one
                    if ($data.currentInput) {
                        $data.currentInput.hide();
                    }
                    $data.button.append(input);
                    $data.currentInput = input;
                };

                // Remove an input
                $data.destroyInput = function(key) {
                    $($data.inputs[key]).remove();
                    delete $data.inputs[key];
                    $data.inputCount--;
                };

                // Drop a file into the queue
                $data.drop = function(e) {
                    $data.queue.selected = 0;
                    $data.queue.replaced = 0;
                    $data.queue.errors = 0;
                    $data.queue.queued = 0;

                    let fileData = e.dataTransfer;

                    let inputName = fileData.name = 'input' + $data.inputCount++;

                    // Add a queue item to the queue for each file
                    let limit = fileData.files.length;

                    $data.queue.selected = limit;
                    if (($data.queue.count + limit) > settings.queueSizeLimit && settings.queueSizeLimit !== 0) {
                        // Check if the queueSizeLimit was reached
                        if ($.inArray('onError', settings.overrideEvents) < 0) {
                            alert('The maximum number of queue items has been reached (' + settings.queueSizeLimit + ').  Please select fewer files.');
                        }

                        // Trigger the onError event
                        if (typeof settings.onError === 'function') {
                            settings.onError.call($this, 'QUEUE_LIMIT_EXCEEDED');
                        }
                    } else {
                        // Add a queue item for each file
                        for (let n = 0; n < limit; n++) {
                            file = fileData.files[n];
                            $data.addQueueItem(file);
                        }

                        // Save the data to the inputs object
                        $data.inputs[inputName] = fileData;
                    }

                    // Upload the file if auto-uploads are enabled
                    if (settings.auto) {
                        methods.upload.call($this);
                    }

                    // Trigger the onDrop event
                    if (typeof settings.onDrop === 'function') {
                        settings.onDrop.call($this, fileData.files, fileData.files.length);
                    }

                    // Stop FireFox from opening the dropped file(s)
                    e.preventDefault();
                    e.stopPropagation();
                };

                // Check if a filename exists in the queue
                $data.fileExistsInQueue = function(file) {
                    for (let key in $data.inputs) {
                        input = $data.inputs[key];
                        limit = input.files.length;
                        for (let n = 0; n < limit; n++) {
                            existingFile = input.files[n];

                            // Check if the filename matches
                            if (existingFile.name == file.name && !existingFile.complete) {
                                return true;
                            }
                        }
                    }
                    return false;
                };

                // Remove an existing file in the queue
                $data.removeExistingFile = function(file) {
                    for (let key in $data.inputs) {
                        let input = $data.inputs[key];
                        let limit = input.files.length;

                        for (let n = 0; n < limit; n++) {
                            let existingFile = input.files[n];

                            // Check if the filename matches
                            if (existingFile.name == file.name && !existingFile.complete) {
                                $data.queue.replaced++;
                                methods.cancel.call($this, existingFile, true);
                            }
                        }
                    }
                };

                // Create the file item template
                if (settings.itemTemplate == false) {
                    $data.queueItem = $('<div class="uploadifive-queue-item">\
                        <a class="close" href="#">X</a>\
                        <div><span class="filename"></span><span class="fileinfo"></span></div>\
                        <div class="progress">\
                            <div class="progress-bar"></div>\
                        </div>\
                    </div>');
                } else {
                    $data.queueItem = $(settings.itemTemplate);
                }

                // Add an item to the queue
                $data.addQueueItem = function(file) {
                    if ($.inArray('onAddQueueItem', settings.overrideEvents) < 0) {
                        // Check if the filename already exists in the queue
                        $data.removeExistingFile(file);

                        // Create a clone of the queue item template
                        file.queueItem = $data.queueItem.clone();

                        // Add an ID to the queue item
                        file.queueItem.attr('id', settings.id + '-file-' + $data.fileID++);

                        // Bind the close event to the close button
                        file.queueItem.find('.close').bind('click', function() {
                            methods.cancel.call($this, file);
                            return false;
                        });
                        let fileName = file.name;

                        if (fileName.length > settings.truncateLength && settings.truncateLength != 0) {
                            fileName = fileName.substring(0, settings.truncateLength) + '...';
                        }
                        file.queueItem.find('.filename').html(fileName);

                        // Add a reference to the file
                        file.queueItem.data('file', file);
                        $data.queueEl.append(file.queueItem);
                    }

                    // Trigger the addQueueItem event
                    if (typeof settings.onAddQueueItem === 'function') {
                        settings.onAddQueueItem.call($this, file);
                    }

                    // Check the filesize
                    if (file.size > settings.fileSizeLimit && settings.fileSizeLimit != 0) {
                        $data.error('FILE_SIZE_LIMIT_EXCEEDED', file);
                    } else {
                        $data.queue.queued++;
                        $data.queue.count++;
                    }
                };

                // Remove an item from the queue
                $data.removeQueueItem = function(file, instant, delay) {
                    // Set the default delay
                    if (!delay) {
                        delay = 0;
                    }
                    let fadeTime = instant ? 0 : 500;

                    if (file.queueItem) {
                        if (file.queueItem.find('.fileinfo').html() != ' - Completed') {
                            file.queueItem.find('.fileinfo').html(' - Cancelled');
                        }
                        file.queueItem.find('.progress-bar').width(0);
                        file.queueItem.delay(delay).fadeOut(fadeTime, function() {
                            $(this).remove();
                        });
                        delete file.queueItem;
                        $data.queue.count--;
                    }
                };

                // Count the number of files that need to be uploaded
                $data.filesToUpload = function() {
                    let filesToUpload = 0;

                    for (let key in $data.inputs) {
                        let input = $data.inputs[key];
                        let limit = input.files.length;

                        for (let n = 0; n < limit; n++) {
                            let file = input.files[n];

                            if (!file.skip && !file.complete) {
                                filesToUpload++;
                            }
                        }
                    }
                    return filesToUpload;
                };

                // Check if a file exists
                $data.checkExists = function(file) {
                    if ($.inArray('onCheck', settings.overrideEvents) < 0) {
                        // This request needs to be synchronous
                        $.ajaxSetup({
                            async: false
                        });

                        // Send the filename to the check script
                        let checkData = $.extend(settings.formData, {filename: file.name});

                        $.post(settings.checkScript, checkData, function(fileExists) {
                            file.exists = parseInt(fileExists);
                        });
                        if (file.exists) {
                            if (!confirm('A file named ' + file.name + ' already exists in the upload folder.\nWould you like to replace it?')) {
                                // If not replacing the file, cancel the upload
                                methods.cancel.call($this, file);
                                return true;
                            }
                        }
                    }

                    // Trigger the check event
                    if (typeof settings.onCheck === 'function') {
                        settings.onCheck.call($this, file, file.exists);
                    }
                    return false;
                };

                // Upload a single file
                $data.uploadFile = function(file, uploadAll) {
                    if (!file.skip && !file.complete && !file.uploading) {
                        file.uploading = true;
                        $data.uploads.current++;
                        $data.uploads.attempted++;

                        // Create a new AJAX request
                        let xhr = file.xhr = new XMLHttpRequest();

                        // Start the upload
                        // Use the faster FormData if it exists
                        if (typeof FormData === 'function' || typeof FormData === 'object') {

                            // Create a new FormData object
                            let formData = new FormData();

                            // Add the form data
                            formData.append(settings.fileObjName, file);

                            // Add the rest of the formData
                            for (let i in settings.formData) {
                                formData.append(i, settings.formData[i]);
                            }

                            // Open the AJAX call
                            xhr.open(settings.method, settings.uploadScript, true);

                            // On progress function
                            xhr.upload.addEventListener('progress', function(e) {
                                if (e.lengthComputable) {
                                    $data.progress(e, file);
                                }
                            }, false);

                            // On complete function
                            xhr.addEventListener('load', function(e) {
                                if (this.readyState == 4) {
                                    file.uploading = false;
                                    if (this.status == 200) {
                                        if (file.xhr.responseText !== 'Invalid file type.') {
                                            $data.uploadComplete(e, file, uploadAll);
                                        } else {
                                            $data.error(file.xhr.responseText, file, uploadAll);
                                        }
                                    } else if (this.status == 404) {
                                        $data.error('404_FILE_NOT_FOUND', file, uploadAll);
                                    } else if (this.status == 403) {
                                        $data.error('403_FORBIDDEN', file, uploadAll);
                                    } else {
                                        $data.error('Unknown Error', file, uploadAll);
                                    }
                                }
                            });

                            // Send the form data (multipart/form-data)
                            xhr.send(formData);

                        } else {

                            // Send as binary
                            let reader = new FileReader();

                            reader.onload = function(e) {

                                // Set some file builder variables
                                let boundary = '-------------------------' + (new Date()).getTime(),
                                    dashes = '--',
                                    eol = '\r\n',
                                    binFile = '';

                                // Build an RFC2388 String
                                binFile += dashes + boundary + eol;

                                // Generate the headers
                                binFile += 'Content-Disposition: form-data; name="' + settings.fileObjName + '"';
                                if (file.name) {
                                    binFile += '; filename="' + file.name + '"';
                                }
                                binFile += eol;
                                binFile += 'Content-Type: application/octet-stream' + eol + eol;
                                binFile += e.target.result + eol;

                                for (key in settings.formData) {
                                    binFile += dashes + boundary + eol;
                                    binFile += 'Content-Disposition: form-data; name="' + key + '"' + eol + eol;
                                    binFile += settings.formData[key] + eol;
                                }

                                binFile += dashes + boundary + dashes + eol;

                                // On progress function
                                xhr.upload.addEventListener('progress', function(e) {
                                    $data.progress(e, file);
                                }, false);

                                // On complete function
                                xhr.addEventListener('load', function(e) {
                                    file.uploading = false;
                                    let status = this.status;

                                    if (status == 404) {
                                        $data.error('404_FILE_NOT_FOUND', file, uploadAll);
                                    } else {
                                        if (file.xhr.responseText != 'Invalid file type.') {
                                            $data.uploadComplete(e, file, uploadAll);
                                        } else {
                                            $data.error(file.xhr.responseText, file, uploadAll);
                                        }
                                    }
                                }, false);

                                // Open the ajax request
                                let url = settings.uploadScript;

                                if (settings.method == 'get') {
                                    let params = $(settings.formData).param();

                                    url += params;
                                }
                                xhr.open(settings.method, settings.uploadScript, true);
                                xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);

                                // Trigger the uploadFile event
                                if (typeof settings.onUploadFile === 'function') {
                                    settings.onUploadFile.call($this, file);
                                }

                                // Send the file for upload
                                xhr.sendAsBinary(binFile);
                            };
                            reader.readAsBinaryString(file);

                        }
                    }
                };

                // Update a file upload's progress
                $data.progress = function(e, file) {
                    if ($.inArray('onProgress', settings.overrideEvents) < 0) {
                        if (e.lengthComputable) {
                            let percent = Math.round((e.loaded / e.total) * 100);
                        }
                        file.queueItem.find('.fileinfo').html(' - ' + percent + '%');
                        file.queueItem.find('.progress-bar').css('width', percent + '%');
                    }

                    // Trigger the progress event
                    if (typeof settings.onProgress === 'function') {
                        settings.onProgress.call($this, file, e);
                    }
                };

                // Trigger an error
                $data.error = function(errorType, file, uploadAll) {
                    if ($.inArray('onError', settings.overrideEvents) < 0) {
                        // Get the error message
                        switch (errorType) {
                            case '404_FILE_NOT_FOUND':
                                errorMsg = '404 Error';
                                break;
                            case '403_FORBIDDEN':
                                errorMsg = '403 Forbidden';
                                break;
                            case 'FORBIDDEN_FILE_TYPE':
                                errorMsg = 'Forbidden File Type';
                                break;
                            case 'FILE_SIZE_LIMIT_EXCEEDED':
                                errorMsg = 'File Too Large';
                                break;
                            default:
                                errorMsg = 'Unknown Error';
                                break;
                        }

                        // Add the error class to the queue item
                        file.queueItem.addClass('error')

                        // Output the error in the queue item
                            .find('.fileinfo').html(' - ' + errorMsg);

                        // Hide the
                        file.queueItem.find('.progress').remove();
                    }

                    // Trigger the error event
                    if (typeof settings.onError === 'function') {
                        settings.onError.call($this, errorType, file);
                    }
                    file.skip = true;
                    if (errorType == '404_FILE_NOT_FOUND') {
                        $data.uploads.errors++;
                    } else {
                        $data.queue.errors++;
                    }
                    if (uploadAll) {
                        methods.upload.call($this, null, true);
                    }
                };

                // Trigger when a single file upload is complete
                $data.uploadComplete = function(e, file, uploadAll) {
                    if ($.inArray('onUploadComplete', settings.overrideEvents) < 0) {
                        file.queueItem.find('.progress-bar').css('width', '100%');
                        file.queueItem.find('.fileinfo').html(' - Completed');
                        file.queueItem.find('.progress').slideUp(250);
                        file.queueItem.addClass('complete');
                    }

                    // Trigger the complete event
                    if (typeof settings.onUploadComplete === 'function') {
                        settings.onUploadComplete.call($this, file, file.xhr.responseText);
                    }
                    if (settings.removeCompleted) {
                        setTimeout(function() {
                            methods.cancel.call($this, file);
                        }, 3000);
                    }
                    file.complete = true;
                    $data.uploads.successful++;
                    $data.uploads.count++;
                    $data.uploads.current--;
                    delete file.xhr;
                    if (uploadAll) {
                        methods.upload.call($this, null, true);
                    }
                };

                // Trigger when all the files are done uploading
                $data.queueComplete = function() {
                    // Trigger the queueComplete event
                    if (typeof settings.onQueueComplete === 'function') {
                        settings.onQueueComplete.call($this, $data.uploads);
                    }
                };

                // ----------------------
                // Initialize UploadiFive
                // ----------------------

                // Check if HTML5 is available
                if (window.File && window.FileList && window.Blob && (window.FileReader || window.FormData)) {
                    // Assign an ID to the object
                    settings.id = 'uploadifive-' + $this.attr('id');

                    // Wrap the file input in a div with overflow set to hidden
                    $data.button = $('<div id="' + settings.id + '" class="uploadifive-button">' + settings.buttonText + '</div>');
                    if (settings.buttonClass) {
                        $data.button.addClass(settings.buttonClass);
                    }

                    // Style the button wrapper
                    $data.button.css({
                        height: settings.height,
                        'line-height': settings.height + 'px',
                        overflow: 'hidden',
                        position: 'relative',
                        'text-align': 'center',
                        width: settings.width
                    });

                    // Insert the button above the file input
                    $this.before($data.button)

                    // Add the file input to the button
                        .appendTo($data.button)

                    // Modify the styles of the file input
                        .hide();

                    // Create a new input
                    $data.createInput.call($this);

                    // Create the queue container
                    if (!settings.queueID) {
                        settings.queueID = settings.id + '-queue';
                        $data.queueEl = $('<div id="' + settings.queueID + '" class="uploadifive-queue" />');
                        $data.button.after($data.queueEl);
                    } else {
                        $data.queueEl = $('#' + settings.queueID);
                    }

                    // Add drag and drop functionality
                    if (settings.dnd) {
                        let $dropTarget = settings.dropTarget ? $(settings.dropTarget) : $data.queueEl.get(0);

                        $dropTarget.addEventListener('dragleave', function(e) {
                            // Stop FireFox from opening the dropped file(s)
                            e.preventDefault();
                            e.stopPropagation();
                        }, false);
                        $dropTarget.addEventListener('dragenter', function(e) {
                            // Stop FireFox from opening the dropped file(s)
                            e.preventDefault();
                            e.stopPropagation();
                        }, false);
                        $dropTarget.addEventListener('dragover', function(e) {
                            // Stop FireFox from opening the dropped file(s)
                            e.preventDefault();
                            e.stopPropagation();
                        }, false);
                        $dropTarget.addEventListener('drop', $data.drop, false);
                    }

                    // Send as binary workaround for Chrome
                    if (!XMLHttpRequest.prototype.sendAsBinary) {
                        XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
                            function byteValue(x) {
                                return x.charCodeAt(0) & 0xff;
                            }
                            let ords = Array.prototype.map.call(datastr, byteValue);
                            let ui8a = new Uint8Array(ords);

                            this.send(ui8a.buffer);
                        };
                    }

                    // Trigger the oninit event
                    if (typeof settings.onInit === 'function') {
                        settings.onInit.call($this);
                    }

                } else {

                    // Trigger the fallback event
                    if (typeof settings.onFallback === 'function') {
                        settings.onFallback.call($this);
                    }
                    return false;

                }

            });

        },


        // Write some data to the console
        debug: function() {

            return this.each(function() {

                console.log($(this).data('uploadifive'));

            });

        },

        // Clear all the items from the queue
        clearQueue: function() {

            this.each(function() {

                let $this = $(this),
                    $data = $this.data('uploadifive'),
                    settings = $data.settings;

                for (let key in $data.inputs) {
                    input = $data.inputs[key];
                    limit = input.files.length;
                    for (i = 0; i < limit; i++) {
                        file = input.files[i];
                        methods.cancel.call($this, file);
                    }
                }

                // Trigger the onClearQueue event
                if (typeof settings.onClearQueue === 'function') {
                    settings.onClearQueue.call($this, $('#' + $data.settings.queueID));
                }

            });

        },

        // Cancel a file upload in progress or remove a file from the queue
        cancel: function(file, fast) {

            this.each(function() {

                let $this = $(this),
                    $data = $this.data('uploadifive'),
                    settings = $data.settings;

                // If user passed a queue item ID instead of file...
                if (typeof file === 'string') {
                    if (!isNaN(file)) {
                        fileID = 'uploadifive-' + $(this).attr('id') + '-file-' + file;
                    }
                    file = $('#' + fileID).data('file');
                }

                file.skip = true;
                $data.filesCancelled++;
                if (file.uploading) {
                    $data.uploads.current--;
                    file.uploading = false;
                    file.xhr.abort();
                    delete file.xhr;
                    methods.upload.call($this);
                }
                if ($.inArray('onCancel', settings.overrideEvents) < 0) {
                    $data.removeQueueItem(file, fast);
                }

                // Trigger the cancel event
                if (typeof settings.onCancel === 'function') {
                    settings.onCancel.call($this, file);
                }

            });

        },

        // Upload the files in the queue
        upload: function(file, keepVars) {

            this.each(function() {

                let $this = $(this),
                    $data = $this.data('uploadifive'),
                    settings = $data.settings;

                if (file) {

                    $data.uploadFile.call($this, file);

                } else {

                    // Check if the upload limit was reached
                    if (($data.uploads.count + $data.uploads.current) < settings.uploadLimit || settings.uploadLimit == 0) {
                        if (!keepVars) {
                            $data.uploads.attempted = 0;
                            $data.uploads.successsful = 0;
                            $data.uploads.errors = 0;
                            let filesToUpload = $data.filesToUpload();

                            // Trigger the onUpload event
                            if (typeof settings.onUpload === 'function') {
                                settings.onUpload.call($this, filesToUpload);
                            }
                        }

                        // Loop through the files
                        $('#' + settings.queueID).find('.uploadifive-queue-item').not('.error, .complete').each(function() {
                            let _file = $(this).data('file');

                            // Check if the simUpload limit was reached
                            if (($data.uploads.current >= settings.simUploadLimit && settings.simUploadLimit !== 0) || ($data.uploads.current >= settings.uploadLimit && settings.uploadLimit !== 0) || ($data.uploads.count >= settings.uploadLimit && settings.uploadLimit !== 0)) {
                                return false;
                            }
                            if (settings.checkScript) {
                                // Let the loop know that we're already processing this file
                                _file.checking = true;
                                skipFile = $data.checkExists(_file);
                                _file.checking = false;
                                if (!skipFile) {
                                    $data.uploadFile(_file, true);
                                }
                            } else {
                                $data.uploadFile(_file, true);
                            }
                        });
                        if ($('#' + settings.queueID).find('.uploadifive-queue-item').not('.error, .complete').size() == 0) {
                            $data.queueComplete();
                        }
                    } else {
                        if ($data.uploads.current == 0) {
                            if ($.inArray('onError', settings.overrideEvents) < 0) {
                                if ($data.filesToUpload() > 0 && settings.uploadLimit != 0) {
                                    alert('The maximum upload limit has been reached.');
                                }
                            }

                            // Trigger the onError event
                            if (typeof settings.onError === 'function') {
                                settings.onError.call($this, 'UPLOAD_LIMIT_EXCEEDED', $data.filesToUpload());
                            }
                        }
                    }

                }

            });

        },

        // Destroy an instance of UploadiFive
        destroy: function() {

            this.each(function() {

                let $this = $(this),
                    $data = $this.data('uploadifive'),
                    settings = $data.settings;

                // Clear the queue
                methods.clearQueue.call($this);

                // Destroy the queue if it was created
                if (!settings.queueID) {
                    $('#' + settings.queueID).remove();
                }

                // Remove extra inputs
                $this.siblings('input').remove();

                // Show the original file input
                $this.show()

                // Move the file input out of the button
                    .insertBefore($data.button);

                // Delete the button
                $data.button.remove();

                // Trigger the destroy event
                if (typeof settings.onDestroy === 'function') {
                    settings.onDestroy.call($this);
                }

            });

        }

    };

    $.fn.upload = function(method) {

        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('The method ' + method + ' does not exist in $.uploadify');
        }

    };

}(jQuery));