/**
* Create an drag and drop uploader.
*
* The `.presentationUiDnduploader()` method creates a drag and drop uploader.
*
* Only applicable to <input type="file"> nodes.
*
* ### Examples
* Explanation for example,
*
* JavaScript:
*
*
* <input type="file" name="NewFile" class="ep-js" data-js="de_epages.presentationUiDnduploader({
* multiple:false,
* accept:'image/*',
* data:{
* ChangeAction:'SaveImage',
* ObjectID:#ObjectID,
* SCALE_WIDTH_l:#Shop.ProductImageLargeWidth,
* SCALE_HEIGHT_l:#Shop.ProductImageLargeHeight,
* SCALE_WIDTH_m:200,
* SCALE_WIDTH_s:100,
* SCALE_HEIGHT_s:100,
* SCALE_WIDTH_h:150,
* SCALE_HEIGHT_h:150,
* SCALE_WIDTH_xs:50,
* SCALE_HEIGHT_xs:33,
* SCALE_NAME_m:'ImageMedium',
* SCALE_NAME_s:'ImageSmall',
* SCALE_NAME_h:'ImageHotDeal'
* },
* callback:'?ViewAction='+ep.config.viewAction+'&ObjectID='+ep.config.objectId+'&DialogArea=ViewArea'
* })"/>
*
*
* @class jQuery.ui.presentationUiDnduploader
* @extends jQuery.widget
*
* @uses jQuery.dict
* @uses jQuery.tmpl
* @uses jQuery.i18n
* @uses jQuery.expr
* @uses jQuery.fn.form
* @uses jQuery.ui.widget
* @uses jQuery.ui.progressbar
* @uses de_epages
* @uses ep.ajaxTransport
* @uses ep.ui.dialog
* @since 6.17.38
*/
/**
* @cfg {String} [accept] A string of MIME types and/or MIME type groups.
*/
/**
* @cfg {String} [url] A string containing the URL to which the request is sent.
*/
/**
* @cfg {String} [buttonText] A string for the upload button
*/
/**
* @cfg {Object} [data] A map of additional data to send with the request.
*/
/**
* @cfg {Function,String} [callback] A url to open or method to call when upload process is finished. The callback method receives data and statusText as arguments.
*/
/**
* @cfg {Boolean} [multiple] A boolean indication whether to allow muplitple files to upload.
*/
/**
* @cfg {Boolean} [big] A boolean boolean short handle set style big.
*/
/**
* @cfg {String} [style] One of the available styles: normal,big,icon.
*/
/**
* @cfg {Array} [exists] An array including already existing files.
*/
/**
* See `jQuery.ui.presentationUiDnduploader` for details.
*
* @param {Object} [options] A map of additional options pass to the method.
* @param {String} [accept] A string of MIME types and/or MIME type groups.
* @param {String} [url] A string containing the URL to which the request is sent.
* @param {String} [buttonText] A string for the upload button
* @param {Object} [data] A map of additional data to send with the request.
* @param {Function,String} [callback] A url to open or method to call when upload process is finished. The callback method receives data and statusText as arguments.
* @param {Boolean} [multiple] A boolean indication whether to allow muplitple files to upload.
* @param {Boolean} [big] A boolean boolean short handle set style big.
* @param {String} [style] One of the available styles: normal,big,icon.
* @param {Array} [exists] An array including already existing files.
*
* @method presentationUiDnduploader
* @member jQuery
*
* @since 6.12.0
*/
/*
* @copyright © Copyright 2006-2010, epages GmbH, All Rights Reserved.
*
* @module de_epages.presentation.ui.dnduploader
*/
define( "de_epages/presentation/ui/dnduploader", [
"jquery",
"ep",
"de_epages",
"util/mime",
"$dict!../dictionary",
"$tmpl!./dnduploader",
"$tmpl!./dnduploader-inputs",
"$tmpl!./dnduploader-sortable_item",
"$tmpl!./dnduploader-mainplaceholder",
"$tmpl!./dnduploader-alert",
"jquery/ui/widget",
"jquery/tmpl",
"jquery/ui/sortable",
"ep/alert"
], function ($, ep, de_epages, mime, presentationDict, tmplDndUploader, tmplDndUploaderInputs, tmplDndUploaderSortableItem, tmplDndUploaderMainPlaceholder, tmplDndUploaderAlert) {
/*
* @dictionary ep.dict
*
* @translation {Cancel}
* {Ignore}
* {Replace}
* {AddImages}
* {Notification}
*
* @dictionary de_epages.presentation.dictionary
*
* @translation {FileUploadSingle}
* {FileUploadMultiple}
* {UploaderError}
* {UploaderReplace}
* {FixErrors}
* {FilesUploaded}
* {FileSizeLimitError}
* {InternalServerError}
* {FontLicenseRestricted}
* {MimetypeNotAcceptError}
* {UnknownError}
* {FeatureLimitExceeded}
* {InvalidMP3File}
* {InvalidImageFile}
* {ImageExceedsMegapixels}
* {VirusFoundError}
* {FileAlreadyExistsOnServer}
* {InvalidImageSize}
*/
var translationsDnduploader = {
addImages: presentationDict.translate('AddImages'),
invalidImageFile: presentationDict.translate('InvalidImageFile'),
notification: presentationDict.translate('Notification')
};
$.widget('ui.presentationUiDnduploader', {
files: {
order: [],
exist: [],
upload: [],
mime_error: []
},
// fileCount,
options: {
// files :{exist: [], order: []},
mime: 'image.*',
multiple: true,
// callback: $.noop,
// url: '?',
// data: {
// 'ViewAction': 'JSONViewResponse'
// },
dimensions: {
width: 150,
height: 100
}
},
_create: function () {
var self = this,
elem = self.element,
o = self.options,
renderedTmpl;
renderedTmpl = tmplDndUploader([{
multiple: o.multiple,
order: JSON.stringify((o.files && o.files.order) ? o.files.order : []),
tAddImages: translationsDnduploader.addImages
}]);
// initialize object for dropZone elements
self.dropZone = {};
// load template and push named tmpl elements to current instance
$.extend(self.dropZone, renderedTmpl.tmplItem('elements'));
elem.append(renderedTmpl);
// register handler for drag and drop and file input
// DragOver-Handler
self.dropZone.area.on('dragover', $.proxy(function (e) {
self._handleDragOver(e);
}, self));
// Drop-Handler
self.dropZone.area.on('drop', $.proxy(function (e) {
self._handleDrop(e);
}, self));
// fileInput-Handler
self.dropZone.fileInput.on('change', $.proxy(function (e) {
self._handleFile(e);
}, self));
// initialize sortable list
self._initSortable();
if (o.files) {
$.extend(self.files, o.files);
// inserts already existing files
if (self.files.exist.length) {
// insert regular images
// and hand over a callback that will be executed after all regular images are loaded
self._insertExistingImages(self._handleUploadDataUrlImages);
}
// inserts files handed over as DataURL (only if no regular images exist)
if (self.files.exist.length===0 && self.files.upload.length) {
self._handleUploadDataUrlImages();
}
}
self._setMainImage();
// init options
// self._setOptions({
// 'accept': mime.mime(o.accept || self.elem.attr('accept') || '*/*'),
// 'multiple': o.multiple || self.elem.attr('multiple') || false
// });
},
_insertExistingImages: function (callback) {
var self = this,
// o = self.options,
$img,
i;
self.fileCount = self.files.exist.length;
for (i=0; i<self.files.exist.length; i++) {
$img = $('<img />');
// write dataUrl to image source
$img
.attr('src', self.files.exist[i].src)
.data('id', self.files.exist[i].id);
// load handler for image
$img.on('load', function(evt) {
var $img = $(this),
id = $img.data('id');
// insert item with image to sortable list
self._insertSortableItem($img, id);
// decrease file counter
self.fileCount--;
// an error occurs after the last uploaded element
if (self.fileCount === 0) {
// sort items by order array
self._sortListByArray();
self.dropZone.sortable.sortable('refresh');
self._setMainImage();
// execute callback after all regular images are loaded
if (callback) {
callback.call(self);
}
}
});
}
},
_handleUploadDataUrlImages: function () {
var self = this;
self.fileCount = self.files.upload.length;
if (self.files.upload_order && self.files.upload_order.length) {
// concatinate order and upload order arrays
self.files.order = self.files.order.concat(self.files.upload_order);
}
// loop over upload images
for (var i=0; i<self.files.upload.length; i++) {
// insert image
self._insertDataUrlImage(self.files.upload[i]);
}
},
_initSortable: function () {
var self = this,
o = self.options;
// initialize sortable list
self.dropZone.sortable.sortable({
placeholder: "ep-dropzone-state-highlight AlignCenter col-xs-12 col-md-6 col-lg-4",
items: "li:not(.ep-dropzone-placeholder)",
stop: function(event, ui) {
// rebuild order array
self._sortOrder(self.dropZone.sortable.find('li.ep-dropzone-sortable-item'));
}
});
self.dropZone.sortable.disableSelection();
},
_insertSortableItem: function ($img, id) {
var self = this,
$body = $('body'),
elements = {},
dimensions = {},
itemTmpl;
itemTmpl = tmplDndUploaderSortableItem([{
id: id
}]);
// load template and push named tmpl elements to object
$.extend(elements, itemTmpl.tmplItem('elements'));
// register click handler for delete button
elements.deleteButton.on('click', $.proxy(function (evt) {
self._deleteItemById($(evt.target).data('id'));
}, self));
elements.itemHolder.append($img);
self.dropZone.placeholder.before(itemTmpl);
},
_sortOrder: function (list) {
var self = this,
o = self.options,
uploadOrder = [],
id;
// reset order array
self.files.order.length = 0;
// rebuild order array
list.each(function(key, item){
id = $(item).data('id');
self.files.order.push(id);
// add id of upload image to array
if(ep('#ep-dropzone-inputs-' + id).length) {
uploadOrder.push(id);
}
});
self.dropZone.orderInput.val(JSON.stringify(self.files.order));
self.dropZone.uploadedDataUrlImages.val(JSON.stringify(self.files.upload));
self.dropZone.uploadedDataUrlImagesOrder.val(JSON.stringify(uploadOrder));
self._setMainImage();
},
// sorts sortable list items by order of corresponding order array
// necessary e.g. if depending to the loading duration the order of the sortable list
// is not equal the order of the files.order array
_sortListByArray: function () {
var self = this,
i;
if (self.files && self.files.order && self.files.order.length) {
for (i=0; i<self.files.order.length; i++) {
self.dropZone.placeholder.before($('#ep-dropzone-sortable-item-' + self.files.order[i]));
}
}
},
_setMainImage: function () {
var self = this,
list = self.dropZone.sortable.find('li.ep-dropzone-sortable-item'),
renderedMain,
elements = {};
self.dropZone.mainImage.empty();
if (list.length > 0) {
self.dropZone.mainImage.addClass('with-content');
self.dropZone.mainImage.append($('<img src=' + list.eq(0).find('img').attr('src') + ' />'));
} else {
renderedMain = tmplDndUploaderMainPlaceholder();
// load template and push named tmpl elements to current instance
$.extend(elements, renderedMain.tmplItem('elements'));
self.dropZone.mainImage.append(elements.mainImagePlaceholder);
self.dropZone.mainImage.removeClass('with-content');
}
},
// DnD
_handleDragOver: function(evt) {
evt.stopPropagation();
evt.preventDefault();
// set effect to copy
evt.originalEvent.dataTransfer.dropEffect = 'copy';
},
// Drop-Handler
_handleDrop: function(evt) {
evt.stopPropagation();
evt.preventDefault();
var self = this;
self._manageFiles(evt.originalEvent.dataTransfer.files);
},
_handleFile: function(evt) {
var self = this;
self._manageFiles(evt.originalTarget.files);
},
_manageFiles: function (files) {
var self = this,
o = self.options,
len = o.multiple ? files.length : 1,
i;
// clear error array
self.files.mime_error.length = 0;
self.fileCount = len;
self.imageLength = 0;
for (i=0; i<len; i++) {
self._fileReader(files[i]);
}
},
// FileReader
_fileReader: function(file) {
var self = this,
reader = new FileReader();
reader.onload = function (evt) {
var fileReader = this;
// Handler aufrufen, um die Datei weiter zu verarbeiten
self._readerSuccessHandler(fileReader, file, evt);
};
reader.onloadstart = function (evt) {
if (!file.type.match(self.options.mime)) {
self.files.mime_error.push({
id: "id_" + file.size + new Date().getTime(),
name: file.name
});
self.fileCount--;
// an error occurs and only one element should be uploaded
if (self.fileCount === 0 && self.imageLength === 0) {
var list = [];
for(var j=0; j<self.files.mime_error.length; j++) {
list.push(self.files.mime_error[j].name);
}
// reset file input
self.dropZone.fileInput.val("");
// open message dialog
self._messageDialog(translationsDnduploader.invalidImageFile, list);
}
reader.abort();
return;
}
};
// reader.onprogress = readerProgressHandler;
// reader.onerror = readerErrorHandler;
// read file as dataUrl
reader.readAsDataURL(file);
},
_readerSuccessHandler: function(fileReader, file, evt) {
var self = this,
o = self.options,
id = "id_" + file.size + new Date().getTime(),
fileData;
self.imageLength++;
fileData = {
id: id,
name: file.name.replace(/_/g, "-"),
dataUrl: fileReader.result
};
// write informations for file on object
self.files.upload.push(fileData);
self._insertDataUrlImage(fileData);
},
_insertDataUrlImage: function (fileData) {
var self = this,
o = self.options,
$img = $('<img />');
// write dataUrl to image source
$img.attr('src', fileData.dataUrl);
// load handler for image
$img.on('load', function(evt) {
var $img = $(this),
inputTmpl;
// insert item with image to sortable list
self._insertSortableItem($img, fileData.id);
// insert input template for image information
inputTmpl = tmplDndUploaderInputs([fileData]);
self.dropZone.holder.append(inputTmpl);
self.fileCount--;
// an error occurs after the last uploaded element
if (self.fileCount === 0) {
// if an upload array is handed over initially
if (self.files.upload_order && self.files.upload_order.length) {
self._sortListByArray();
// delete order property for upload array to avoid sorting more than one time
delete self.files.upload_order;
}
self.dropZone.sortable.sortable('refresh');
// rebuild order array
self._sortOrder(self.dropZone.sortable.find('li.ep-dropzone-sortable-item'));
// reset file input
self.dropZone.fileInput.val("");
self._setMainImage();
// if an error occurs during the readAsDataUrl method
if (self.files.mime_error.length > 0) {
var list = [],
alertTmpl;
for(var j=0; j<self.files.mime_error.length; j++) {
list.push(self.files.mime_error[j].name);
}
// open message dialog
self._messageDialog(translationsDnduploader.invalidImageFile, list);
}
}
});
},
_messageDialog: function(info, messages) {
var self = this,
alertTmpl;
// render alert template
alertTmpl = tmplDndUploaderAlert([{
messages: messages,
info: info
}]);
// if already an alert dialog exists
if (self.alert) {
// clean up previous dialog and append new message
self.alert.find('.Message')
.empty()
.append(alertTmpl);
// open alert dialog
self.alert.uiDialog('open');
} else {
// show alert
self.alert = ep.alert({
title: translationsDnduploader.notification,
type: 'notification',
size: 'l',
content: alertTmpl,
close: function( event ){
// do something on close
},
ok: function( event ){
// do something on confirm
}
});
}
},
_readerProgressHandler: function() {
// ToDo: Handler für die Fortschrittsanzeige
//console.log("readerProgressHandler: ", arguments);
},
_readerErrorHandler: function() {
// ToDo: Handler für den Fehlerfall
//console.log("readerErrorHandler: ", arguments);
},
// Helper
_recalcDimensions: function(dimensions) {
var self = this,
o = self.options,
factor;
dimensions.origWidth = dimensions.width;
dimensions.origHeight = dimensions.height;
if (dimensions.width > o.dimensions.width) {
factor = o.dimensions.width / dimensions.width;
dimensions.width = o.dimensions.width;
dimensions.height = dimensions.height * factor;
}
if (dimensions.height > o.dimensions.height) {
factor = o.dimensions.height / dimensions.height;
dimensions.height = o.dimensions.height;
dimensions.width = dimensions.width * factor;
}
dimensions.width = Math.floor(dimensions.width);
dimensions.height = Math.floor(dimensions.height);
return dimensions;
},
// _setOption: function (name, value) {
// var o = this.options;
// if (value !== undef) {
// switch (name) {
// case 'accept':
// this.elem.attr(name, mime.mime(value));
// break;
// case 'multiple':
// this.elem.attr(name, value);
// this._setOption('style', o.style);
// break;
// }
// }
// return this._superApply(arguments);
// },
_upload: function (event, doReplace) {
var self = this,
o = self.options;
},
_getItemById: function (id) {
var self = this,
i;
// loop over already existing images
for (i=0; i<self.files.exist.length; i++) {
if (self.files.exist[i].id === id){
// return self.files.exist[i];
return {array: self.files.exist, index: i};
}
}
// loop over new images
for (i=0; i<self.files.upload.length; i++) {
if (self.files.upload[i].id === id){
// return self.files.upload[i];
return {array: self.files.upload, index: i};
}
}
return false;
},
_deleteItemById: function (id) {
var self = this,
item,
i;
// remove container for image information input from DOM
$('#ep-dropzone-inputs-' + id).remove();
// delete item from files array
item = self._getItemById(id);
item.array.splice(item.index, 1);
// remove item from sortable list
$('#ep-dropzone-sortable-item-' + id).remove();
// refresh sortable list
self.dropZone.sortable.sortable('refresh');
// rebuild order array
self._sortOrder(self.dropZone.sortable.find('li.ep-dropzone-sortable-item'));
// ToDo: initiate AJAX request by sending file id to remove image from server
// ! Only if image is in exits array
},
/**
* This method removes the container on which the Uploader has been appended to.
*
* @method destroy
* @member jQuery.ui.presentationUiUploader
*
* @since 6.12.0
*/
destroy: function () {
var self = this;
self.elem.off('.presentationUiDnduploader');
self._superApply(arguments);
}
});
return de_epages;
});