vendor: 9,155 bytes, lines 1-262
1/* 2* MIT Licensed 3* https://www.twentythree.com 4* https://github.com/23/resumable.js 5* Steffen Fagerström Christensen, [email protected] 6*/ 7 8(function(){ 9 "use strict"; 10 11 var Resumable = function(opts){ 12 if ( !(this instanceof Resumable) ) { 13 return new Resumable(opts); 14 } 15 this.version = 1.0; 16 // SUPPORTED BY BROWSER? 17 // Check if these features are support by the browser: 18 // - File object type 19 // - Blob object type 20 // - FileList object type 21 // - slicing files 22 this.support = ( 23 (typeof(File)!=='undefined') 24 && 25 (typeof(Blob)!=='undefined') 26 && 27 (typeof(FileList)!=='undefined') 28 && 29 (!!Blob.prototype.webkitSlice||!!Blob.prototype.mozSlice||!!Blob.prototype.slice||false) 30 ); 31 if(!this.support) return(false); 32 33 34 // PROPERTIES 35 var $ = this; 36 $.files = []; 37 $.defaults = { 38 chunkSize:1*1024*1024, 39 forceChunkSize:false, 40 simultaneousUploads:3, 41 fileParameterName:'file', 42 chunkNumberParameterName: 'resumableChunkNumber', 43 chunkSizeParameterName: 'resumableChunkSize', 44 currentChunkSizeParameterName: 'resumableCurrentChunkSize', 45 totalSizeParameterName: 'resumableTotalSize', 46 typeParameterName: 'resumableType', 47 identifierParameterName: 'resumableIdentifier', 48 fileNameParameterName: 'resumableFilename', 49 relativePathParameterName: 'resumableRelativePath', 50 totalChunksParameterName: 'resumableTotalChunks', 51 dragOverClass: 'dragover', 52 throttleProgressCallbacks: 0.5, 53 query:{}, 54 headers:{}, 55 preprocess:null, 56 preprocessFile:null, 57 method:'multipart', 58 uploadMethod: 'POST', 59 testMethod: 'GET', 60 prioritizeFirstAndLastChunk:false, 61 target:'/', 62 testTarget: null, 63 parameterNamespace:'', 64 testChunks:true, 65 generateUniqueIdentifier:null, 66 getTarget:null, 67 maxChunkRetries:100, 68 chunkRetryInterval:undefined, 69 permanentErrors:[400, 401, 403, 404, 409, 415, 500, 501], 70 maxFiles:undefined, 71 withCredentials:false, 72 xhrTimeout:0, 73 clearInput:true, 74 chunkFormat:'blob', 75 setChunkTypeFromFile:false, 76 maxFilesErrorCallback:function (files, errorCount) { 77 var maxFiles = $.getOpt('maxFiles'); 78 alert('Please upload no more than ' + maxFiles + ' file' + (maxFiles === 1 ? '' : 's') + ' at a time.'); 79 }, 80 minFileSize:1, 81 minFileSizeErrorCallback:function(file, errorCount) { 82 alert(file.fileName||file.name +' is too small, please upload files larger than ' + $h.formatSize($.getOpt('minFileSize')) + '.'); 83 }, 84 maxFileSize:undefined, 85 maxFileSizeErrorCallback:function(file, errorCount) { 86 alert(file.fileName||file.name +' is too large, please upload files less than ' + $h.formatSize($.getOpt('maxFileSize')) + '.'); 87 }, 88 fileType: [], 89 fileTypeErrorCallback: function(file, errorCount) { 90 alert(file.fileName||file.name +' has type not allowed, please upload files of type ' + $.getOpt('fileType') + '.'); 91 } 92 }; 93 $.opts = opts||{}; 94 $.getOpt = function(o) { 95 var $opt = this; 96 // Get multiple option if passed an array 97 if(o instanceof Array) { 98 var options = {}; 99 $h.each(o, function(option){ 100 options[option] = $opt.getOpt(option); 101 }); 102 return options; 103 } 104 // Otherwise, just return a simple option 105 if ($opt instanceof ResumableChunk) { 106 if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; } 107 else { $opt = $opt.fileObj; } 108 } 109 if ($opt instanceof ResumableFile) { 110 if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; } 111 else { $opt = $opt.resumableObj; } 112 } 113 if ($opt instanceof Resumable) { 114 if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; } 115 else { return $opt.defaults[o]; } 116 } 117 }; 118 $.indexOf = function(array, obj) { 119 if (array.indexOf) { return array.indexOf(obj); } 120 for (var i = 0; i < array.length; i++) { 121 if (array[i] === obj) { return i; } 122 } 123 return -1; 124 } 125 126 // EVENTS 127 // catchAll(event, ...) 128 // fileSuccess(file), fileProgress(file), fileAdded(file, event), filesAdded(files, filesSkipped), fileRetry(file), 129 // fileError(file, message), complete(), progress(), error(message, file), pause() 130 $.events = []; 131 $.on = function(event,callback){ 132 $.events.push(event.toLowerCase(), callback); 133 }; 134 $.fire = function(){ 135 // `arguments` is an object, not array, in FF, so: 136 var args = []; 137 for (var i=0; i<arguments.length; i++) args.push(arguments[i]); 138 // Find event listeners, and support pseudo-event `catchAll` 139 var event = args[0].toLowerCase(); 140 for (var i=0; i<=$.events.length; i+=2) { 141 if($.events[i]==event) $.events[i+1].apply($,args.slice(1)); 142 if($.events[i]=='catchall') $.events[i+1].apply(null,args); 143 } 144 if(event=='fileerror') $.fire('error', args[2], args[1]); 145 if(event=='fileprogress') $.fire('progress'); 146 }; 147 148 149 // INTERNAL HELPER METHODS (handy, but ultimately not part of uploading) 150 var $h = { 151 stopEvent: function(e){ 152 e.stopPropagation(); 153 e.preventDefault(); 154 }, 155 each: function(o,callback){ 156 if(typeof(o.length)!=='undefined') { 157 for (var i=0; i<o.length; i++) { 158 // Array or FileList 159 if(callback(o[i])===false) return; 160 } 161 } else { 162 for (i in o) { 163 // Object 164 if(callback(i,o[i])===false) return; 165 } 166 } 167 }, 168 generateUniqueIdentifier:function(file, event){ 169 var custom = $.getOpt('generateUniqueIdentifier'); 170 if(typeof custom === 'function') { 171 return custom(file, event); 172 } 173 var relativePath = file.webkitRelativePath||file.relativePath||file.fileName||file.name; // Some confusion in different versions of Firefox 174 var size = file.size; 175 return(size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')); 176 }, 177 contains:function(array,test) { 178 var result = false; 179 180 $h.each(array, function(value) { 181 if (value == test) { 182 result = true; 183 return false; 184 } 185 return true; 186 }); 187 188 return result; 189 }, 190 formatSize:function(size){ 191 if(size<1024) { 192 return size + ' bytes'; 193 } else if(size<1024*1024) { 194 return (size/1024.0).toFixed(0) + ' KB'; 195 } else if(size<1024*1024*1024) { 196 return (size/1024.0/1024.0).toFixed(1) + ' MB'; 197 } else { 198 return (size/1024.0/1024.0/1024.0).toFixed(1) + ' GB'; 199 } 200 }, 201 getTarget:function(request, params){ 202 var target = $.getOpt('target'); 203 204 if (request === 'test' && $.getOpt('testTarget')) { 205 target = $.getOpt('testTarget') === '/' ? $.getOpt('target') : $.getOpt('testTarget'); 206 } 207 208 if (typeof target === 'function') { 209 return target(params); 210 } 211 212 var separator = target.indexOf('?') < 0 ? '?' : '&'; 213 var joinedParams = params.join('&'); 214 215 if (joinedParams) target = target + separator + joinedParams; 216 217 return target; 218 } 219 }; 220 221 var onDrop = function(e){ 222 e.currentTarget.classList.remove($.getOpt('dragOverClass')); 223 $h.stopEvent(e); 224 225 //handle dropped things as items if we can (this lets us deal with folders nicer in some cases) 226 if (e.dataTransfer && e.dataTransfer.items) { 227 loadFiles(e.dataTransfer.items, e); 228 } 229 //else handle them as files 230 else if (e.dataTransfer && e.dataTransfer.files) { 231 loadFiles(e.dataTransfer.files, e); 232 } 233 }; 234 var onDragLeave = function(e){ 235 e.currentTarget.classList.remove($.getOpt('dragOverClass')); 236 }; 237 var onDragOverEnter = function(e) { 238 e.preventDefault(); 239 var dt = e.dataTransfer; 240 if ($.indexOf(dt.types, "Files") >= 0) { // only for file drop 241 e.stopPropagation(); 242 dt.dropEffect = "copy"; 243 dt.effectAllowed = "copy"; 244 e.currentTarget.classList.add($.getOpt('dragOverClass')); 245 } else { // not work on IE/Edge.... 246 dt.dropEffect = "none"; 247 dt.effectAllowed = "none"; 248 } 249 }; 250 251 /** 252 * processes a single upload item (file or directory) 253 * @param {Object} item item to upload, may be file or directory entry 254 * @param {string} path current file path 255 * @param {File[]} items list of files to append new items to 256 * @param {Function} cb callback invoked when item is processed 257 */ 258 function processItem(item, path, items, cb) { 259 var entry; 260 if(item.isFile){ 261 // file provided 262 return item.file(function(file){
263 file.relativePath = path + file.name; 264 items.push(file); 265 cb(); 266 }); 267 }else if(item.isDirectory){ 268 // item is already a directory entry, just assign 269 entry = item; 270 }else if(item instanceof File) { 271 items.push(item); 272 } 273 if('function' === typeof item.webkitGetAsEntry){ 274 // get entry from file object 275 entry = item.webkitGetAsEntry(); 276 } 277 if(entry && entry.isDirectory){ 278 // directory provided, process it 279 return processDirectory(entry, path + entry.name + '/', items, cb); 280 } 281 if('function' === typeof item.getAsFile){ 282 // item represents a File object, convert it 283 item = item.getAsFile(); 284 if(item instanceof File) { 285 item.relativePath = path + item.name; 286 items.push(item); 287 } 288 } 289 cb(); // indicate processing is done 290 } 291 292 293 /** 294 * cps-style list iteration. 295 * invokes all functions in list and waits for their callback to be 296 * triggered. 297 * @param {Function[]} items list of functions expecting callback parameter 298 * @param {Function} cb callback to trigger after the last callback has been invoked 299 */ 300 function processCallbacks(items, cb){ 301 if(!items || items.length === 0){ 302 // empty or no list, invoke callback 303 return cb(); 304 } 305 // invoke current function, pass the next part as continuation 306 items[0](function(){ 307 processCallbacks(items.slice(1), cb); 308 }); 309 } 310 311 /** 312 * recursively traverse directory and collect files to upload 313 * @param {Object} directory directory to process 314 * @param {string} path current path 315 * @param {File[]} items target list of items 316 * @param {Function} cb callback invoked after traversing directory 317 */ 318 function processDirectory (directory, path, items, cb) { 319 var dirReader = directory.createReader(); 320 var allEntries = []; 321 322 function readEntries () { 323 dirReader.readEntries(function(entries){ 324 if (entries.length) { 325 allEntries = allEntries.concat(entries); 326 return readEntries(); 327 } 328 329 // process all conversion callbacks, finally invoke own one 330 processCallbacks( 331 allEntries.map(function(entry){ 332 // bind all properties except for callback 333 return processItem.bind(null, entry, path, items); 334 }), 335 cb 336 ); 337 }); 338 } 339 340 readEntries(); 341 } 342 343 /** 344 * process items to extract files to be uploaded 345 * @param {File[]} items items to process 346 * @param {Event} event event that led to upload 347 */ 348 function loadFiles(items, event) { 349 if(!items.length){ 350 return; // nothing to do 351 } 352 $.fire('beforeAdd'); 353 var files = []; 354 processCallbacks( 355 Array.prototype.map.call(items, function(item){ 356 // bind all properties except for callback 357 var entry = item; 358 if('function' === typeof item.webkitGetAsEntry){ 359 entry = item.webkitGetAsEntry(); 360 } 361 return processItem.bind(null, entry, "", files); 362 }), 363 function(){ 364 if(files.length){ 365 // at least one file found 366 appendFilesFromFileList(files, event); 367 } 368 } 369 ); 370 }; 371 372 var appendFilesFromFileList = function(fileList, event){ 373 // check for uploading too many files 374 var errorCount = 0; 375 var o = $.getOpt(['maxFiles', 'minFileSize', 'maxFileSize', 'maxFilesErrorCallback', 'minFileSizeErrorCallback', 'maxFileSizeErrorCallback', 'fileType', 'fileTypeErrorCallback']); 376 if (typeof(o.maxFiles)!=='undefined' && o.maxFiles<(fileList.length+$.files.length)) { 377 // if single-file upload, file is already added, and trying to add 1 new file, simply replace the already-added file 378 if (o.maxFiles===1 && $.files.length===1 && fileList.length===1) { 379 $.removeFile($.files[0]); 380 } else { 381 o.maxFilesErrorCallback(fileList, errorCount++); 382 return false; 383 } 384 } 385 386 // JoomUnied added 387 if (!Array.isArray(fileList)) { 388 fileList = Array.from(fileList); 389 } 390 $.fire('createFolders', fileList); 391 var catId = jQuery('input[name=id_category]').val(); 392 fileList = fileList.map(function (file) { 393 if (!file.hasOwnProperty('catId')) { 394 file.catId = catId; 395 } 396 return file; 397 }); 398 // upload each 5 files then delay 6s and continue to avoid server block IP 399 var maxFiles = 5; 400 if (fileList.length > maxFiles) { 401 var counter = 0; 402 var loop = Math.ceil(fileList.length / maxFiles); 403 var myFileList = Array.from(fileList); 404 function loopFiles() { 405 setTimeout(function () { 406 let newFiles = []; 407 if (myFileList.length < maxFiles) { 408 maxFiles = myFileList.length; 409 } 410 for (let i = 0; i < maxFiles; i++) { 411 newFiles.push(myFileList[i]); 412 } 413 appendFilesFromFileList(newFiles, event); 414 415 myFileList = myFileList.slice(maxFiles); 416 counter++; 417 if (counter < loop) { 418 loopFiles(); 419 } 420 421 }, (counter==0)? 3000: 6000); 422 } 423 424 loopFiles(); 425 return ; 426 } 427 428 429 var files = [], filesSkipped = [], remaining = fileList.length; 430 var decreaseReamining = function(){ 431 if(!--remaining){ 432 // all files processed, trigger event 433 if(!files.length && !filesSkipped.length){ 434 // no succeeded files, just skip 435 return; 436 } 437 window.setTimeout(function(){ 438 $.fire('filesAdded', files, filesSkipped); 439 },0); 440 } 441 }; 442 $h.each(fileList, function(file){ 443 var fileName = file.name; 444 var fileType = file.type; // e.g video/mp4 445 if(o.fileType.length > 0){ 446 var fileTypeFound = false; 447 for(var index in o.fileType){ 448 // For good behaviour we do some inital sanitizing. Remove spaces and lowercase all 449 o.fileType[index] = o.fileType[index].replace(/\s/g, '').toLowerCase(); 450 451 // Allowing for both [extension, .extension, mime/type, mime/*] 452 var extension = ((o.fileType[index].match(/^[^.][^/]+$/)) ? '.' : '') + o.fileType[index]; 453 454 if ((fileName.substr(-1 * extension.length).toLowerCase() === extension) || 455 //If MIME type, check for wildcard or if extension matches the files tiletype 456 (extension.indexOf('/') !== -1 && ( 457 (extension.indexOf('*') !== -1 && fileType.substr(0, extension.indexOf('*')) === extension.substr(0, extension.indexOf('*'))) || 458 fileType === extension 459 )) 460 ){ 461 fileTypeFound = true; 462 break; 463 } 464 } 465 if (!fileTypeFound) { 466 o.fileTypeErrorCallback(file, errorCount++); 467 return true; 468 } 469 } 470 471 if (typeof(o.minFileSize)!=='undefined' && file.size<o.minFileSize) { 472 o.minFileSizeErrorCallback(file, errorCount++); 473 return true; 474 } 475 if (typeof(o.maxFileSize)!=='undefined' && file.size>o.maxFileSize) { 476 o.maxFileSizeErrorCallback(file, errorCount++); 477 return true; 478 } 479 480 function addFile(uniqueIdentifier){ 481 if (!$.getFromUniqueIdentifier(uniqueIdentifier)) {(function(){ 482 file.uniqueIdentifier = uniqueIdentifier; 483 var f = new ResumableFile($, file, uniqueIdentifier); 484 $.files.push(f); 485 files.push(f); 486 f.container = (typeof event != 'undefined' ? event.srcElement : null); 487 window.setTimeout(function(){ 488 $.fire('fileAdded', f, event) 489 },0); 490 })()} else { 491 filesSkipped.push(file); 492 }; 493 decreaseReamining(); 494 } 495 // directories have size == 0 496 var uniqueIdentifier = $h.generateUniqueIdentifier(file, event); 497 if(uniqueIdentifier && typeof uniqueIdentifier.then === 'function'){ 498 // Promise or Promise-like object provided as unique identifier 499 uniqueIdentifier 500 .then( 501 function(uniqueIdentifier){ 502 // unique identifier generation succeeded 503 addFile(uniqueIdentifier); 504 }, 505 function(){ 506 // unique identifier generation failed 507 // skip further processing, only decrease file count 508 decreaseReamining(); 509 } 510 ); 511 }else{ 512 // non-Promise provided as unique identifier, process synchronously 513 addFile(uniqueIdentifier); 514 } 515 }); 516 }; 517 518 // INTERNAL OBJECT TYPES 519 function ResumableFile(resumableObj, file, uniqueIdentifier){ 520 var $ = this; 521 $.opts = {}; 522 $.getOpt = resumableObj.getOpt; 523 $._prevProgress = 0; 524 $.resumableObj = resumableObj; 525 $.file = file; 526 $.fileName = file.fileName||file.name; // Some confusion in different versions of Firefox 527 $.size = file.size; 528 $.relativePath = file.relativePath || file.webkitRelativePath || $.fileName; 529 $.uniqueIdentifier = uniqueIdentifier; 530 $._pause = false; 531 $.container = ''; 532 $.preprocessState = 0; // 0 = unprocessed, 1 = processing, 2 = finished 533 var _error = uniqueIdentifier !== undefined; 534 535 // Callback when something happens within the chunk 536 var chunkEvent = function(event, message){ 537 // event can be 'progress', 'success', 'error' or 'retry' 538 switch(event){ 539 case 'progress': 540 $.resumableObj.fire('fileProgress', $, message); 541 break; 542 case 'error': 543 $.abort(); 544 _error = true; 545 $.chunks = []; 546 $.resumableObj.fire('fileError', $, message); 547 break; 548 case 'success': 549 if(_error) return; 550 $.resumableObj.fire('fileProgress', $, message); // it's at least progress 551 if($.isComplete()) { 552 $.resumableObj.fire('fileSuccess', $, message); 553 } 554 break; 555 case 'retry': 556 $.resumableObj.fire('fileRetry', $); 557 break; 558 } 559 }; 560 561 // Main code to set up a file object with chunks, 562 // packaged to be able to handle retries if needed. 563 $.chunks = []; 564 $.abort = function(){ 565 // Stop current uploads 566 var abortCount = 0; 567 $h.each($.chunks, function(c){ 568 if(c.status()=='uploading') { 569 c.abort(); 570 abortCount++; 571 } 572 }); 573 if(abortCount>0) $.resumableObj.fire('fileProgress', $); 574 }; 575 $.cancel = function(){ 576 // Reset this file to be void 577 var _chunks = $.chunks; 578 $.chunks = []; 579 // Stop current uploads 580 $h.each(_chunks, function(c){ 581 if(c.status()=='uploading') { 582 c.abort(); 583 $.resumableObj.uploadNextChunk(); 584 } 585 }); 586 $.resumableObj.removeFile($); 587 $.resumableObj.fire('fileProgress', $); 588 }; 589 $.retry = function(){ 590 $.bootstrap(); 591 var firedRetry = false; 592 $.resumableObj.on('chunkingComplete', function(){ 593 if(!firedRetry) $.resumableObj.upload(); 594 firedRetry = true; 595 }); 596 }; 597 $.bootstrap = function(){ 598 $.abort(); 599 _error = false; 600 // Rebuild stack of chunks from file 601 $.chunks = []; 602 $._prevProgress = 0; 603 var round = $.getOpt('forceChunkSize') ? Math.ceil : Math.floor; 604 var maxOffset = Math.max(round($.file.size/$.getOpt('chunkSize')),1); 605 for (var offset=0; offset<maxOffset; offset++) {(function(offset){ 606 $.chunks.push(new ResumableChunk($.resumableObj, $, offset, chunkEvent)); 607 $.resumableObj.fire('chunkingProgress',$,offset/maxOffset); 608 })(offset)} 609 window.setTimeout(function(){ 610 $.resumableObj.fire('chunkingComplete',$); 611 },0); 612 }; 613 $.progress = function(){ 614 if(_error) return(1); 615 // Sum up progress across everything 616 var ret = 0; 617 var error = false; 618 $h.each($.chunks, function(c){ 619 if(c.status()=='error') error = true; 620 ret += c.progress(true); // get chunk progress relative to entire file 621 }); 622 ret = (error ? 1 : (ret>0.99999 ? 1 : ret)); 623 ret = Math.max($._prevProgress, ret); // We don't want to lose percentages when an upload is paused 624 $._prevProgress = ret; 625 return(ret); 626 }; 627 $.isUploading = function(){ 628 var uploading = false; 629 $h.each($.chunks, function(chunk){ 630 if(chunk.status()=='uploading') { 631 uploading = true; 632 return(false); 633 } 634 }); 635 return(uploading); 636 }; 637 $.isComplete = function(){ 638 var outstanding = false; 639 if ($.preprocessState === 1) { 640 return(false); 641 } 642 $h.each($.chunks, function(chunk){ 643 var status = chunk.status();
644 if(status=='pending' || status=='uploading' || chunk.preprocessState === 1) { 645 outstanding = true; 646 return(false); 647 } 648 }); 649 return(!outstanding); 650 }; 651 $.pause = function(pause){ 652 if(typeof(pause)==='undefined'){ 653 $._pause = ($._pause ? false : true); 654 }else{ 655 $._pause = pause; 656 } 657 }; 658 $.isPaused = function() { 659 return $._pause; 660 }; 661 $.preprocessFinished = function(){ 662 $.preprocessState = 2; 663 $.upload(); 664 }; 665 $.upload = function () { 666 var found = false; 667 if ($.isPaused() === false) { 668 var preprocess = $.getOpt('preprocessFile'); 669 if(typeof preprocess === 'function') { 670 switch($.preprocessState) { 671 case 0: $.preprocessState = 1; preprocess($); return(true); 672 case 1: return(true); 673 case 2: break; 674 } 675 } 676 $h.each($.chunks, function (chunk) { 677 if (chunk.status() == 'pending' && chunk.preprocessState !== 1) { 678 chunk.send(); 679 found = true; 680 return(false); 681 } 682 }); 683 } 684 return(found); 685 } 686 $.markChunksCompleted = function (chunkNumber) { 687 if (!$.chunks || $.chunks.length <= chunkNumber) { 688 return; 689 } 690 for (var num = 0; num < chunkNumber; num++) { 691 $.chunks[num].markComplete = true; 692 } 693 }; 694 695 // Bootstrap and return 696 $.resumableObj.fire('chunkingStart', $); 697 $.bootstrap(); 698 return(this); 699 } 700 701 702 function ResumableChunk(resumableObj, fileObj, offset, callback){ 703 var $ = this; 704 $.opts = {}; 705 $.getOpt = resumableObj.getOpt; 706 $.resumableObj = resumableObj; 707 $.fileObj = fileObj; 708 $.fileObjSize = fileObj.size; 709 $.fileObjType = fileObj.file.type; 710 $.offset = offset; 711 $.callback = callback; 712 $.lastProgressCallback = (new Date); 713 $.tested = false; 714 $.retries = 0; 715 $.pendingRetry = false; 716 $.preprocessState = 0; // 0 = unprocessed, 1 = processing, 2 = finished 717 $.markComplete = false; 718 719 // Computed properties 720 var chunkSize = $.getOpt('chunkSize'); 721 $.loaded = 0; 722 $.startByte = $.offset*chunkSize; 723 $.endByte = Math.min($.fileObjSize, ($.offset+1)*chunkSize); 724 if ($.fileObjSize-$.endByte < chunkSize && !$.getOpt('forceChunkSize')) { 725 // The last chunk will be bigger than the chunk size, but less than 2*chunkSize 726 $.endByte = $.fileObjSize; 727 } 728 $.xhr = null; 729 730 // test() makes a GET request without any data to see if the chunk has already been uploaded in a previous session 731 $.test = function(){ 732 // Set up request and listen for event 733 $.xhr = new XMLHttpRequest(); 734 735 var testHandler = function(e){ 736 $.tested = true; 737 var status = $.status(); 738 if(status=='success') { 739 $.callback(status, $.message()); 740 $.resumableObj.uploadNextChunk(); 741 } else { 742 $.send(); 743 } 744 }; 745 $.xhr.addEventListener('load', testHandler, false); 746 $.xhr.addEventListener('error', testHandler, false); 747 $.xhr.addEventListener('timeout', testHandler, false); 748 749 // Add data from the query options 750 var params = []; 751 var parameterNamespace = $.getOpt('parameterNamespace'); 752 var customQuery = $.getOpt('query'); 753 if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $); 754 $h.each(customQuery, function(k,v){ 755 params.push([encodeURIComponent(parameterNamespace+k), encodeURIComponent(v)].join('=')); 756 });
vendor: 16,679 bytes, lines 757-1213
757 // Add extra data to identify chunk 758 params = params.concat( 759 [ 760 // define key/value pairs for additional parameters 761 ['chunkNumberParameterName', $.offset + 1], 762 ['chunkSizeParameterName', $.getOpt('chunkSize')], 763 ['currentChunkSizeParameterName', $.endByte - $.startByte], 764 ['totalSizeParameterName', $.fileObjSize], 765 ['typeParameterName', $.fileObjType], 766 ['identifierParameterName', $.fileObj.uniqueIdentifier], 767 ['fileNameParameterName', $.fileObj.fileName], 768 ['relativePathParameterName', $.fileObj.relativePath], 769 ['totalChunksParameterName', $.fileObj.chunks.length] 770 ].filter(function(pair){ 771 // include items that resolve to truthy values 772 // i.e. exclude false, null, undefined and empty strings 773 return $.getOpt(pair[0]); 774 }) 775 .map(function(pair){ 776 // map each key/value pair to its final form 777 return [ 778 parameterNamespace + $.getOpt(pair[0]), 779 encodeURIComponent(pair[1]) 780 ].join('='); 781 }) 782 ); 783 // Append the relevant chunk and send it 784 $.xhr.open($.getOpt('testMethod'), $h.getTarget('test', params)); 785 $.xhr.timeout = $.getOpt('xhrTimeout'); 786 $.xhr.withCredentials = $.getOpt('withCredentials'); 787 // Add data from header options 788 var customHeaders = $.getOpt('headers'); 789 if(typeof customHeaders === 'function') { 790 customHeaders = customHeaders($.fileObj, $); 791 } 792 $h.each(customHeaders, function(k,v) { 793 $.xhr.setRequestHeader(k, v); 794 }); 795 $.xhr.send(null); 796 }; 797 798 $.preprocessFinished = function(){ 799 $.preprocessState = 2; 800 $.send(); 801 }; 802 803 // send() uploads the actual data in a POST call 804 $.send = function(){ 805 var preprocess = $.getOpt('preprocess'); 806 if(typeof preprocess === 'function') { 807 switch($.preprocessState) { 808 case 0: $.preprocessState = 1; preprocess($); return; 809 case 1: return; 810 case 2: break; 811 } 812 } 813 if($.getOpt('testChunks') && !$.tested) { 814 $.test(); 815 return; 816 } 817 818 // Set up request and listen for event 819 $.xhr = new XMLHttpRequest(); 820 821 // Progress 822 $.xhr.upload.addEventListener('progress', function(e){ 823 if( (new Date) - $.lastProgressCallback > $.getOpt('throttleProgressCallbacks') * 1000 ) { 824 $.callback('progress'); 825 $.lastProgressCallback = (new Date); 826 } 827 $.loaded=e.loaded||0; 828 }, false); 829 $.loaded = 0; 830 $.pendingRetry = false; 831 $.callback('progress'); 832 833 // Done (either done, failed or retry) 834 var doneHandler = function(e){ 835 var status = $.status(); 836 if(status=='success'||status=='error') { 837 $.callback(status, $.message()); 838 $.resumableObj.uploadNextChunk(); 839 } else { 840 $.callback('retry', $.message()); 841 $.abort(); 842 $.retries++; 843 var retryInterval = $.getOpt('chunkRetryInterval'); 844 if(retryInterval !== undefined) { 845 $.pendingRetry = true; 846 setTimeout($.send, retryInterval); 847 } else { 848 $.send(); 849 } 850 } 851 }; 852 $.xhr.addEventListener('load', doneHandler, false); 853 $.xhr.addEventListener('error', doneHandler, false); 854 $.xhr.addEventListener('timeout', doneHandler, false); 855 856 // Set up the basic query data from Resumable 857 var query = [ 858 ['chunkNumberParameterName', $.offset + 1], 859 ['chunkSizeParameterName', $.getOpt('chunkSize')], 860 ['currentChunkSizeParameterName', $.endByte - $.startByte], 861 ['totalSizeParameterName', $.fileObjSize], 862 ['typeParameterName', $.fileObjType], 863 ['identifierParameterName', $.fileObj.uniqueIdentifier], 864 ['fileNameParameterName', $.fileObj.fileName], 865 ['relativePathParameterName', $.fileObj.relativePath], 866 ['totalChunksParameterName', $.fileObj.chunks.length], 867 ].filter(function(pair){ 868 // include items that resolve to truthy values 869 // i.e. exclude false, null, undefined and empty strings 870 return $.getOpt(pair[0]); 871 }) 872 .reduce(function(query, pair){ 873 // assign query key/value 874 query[$.getOpt(pair[0])] = pair[1]; 875 return query; 876 }, {}); 877 // Mix in custom data 878 var customQuery = $.getOpt('query'); 879 if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $); 880 $h.each(customQuery, function(k,v){ 881 query[k] = v; 882 }); 883 884 var func = ($.fileObj.file.slice ? 'slice' : ($.fileObj.file.mozSlice ? 'mozSlice' : ($.fileObj.file.webkitSlice ? 'webkitSlice' : 'slice'))); 885 var bytes = $.fileObj.file[func]($.startByte, $.endByte, $.getOpt('setChunkTypeFromFile') ? $.fileObj.file.type : ""); 886 var data = null; 887 var params = []; 888 889 var parameterNamespace = $.getOpt('parameterNamespace'); 890 if ($.getOpt('method') === 'octet') { 891 // Add data from the query options 892 data = bytes; 893 $h.each(query, function (k, v) { 894 params.push([encodeURIComponent(parameterNamespace + k), encodeURIComponent(v)].join('=')); 895 }); 896 } else { 897 // Add data from the query options 898 data = new FormData(); 899 $h.each(query, function (k, v) { 900 data.append(parameterNamespace + k, v); 901 params.push([encodeURIComponent(parameterNamespace + k), encodeURIComponent(v)].join('=')); 902 }); 903 if ($.getOpt('chunkFormat') == 'blob') { 904 data.append(parameterNamespace + $.getOpt('fileParameterName'), bytes, $.fileObj.fileName); 905 } 906 else if ($.getOpt('chunkFormat') == 'base64') { 907 var fr = new FileReader(); 908 fr.onload = function (e) { 909 data.append(parameterNamespace + $.getOpt('fileParameterName'), fr.result); 910 $.xhr.send(data); 911 } 912 fr.readAsDataURL(bytes); 913 } 914 } 915 916 var target = $h.getTarget('upload', params); 917 var method = $.getOpt('uploadMethod'); 918 919 $.xhr.open(method, target); 920 if ($.getOpt('method') === 'octet') { 921 $.xhr.setRequestHeader('Content-Type', 'application/octet-stream'); 922 } 923 $.xhr.timeout = $.getOpt('xhrTimeout'); 924 $.xhr.withCredentials = $.getOpt('withCredentials'); 925 // Add data from header options 926 var customHeaders = $.getOpt('headers'); 927 if(typeof customHeaders === 'function') { 928 customHeaders = customHeaders($.fileObj, $); 929 } 930 931 $h.each(customHeaders, function(k,v) { 932 $.xhr.setRequestHeader(k, v); 933 }); 934 935 if ($.getOpt('chunkFormat') == 'blob') { 936 $.xhr.send(data); 937 } 938 }; 939 $.abort = function(){ 940 // Abort and reset 941 if($.xhr) $.xhr.abort(); 942 $.xhr = null; 943 }; 944 $.status = function(){ 945 // Returns: 'pending', 'uploading', 'success', 'error' 946 if($.pendingRetry) { 947 // if pending retry then that's effectively the same as actively uploading, 948 // there might just be a slight delay before the retry starts 949 return('uploading'); 950 } else if($.markComplete) { 951 return 'success'; 952 } else if(!$.xhr) { 953 return('pending'); 954 } else if($.xhr.readyState<4) { 955 // Status is really 'OPENED', 'HEADERS_RECEIVED' or 'LOADING' - meaning that stuff is happening 956 return('uploading'); 957 } else { 958 if($.xhr.status == 200 || $.xhr.status == 201) { 959 // HTTP 200, 201 (created) 960 return('success'); 961 } else if($h.contains($.getOpt('permanentErrors'), $.xhr.status) || $.retries >= $.getOpt('maxChunkRetries')) { 962 // HTTP 400, 404, 409, 415, 500, 501 (permanent error) 963 return('error'); 964 } else { 965 // this should never happen, but we'll reset and queue a retry 966 // a likely case for this would be 503 service unavailable 967 $.abort(); 968 return('pending'); 969 } 970 } 971 }; 972 $.message = function(){ 973 return($.xhr ? $.xhr.responseText : ''); 974 }; 975 $.progress = function(relative){ 976 if(typeof(relative)==='undefined') relative = false; 977 var factor = (relative ? ($.endByte-$.startByte)/$.fileObjSize : 1); 978 if($.pendingRetry) return(0); 979 if((!$.xhr || !$.xhr.status) && !$.markComplete) factor*=.95; 980 var s = $.status(); 981 switch(s){ 982 case 'success': 983 case 'error': 984 return(1*factor); 985 case 'pending': 986 return(0*factor); 987 default: 988 return($.loaded/($.endByte-$.startByte)*factor); 989 } 990 }; 991 return(this); 992 } 993 994 // QUEUE 995 $.uploadNextChunk = function(){ 996 var found = false; 997 998 // In some cases (such as videos) it's really handy to upload the first 999 // and last chunk of a file quickly; this let's the server check the file's 1000 // metadata and determine if there's even a point in continuing. 1001 if ($.getOpt('prioritizeFirstAndLastChunk')) { 1002 $h.each($.files, function(file){ 1003 if(file.chunks.length && file.chunks[0].status()=='pending' && file.chunks[0].preprocessState === 0) { 1004 file.chunks[0].send(); 1005 found = true; 1006 return(false); 1007 } 1008 if(file.chunks.length>1 && file.chunks[file.chunks.length-1].status()=='pending' && file.chunks[file.chunks.length-1].preprocessState === 0) { 1009 file.chunks[file.chunks.length-1].send(); 1010 found = true; 1011 return(false); 1012 } 1013 }); 1014 if(found) return(true); 1015 } 1016 1017 // Now, simply look for the next, best thing to upload 1018 $h.each($.files, function(file){ 1019 found = file.upload(); 1020 if(found) return(false); 1021 }); 1022 if(found) return(true); 1023 1024 // The are no more outstanding chunks to upload, check is everything is done 1025 var outstanding = false; 1026 $h.each($.files, function(file){ 1027 if(!file.isComplete()) { 1028 outstanding = true; 1029 return(false); 1030 } 1031 }); 1032 if(!outstanding) { 1033 // All chunks have been uploaded, complete 1034 $.fire('complete'); 1035 } 1036 return(false); 1037 }; 1038 1039 1040 // PUBLIC METHODS FOR RESUMABLE.JS 1041 $.assignBrowse = function(domNodes, isDirectory){ 1042 if(typeof(domNodes.length)=='undefined') domNodes = [domNodes]; 1043 $h.each(domNodes, function(domNode) { 1044 var input; 1045 if(domNode.tagName==='INPUT' && domNode.type==='file'){ 1046 input = domNode; 1047 } else { 1048 input = document.createElement('input'); 1049 input.setAttribute('type', 'file'); 1050 input.style.display = 'none'; 1051 domNode.addEventListener('click', function(){ 1052 input.style.opacity = 0; 1053 input.style.display='block'; 1054 input.focus(); 1055 input.click(); 1056 input.style.display='none'; 1057 }, false); 1058 domNode.appendChild(input); 1059 } 1060 var maxFiles = $.getOpt('maxFiles'); 1061 if (typeof(maxFiles)==='undefined'||maxFiles!=1){ 1062 input.setAttribute('multiple', 'multiple'); 1063 } else { 1064 input.removeAttribute('multiple'); 1065 } 1066 if(isDirectory){ 1067 input.setAttribute('webkitdirectory', 'webkitdirectory'); 1068 } else { 1069 input.removeAttribute('webkitdirectory'); 1070 } 1071 var fileTypes = $.getOpt('fileType'); 1072 if (typeof (fileTypes) !== 'undefined' && fileTypes.length >= 1) { 1073 input.setAttribute('accept', fileTypes.map(function (e) { 1074 e = e.replace(/\s/g, '').toLowerCase(); 1075 if(e.match(/^[^.][^/]+$/)){ 1076 e = '.' + e; 1077 } 1078 return e; 1079 }).join(',')); 1080 } 1081 else { 1082 input.removeAttribute('accept'); 1083 } 1084 1085 if (isDirectory) { 1086 input.removeAttribute('accept'); 1087 } 1088 1089 // When new files are added, simply append them to the overall list 1090 input.addEventListener('change', function(e){ 1091 appendFilesFromFileList(e.target.files,e); 1092 var clearInput = $.getOpt('clearInput'); 1093 if (clearInput) { 1094 e.target.value = ''; 1095 } 1096 }, false); 1097 }); 1098 }; 1099 $.assignDrop = function(domNodes){ 1100 if(typeof(domNodes.length)=='undefined') domNodes = [domNodes]; 1101 1102 $h.each(domNodes, function(domNode) { 1103 domNode.addEventListener('dragover', onDragOverEnter, false); 1104 domNode.addEventListener('dragenter', onDragOverEnter, false); 1105 domNode.addEventListener('dragleave', onDragLeave, false); 1106 domNode.addEventListener('drop', onDrop, false); 1107 }); 1108 }; 1109 $.unAssignDrop = function(domNodes) { 1110 if (typeof(domNodes.length) == 'undefined') domNodes = [domNodes]; 1111 1112 $h.each(domNodes, function(domNode) { 1113 domNode.removeEventListener('dragover', onDragOverEnter); 1114 domNode.removeEventListener('dragenter', onDragOverEnter); 1115 domNode.removeEventListener('dragleave', onDragLeave); 1116 domNode.removeEventListener('drop', onDrop); 1117 }); 1118 }; 1119 $.isUploading = function(){ 1120 var uploading = false; 1121 $h.each($.files, function(file){ 1122 if (file.isUploading()) { 1123 uploading = true; 1124 return(false); 1125 } 1126 }); 1127 return(uploading); 1128 }; 1129 $.upload = function(){ 1130 // Make sure we don't start too many uploads at once 1131 if($.isUploading()) return; 1132 // Kick off the queue 1133 $.fire('uploadStart'); 1134 for (var num=1; num<=$.getOpt('simultaneousUploads'); num++) { 1135 $.uploadNextChunk(); 1136 } 1137 }; 1138 $.pause = function(){ 1139 // Resume all chunks currently being uploaded 1140 $h.each($.files, function(file){ 1141 file.abort(); 1142 }); 1143 $.fire('pause'); 1144 }; 1145 $.cancel = function(){ 1146 $.fire('beforeCancel'); 1147 for(var i = $.files.length - 1; i >= 0; i--) { 1148 $.files[i].cancel(); 1149 } 1150 $.fire('cancel'); 1151 }; 1152 $.progress = function(){ 1153 var totalDone = 0; 1154 var totalSize = 0; 1155 // Resume all chunks currently being uploaded 1156 $h.each($.files, function(file){ 1157 totalDone += file.progress()*file.size; 1158 totalSize += file.size; 1159 }); 1160 return(totalSize>0 ? totalDone/totalSize : 0); 1161 }; 1162 $.addFile = function(file, event){ 1163 appendFilesFromFileList([file], event); 1164 }; 1165 $.addFiles = function(files, event){ 1166 appendFilesFromFileList(files, event); 1167 }; 1168 $.removeFile = function(file){ 1169 for(var i = $.files.length - 1; i >= 0; i--) { 1170 if($.files[i] === file) { 1171 $.files.splice(i, 1); 1172 } 1173 } 1174 }; 1175 $.getFromUniqueIdentifier = function(uniqueIdentifier){ 1176 var ret = false; 1177 $h.each($.files, function(f){ 1178 if(f.uniqueIdentifier==uniqueIdentifier) ret = f; 1179 }); 1180 return(ret); 1181 }; 1182 $.getSize = function(){ 1183 var totalSize = 0; 1184 $h.each($.files, function(file){ 1185 totalSize += file.size; 1186 }); 1187 return(totalSize); 1188 }; 1189 $.handleDropEvent = function (e) { 1190 onDrop(e); 1191 }; 1192 $.handleChangeEvent = function (e) { 1193 appendFilesFromFileList(e.target.files, e); 1194 e.target.value = ''; 1195 }; 1196 $.updateQuery = function(query){ 1197 $.opts.query = query; 1198 }; 1199 1200 return(this); 1201 }; 1202 1203 1204 // Node.js-style export for Node and Component 1205 if (typeof module != 'undefined') { 1206 // left here for backwards compatibility 1207 module.exports = Resumable; 1208 module.exports.Resumable = Resumable; 1209 } else if (typeof define === "function" && define.amd) { 1210 // AMD/requirejs: Define the module 1211 define(function(){ 1212 return Resumable; 1213 });
1214 } else { 1215 // Browser: Expose to window 1216 window.Resumable = Resumable; 1217 } 1218 1219})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.