egroupware/etemplate/js/et2_widget_file.js
2012-05-22 20:44:06 +00:00

376 lines
10 KiB
JavaScript

/**
* eGroupWare eTemplate2 - JS Number object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
"use strict";
/*egw:uses
et2_core_inputWidget;
phpgwapi.jquery.jquery.html5_upload;
*/
/**
* Class which implements file upload
*/
var et2_file = et2_inputWidget.extend({
attributes: {
"multiple": {
"name": "Multiple files",
"type": "boolean",
"default": false,
"description": "Allow the user to select more than one file to upload at a time. Subject to browser support."
},
"max_file_size": {
"name": "Maximum file size",
"type": "integer",
"default":"8388608",
"description": "Largest file accepted, in bytes. Subject to server limitations. 8Mb = 8388608"
},
"mime": {
"name": "Allowed file types",
"type": "string",
"default": et2_no_init,
"description": "Mime type (eg: image/png) or regex (eg: /^text\//i) for allowed file types"
},
"blur": {
"name": "Placeholder",
"type": "string",
"default": "",
"description": "This text get displayed if an input-field is empty and does not have the input-focus (blur). It can be used to show a default value or a kind of help-text."
},
"progress": {
"name": "Progress node",
"type": "string",
"default": et2_no_init,
"description": "The ID of an alternate node (div) to display progress and results."
},
"onStart": {
"name": "Start event handler",
"type": "any",
"default": et2_no_init,
"description": "A (js) function called when an upload starts. Return true to continue with upload, false to cancel."
},
"onFinish": {
"name": "Finish event handler",
"type": "any",
"default": et2_no_init,
"description": "A (js) function called when all files to be uploaded are finished."
}
},
asyncOptions: {},
init: function() {
this._super.apply(this, arguments)
this.node = null;
this.input = null;
this.progress = null;
if(!this.options.id) {
console.warn("File widget needs an ID. Used 'file_widget'.");
this.options.id = "file_widget";
}
// Legacy - id ending in [] means multiple
if(this.options.id.substr(-2) == "[]")
{
this.options.multiple = true;
}
// Set up the URL to have the request ID & the widget ID
var instance = this.getInstanceManager();
var self = this;
this.asyncOptions = jQuery.extend({
// Callbacks
onStart: function(event, file_count) {
// Hide any previous errors
self.hideMessage();
return self.onStart(event, file_count);
},
onFinish: function(event, file_count) {
self.onFinish(event, file_count);
// Fire legacy change action when done
self.change(self.input);
},
onStartOne: function(event, file_name, index, file_count) {
// Here 'this' is the input
if(self.checkMime(this.files[index]))
{
return self.createStatus(event,file_name, index,file_count);
}
else
{
// Wrong mime type - show in the list of files
return self.createStatus(
self.egw().lang("File is of wrong type (%1 != %2)!", this.files[index].type, self.options.mime),
file_name
);
}
},
onFinishOne: function(event, response, name, number, total) { return self.finishUpload(event,response,name,number,total);},
onProgress: function(event, progress, name, number, total) { return self.onProgress(event,progress,name,number,total);},
onError: function(event, name, error) { return self.onError(event,name,error);},
sendBoundary: window.FormData || jQuery.browser.mozilla,
beforeSend: function(form) { return self.beforeSend(form);},
url: egw_json_request.prototype._assembleAjaxUrl("etemplate_widget_file::ajax_upload::etemplate"),
autoclear: !(this.options.onchange)
},this.asyncOptions);
this.asyncOptions.fieldName = this.options.id;
this.createInputWidget();
},
destroy: function() {
this.node = null;
this.input = null;
this.progress = null;
},
createInputWidget: function() {
this.node = $j(document.createElement("div")).addClass("et2_file");
this.input = $j(document.createElement("input"))
.attr("type", "file").attr("placeholder", this.options.blur)
.appendTo(this.node);
// Check for File interface, should fall back to normal form submit if missing
if(typeof File != "undefined" && typeof (new XMLHttpRequest()).upload != "undefined")
{
this.input.html5_upload(this.asyncOptions);
}
else
{
// This may be a problem submitting via ajax
}
this.progress = this.options.progress ?
$j(document.getElementById(this.options.progress)) :
$j(document.createElement("div")).appendTo(this.node);
this.progress.addClass("progress");
if(this.options.multiple)
{
this.input.attr("multiple","multiple");
}
this.setDOMNode(this.node[0]);
},
attachToDOM: function() {
this._super.apply(this, arguments);
// Override parent's behaviour to fire legacy change when finished
if (this.onchange)
{
this.input.unbind("change.et2_inputWidget");
}
},
getValue: function() {
var value = this.options.value ? this.options.value : this.input.val();
return value;
},
getInputNode: function() {
return this.input[0];
},
set_mime: function(mime) {
if(!mime)
{
this.options.mime = null;
}
if(mime.indexOf("/") != 0)
{
// Lower case it now, if it's not a regex
this.options.mime = mime.toLowerCase();
}
else
{
// Convert into a js regex
var parts = mime.substr(1).match(/(.*)\/([igm]?)$/)
this.options.mime = new RegExp(parts[1],parts.length > 2 ? parts[2] : "");
}
},
set_multiple: function(_multiple) {
this.options.multiple = _multiple;
if(_multiple)
{
return this.input.attr("multiple", "multiple");
}
return this.input.removeAttr("multiple");
},
/**
* Check to see if the provided file's mimetype matches
*
* @param f File object
* @return boolean
*/
checkMime: function(f) {
// If missing, let the server handle it
if(!this.options.mime || !f.type) return true;
var is_preg = (typeof this.options.mime == "object");
if(!is_preg && f.type.toLowerCase() == this.options.mime || is_preg && this.options.mime.test(f.type))
{
return true;
}
// Not right mime
return false;
},
/**
* Add in the request id
*/
beforeSend: function(form) {
var instance = this.getInstanceManager();
// Form object available, mess with it directly
if(form) {
return form.append("request_id", instance.etemplate_exec_id);
}
// No Form object, add in as string
return 'Content-Disposition: form-data; name="request_id"\r\n\r\n'+instance.etemplate_exec_id+'\r\n';
},
/**
* Disables submit buttons while uploading
*/
onStart: function(event, file_count) {
this.disabled_buttons = $j("input[type='submit'], button").not("[disabled]").attr("disabled", true);
event.data = this;
if(this.options.onStart && typeof this.options.onStart == 'function') return this.options.onStart(event,file_count);
return true;
},
/**
* Re-enables submit buttons when done
*/
onFinish: function(event, file_count) {
this.disabled_buttons.attr("disabled", false);
event.data = this;
if(this.options.onFinish && typeof this.options.onFinish == 'function') return this.options.onFinish(event,file_count);
},
/**
* Creates the elements used for displaying the file, and it's upload status, and
* attaches them to the DOM
*
* @param _event Either the event, or an error message
*/
createStatus: function(_event, file_name, index, file_count) {
var error = (typeof _event == "object" ? "" : _event);
if(this.input[0].files[index]) {
var file = this.input[0].files[index];
if(file.size > this.options.max_file_size) {
error = this.egw().lang("File too large. Maximum %1", et2_vfsSize.prototype.human_size(this.options.max_file_size));
}
}
if(this.progress)
{
var status = $j("<li file='"+file_name+"'>"+file_name
+"<div class='remove'/><span class='progressBar'><p/></span></li>")
.appendTo(this.progress);
$j("div.remove",status).click(this, this.cancel);
if(error != "")
{
status.addClass("message validation_error");
status.append("<div>"+error+"</diff>");
$j(".progressBar",status).css("display", "none");
}
}
return error == "";
},
onProgress: function(event, percent, name, number, total) {
if(this.progress)
{
$j("li[file='"+name+"'] > span.progressBar > p").css("width", Math.ceil(percent*100)+"%");
}
return true;
},
onError: function(event, name, error) {
console.warn(event,name,error);
},
/**
* A file upload is finished, update the UI
*/
finishUpload: function(event, response, name, number, total) {
if(typeof response == 'string') response = jQuery.parseJSON(response);
if(response.response[0] && typeof response.response[0].data.length == 'undefined') {
if(typeof this.options.value != 'object') this.options.value = {};
for(var key in response.response[0].data) {
if(typeof response.response[0].data[key] == "string")
{
// Message from server - probably error
$j("[file='"+name+"']",this.progress)
.addClass("error")
.css("display", "block")
.text(response.response[0].data[key]);
}
else
{
this.options.value[key] = response.response[0].data[key];
if(this.progress)
{
$j("[file='"+name+"']",this.progress).addClass("message success");
}
}
}
}
else if (this.progress)
{
$j("[file='"+name+"']",this.progress)
.addClass("error")
.css("display", "block")
.text(this.egw().lang("Server error"));
}
if(this.options.onFinishOne && typeof this.options.onFinishOne == 'function')
{
return this.options.onFinishOne(event,response,name,number,total);
}
return true;
},
/**
* Cancel a file
*/
cancel: function(e) {
e.preventDefault();
// Look for file name in list
var target = $j(e.target).parents("li.message");
console.info(target);
for(var key in e.data.options.value) {
if(e.data.options.value[key].name == target.attr("file"))
{
delete e.data.options.value[key];
target.remove();
return;
}
}
// In case it didn't make it to the list (error)
target.remove();
$j(e.target).remove();
}
});
et2_register_widget(et2_file, ["file"]);