/**
* Project : AMDA-NG
* Name : TimeTableUI.js
* @class amdaUI.TimeTableUI
* @extends Ext.container.Container
* @brief Time Table Module UI definition (View)
* @author Myriam
* @version $Id: TimeTableUI.js 2075 2014-02-11 11:30:14Z elena $
*/
Ext.define('amdaUI.TimeTableUI', {
extend: 'Ext.container.Container',
alias: 'widget.panelTimeTable',
requires: [
'Ext.ux.grid.FiltersFeature',
'Ext.ux.grid.filter.DateFilter',
'Ext.ux.grid.filter.NumericFilter',
'amdaUI.OperationsTT',
'amdaUI.StatisticalPlug',
'Ext.grid.plugin.BufferedRenderer'
],
statics: {
COL_TO_HIDE : 'colToHide'
},
status: null,
constructor: function(config) {
this.init(config);
this.callParent(arguments);
// load object into view
this.loadObject();
},
/**
* set the current editing object
* this method will be used on timetable edition when this win is already opened
*/
setObject : function (object)
{
// set object
this.object = object;
// load object into view
this.loadObject();
// show the default duration column
this.TTGrid.headerCt.getGridColumns();
Ext.Array.each(this.TTGrid.headerCt.getGridColumns(), function(item,index,all){
// if item is the default duration column
if ( item.id == amdaUI.TimeTableUI.COL_TO_HIDE+'2' ) {
// show this column
item.show();
}
});
// fire the refresh event (to statistical plugin)
this.fireEvent("refresh");
// global event
myDesktopApp.EventManager.fireEvent("refresh");
},
/**
* load object timetable into this view
*/
loadObject : function(){
// load object into form
this.formPanel.getForm().loadRecord(this.object);
this.status = null;
//
var me = this;
var onAfterInit = function(result, e) {
if (!result || !result.success)
{
if (result.message)
myDesktopApp.errorMsg(result.message);
else
myDesktopApp.errorMsg('Unknown error during cache initialisation');
return;
}
me.TTGrid.getSelectionModel().deselectAll();
// clear filters
me.TTGrid.getStore().clearFilter(true);
//clear sort
me.TTGrid.getStore().sorters.clear();
//me.TTGrid.getStore().sorters = new Ext.util.MixedCollection();
//set cache token to the Time Table object
me.object.set('cacheToken', result.token);
me.TTGrid.getStore().load();
me.status = result.status;
//Statistical plugin
me.fireEvent("refresh");
};
if (this.object.get('fromPlugin'))
{
if (this.object.get('objFormat') && this.object.get('objFormat') != '')
{
//From uploaded file
AmdaAction.initTTCacheFromUploadedFile(this.object.get('objName'), this.object.get('objFormat'), false, onAfterInit);
}
else
{
//From tmp object (ie Search result)
AmdaAction.initTTCacheFromTmpObject(this.object.get('folderId'), this.object.get('objName'), false, onAfterInit);
}
}
else
{
var typeTT = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id).linkedNode.data.nodeType;
if (this.object.get('id') == '')
{
//Init empty cache
AmdaAction.initTTCache(false,0,onAfterInit);
}
else
{
//From existing TT file
AmdaAction.initTTCacheFromTT(this.object.get('id'), typeTT, onAfterInit);
}
}
},
/**
* update this.object from form
*/
updateObject : function()
{
this.updateCount();
// get the basic form
var basicForm = this.formPanel.getForm();
var updateStatus = true;
var fieldsWithoutName = basicForm.getFields().items;
Ext.Array.each(fieldsWithoutName, function(item, index,allItems){
if(item !== this.fieldName) {
if (!item.isValid()) {
// set update isn't allowed
updateStatus = false;
}
}
}, this);
// if the update is allowed
if (updateStatus) {
/// real object update
// update TimeTable object with the content of form
basicForm.updateRecord(this.object);
}
// return the update status
return updateStatus;
},
updateCount : function()
{
this.object.set('nbIntervals',this.TTGrid.getStore().getTotalCount());
this.formPanel.getForm().findField('nbIntervals').setValue(this.object.get('nbIntervals'));
},
/*
* save method called by Save button
*/
saveProcess : function(toRename)
{
var timeTableModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id);
// if the name has been modified this is a creation
if (timeTableModule.contextNode && (timeTableModule.contextNode.data.id == 'sharedtimeTable-treeRootNode'))
{
timeTableModule.linkedNode = null;
timeTableModule.createLinkedNode();
timeTableModule.createObject(this.object.getJsonValues());
var ttobj = timeTableModule.linkedNode.get('object');
// synchronisation of objects
this.object = ttobj;
timeTableModule.linkedNode.create();
}
else if (this.fclose()) /*TimeTable object has been modified*/
{
if (this.object.isModified('name') || this.object.get('fromPlugin'))
{
// if object already has an id : it's a 'rename' of an existing TimeTable
if (this.object.get('id'))
{
// the context Node is the parent node of current edited one
var contextNode = timeTableModule.linkedNode.parentNode;
// link a new node to the TimeTableModule
timeTableModule.createLinkedNode();
// set the contextNode
timeTableModule.linkedNode.set('contextNode',contextNode);
// create a new object linked
timeTableModule.createObject(this.object.getJsonValues());
var ttobj = timeTableModule.linkedNode.get('object');
// synchronisation of objects
this.object = ttobj;
if (toRename) timeTableModule.linkedNode.toRename = true;
}
timeTableModule.linkedNode.create({callback : function ($action) {
if (timeTableModule.linkedNode.get('object').get('fromPlugin'))
timeTableModule.linkedNode.get('object').set('fromPlugin',false);
timeTableModule.linkedNode.update();},
scope : this});
} else {
//update
timeTableModule.linkedNode.update();
}
}
},
/**
* overwrite metod called by Save button
*/
overwriteProcess : function(btn)
{
if (btn == 'cancel') return;
this.fieldName.clearInvalid();
this.saveProcess(true);
},
addInterval : function(start, stop)
{
var row = this.TTGrid.getStore().getTotalCount();
var me = this;
this.TTGrid.getSelectionModel().deselectAll();
AmdaAction.addTTCacheInterval({'start' : start, 'stop' : stop, 'index' : row},function (result, e) {
this.status = result.status;
this.TTGrid.getStore().reload({
callback : function(records, options, success) {
me.TTGrid.getView().bufferedRenderer.scrollTo(row, false, function() {
me.TTGrid.getView().select(row);
}, me);
}
});
}, this);
},
init : function(config)
{
this.object = config.object;
this.fieldName = new Ext.form.field.Text({
fieldLabel: 'Name*',
allowBlank : false,
stripCharsRe: /(^\s+|\s+$)/g,
emptyText: 'Please no spaces!',
name: 'name',
anchor: '100%',
validateOnChange: false,
validateOnBlur: false,
validFlag: false,
validator : function() {
return this.validFlag;
}
});
this.formPanel = new Ext.form.Panel({
bodyStyle: {background : '#dfe8f6'},
id: 'formTimeTable',
flex: 4,
model : 'amdaModel.TimeTable',
trackResetOnLoad : true, // reset to the last loaded record
border : false,
fieldDefaults: { labelWidth: 80 },
items: [
this.fieldName,
{
xtype: 'fieldcontainer',
layout: 'hbox',
fieldLabel:'Creation date',
items: [
{
xtype:'datefield', width: 180,
name: 'created', disabled: true,
hideTrigger: true, format: 'Y/m/d H:i:s'
},
{ xtype:'component', width: 20 },
{ xtype:'displayfield', value: 'Intervals:', width: 50 },
{ xtype:'component', width: 8 },
{ xtype:'textfield', name: 'nbIntervals', disabled: true, width: 70 }
]
},
{
xtype: 'textarea',
name: 'description',
fieldLabel: 'Description',
anchor: '100% 50%'
},
{
xtype: 'textarea',
name: 'history',
fieldLabel: 'Operation log',
anchor: '100% 30%'
}
]
});
var store = Ext.create('Ext.data.Store', {
model: 'amdaModel.Interval',
autoDestroy: false,
pageSize : 200,
buffered : true,
autoLoad: true,
purgePageCount: 0,
remoteSort: true,
listeners: {
load: function(store,records) {
// alert('nb of records in store:'+records.length );
myDesktopApp.EventManager.fireEvent('refresh');
this.TTGrid.getView().refresh();
this.TTGrid.getSelectionModel().refresh();
this.updateCount();
//Statistical plugin
this.fireEvent("refresh");
},
prefetch : function(store, records, successful, operation, eOpts) {
if (operation && (operation.action == 'read'))
{
if (operation.response && operation.response.result && operation.response.result.success)
this.status = operation.response.result.status;
}
},
remove: function(store) {
this.updateCount();
//Statistical plugin
this.fireEvent("refresh");
},
add: function(store) {
this.updateCount();
//Statistical plugin
this.fireEvent("refresh");
},
datachanged: function(store){
this.updateCount();
//Statistical plugin
this.fireEvent("refresh");
},
scope : this
}
});
var filters = {
ftype: 'filters',
encode: true, // json encode the filter query
local: false, // defaults to false (remote filte
filters: [
{ type: 'numeric', dataIndex: 'durationHour'},
{ type: 'numeric', dataIndex: 'durationMin'},
{ type: 'numeric', dataIndex: 'durationSec'},
{ type: 'date', dataIndex: 'start', dateFormat: 'Y-m-d'},
{ type: 'date', dataIndex: 'stop', dateFormat: 'Y-m-d' }
]
};
var cellEditing = Ext.create('Ext.grid.plugin.CellEditing',{
onEditComplete : function(ed, value, startValue) {
var me = this,
activeColumn = me.getActiveColumn(),
context = me.context,
record;
if (activeColumn) {
record = context.record;
me.setActiveEditor(null);
me.setActiveColumn(null);
me.setActiveRecord(null);
context.value = value;
if (!me.validateEdit()) {
me.editing = false;
return;
}
// Only update the record if the new value is different than the
// startValue. When the view refreshes its el will gain focus
if (!record.isEqual(value, startValue)) {
var obj = null;
if (activeColumn.dataIndex == 'start')
obj = {
'cacheId' : record.get('cacheId'),
'start' : value
};
else if (activeColumn.dataIndex == 'stop')
obj = {
'cacheId' : record.get('cacheId'),
'stop' : value
};
else
{
me.editing = false;
return;
}
//context.grid.getSelectionModel().deselectAll();
//Interval is modified on the server side
me.editing = true;
AmdaAction.modifyTTCacheInterval(obj, function (result, e) {
var ttModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id);
if (ttModule)
ttModule.getUiContent().status = result.status;
context.grid.getSelectionModel().deselectAll();
context.store.reload({
callback : function(records, options, success) {
context.view.bufferedRenderer.scrollTo(context.rowIdx, true, function() {
me.fireEvent('edit', me, context);
me.editing = false;
}, me);
}
});
}, this);
}
else
me.editing = false;
}
}
});
this.TTGrid = Ext.create('Ext.grid.Panel', {
store : store,
features: [filters],
columnLines: true,
columns: [
{
xtype: 'rownumberer',
width: 50,
renderer: function(value, metaData, record, row, col, store, gridView){
var msg = record.index;
if (record.get('isNew') || record.get('isModified'))
{
msg += ' *';
metaData.style = 'font-weight: bold'
}
return msg;
}
},
{
header: 'Start Time', dataIndex: 'start', width: 120,
editor: { xtype:'datefield', allowBlank:false, hideTrigger: true, format : 'Y-m-d\\TH:i:s'},
renderer: function(value){
if (value != null) {
if(Ext.isDate(value)){
return Ext.Date.format(value, 'Y-m-d\\TH:i:s');
} else {
return Ext.Date.format(new Date (value), 'Y-m-d\\TH:i:s');
}
} else {
return value;
}
}
},
{
header: 'Stop Time', dataIndex: 'stop', width: 120,
editor: { xtype: 'datefield', allowBlank: false, hideTrigger: true, format : 'Y-m-d\\TH:i:s'},
renderer: function(value) {
if (value != null) {
if(Ext.isDate(value)){
return Ext.Date.format(value, 'Y-m-d\\TH:i:s');
} else {
return Ext.Date.format(new Date (value), 'Y-m-d\\TH:i:s');
}
} else {
return value;
}
}
},
{
header: 'Duration (hour)', width: 120, dataIndex: 'durationHour',
id: amdaUI.TimeTableUI.COL_TO_HIDE+'1',
hidden: true,
renderer: function(value) {
return Ext.util.Format.number(value,'0.00');
},
listeners: {
beforeshow : function(){
Ext.Array.each(this.ownerCt.getGridColumns(), function(item,index,all){
// if item is a column to hide automatically
if ( Ext.util.Format.substr(item.id, 0, amdaUI.TimeTableUI.COL_TO_HIDE.length) == amdaUI.TimeTableUI.COL_TO_HIDE ) {
// if item isn't the column which is being declared and is not hidden
if ( item.id != amdaUI.TimeTableUI.COL_TO_HIDE+'1' && !item.isHidden() ){
// hide this column
item.hide();
}
}
});
}
}
},
{
header: 'Duration (min)', width: 120, dataIndex: 'durationMin',
id: amdaUI.TimeTableUI.COL_TO_HIDE+'2',
renderer: function(value) {
return Ext.util.Format.number(value,'0.00');
},
listeners: {
beforeshow : function(){
Ext.Array.each(this.ownerCt.getGridColumns(), function(item,index,all){
// if item is a column to hide automatically
if ( Ext.util.Format.substr(item.id, 0, amdaUI.TimeTableUI.COL_TO_HIDE.length) == amdaUI.TimeTableUI.COL_TO_HIDE ) {
// if item isn't the column which is being declared and is not hidden
if ( item.id != amdaUI.TimeTableUI.COL_TO_HIDE+'2' && !item.isHidden() ){
// hide this column
item.hide();
}
}
});
}
}
},
{
header: 'Duration (sec)', width: 120, dataIndex: 'durationSec',
id: amdaUI.TimeTableUI.COL_TO_HIDE+'3',
hidden: true,
renderer: function(value) {
return Ext.util.Format.number(value,'0.00');
},
listeners: {
beforeshow : function(){
Ext.Array.each(this.ownerCt.getGridColumns(), function(item,index,all){
// if item is a column to hide automatically
if ( Ext.util.Format.substr(item.id, 0, amdaUI.TimeTableUI.COL_TO_HIDE.length) == amdaUI.TimeTableUI.COL_TO_HIDE ) {
// if item isn't the column which is being declared and is not hidden
if ( item.id != amdaUI.TimeTableUI.COL_TO_HIDE+'3' && !item.isHidden() ){
// hide this column
item.hide();
}
}
});
}
}
}
],
frame: true,
dockedItems: [{
xtype: 'toolbar',
items: [{
iconCls: 'icon-add',
scope: this,
handler: function(){
cellEditing.cancelEdit();
var selection = this.TTGrid.getView().getSelectionModel().getSelection()[0];
var row = 0;
if (selection)
row = store.indexOf(selection) + 1;
this.TTGrid.getSelectionModel().deselectAll();
var me = this;
AmdaAction.addTTCacheInterval({'index' : row}, function (result, e) {
this.status = result.status;
this.TTGrid.getStore().reload({
callback : function(records, options, success) {
me.TTGrid.getView().bufferedRenderer.scrollTo(row, false, function() {
me.TTGrid.getView().select(row);
cellEditing.startEditByPosition({row: row, column: 1});
}, me);
}
});
}, this);
}
}, {
iconCls: 'icon-delete',
disabled: true,
itemId: 'delete',
scope: this,
handler: function(){
var selection = this.TTGrid.getView().getSelectionModel().getSelection()[0];
if (selection) {
var rowId = selection.get('cacheId');
this.TTGrid.getSelectionModel().deselectAll();
AmdaAction.removeTTCacheIntervalFromId(rowId, false, function (result, e) {
this.status = result.status;
this.TTGrid.getStore().reload();
}, this);
}
}
},
'->',
{
text: 'Clear Filters',
scope: this,
handler: function () {
this.TTGrid.getStore().clearFilter(true);
}
}
]
}],
plugins: [ cellEditing, {ptype : 'bufferedrenderer'} ],
listeners : {
scope : this,
edit : function(editor,e) {
if (e.record.get('stop') != null && e.record.get('start') != null) {
e.record.set('durationHour', (e.record.get('stop') - e.record.get('start'))/3600000.0);
e.record.set('durationMin', (e.record.get('stop') - e.record.get('start'))/60000.0);
e.record.set('durationSec', (e.record.get('stop') - e.record.get('start'))/1000.0);
// send refresh event to statistical plugin
this.fireEvent("refresh");
}
}
}
});
this.TTGrid.getSelectionModel().on('selectionchange', function(selModel,selections){
this.TTGrid.down('#delete').setDisabled(selections.length === 0);
}, this);
var myConf = {
layout: 'border',
defaults: { layout: 'fit', border: false },
items : [
{
xtype: 'form',
region: 'center',
buttonAlign: 'left',
bodyStyle: {background : '#dfe8f6'},
padding: '5 5 5 5',
layout: {type: 'hbox', pack: 'start', align: 'stretch'},
items: [
{
xtype: 'container',
flex: 3.6,
layout: {type: 'vbox', pack: 'start', align: 'stretch'},
items: [
this.formPanel,
{
xtype: 'operationsTT',
parent: this,
flex: 2.5,
id: 'operation'
}
]
},
{
xtype: 'container',
border: false,
padding: '0 0 5 15',
flex: 4,
layout: 'fit',
items: [ this.TTGrid ]
}
],
fbar:[
{
xtype: 'button',
text: 'Save',
width: 65,
scope : this,
handler: function () {
if (this.updateObject()){
var basicForm = this.formPanel.getForm();
// if there's at least one record in the store of TTGrid
if (this.TTGrid.getStore().getTotalCount()>0) {
// update TimeTable object which the content of form
basicForm.updateRecord(this.object);
var me = this;
this.checkIntervalsStatusForSave(function () {
//Name validation
var ttModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id);
if (!ttModule)
return;
ttModule.linkedNode.isValidName(me.fieldName.getValue(), function (res) {
if (!res)
{
me.fieldName.validFlag = 'Error during object validation';
myDesktopApp.errorMsg(me.fieldName.validFlag);
me.fieldName.validate();
return;
}
if (!res.valid)
{
if (res.error)
{
if (res.error.search('subtree') != -1) {
Ext.MessageBox.show({title:'Warning',
msg: res.error+'
Do you want to overwrite it?',
width: 300,
buttons: Ext.MessageBox.OKCANCEL,
fn : me.overwriteProcess,
icon: Ext.MessageBox.WARNING,
scope : me
});
me.fieldName.validFlag = true;
}
else
me.fieldName.validFlag = res.error;
}
else
{
me.fieldName.validFlag = 'Invalid object name';
myDesktopApp.errorMsg(me.fieldName.validFlag);
}
me.fieldName.validate();
return;
}
me.fieldName.validFlag = true;
me.fieldName.validate();
me.saveProcess(false);
});
});
} else {
// warning:
Ext.Msg.alert('No intervals', 'Your time table is invalid,
you must have at least one interval');
}
}
}
},{
xtype: 'button',
text: 'Reset',
width: 65,
scope: this,
handler: function() {
var ttModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id);
ttModule.createLinkedNode();
ttModule.createObject();
this.setObject(ttModule.getLinkedNode().get('object'));
}
}
]
},
{
xtype: 'panel', region: 'south',
title: 'Information',
collapsible: true,
collapseMode: 'header',
height: 100,
autoHide: false,
iconCls: 'icon-information',
bodyStyle: 'padding:5px',
loader: {
autoLoad: true,
url: helpDir+'timetableHOWTO'
}
}
],
plugins: [ {ptype: 'statisticalPlugin'} ]
};
Ext.apply (this , Ext.apply (arguments, myConf));
},
checkIntervalsStatusForSave : function(onStatusOk) {
if (this.status == null)
return;
if (this.status.nbValid <= 0)
{
myDesktopApp.errorMsg('Your time table is invalid,
you must have at least one valid interval');
return;
}
var msg = '';
if (this.status.nbInvalid > 0)
msg += 'There are some invalid intervals. Only valid intervals will be saved!
';
if (this.status.nbFiltered > 0)
msg += 'There are some filtered intervals. Filtered intervals will not be saved!
';
if (msg != '')
{
msg += 'Do you want to continue?';
Ext.Msg.show({
title:'Warning!',
msg: msg,
buttons: Ext.Msg.OKCANCEL,
fn: function(btnId){
if (btnId==='cancel'){
// cancel the save action
} else {
onStatusOk();
}
},
scope: this,
icon: Ext.Msg.WARNING
});
return;
}
onStatusOk();
},
/**
* Check if changes were made before closing window
* @return true if changes
*/
fclose : function() {
if (this.status == null)
return false;
var isDirty = this.formPanel.getForm().isDirty() || (this.status.isModified) || (this.status.nbModified > 0) || (this.status.nbNew > 0);
return isDirty;
}
});