1Cargo.utils = Cargo.utils || {}; 2 3/** 4 * @todo: process fields: totalBytes, uploadedBytes 5 * global progress percentage calculated with the progress from all processes. 6 * 7 */ 8 9Cargo.utils.ImageUploader = function(options){ 10 11 $.extend(this.options = {}, this.defaultOptions, options); 12 13 this.activeProcesses = []; 14 15} 16 17Cargo.utils.ImageUploader.prototype = { 18 19 upload: function(files, pid, customParameters){ 20 21 if(files === undefined){ 22 return undefined; 23 } 24 25 // TODO: handle either a single file or an array of files 26 if(files.length === 0) { 27 return undefined; 28 }; 29 30 var _this = this; 31 32 // wrap all files in a process. This allows for 33 // global progress tracking when uploading an array 34 // of files and easier management of multiple 35 // concurrent processes. Also allows you to swap out processes 36 // between uploader instances or re-upload/re-use a certain process. 37 38 var uploadProcess = { 39 uid: Cargo.Model.Site.GetId(), 40 url: Cargo.Helper.GetUrl(), 41 pid: (pid !== undefined ? pid : Cargo.Helper.GetCurrentProjectId()), 42 fileIndex: -1, 43 aborted: false, 44 totalBytes: 0, 45 uploadedBytes: 0, 46 customParameters: customParameters, 47 getUploadProgress: function(){ 48 return (this.uploadedBytes / this.totalBytes) * 100; 49 }, 50 files: [], 51 events: { 52 onFileComplete: function(file, event){}, 53 onFileProgress: function(file, event){}, 54 onFileFail: function(file, event){}, 55 onFileError: function(file, event){}, 56 onAbort: function(uploadProcess){}, 57 onComplete: function(uploadProcess){} 58 }, 59 getNextFile: function(){ 60 61 if(this.fileIndex < this.files.length - 1 && this.aborted !== true){ 62 return this.files[++this.fileIndex]; 63 } 64 65 return undefined; 66 }, 67 getCurrentFile: function(){ 68 return this.files[this.fileIndex]; 69 }, 70 abort: function(){ 71 72 if(this.xhr !== undefined){ 73 74 this.aborted = true; 75 this.xhr.abort(); 76 this.events.onAbort(this); 77 78 var processIndex = _this.activeProcesses.indexOf(this); 79 80 if(processIndex !== -1){ 81 _this.activeProcesses.splice(processIndex, 1); 82 } 83 84 } 85 86 }, 87 addFile: function(file){ 88 89 if(this.files.indexOf(file) === -1) { 90 91 if(_this.options.acceptFileTypes.test(file.type) && _this.options.acceptFileTypes.test(file.name)){ 92 93 uploadProcess.files.push(file); 94 95 this.totalBytes += file.size; 96 97 } else { 98 99 // TODO: Think this through again. 100 uploadProcess.events.onFileError(file, { 101 message: 'File type not allowed' 102 }); 103 104 } 105 106 } 107 108 } 109 }; 110 111 [].forEach.call(files, function(file){ 112 uploadProcess.addFile(file); 113 }); 114 115 if(uploadProcess.files.length === 0){ 116 uploadProcess = null; 117 return undefined; 118 } 119 120 this.activeProcesses.push(uploadProcess); 121 122 this.handleUploadProcess(uploadProcess); 123 124 return uploadProcess; 125 126 }, 127 128 handleUploadProcess: function(uploadProcess){ 129 130 var file = uploadProcess.getNextFile(), 131 _this = this; 132 133 if(file === undefined) { 134 135 // no more files. 136 uploadProcess.xhr = undefined; 137 uploadProcess.events.onComplete(uploadProcess); 138 139 uploadProcess.fileIndex = -1; 140 141 var processIndex = _this.activeProcesses.indexOf(uploadProcess); 142 143 if(processIndex !== -1){ 144 _this.activeProcesses.splice(processIndex, 1); 145 } 146 147 return; 148 } 149 150 file.lastProgress = 0; 151 152 var formData = new FormData(); 153 154 formData.append('Filedata[]', file); 155 156 formData.append('pid', uploadProcess.pid); 157 formData.append('url', uploadProcess.url); 158 formData.append('uid', uploadProcess.uid); 159 160 if(typeof uploadProcess.customParameters === "object"){ 161 162 Object.keys(uploadProcess.customParameters).forEach(function(key){ 163 164 formData.append(key, uploadProcess.customParameters[key]); 165 166 }); 167 168 } 169 170 uploadProcess.xhr = new Cargo.utils.CargoXHR({ 171 172 url: this.options.posturl, 173 type: 'POST', 174 data: formData, 175 progress: function(e){ 176 177 if(e.lengthComputable){ 178 179 uploadProcess.uploadedBytes += e.loaded - file.lastProgress; 180 file.lastProgress = e.loaded; 181 182 } 183 184 uploadProcess.events.onFileProgress(file, e); 185 186 }, 187 success: function(e){ 188 189 if (this.xhr.status == 200) { 190 191 var response = JSON.parse(this.xhr.responseText); 192 uploadProcess.events.onFileComplete.call(this, file, e); 193 194 } else { 195 196 uploadProcess.events.onFileFail.call(this, file, e); 197 198 } 199 200 // keep going until we run out of files to upload 201 _this.handleUploadProcess(uploadProcess); 202 203 } 204 205 }); 206 207 }, 208 209 defaultOptions: { 210 acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, 211 posturl: '/dispatch/adminproject/handleMultiFileUpload' 212 } 213};
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.