1/* 2 * File upload form field control 3 * 4 * Data attributes: 5 * - data-control="fileupload" - enables the file upload plugin 6 * - data-unique-id="XXX" - an optional identifier for multiple uploaders on the same page, this value will 7 * appear in the postback variable called X_OCTOBER_FILEUPLOAD 8 * - data-template - a Dropzone.js template to use for each item 9 * 10 * JavaScript API: 11 * $('div').fileUploader() 12 * 13 * Dependancies: 14 * - Dropzone.js 15 */ 16+function ($) { "use strict"; 17 18 // FILEUPLOAD CLASS DEFINITION 19 // ============================ 20 21 var FileUpload = function (element, options) { 22 this.$el = $(element) 23 this.options = options || {} 24 25 this.init() 26 } 27 28 FileUpload.prototype.init = function() { 29 if (this.options.isMulti === null) { 30 this.options.isMulti = this.$el.hasClass('is-multi') 31 } 32 33 if (this.options.isPreview === null) { 34 this.options.isPreview = this.$el.hasClass('is-preview') 35 } 36 37 this.$uploadButton = $('.upload-button', this.$el) 38 this.$filesContainer = $('.upload-files-container', this.$el) 39 this.uploaderOptions = {} 40 41 this.$el.on('click', '.upload-object.is-success', $.proxy(this.onClickSuccessObject, this)) 42 this.$el.on('click', '.upload-object.is-error', $.proxy(this.onClickErrorObject, this)) 43 44 // Stop here for preview mode 45 if (this.options.isPreview) 46 return 47 48 this.$el.on('click', '.upload-remove-button', $.proxy(this.onRemoveObject, this)) 49 50 this.bindUploader() 51 } 52 53 FileUpload.prototype.dispose = function() { 54 55 this.$el.off('click', '.upload-object.is-success', $.proxy(this.onClickSuccessObject, this)) 56 this.$el.off('click', '.upload-object.is-error', $.proxy(this.onClickErrorObject, this)) 57 this.$el.off('click', '.upload-remove-button', $.proxy(this.onRemoveObject, this)) 58 59 this.$el.removeData('oc.fileUpload') 60 61 this.$el = null 62 this.$uploadButton = null 63 this.$filesContainer = null 64 this.uploaderOptions = null 65 66 // In some cases options could contain callbacks, 67 // so it's better to clean them up too. 68 this.options = null 69 } 70 71 // 72 // Uploading 73 // 74 75 FileUpload.prototype.bindUploader = function() { 76 this.uploaderOptions = { 77 url: this.options.url, 78 paramName: this.options.paramName, 79 clickable: this.$uploadButton.get(0), 80 previewsContainer: this.$filesContainer.get(0), 81 maxFiles: !this.options.isMulti ? 1 : null, 82 headers: {} 83 } 84 85 if (this.options.fileTypes) { 86 this.uploaderOptions.acceptedFiles = this.options.fileTypes 87 } 88 89 if (this.options.template) { 90 this.uploaderOptions.previewTemplate = $(this.options.template).html() 91 } 92 93 if (this.options.uniqueId) { 94 this.uploaderOptions.headers['X-OCTOBER-FILEUPLOAD'] = this.options.uniqueId 95 } 96 97 this.uploaderOptions.thumbnailWidth = this.options.thumbnailWidth 98 ? this.options.thumbnailWidth : null 99 100 this.uploaderOptions.thumbnailHeight = this.options.thumbnailHeight 101 ? this.options.thumbnailHeight : null 102 103 this.uploaderOptions.resize = this.onResizeFileInfo 104 105 /* 106 * Add CSRF token to headers 107 */ 108 var token = $('meta[name="csrf-token"]').attr('content') 109 if (token) { 110 this.uploaderOptions.headers['X-CSRF-TOKEN'] = token 111 } 112 113 this.dropzone = new Dropzone(this.$el.get(0), this.uploaderOptions) 114 this.dropzone.on('addedfile', $.proxy(this.onUploadAddedFile, this)) 115 this.dropzone.on('sending', $.proxy(this.onUploadSending, this)) 116 this.dropzone.on('success', $.proxy(this.onUploadSuccess, this)) 117 this.dropzone.on('error', $.proxy(this.onUploadError, this)) 118 119 var _this = this; 120 121 this.dropzone.on("queuecomplete", function (file) { 122 if (this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0) { 123 $.event.trigger({ 124 uploader: _this, 125 type : "uploadfinished", 126 message : "Dropzone Files upload finished", 127 }); 128 } 129 }); 130 131 this.dropzone.on("sending", function (file) { 132 $.event.trigger({ 133 uploader: _this, 134 type : "uploadstarted", 135 message : "Dropzone File upload started", 136 }); 137 }); 138 139 } 140 141 FileUpload.prototype.onResizeFileInfo = function(file) { 142 var info, 143 targetWidth, 144 targetHeight 145 146 if (!this.options.thumbnailWidth && !this.options.thumbnailWidth) { 147 targetWidth = targetHeight = 100 148 } 149 else if (this.options.thumbnailWidth) { 150 targetWidth = this.options.thumbnailWidth 151 targetHeight = this.options.thumbnailWidth * file.height / file.width 152 } 153 else if (this.options.thumbnailHeight) { 154 targetWidth = this.options.thumbnailHeight * file.height / file.width 155 targetHeight = this.options.thumbnailHeight 156 } 157 158 // drawImage(image, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight) takes an image, clips it to 159 // the rectangle (srcX, srcY, srcWidth, srcHeight), scales it to dimensions (trgWidth, trgHeight), and draws it
160 // on the canvas at coordinates (trgX, trgY). 161 info = { 162 srcX: 0, 163 srcY: 0, 164 srcWidth: file.width, 165 srcHeight: file.height, 166 trgX: 0, 167 trgY: 0, 168 trgWidth: targetWidth, 169 trgHeight: targetHeight 170 } 171 172 return info 173 } 174 175 FileUpload.prototype.onUploadAddedFile = function(file) { 176 var $object = $(file.previewElement).data('dzFileObject', file) 177 178 // Remove any exisiting objects for single variety 179 if (!this.options.isMulti) { 180 this.removeFileFromElement($object.siblings()) 181 } 182 183 this.evalIsPopulated() 184 } 185 186 FileUpload.prototype.onUploadSending = function(file, xhr, formData) { 187 this.addExtraFormData(formData) 188 } 189 190 FileUpload.prototype.onUploadSuccess = function(file, response) { 191 var $preview = $(file.previewElement), 192 $img = $('.image img', $preview) 193 194 $preview.addClass('is-success') 195 196 if (response.id) { 197 $preview.data('id', response.id) 198 $preview.data('path', response.path) 199 $('.upload-remove-button', $preview).data('request-data', { file_id: response.id }) 200 $img.attr('src', response.thumb) 201 } 202 203 /* 204 * Trigger change event (Compatability with october.form.js) 205 */ 206 this.$el.closest('[data-field-name]').trigger('change.oc.formwidget') 207 } 208 209 FileUpload.prototype.onUploadError = function(file, error) { 210 var $preview = $(file.previewElement) 211 $preview.addClass('is-error') 212 } 213 214 FileUpload.prototype.addExtraFormData = function(formData) { 215 if (this.options.extraData) { 216 $.each(this.options.extraData, function (name, value) { 217 formData.append(name, value) 218 }) 219 } 220 221 var $form = this.$el.closest('form') 222 if ($form.length > 0) { 223 $.each($form.serializeArray(), function (index, field) { 224 formData.append(field.name, field.value) 225 }) 226 } 227 } 228 229 FileUpload.prototype.removeFileFromElement = function($element) { 230 var self = this 231 232 $element.each(function() { 233 var $el = $(this), 234 obj = $el.data('dzFileObject') 235 236 if (obj) { 237 self.dropzone.removeFile(obj) 238 } 239 else { 240 $el.remove() 241 } 242 }) 243 } 244 245 // 246 // User interaction 247 // 248 249 FileUpload.prototype.onRemoveObject = function(ev) { 250 var self = this, 251 $object = $(ev.target).closest('.upload-object') 252 253 $(ev.target) 254 .closest('.upload-remove-button') 255 .one('ajaxPromise', function(){ 256 $object.addClass('is-loading') 257 }) 258 .one('ajaxDone', function(){ 259 self.removeFileFromElement($object) 260 self.evalIsPopulated() 261 }) 262 .request() 263 264 ev.stopPropagation() 265 } 266 267 FileUpload.prototype.onClickSuccessObject = function(ev) { 268 // if ($(ev.target).closest('.meta').length) return 269 // 270 // var $target = $(ev.target).closest('.upload-object') 271 // window.open($target.data('path')) 272 } 273 274 FileUpload.prototype.onClickErrorObject = function(ev) { 275 var 276 self = this, 277 $object = $(ev.target).closest('.upload-object'), 278 errorMsg = $('[data-dz-errormessage]', $object).text() 279 280 alert(errorMsg) 281 282 this.removeFileFromElement($object) 283 self.evalIsPopulated() 284 } 285 286 // 287 // Helpers 288 // 289 290 FileUpload.prototype.evalIsPopulated = function() { 291 var isPopulated = !!$('.upload-object', this.$filesContainer).length 292 this.$el.toggleClass('is-populated', isPopulated) 293 294 // Reset maxFiles counter 295 if (!isPopulated) { 296 this.dropzone.removeAllFiles() 297 } 298 } 299 300 FileUpload.DEFAULTS = { 301 url: window.location, 302 uniqueId: null, 303 extraData: {}, 304 paramName: 'file_data', 305 fileTypes: null, 306 template: null, 307 isMulti: null, 308 isPreview: null, 309 thumbnailWidth: 120, 310 thumbnailHeight: 120 311 } 312 313 // FILEUPLOAD PLUGIN DEFINITION 314 // ============================ 315 316 var old = $.fn.fileUploader 317 318 $.fn.fileUploader = function (option) { 319 return this.each(function () { 320 var $this = $(this) 321 var data = $this.data('oc.fileUpload') 322 var options = $.extend({}, FileUpload.DEFAULTS, $this.data(), typeof option == 'object' && option) 323 if (!data) $this.data('oc.fileUpload', (data = new FileUpload(this, options))) 324 if (typeof option == 'string') data[option].call($this) 325 }) 326 } 327 328 $.fn.fileUploader.Constructor = FileUpload 329 330 // FILEUPLOAD NO CONFLICT 331 // ================= 332 333 $.fn.fileUploader.noConflict = function () { 334 $.fn.fileUpload = old 335 return this 336 } 337 338 // FILEUPLOAD DATA-API 339 // =============== 340 $(document).render(function () {
341 $('[data-control="fileupload"]').fileUploader() 342 }) 343 344}(window.jQuery); 345 346 347var uploadDropZones = {}; 348 349$(document).on("uploadstarted", function(event) { 350 var frm = $(event.uploader.dropzone.element).parents('form'); 351 frm.find(':submit').prop('disabled', true); 352}); 353 354var martin; 355$(document).on("uploadfinished", function(event) { 356 var frm = $(event.uploader.dropzone.element).parents('form'); 357 frm.find(':submit').prop('disabled', false); 358 var dz = $(event.uploader.dropzone.element).data('unique-id'); 359 uploadDropZones[dz] = event; 360});
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.