dropzone
Version:
Handles drag and drop of files for you.
408 lines (373 loc) • 14.3 kB
JavaScript
// Generated by CoffeeScript 1.3.3
(function() {
var $, Dropzone, bean, noOp, without,
__slice = [].slice;
$ = ender;
$.ender({
dropzone: function(options) {
var element, _i, _len, _results;
_results = [];
for (_i = 0, _len = this.length; _i < _len; _i++) {
element = this[_i];
_results.push(new Dropzone(element, options));
}
return _results;
}
}, true);
$.domReady(function() {
return $(".dropzone").dropzone();
});
bean = require("bean");
noOp = function() {};
Dropzone = (function() {
var defaultOptions;
Dropzone.prototype.version = "0.3.1";
/*
This is a list of all available events you can register on a dropzone object.
You can register an event handler like this:
var bean = require("bean");
bean.add(dropzone, "dragEnter", function() { });
*/
Dropzone.prototype.events = ["fallback", "drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "thumbnail", "error", "processingfile", "uploadprogress", "finished"];
defaultOptions = {
url: null,
parallelUploads: 2,
maxFilesize: 256,
paramName: "file",
createImageThumbnails: true,
maxThumbnailFilesize: 2,
thumbnailWidth: 100,
thumbnailHeight: 100,
accept: function(file) {
return true;
},
/*
Those functions register themselves to the events on init.
You can overwrite them if you don't like the default behavior. If you just want to add an additional
event handler, register it on the dropzone object and don't overwrite those options.
*/
fallback: function() {
return this.element.find(".message span").html("Your browser does not support drag'n'drop file uploads.");
},
drop: function(e) {
this.element.removeClass("drag-hover");
return this.element.addClass("started");
},
dragstart: function(e) {},
dragend: function(e) {
return this.element.removeClass("drag-hover");
},
dragenter: function(e) {
return this.element.addClass("drag-hover");
},
dragover: function(e) {
return this.element.addClass("drag-hover");
},
dragleave: function(e) {
return this.element.removeClass("drag-hover");
},
addedfile: function(file) {
file.previewTemplate = $(this.options.previewTemplate);
this.element.append(file.previewTemplate);
file.previewTemplate.find(".filename span").text(file.name);
return file.previewTemplate.find(".details").append($("<div class=\"size\">" + (this.filesize(file.size)) + "</div>"));
},
thumbnail: function(file, dataUrl) {
file.previewTemplate.removeClass("file-preview").addClass("image-preview");
return file.previewTemplate.find(".details").append($("<img alt=\"" + file.name + "\" src=\"" + dataUrl + "\"/>"));
},
error: function(file, message) {
file.previewTemplate.addClass("error");
return file.previewTemplate.find(".error-message span").text(message);
},
processingfile: function(file) {
return file.previewTemplate.addClass("processing");
},
uploadprogress: function(file, progress) {
return file.previewTemplate.find(".progress .upload").css({
width: "" + progress + "%"
});
},
finished: function(file) {
return file.previewTemplate.addClass("success");
},
previewTemplate: "<div class=\"preview file-preview\">\n <div class=\"details\"></div>\n <div class=\"progress\"><span class=\"load\"></span><span class=\"upload\"></span></div>\n <div class=\"success-mark\"><span>✔</span></div>\n <div class=\"error-mark\"><span>✘</span></div>\n <div class=\"error-message\"><span></span></div>\n <div class=\"filename\"><span></span></div>\n</div>"
};
defaultOptions.previewTemplate = defaultOptions.previewTemplate.replace(/\n*/g, "");
function Dropzone(element, options) {
var extend;
this.element = $(element);
if (this.element.length !== 1) {
throw new Error("You can only instantiate dropzone on a single element.");
}
if (this.element.data("dropzone")) {
throw new Error("Dropzone already attached.");
}
this.element.data("dropzone", this);
this.elementTagName = this.element.get(0).tagName;
extend = function() {
var key, object, objects, target, val, _i, _len;
target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
for (_i = 0, _len = objects.length; _i < _len; _i++) {
object = objects[_i];
for (key in object) {
val = object[key];
target[key] = val;
}
}
return target;
};
this.options = extend({}, defaultOptions, options != null ? options : {});
if (this.options.url == null) {
this.options.url = this.element.attr("action");
}
this.init();
}
Dropzone.prototype.init = function() {
var _ref;
if (!(window.File && window.FileReader && window.FileList && window.Blob && window.FormData)) {
this.options.fallback.call(this);
return;
}
this.files = [];
this.files.queue = [];
this.files.processing = [];
this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL;
this.setupEventListeners();
if (this.element.find(".message").length === 0) {
return this.element.append($("<div class=\"message\"><span>Drop files here to upload</span></div>"));
}
};
Dropzone.prototype.getFallbackForm = function() {
return $("<form action=\"" + this.options.url + "\" enctype=\"multipart/form-data\" method=\"post\"><input type=\"file\" name=\"newFiles\" multiple=\"multiple\" /><button type=\"submit\">Upload!</button></form>");
};
Dropzone.prototype.setupEventListeners = function() {
var eventName, noPropagation, _i, _len, _ref,
_this = this;
_ref = this.events;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
eventName = _ref[_i];
bean.add(this, eventName, this.options[eventName]);
}
noPropagation = function(e) {
e.stopPropagation();
return e.preventDefault();
};
this.element.on("dragstart", function(e) {
return bean.fire(_this, "dragstart", e);
});
this.element.on("dragenter", function(e) {
noPropagation(e);
return bean.fire(_this, "dragenter", e);
});
this.element.on("dragover", function(e) {
noPropagation(e);
return bean.fire(_this, "dragover", e);
});
this.element.on("dragleave", function(e) {
return bean.fire(_this, "dragleave", e);
});
this.element.on("drop", function(e) {
noPropagation(e);
_this.drop(e);
return bean.fire(_this, "drop", e);
});
return this.element.on("dragend", function(e) {
return bean.fire(_this, "dragend", e);
});
};
Dropzone.prototype.filesize = function(size) {
var string;
if (size >= 100000000000) {
size = size / 100000000000;
string = "tb";
} else if (size >= 100000000) {
size = size / 100000000;
string = "gb";
} else if (size >= 100000) {
size = size / 100000;
string = "mb";
} else if (size >= 100) {
size = size / 100;
string = "kb";
} else {
size = size * 10;
string = "by";
}
return "" + (Math.round(size) / 10) + " " + string;
};
Dropzone.prototype.drop = function(e) {
var files;
if (!e.dataTransfer) {
return;
}
files = e.dataTransfer.files;
if (files.length) {
return this.handleFiles(files);
}
};
Dropzone.prototype.handleFiles = function(files) {
var file, _i, _len;
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
if (this.accept(file)) {
this.addFile(file);
}
}
return this.processQueue();
};
Dropzone.prototype.accept = function(file) {
return this.options.accept.call(this, file);
};
Dropzone.prototype.addFile = function(file) {
this.files.push(file);
this.files.queue.push(file);
bean.fire(this, "addedfile", file);
if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {
return this.createThumbnail(file);
}
};
Dropzone.prototype.createThumbnail = function(file) {
var blobUrl, img,
_this = this;
img = new Image();
blobUrl = this.URL.createObjectURL(file);
img.onerror = img.onabort = function() {
this.URL.revokeObjectURL(blobUrl);
return img = null;
};
img.onload = function() {
var canvas, ctx, srcHeight, srcRatio, srcWidth, srcX, srcY, thumbnail, trgHeight, trgRatio, trgWidth, trgX, trgY;
canvas = document.createElement("canvas");
ctx = canvas.getContext("2d");
srcX = 0;
srcY = 0;
srcWidth = img.width;
srcHeight = img.height;
canvas.width = _this.options.thumbnailWidth;
canvas.height = _this.options.thumbnailHeight;
trgX = 0;
trgY = 0;
trgWidth = canvas.width;
trgHeight = canvas.height;
srcRatio = img.width / img.height;
trgRatio = canvas.width / canvas.height;
if (img.height < canvas.height || img.width < canvas.width) {
trgHeight = srcHeight;
trgWidth = srcWidth;
} else {
if (srcRatio > trgRatio) {
srcHeight = img.height;
srcWidth = srcHeight * trgRatio;
} else {
srcWidth = img.width;
srcHeight = srcWidth / trgRatio;
}
}
srcX = (img.width - srcWidth) / 2;
srcY = (img.height - srcHeight) / 2;
trgY = (canvas.height - trgHeight) / 2;
trgX = (canvas.width - trgWidth) / 2;
ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight);
thumbnail = canvas.toDataURL("image/png");
bean.fire(_this, "thumbnail", [file, thumbnail]);
_this.URL.revokeObjectURL(blobUrl);
return img = null;
};
return img.src = blobUrl;
};
Dropzone.prototype.processQueue = function() {
var i, parallelUploads, processingLength;
parallelUploads = this.options.parallelUploads;
processingLength = this.files.processing.length;
i = processingLength;
while (i < parallelUploads) {
if (!this.files.queue.length) {
return;
}
this.processFile(this.files.queue.shift());
i++;
}
};
Dropzone.prototype.processFile = function(file) {
var fileReader;
fileReader = new FileReader();
this.files.processing.push(file);
bean.fire(this, "processingfile", file);
if (file.size > this.options.maxFilesize * 1024 * 1024) {
return this.errorProcessing(file, "File is too big (" + (Math.round(file.size / 1024 / 10.24) / 100) + "MB). Max filesize: " + this.options.maxFilesize + "MB");
} else {
return this.uploadFile(file);
}
};
Dropzone.prototype.uploadFile = function(file) {
var formData, handleError, input, inputElement, inputName, progressObj, xhr, _i, _len, _ref, _ref1,
_this = this;
xhr = new XMLHttpRequest();
formData = new FormData();
formData.append(this.options.paramName, file);
if (this.elementTagName = "FORM") {
_ref = this.element.find("input, textarea, select, button");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
inputElement = _ref[_i];
input = $(inputElement);
inputName = input.attr("name");
if (!input.attr("type") || input.attr("type").toLowerCase() !== "checkbox" || inputElement.checked) {
formData.append(input.attr("name"), input.val());
}
}
}
xhr.open("POST", this.options.url, true);
handleError = function() {
return _this.errorProcessing(file, xhr.responseText || ("Server responded with " + xhr.status + " code."));
};
xhr.onload = function(e) {
var response;
if (xhr.status !== 200) {
return handleError();
} else {
bean.fire(_this, "uploadprogress", [file, 100]);
response = xhr.responseText;
if (~xhr.getResponseHeader("content-type").indexOf("application/json")) {
response = JSON.parse(response);
}
return _this.finished(file, response, e);
}
};
xhr.onerror = function() {
return handleError();
};
progressObj = (_ref1 = xhr.upload) != null ? _ref1 : xhr;
progressObj.onprogress = function(e) {
return bean.fire(_this, "uploadprogress", [file, Math.max(0, Math.min(100, (e.loaded / e.total) * 100))]);
};
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", file.name);
return xhr.send(formData);
};
Dropzone.prototype.finished = function(file, responseText, e) {
this.files.processing = without(this.files.processing, file);
bean.fire(this, "finished", [file, responseText, e]);
return this.processQueue();
};
Dropzone.prototype.errorProcessing = function(file, message) {
this.files.processing = without(this.files.processing, file);
bean.fire(this, "error", [file, message]);
return this.processQueue();
};
return Dropzone;
})();
without = function(list, rejectedItem) {
var item, _i, _len, _results;
_results = [];
for (_i = 0, _len = list.length; _i < _len; _i++) {
item = list[_i];
if (item !== rejectedItem) {
_results.push(item);
}
}
return _results;
};
}).call(this);