(function() {
var util = YAHOO.util, lang = YAHOO.lang, Dom = util.Dom, Event = util.Event, msgs = inputEx.messages;
/**
* Create an editable datatable
* @class inputEx.widget.DataTable
* @constructor
* @param {Object} options Options:
* <ul>
* <li>parentEl: DOMelement in which we have to insert the datatable</li>
*
* <li>datasource (or datasourceConfig)</li>
* <li>datatableOpts: additionnal datatable options</li>
* <li>fields: inputEx fields</li>
* <li>dialogLabel: title of the dialog</li>
* <li>columnDefs: YUI datatable columnDefs</li>
*
* <li>id: (optional, default is autogenerated) sets the id of the div wrapper around the widget</li>
* <li>allowInsert: adds the 'Insert' button (optional, default true)</li>
* <li>allowModify: default true</li>
* <li>allowDelete: default true</li>
* <li>showHideColumnsDlg: add a link to a dialog to show/hide columns</li>
* <li>panelConfig: (optional) YUI's dialog panelConfig object</li>
*
* </ul>
*/
inputEx.widget.DataTable = function(options) {
this.setOptions(options);
this.render();
this.initEvents();
};
inputEx.widget.DataTable.prototype = {
/**
* Set the options
*/
setOptions: function(options) {
this.options = {};
this.options.id = options.id || Dom.generateId();
this.options.parentEl = lang.isString(options.parentEl) ? Dom.get(options.parentEl) : options.parentEl;
this.options.columnDefs = options.columnDefs;
this.options.allowInsert = lang.isUndefined(options.allowInsert) ? true : options.allowInsert;
this.options.allowModify = lang.isUndefined(options.allowModify) ? true : options.allowModify;
this.options.allowDelete = lang.isUndefined(options.allowDelete) ? true : options.allowDelete;
this.options.showHideColumnsDlg = lang.isUndefined(options.showHideColumnsDlg) ? false : options.showHideColumnsDlg;
this.options.datasource = options.datasource;
// Create a datasource if it does not exist, from the datasourceConfig Object
if(!options.datasource && options.datasourceConfig) {
var ds = new YAHOO.util.DataSource(options.datasourceConfig.url), fields = [];
if(options.datasourceConfig.keys) {
for ( var i = 0 ; i < options.datasourceConfig.keys.length ; i++ ) {
fields.push({ key: options.datasourceConfig.keys[i] });
}
}
ds.responseType = options.datasourceConfig.responseType || YAHOO.util.DataSource.TYPE_JSON;
ds.responseSchema = options.datasourceConfig.responseSchema || { resultsList : "Result", fields : fields};
this.options.datasource = ds;
}
this.options.datatableOpts = options.datatableOpts;
this.options.fields = options.fields;
this.options.dialogId = options.dialogId || null;
this.options.dialogLabel = options.dialogLabel || "";
this.options.panelConfig = options.panelConfig || {
constraintoviewport: true,
underlay:"shadow",
close:true,
fixedcenter: true,
visible:true,
draggable:true,
modal: true
};
},
/**
* Init the events
*/
initEvents: function() {
// Call the rendering method when the container is available
Event.onAvailable(this.options.id, this.renderDatatable, this, true);
// Table options
if(this.options.showHideColumnsDlg) {
Event.addListener(this.tableOptions, 'click', this.showTableOptions, this, true);
}
/**
* @event Event fired when an item is removed
* @param {YAHOO.widget.Record} Removed record
* @desc YAHOO custom event fired when an item is removed
*/
this.itemRemovedEvt = new util.CustomEvent('itemRemoved', this);
/**
* @event Event fired when an item is added
* @param {YAHOO.widget.Record} Added record
* @desc YAHOO custom event fired when an item is added
*/
this.itemAddedEvt = new util.CustomEvent('itemAdded', this);
/**
* @event Event fired when an item is modified
* @param {YAHOO.widget.Record} Modified record
* @desc YAHOO custom event fired when an item is modified
*/
this.itemModifiedEvt = new util.CustomEvent('itemModified', this);
},
/**
* Render the main container only (not the datatable)
*/
render: function() {
/**
* Main container
*/
this.element = inputEx.cn('div', {id: this.options.id });
if(this.options.showHideColumnsDlg) {
this.renderShowHideColumnsDlg();
}
// append it immediatly to the parent DOM element
this.options.parentEl.appendChild(this.element);
},
/**
* Render the datatable
*/
renderDatatable: function() {
var columndefs = this.setColumnDefs();
/**
* YUI's datatable instance
*/
this.datatable = new YAHOO.widget.DataTable(this.element, columndefs, this.options.datasource, this.options.datatableOpts);
this.datatable.subscribe('cellClickEvent', this._onCellClick, this, true);
// Automatically set up the paginator
if(this.options.datatableOpts && this.options.datatableOpts.paginator) {
this.datatable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
if(oPayload) {
oPayload.totalRecords = oResponse.meta.totalRecords;
}
return oPayload;
};
}
// Insert button
if ( this.options.allowInsert ){
this.insertButton = inputEx.cn('input', {type:'button', value:msgs.insertItemText}, null, null);
Event.addListener(this.insertButton, 'click', this.onInsertButton, this, true);
this.options.parentEl.appendChild(this.insertButton);
}
// Set up editing flow
var highlightEditableCell = function(oArgs) {
var elCell = oArgs.target;
if(Dom.hasClass(elCell, "yui-dt-editable") || Dom.hasClass(elCell,"yui-dt-col-delete") || Dom.hasClass(elCell,"yui-dt-col-modify") ) {
this.highlightCell(elCell);
}
};
// Locals
this.datatable.set("MSG_LOADING", msgs.loadingText );
this.datatable.set("MSG_EMPTY", msgs.emptyDataText );
this.datatable.set("MSG_ERROR", msgs.errorDataText );
this.datatable.subscribe("cellMouseoverEvent", highlightEditableCell);
this.datatable.subscribe("cellMouseoutEvent", this.datatable.onEventUnhighlightCell);
},
/**
* Set the column definitions, create them if none from the fields, adds the modify and delete buttons
*/
setColumnDefs: function() {
var columndefs = this.options.columnDefs || this.fieldsToColumndefs(this.options.fields);
// Adding modify column if we use form editing and if allowModify is true
if(this.options.allowModify ) {
columndefs = columndefs.concat([{
key:'modify',
label:' ',
formatter:function(elCell) {
elCell.innerHTML = msgs.modifyText;
elCell.style.cursor = 'pointer';
}
}]);
}
// Adding delete column
if(this.options.allowDelete) {
columndefs = columndefs.concat([{
key:'delete',
label:' ',
formatter:function(elCell) {
elCell.innerHTML = msgs.deleteText;
elCell.style.cursor = 'pointer';
}
}]);
}
return columndefs;
},
/**
* Render the dialog for row edition
*/
renderDialog: function() {
var that = this;
this.dialog = new inputEx.widget.Dialog({
id: this.options.dialogId,
inputExDef: {
type: 'form',
fields: this.options.fields,
buttons: [
{type: 'submit', value: msgs.saveText, onClick: function() { that.onDialogSave(); return false; /* prevent form submit */} },
{type: 'link', value: msgs.cancelText, onClick: function() { that.onDialogCancel(); } }
]
},
title: this.options.dialogLabel,
panelConfig: this.options.panelConfig
});
// Add a listener on the closing button and hook it to onDialogCancel()
YAHOO.util.Event.addListener(that.dialog.close,"click",function(){
that.onDialogCancel();
},that);
},
/**
* When saving the Dialog
*/
onDialogSave: function() {
var newvalues, record;
//Validate the Form
if ( !this.dialog.getForm().validate() ) return ;
// Update the record
if(!this.insertNewRecord){
// Update the row
newvalues = this.dialog.getValue();
this.datatable.updateRow( this.selectedRecord , newvalues );
// Get the new record
record = this.datatable.getRecord(this.selectedRecord);
// Fire the modify event
this.itemModifiedEvt.fire(record);
}
// Adding new record
else{
// Insert a new row
this.datatable.addRow({});
// Set the Selected Record
var rowIndex = this.datatable.getRecordSet().getLength() - 1;
this.selectedRecord = rowIndex;
// Update the row
newvalues = this.dialog.getValue();
this.datatable.updateRow( this.selectedRecord , newvalues );
// Get the new record
record = this.datatable.getRecord(this.selectedRecord);
// Fire the add event
this.itemAddedEvt.fire(record);
}
this.dialog.hide();
},
/**
* When canceling the Dialog
*/
onDialogCancel: function(){
this.insertNewRecord = false;
this.dialog.hide();
},
/**
* Handling cell click events
*/
_onCellClick: function(ev,args) {
var target = Event.getTarget(ev);
var column = this.datatable.getColumn(target);
var rowIndex = this.datatable.getTrIndex(target);
if (column.key == 'delete') {
if (confirm(msgs.confirmDeletion)) {
var record = this.datatable.getRecord(target);
if(this.editingNewRecord) {
this.editingNewRecord = false;
}
else {
this.itemRemovedEvt.fire( record );
}
this.datatable.deleteRow(target);
this.hideSubform();
}
}
else if(column.key == 'modify') {
this.onClickModify(rowIndex);
}
else {
this.onCellClick(ev,rowIndex);
}
},
/**
* Public cell click handler
*/
onCellClick: function(ev, rowIndex) {
},
/**
* Opens the Dialog to edit the row
* Called when the user clicked on modify button
*/
onClickModify: function(rowIndex) {
if(!this.dialog) {
this.renderDialog();
}
// NOT Inserting new record
this.insertNewRecord = false;
// Set the selected Record
this.selectedRecord = rowIndex;
// Get the selected Record
var record = this.datatable.getRecord(this.selectedRecord);
this.dialog.whenFormAvailable({
fn: function() {
this.dialog.setValue(record.getData());
this.dialog.show();
},
scope: this
});
},
/**
* Insert button event handler
*/
onInsertButton: function(e) {
if(!this.dialog) {
this.renderDialog();
}
// Inserting new record
this.insertNewRecord = true;
this.dialog.whenFormAvailable({
fn: function() {
this.dialog.getForm().clear();
this.dialog.show();
},
scope: this
});
},
/**
* Remove the record that has not been saved
*/
removeUnsavedRecord: function(record) {
this.datatable.deleteRow(record);
},
/**
* Cancel row edition
*/
onCancelForm: function(e) {
Event.stopEvent(e);
this.hideSubform();
if(this.editingNewRecord) {
this.removeUnsavedRecord();
this.editingNewRecord = false;
}
},
/**
* Convert an inputEx fields definition to a DataTable columns definition
*/
fieldsToColumndefs: function(fields) {
var columndefs = [];
for(var i = 0 ; i < fields.length ; i++) {
columndefs.push( this.fieldToColumndef(fields[i]) );
}
return columndefs;
},
/**
* Convert a single inputEx field definition to a DataTable column definition
*/
fieldToColumndef: function(field) {
var key, label, colmunDef;
// Retro-compatibility with inputParms
if (lang.isObject(field.inputParams)) {
key = field.inputParams.name;
label = field.inputParams.label;
// New prefered way to set options of a field
} else {
key = field.name;
label = field.label;
}
columnDef = {
key: key,
label: label,
sortable: true,
resizeable: true
};
// Field formatter
if(field.type == "date") {
columnDef.formatter = YAHOO.widget.DataTable.formatDate;
}
else if(field.type == "integer" || field.type == "number") {
columnDef.formatter = YAHOO.widget.DataTable.formatNumber;
/*columnDef.sortOptions = {
defaultDir: "asc",
sortFunction: // TODO: sort numbers !!!
}*/
}
// TODO: other formatters
return columnDef;
},
/**
* Render the dialog (+link) to show/hide columns
*/
renderShowHideColumnsDlg: function() {
this.tableOptions = inputEx.cn('a', {href: '#'}, null, msgs.tableOptions);
this.options.parentEl.appendChild(this.tableOptions);
this.tableOptionsDlg = new YAHOO.widget.SimpleDialog( Dom.generateId(), {
width: "30em",
visible: false,
modal: true,
buttons: [
{ text:msgs.columnDialogCloseButton, handler: function(e) { this.hide(); } }
],
fixedcenter: true,
constrainToViewport: true
});
Dom.addClass(this.tableOptionsDlg.element.firstChild, "inputex-datatable-columnsDlg");
this.tableOptionsDlg.bodyId = Dom.generateId();
this.tableOptionsDlg.setHeader(msgs.columnDialogTitle);
this.tableOptionsDlg.setBody("<div id='"+this.tableOptionsDlg.bodyId+"'></div>");
this.tableOptionsDlg.render(document.body);
},
/**
* Display the dialog to show/hide fields
*/
showTableOptions: function(e) {
Event.stopEvent(e);
if(!this.noNewCols) {
var that = this;
var handleButtonClick = function(e, oSelf) {
var sKey = this.get("name");
if(this.get("value") === "Hide") {
// Hides a Column
that.datatable.hideColumn(sKey);
}
else {
// Shows a Column
that.datatable.showColumn(sKey);
}
};
// Populate Dialog
// Using a template to create elements for the SimpleDialog
var allColumns = this.datatable.getColumnSet().keys;
var elPicker = Dom.get(this.tableOptionsDlg.bodyId);
var elTemplateCol = document.createElement("div");
Dom.addClass(elTemplateCol, "dt-dlg-pickercol");
var elTemplateKey = elTemplateCol.appendChild(document.createElement("span"));
Dom.addClass(elTemplateKey, "dt-dlg-pickerkey");
var elTemplateBtns = elTemplateCol.appendChild(document.createElement("span"));
Dom.addClass(elTemplateBtns, "dt-dlg-pickerbtns");
var onclickObj = {fn:handleButtonClick, obj:this, scope:false };
// Create one section in the SimpleDialog for each Column
var elColumn, elKey, elButton, oButtonGrp;
for(var i=0,l=allColumns.length;i<l;i++) {
var oColumn = allColumns[i];
// Use the template
elColumn = elTemplateCol.cloneNode(true);
// Write the Column key
elKey = elColumn.firstChild;
elKey.innerHTML = (oColumn.label && oColumn.label !== "") ? oColumn.label : oColumn.getKey();
if(oColumn.getKey() != "delete" && oColumn.getKey() != "modify") {
// Create a ButtonGroup
oButtonGrp = new YAHOO.widget.ButtonGroup({
id: "buttongrp"+i,
name: oColumn.getKey(),
container: elKey.nextSibling
});
oButtonGrp.addButtons([
{ label: msgs.showColumnButton, value: "Show", checked: ((!oColumn.hidden)), onclick: onclickObj},
{ label: msgs.hideColumnButton, value: "Hide", checked: ((oColumn.hidden)), onclick: onclickObj}
]);
elPicker.appendChild(elColumn);
}
}
this.noNewCols = true;
}
this.tableOptionsDlg.show();
}
};
msgs.saveText = "Save";
msgs.cancelText = "Cancel";
msgs.deleteText = "delete";
msgs.modifyText = "modify";
msgs.insertItemText = "Insert";
msgs.addButtonText = "Add";
msgs.loadingText = "Loading...";
msgs.emptyDataText = "No records found.";
msgs.errorDataText = "Data error.";
msgs.confirmDeletion = "Are you sure?";
msgs.tableOptions = "Table options";
msgs.showColumnButton = "Show";
msgs.hideColumnButton = "Hide";
msgs.columnDialogTitle = "Choose which columns you would like to see";
msgs.columnDialogCloseButton = "Close";
})();