import jQuery.*;
import js.html.*;
enum Mode{
Single;
Prompt;
}
enum Upload{
Empty;
//Canceled;
//Errored;
Pending(file:File);
Processing(file:File);
Finished(file:File, response:Dynamic);
}
enum FileUploaderType{
Droparea(droparea:String);
Selector(field:String, button:String);
DropareaAndSelector(droparea:String, field:String, button:String);
}
typedef FileUploadParam = {
action : String,
method : String,
query : String,
}
typedef Filter = {
type : String,
size : Int,
}
typedef Callback = {
after_upload : String,
}
typedef Option = {
filter : Filter,
callback : Callback,
}
class FileUploader extends Havior {
public static function main(){
Havior.start("system/file_uploader");
}
public override function onLoad(){
var mode = Single;
var type = DropareaAndSelector("#droparea","#file_field","#upload_button");
var param = {
action: getParam("action"),
method: getParam("method"),
query: getParam("query"),
};
var option:Option = {
filter: {
type: getParam("type_filter"),
size: haxe.Json.parse( getParam("size_filter") ),
},
callback: {
after_upload: getParam("after_upload"),
},
};
initialize(mode, type, param, option);
}
private var debug = true;
private var mode : Mode;
private var param : FileUploadParam;
private var option : Option;
private var request : XMLHttpRequest;
private var queue : Array;
private var results : Array< Upload >;
private var uploading : Upload;
public function initialize(mode: Mode, type:FileUploaderType,
param:FileUploadParam, ?option:Option){
this.mode = mode;
this.request = new XMLHttpRequest();
this.param = param;
this.queue = new Array();
this.results = new Array< Upload >();
this.uploading = Empty;
this.option = option;
setRequestEvents();
setFileUploaderEvents(type);
}
private function setRequestEvents(){
this.request.upload.onprogress = function(ev){
var rate = (ev.loaded * 1.0) / ev.total;
onProgressed(rate, ev.loaded, ev.total);
};
this.request.upload.onload = function(ev){
onUploaded();
};
this.request.onreadystatechange = function(ev){
if(this.request.readyState == 4){
var json = haxe.Json.parse(this.request.responseText);
finishUploading(json);
onResponsed(json);
callback(getOption(this.option,"callback.after_upload"), json);
}
};
}
public function setFileUploaderEvents(type:FileUploaderType){
switch(type){
case Droparea(d):
setDropareaEvents(d);
case Selector(f,b):
setSelectorEvents(f,b);
case DropareaAndSelector(d,f,b):
setDropareaEvents(d);
setSelectorEvents(f,b);
}
}
private function setDropareaEvents(droparea:String){
// for miss drop
new JQuery(js.Browser.window).on("dragenter dragover dragexit dragleave drop", function(ev){
ev.stopPropagation();
ev.preventDefault();
});
var area = new JQuery(droparea);
area.on("dragenter dragover", function(ev){
ev.stopPropagation();
ev.preventDefault();
if(mode==Single && this.uploading != Empty){
onEnteredForbidden();
}else{
onEnteredDropArea();
}
});
area.on("dragexit dragleave drop", function(ev){
ev.stopPropagation();
ev.preventDefault();
if(mode==Single && this.uploading != Empty){
onExitedForbidden();
}else{
onExitedDropArea();
}
});
area.on("drop", function(ev){
ev.stopPropagation();
ev.preventDefault();
var files = ev.originalEvent.dataTransfer.files;
if(mode==Single && this.uploading != Empty){
onDroppedForbidden(files);
}else{
onDroppedFiles(files);
setFiles(files);
}
});
}
private function setSelectorEvents(field:String, button:String){
new JQuery(button).click(function(ev){
ev.stopPropagation();
ev.preventDefault();
var files = cast(new JQuery(field).prop("files"), FileList);
if(files.length > 0){
onPressedButton(files);
setFiles(files);
}
});
}
private function setFiles(files:FileList){
this.request.onload = function(ev){ }; // reset callback
switch(this.mode){
case Single:
sendFile(files.item(0));
case Prompt:
enqueueFiles(files);
if(this.uploading == Empty){
this.results = new Array< Upload >();
this.request.onload = function(ev){ promptUpload(); };
promptUpload();
}
}
}
private function enqueueFiles(files:FileList){
for(file in files){
if(applyFilter(file)) queue.push(file);
}
refreshView();
}
private function finishUploading(json: Dynamic){
switch(this.uploading){
case Processing(file):
var x = Finished(file, json);
this.results.push(x);
this.uploading = Empty;
refreshView();
default:
}
}
// for Single
private function sendFile(file:File){
if(this.uploading == Empty){
this.request.onload = function(ev){
onFinished(this.results);
};
this.uploading = Pending(file);
this.results = new Array< Upload >();
refreshView();
send(this.uploading);
}
}
// for Prompt
private function promptUpload(){
if(queue.length > 0){
this.uploading = Pending(queue.shift());
refreshView();
send(this.uploading);
}else{
onFinished(this.results);
}
}
// send a file
private function send(file:Upload){
switch(file){
case Pending(f):
this.uploading = Processing(f);
this.request.open(this.param.method, this.param.action, true);
var token = new JQuery("meta[name=csrf-token]").attr("content");
this.request.setRequestHeader("X-CSRF-Token", token);
var query = this.param.query;
var fd = buildFormData([{name:query, value:f}]);
trace(fd);
this.request.send(fd);
default:
}
}
private function buildFormData(xs:Array<{name:String,value:Dynamic}>){
var fd = untyped __js__("new FormData()");
untyped __js__("for(var x of xs){ fd.append(x.name, x.value); }");
return fd;
}
// filters
private function applyFilter(file:File){
var fs = [ typeFilter, sizeFilter, customFilter ];
for(f in fs){
if(!f(file)) return false;
}
return true;
}
private function typeFilter(file:File){
var type = getOption(this.option, "filter.type");
if(type != null){
var r = new EReg(type, "");
if(!r.match(file.type) ){
return false;
}
}
return true;
}
private function sizeFilter(file:File){
var size = getOption(this.option, "filter.size");
if(size != null){
return file.size <= size;
}
return true;
}
private function refreshView(){
refresh(this.results, this.uploading, this.queue);
}
/// callbacks
public function refresh(results:Array,
uploading:Upload, queue:Array){
if(debug) trace("refresh view");
}
public function customFilter(file:File){
if(debug) trace("apply custom filter");
return true;
}
public function onProgressed(rate:Float, loaded:Int, total:Int){
if(debug) trace("progress:" + (Math.ceil(rate*100)) + "%");
children("#status").html(Math.ceil(rate*100) + "%");
}
public function onUploaded(){
if(debug) trace("uploaded, now saving.");
}
public function onResponsed(response:Dynamic){
if(debug) { trace("responsed:"); trace(response); }
if(response.result == "success"){
children("#image").html("");
}
}
public function onFinished(results:Array< Upload >){
if(debug) { trace("finished:"); trace(results); }
}
public function onEnteredDropArea(){
if(debug) trace("enterd");
}
public function onExitedDropArea(){
if(debug) trace("exited");
}
public function onDroppedFiles(files:FileList){
if(debug){ trace("dropped"); trace(files); }
}
public function onPressedButton(files:FileList){
if(debug) trace("pressed button");
}
public function onEnteredForbidden(){
if(debug) trace("forbidden entered");
}
public function onExitedForbidden(){
if(debug) trace("forbidden exited");
}
public function onDroppedForbidden(files:FileList){
if(debug){ trace("forbidden dropped"); trace(files); }
}
}