ORIENT/modules/backend/formwidgets/fileupload/assets/js/fileupload.js

329 lines
10 KiB
JavaScript

/*
* File upload form field control
*
* Data attributes:
* - data-control="fileupload" - enables the file upload plugin
* - data-upload-link="a.link" - reference to a trigger to open the file browser window
* - data-upload-input="input" - a file typed html input, this input name will determine the postback variable
* - data-loading-class="loading" - this class is added to the container when file is uploading
* - data-progress-bar=".bar" - reference to a progress bar, it's width is modified when file is uploading
* - data-unique-id="XXX" - an optional identifier for multiple uploaders on the same page, this value will
* appear in the postback variable called X_OCTOBER_FILEUPLOAD
* - data-item-template - a Mustache template to render each item with
*
* JavaScript API:
* $('div').fileUploader()
*
* Dependancies:
* - blueimp File Upload (blueimp/jQuery-File-Upload)
*/
+function ($) { "use strict";
// FILEUPLOAD CLASS DEFINITION
// ============================
var FileUpload = function(element, options) {
this.options = options
this.$el = $(element)
this.editor = null
this.dataCache = null
this.locked = false
this.dropzone = null
var self = this
/*
* Validate requirements
*/
this.isMulti = this.$el.hasClass('is-multi');
this.templateHtml = $('#' + this.options.itemTemplate).html()
this.uploadLink = $(this.options.uploadLink, this.$el)
if (this.options.uniqueId) {
this.options.extraData = $.extend({}, this.options.extraData, { X_OCTOBER_FILEUPLOAD: this.options.uniqueId })
}
/*
* Populate found images
*/
var populated = this.$el.data('populatedData')
if (populated)
$.each(populated, function(index, item){ self.renderItem(item) })
/*
* Bind the uploader
*/
this.bindUploader()
/*
* Set up the progress bar
*/
this.progressBar = $(this.options.progressBar, this.$el).find('>.progress-bar')
/*
* Bind remove link
*/
this.$el.on('click', this.options.removeLink, function(){
var item = $(this).closest('.attachment-item')
$(this).on('ajaxDone', function() {
self.removeItem(item)
})
self.toggleLoading(true, item)
})
/*
* When configuration form is saved, refresh the item
*/
this.$el.on('popupComplete', this.options.configLink, function(event, element, modal){
var popupEl = $(this),
currentItem = popupEl.closest('.attachment-item')
modal.on('ajaxDone', 'button[type=submit]', function(e, context, data){
popupEl.popup('hide')
if (data.error) alert(data.error)
else self.renderItem(data.item, currentItem)
self.toggleLoading(false, currentItem)
}).on('ajaxPromise', 'button[type=submit]', function(){
popupEl.popup('visible', false)
self.toggleLoading(true, currentItem)
})
})
/*
* Sortable items
*/
if (this.$el.hasClass('is-sortable')) {
var placeholderEl = $('<li class="placeholder"/>').css({
width: this.options.imageWidth,
height: this.options.imageHeight
})
this.$el.find('ul, ol').sortable({
itemSelector: 'li:not(.attachment-uploader)',
vertical: false,
tolerance: -100,
placeholder: placeholderEl,
onDrop: function ($item, container, _super) {
_super($item, container)
self.onSortAttachments()
},
distance: 10
})
}
}
FileUpload.prototype.bindUploader = function() {
var self = this
/*
* Build uploader options
*/
var uploaderOptions = {
url: this.options.url,
clickable: this.uploadLink.get(0),
paramName: this.options.paramName,
previewsContainer: $('<div />').get(0),
maxFiles: !this.isMulti ? 1 : null,
method: 'POST'
}
/*
* Bind uploader
*/
this.dropzone = new Dropzone(this.$el.get(0), uploaderOptions)
this.dropzone.on('error', $.proxy(self.onUploadFail, self))
this.dropzone.on('success', $.proxy(self.onUploadComplete, self))
this.dropzone.on('uploadprogress', $.proxy(self.onUploadProgress, self))
this.dropzone.on('thumbnail', $.proxy(self.onUploadThumbnail, self))
this.dropzone.on('sending', function(file, xhr, formData) {
self.addExtraFormData(formData)
self.onUploadStart()
})
}
FileUpload.prototype.addExtraFormData = function(formData) {
if (this.options.extraData) {
$.each(this.options.extraData, function (name, value) {
formData.append(name, value)
})
}
var $form = this.$el.closest('form')
if ($form.length > 0) {
$.each($form.serializeArray(), function (index, field) {
formData.append(field.name, field.value)
})
}
}
FileUpload.prototype.removeItem = function(item) {
if (this.isMulti) {
item.closest('li').fadeOut(500, function(){
item.remove()
})
}
else {
item.find('.active-image, .active-file, .uploader-toolbar').remove()
this.uploadLink.show()
this.toggleLoading(false, item)
// Reset the file counter
this.dropzone.removeAllFiles()
}
}
FileUpload.prototype.renderItem = function(item, replaceItem) {
var newItem = $(Mustache.to_html(this.templateHtml, item))
if (this.isMulti) {
if (replaceItem)
replaceItem.closest('li').replaceWith(newItem)
else
this.uploadLink.closest('li').before(newItem)
$('p', newItem).ellipsis()
return newItem
}
else {
if (replaceItem)
this.removeItem(replaceItem)
this.uploadLink.hide().before(newItem)
$('p', newItem).ellipsis()
return newItem.closest('.attachment-item')
}
}
FileUpload.prototype.onSortAttachments = function() {
if (this.options.sortHandler) {
/*
* Build an object of ID:ORDER
*/
var orderData = {}
this.$el.find('.attachment-item:not(.attachment-uploader)')
.each(function(index){
var id = $(this).data('attachment-id')
orderData[id] = index + 1
})
this.$el.request(this.options.sortHandler, {
data: { sortOrder: orderData }
})
}
}
FileUpload.prototype.onUploadStart = function() {
this.locked = true
this.toggleLoading(true, this.progressBar.closest('.attachment-item'))
this.options.onStart && this.options.onStart()
}
FileUpload.prototype.onUploadThumbnail = function(file, thumbUrl) {
// @todo
}
FileUpload.prototype.onUploadComplete = function(file, data) {
if (data.error)
return this.onUploadFail(null, data.error)
this.options.onComplete && this.options.onComplete()
var newItem = this.renderItem(data)
newItem.css({ opacity: 0 })
.animate({ opacity: 1}, 500)
this.resetAll()
}
FileUpload.prototype.onUploadFail = function(file, error) {
alert('Error uploading file: ' + error)
this.options.onFail && this.options.onFail()
this.resetAll()
}
FileUpload.prototype.onUploadProgress = function(file, progress, bytesSent) {
this.progressBar.css('width', progress + '%')
}
FileUpload.prototype.resetAll = function() {
this.toggleLoading(false, this.progressBar.closest('.attachment-item'))
this.locked = false
}
FileUpload.prototype.toggleLoading = function(isLoading, el) {
var self = this
if (!this.options.loadingClass)
return
if (!el)
el = this.$el
if (isLoading) {
el.addClass(this.options.loadingClass)
} else {
el.removeClass(this.options.loadingClass)
}
this.progressBar.css('width', '0')
}
FileUpload.DEFAULTS = {
url: window.location,
uniqueId: null,
extraData: {},
sortHandler: null,
loadingClass: 'loading',
removeLink: '.uploader-remove',
uploadLink: '.uploader-link',
configLink: '.uploader-config',
progressBar: '.uploader-progress',
onComplete: null,
onStart: null,
onFail: null,
imageWidth: 100,
imageHeight: 100,
itemTemplate: null,
paramName: 'file_data'
}
// FILEUPLOAD PLUGIN DEFINITION
// ============================
var old = $.fn.fileUploader
$.fn.fileUploader = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.fileUpload')
var options = $.extend({}, FileUpload.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.fileUpload', (data = new FileUpload(this, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.fileUploader.Constructor = FileUpload
// FILEUPLOAD NO CONFLICT
// =================
$.fn.fileUploader.noConflict = function () {
$.fn.fileUpload = old
return this
}
// FILEUPLOAD DATA-API
// ===============
$(document).render(function () {
$('[data-control="fileupload"]').fileUploader()
})
}(window.jQuery);