aolists-webtop
Version:
Web interface for aoLists
851 lines (792 loc) • 26.7 kB
JavaScript
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
* Ext.ux.FileUploader
*
* @author Ing. Jozef Sakáloš
* @version $Id: Ext.ux.FileUploader.js 302 2008-08-03 20:57:33Z jozo $
* @date 15. March 2008
*
* @license Ext.ux.FileUploader is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/
/*global Ext */
/**
* @class Ext.ux.FileUploader
* @extends Ext.util.Observable
* @constructor
*/
Ext.ux.FileUploader = function (config) {
Ext.apply(this, config);
// call parent
Ext.ux.FileUploader.superclass.constructor.apply(this, arguments);
// ADDED BY ECANDIDUS
if (!this.store) {
// create store
// fields for record
var fields = [{
name: 'id',
type: 'text',
system: true
}, {
name: 'shortName',
type: 'text',
system: true
}, {
name: 'fileName',
type: 'text',
system: true
}, {
name: 'filePath',
type: 'text',
system: true
}, {
name: 'fileCls',
type: 'text',
system: true
}, {
name: 'input',
system: true
}, {
name: 'form',
system: true
}, {
name: 'state',
type: 'text',
system: true
}, {
name: 'error',
type: 'text',
system: true
}, {
name: 'progressId',
type: 'int',
system: true
}, {
name: 'bytesTotal',
type: 'int',
system: true
}, {
name: 'bytesUploaded',
type: 'int',
system: true
}, {
name: 'estSec',
type: 'int',
system: true
}, {
name: 'filesUploaded',
type: 'int',
system: true
}, {
name: 'speedAverage',
type: 'int',
system: true
}, {
name: 'speedLast',
type: 'int',
system: true
}, {
name: 'timeLast',
type: 'int',
system: true
}, {
name: 'timeStart',
type: 'int',
system: true
}, {
name: 'pctComplete',
type: 'int',
system: true
}
// ADDED BY ECANDIDUS
, {
name: 'passback',
type: 'text',
system: true
}
// END
];
// add custom fields if passed
if (Ext.isArray(this.customFields)) {
fields.push(this.customFields);
}
// create store
this.store = new Ext.data.SimpleStore({
id: 0,
fields: fields,
data: []
});
// Add update
this.addToStore = function (id, values) {
var rec = new this.store.recordType(values, id);
rec.commit();
this.store.add(rec);
};
// Add reset
this.clearStore = function () {
this.store.removeAll();
};
}
// END
// add events
// {{{
this.addEvents(
/**
* @event beforeallstart
* Fires before an upload (of all files) is started. Return false to cancel the event.
* @param {Ext.ux.FileUploader} this
*/
'beforeallstart'
/**
* @event allfinished
* Fires after upload (of all files) is finished
* @param {Ext.ux.FileUploader} this
*/
, 'allfinished'
/**
* @event beforefilestart
* Fires before the file upload is started. Return false to cancel the event.
* Fires only when singleUpload = false
* @param {Ext.ux.FileUploader} this
* @param {Ext.data.Record} record upload of which is being started
*/
, 'beforefilestart'
/**
* @event filefinished
* Fires when file finished uploading.
* Fires only when singleUpload = false
* @param {Ext.ux.FileUploader} this
* @param {Ext.data.Record} record upload of which has finished
*/
, 'filefinished'
/**
* @event progress
* Fires when progress has been updated
* @param {Ext.ux.FileUploader} this
* @param {Object} data Progress data object
* @param {Ext.data.Record} record Only if singleUpload = false
*/
, 'progress'
);
// }}}
}; // eo constructor
Ext.extend(Ext.ux.FileUploader, Ext.util.Observable, {
// configuration options
// {{{
/**
* @cfg {Object} baseParams baseParams are sent to server in each request.
*/
baseParams: {
cmd: 'upload',
dir: '.'
}
/**
* @cfg {Boolean} concurrent true to start all requests upon upload start, false to start
* the next request only if previous one has been completed (or failed). Applicable only if
* singleUpload = false
*/
,
concurrent: true
/**
* @cfg {Boolean} enableProgress true to enable querying server for progress information
*/
,
enableProgress: true
/**
* @cfg {String} jsonErrorText Text to use for json error
*/
,
jsonErrorText: 'Cannot decode JSON object'
/**
* @cfg {Number} Maximum client file size in bytes
*/
,
maxFileSize: 524288
/**
* @cfg {String} progressIdName Name to give hidden field for upload progress identificator
*/
,
progressIdName: 'UPLOAD_IDENTIFIER'
/**
* @cfg {Number} progressInterval How often (in ms) is progress requested from server
*/
,
progressInterval: 2000
/**
* @cfg {String} progressUrl URL to request upload progress from
*/
,
progressUrl: 'progress.php'
/**
* @cfg {Object} progressMap Mapping of received progress fields to store progress fields
*/
,
progressMap: {
bytes_total: 'bytesTotal',
bytes_uploaded: 'bytesUploaded',
est_sec: 'estSec',
files_uploaded: 'filesUploaded',
speed_average: 'speedAverage',
speed_last: 'speedLast',
time_last: 'timeLast',
time_start: 'timeStart'
}
/**
* @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
*/
,
singleUpload: false
/**
* @cfg {Ext.data.Store} store Mandatory. Store that holds files to upload
*/
/**
* @cfg {String} unknownErrorText Text to use for unknow error
*/
,
unknownErrorText: 'Unknown error'
/**
* @cfg {String} url Mandatory. URL to upload to
*/
// }}}
// private
// {{{
/**
* uploads in progress count
* @private
*/
,
upCount: 0
// }}}
// methods
// {{{
/**
* creates form to use for upload.
* @private
* @return {Ext.Element} form
*/
,
createForm: function (record) {
var progressId = parseInt(Math.random() * 1e10, 10);
var form = Ext.getBody().createChild({
tag: 'form',
action: this.url,
method: 'post',
cls: 'x-hidden'
// MODIFIED BY ECANDIDUS
,
id: Ext.id()
// END
,
cn: [{
tag: 'input',
type: 'hidden',
name: 'APC_UPLOAD_ID',
value: this.objid
}, {
tag: 'input',
type: 'hidden',
name: 'APC_UPLOAD_PROGRESS',
value: progressId
}, {
tag: 'input',
type: 'hidden',
name: this.progressIdName,
value: progressId
}, {
tag: 'input',
type: 'hidden',
name: 'MAX_FILE_SIZE',
value: this.maxFileSize
}]
});
if (record) {
record.set('form', form);
record.set('progressId', progressId);
} else {
this.progressId = progressId;
}
return form;
} // eo function createForm
// }}}
// {{{
,
deleteForm: function (form, record) {
form.remove();
if (record) {
record.set('form', null);
}
} // eo function deleteForm
// }}}
// {{{
/**
* Fires event(s) on upload finish/error
* @private
*/
,
fireFinishEvents: function (options) {
if (true !== this.eventsSuspended && !this.singleUpload) {
this.fireEvent('filefinished', this, options && options.record);
}
if (true !== this.eventsSuspended && 0 === this.upCount) {
this.stopProgress();
this.fireEvent('allfinished', this);
}
} // eo function fireFinishEvents
// }}}
// {{{
/**
* Geg the iframe identified by record
* @private
* @param {Ext.data.Record} record
* @return {Ext.Element} iframe or null if not found
*/
,
getIframe: function (record) {
var iframe = null;
var form = record.get('form');
if (form && form.dom && form.dom.target) {
iframe = Ext.get(form.dom.target);
}
return iframe;
} // eo function getIframe
// }}}
// {{{
/**
* returns options for Ajax upload request
* @private
* @param {Ext.data.Record} record
* @param {Object} params params to add
*/
,
getOptions: function (record, params) {
var o = {
url: this.url,
method: 'post',
isUpload: true,
scope: this,
callback: this.uploadCallback,
record: record,
params: this.getParams(record, params)
};
return o;
} // eo function getOptions
// }}}
// {{{
/**
* get params to use for request
* @private
* @return {Object} params
*/
,
getParams: function (record, params) {
var p = {
path: this.path
};
Ext.apply(p, this.baseParams || {}, params || {});
return p;
}
// }}}
// {{{
/**
* processes success response
* @private
* @param {Object} options options the request was called with
* @param {Object} response request response object
* @param {Object} o decoded response.responseText
*/
,
processSuccess: function (options, response, o) {
var record = false;
// all files uploadded ok
if (this.singleUpload) {
this.store.each(function (r) {
r.set('state', 'done');
r.set('error', '');
// ADDED BY ECANDIDUS
r.set('passback', o.passback);
// END
r.commit();
});
} else {
record = options.record;
record.set('state', 'done');
record.set('error', '');
// ADDED BY ECANDIDUS
record.set('passback', o.passback);
// END
record.commit();
}
this.deleteForm(options.form, record);
} // eo processSuccess
// }}}
// {{{
/**
* processes failure response
* @private
* @param {Object} options options the request was called with
* @param {Object} response request response object
* @param {String/Object} error Error text or JSON decoded object. Optional.
*/
,
processFailure: function (options, response, error) {
var record = options.record;
var records;
// singleUpload - all files uploaded in one form
if (this.singleUpload) {
// some files may have been successful
records = this.store.queryBy(function (r) {
var state = r.get('state');
return 'done' !== state && 'uploading' !== state;
});
records.each(function (record) {
var e = error.errors ? error.errors[record.id] : this.unknownErrorText;
if (e) {
record.set('state', 'failed');
record.set('error', e);
Ext.getBody().appendChild(record.get('input'));
} else {
record.set('state', 'done');
record.set('error', '');
}
record.commit();
}, this);
this.deleteForm(options.form);
}
// multipleUpload - each file uploaded in it's own form
else {
if (error && 'object' === Ext.type(error)) {
record.set('error', error.errors && error.errors[record.id] ? error.errors[record.id] : this.unknownErrorText);
} else if (error) {
record.set('error', error);
} else if (response && response.responseText) {
record.set('error', response.responseText);
} else {
record.set('error', this.unknownErrorText);
}
record.set('state', 'failed');
record.commit();
}
} // eof processFailure
// }}}
// {{{
/**
* Delayed task callback
*/
,
requestProgress: function () {
var records, p;
// ADDED BY ECANDIDUS
if (this.progressUrl) {
var o = {
url: this.progressUrl,
method: 'post',
params: {},
scope: this,
callback: function (options, success, response) {
var o;
if (true !== success) {
return;
}
try {
o = Ext.decode(response.responseText);
} catch (e) {
return;
}
if ('object' !== Ext.type(o) || true !== o.success) {
return;
}
if (this.singleUpload) {
this.progress = {};
for (p in o) {
if (this.progressMap[p]) {
this.progress[this.progressMap[p]] = parseInt(o[p], 10);
}
}
if (true !== this.eventsSuspended) {
this.fireEvent('progress', this, this.progress);
}
} else {
for (p in o) {
if (this.progressMap[p] && options.record) {
options.record.set(this.progressMap[p], parseInt(o[p], 10));
}
}
if (options.record) {
options.record.commit();
if (true !== this.eventsSuspended) {
this.fireEvent('progress', this, options.record.data, options.record);
}
}
}
this.progressTask.delay(this.progressInterval);
}
};
if (this.singleUpload) {
o.params[this.progressIdName] = this.progressId;
o.params.APC_UPLOAD_PROGRESS = this.progressId;
Ext.Ajax.request(o);
} else {
records = this.store.query('state', 'uploading');
records.each(function (r) {
o.params[this.progressIdName] = r.get('progressId');
o.params.APC_UPLOAD_PROGRESS = o.params[this.progressIdName];
o.record = r;
(function () {
Ext.Ajax.request(o);
}).defer(250);
}, this);
}
}
} // eo function requestProgress
// }}}
// {{{
/**
* path setter
* @private
*/
,
setPath: function (path) {
this.path = path;
} // eo setPath
// }}}
// {{{
/**
* url setter
* @private
*/
,
setUrl: function (url) {
this.url = url;
} // eo setUrl
// }}}
// {{{
/**
* Starts progress fetching from server
* @private
*/
,
startProgress: function () {
if (!this.progressTask) {
this.progressTask = new Ext.util.DelayedTask(this.requestProgress, this);
}
this.progressTask.delay.defer(this.progressInterval / 2, this.progressTask, [this.progressInterval]);
} // eo function startProgress
// }}}
// {{{
/**
* Stops progress fetching from server
* @private
*/
,
stopProgress: function () {
if (this.progressTask) {
this.progressTask.cancel();
}
} // eo function stopProgress
// }}}
// {{{
/**
* Stops all currently running uploads
*/
,
stopAll: function () {
var records = this.store.query('state', 'uploading');
records.each(this.stopUpload, this);
} // eo function stopAll
// }}}
// {{{
/**
* Stops currently running upload
* @param {Ext.data.Record} record Optional, if not set singleUpload = true is assumed
* and the global stop is initiated
*/
,
stopUpload: function (record) {
// single abord
var iframe = false;
if (record) {
iframe = this.getIframe(record);
this.stopIframe(iframe);
this.upCount--;
this.upCount = 0 > this.upCount ? 0 : this.upCount;
record.set('state', 'stopped');
this.fireFinishEvents({
record: record
});
}
// all abort
else if (this.form) {
iframe = Ext.fly(this.form.dom.target);
this.stopIframe(iframe);
this.upCount = 0;
this.fireFinishEvents();
}
} // eo function abortUpload
// }}}
// {{{
/**
* Stops uploading in hidden iframe
* @private
* @param {Ext.Element} iframe
*/
,
stopIframe: function (iframe) {
if (iframe) {
try {
iframe.dom.contentWindow.stop();
iframe.remove.defer(250, iframe);
} catch (e) {}
}
} // eo function stopIframe
// }}}
// {{{
/**
* Main public interface function. Preforms the upload
*/
,
upload: function () {
var records = this.store.queryBy(function (r) {
return 'done' !== r.get('state');
});
if (!records.getCount()) {
return;
}
// fire beforeallstart event
if (true !== this.eventsSuspended && false === this.fireEvent('beforeallstart', this)) {
return;
}
if (this.singleUpload) {
this.uploadSingle();
} else {
records.each(this.uploadFile, this);
}
if (true === this.enableProgress) {
this.startProgress();
}
} // eo function upload
// }}}
// {{{
/**
* called for both success and failure. Does nearly nothing
* @private
* but dispatches processing to processSuccess and processFailure functions
*/
,
uploadCallback: function (options, success, response) {
var o;
this.upCount--;
this.form = false;
// process ajax success
if (true === success) {
// ADDED BY ECANDIDUS
var start = response.responseText.indexOf('{');
if (start != -1) {
response.responseText = response.responseText.substring(start);
var end = response.responseText.lastIndexOf('}');
if (end != -1) {
response.responseText = response.responseText.substring(0, end + 1);
}
}
// END
try {
o = Ext.decode(response.responseText);
} catch (e) {
this.processFailure(options, response, this.jsonErrorText);
this.fireFinishEvents(options);
return;
}
// process command success
// CHANGED BY ECANDIDUS
if (true === o.success || !o.err) {
//if (true === o.success) {
// END
this.processSuccess(options, response, o);
}
// process command failure
else {
this.processFailure(options, response, o);
}
}
// process ajax failure
else {
this.processFailure(options, response);
}
this.fireFinishEvents(options);
} // eo function uploadCallback
// }}}
// {{{
/**
* Uploads one file
* @param {Ext.data.Record} record
* @param {Object} params Optional. Additional params to use in request.
*/
,
uploadFile: function (record, params) {
// fire beforestart event
if (true !== this.eventsSuspended && false === this.fireEvent('beforefilestart', this, record)) {
return;
}
// create form for upload
var form = this.createForm(record);
// append input to the form
var inp = record.get('input');
inp.set({
name: inp.id
});
form.appendChild(inp);
// get params for request
var o = this.getOptions(record, params);
o.form = form;
// set state
record.set('state', 'uploading');
record.set('pctComplete', 0);
// increment active uploads count
this.upCount++;
// request upload
Ext.Ajax.request(o);
// todo:delete after devel
this.getIframe.defer(100, this, [record]);
} // eo function uploadFile
// }}}
// {{{
/**
* Uploads all files in single request
*/
,
uploadSingle: function () {
// get records to upload
var records = this.store.queryBy(function (r) {
return 'done' !== r.get('state');
});
if (!records.getCount()) {
return;
}
// create form and append inputs to it
var form = this.createForm();
records.each(function (record) {
var inp = record.get('input');
inp.set({
name: inp.id
});
form.appendChild(inp);
record.set('state', 'uploading');
}, this);
// create options for request
var o = this.getOptions();
o.form = form;
// save form for stop
this.form = form;
// increment active uploads counter
this.upCount++;
// request upload
Ext.Ajax.request(o);
} // eo function uploadSingle
// }}}
}); // eo extend
// register xtype
Ext.reg('fileuploader', Ext.ux.FileUploader);
// eof