added: DataTable asset

This commit is contained in:
Doug Bell 2008-10-28 22:27:17 +00:00
parent 828930d193
commit 3e1f66a0e7
9 changed files with 1638 additions and 0 deletions

Binary file not shown.

View file

@ -37,8 +37,54 @@ upgradeToYui26($session);
addUsersOnlineMacro($session);
addProfileExtrasField($session);
addWorkflowToDataform( $session );
installDataTableAsset( $session );
installAjaxI18N( $session );
finish($session); # this line required
#----------------------------------------------------------------------------
# installDataTableAsset
# Install the asset by creating the DB table and adding it to the config file
sub installDataTableAsset {
my $session = shift;
print "\tInstalling the DataTable asset... " unless $quiet;
$session->db->write( <<'ENDSQL' );
CREATE TABLE DataTable (
assetId VARCHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
data LONGTEXT,
templateId VARCHAR(22) BINARY,
PRIMARY KEY ( assetId, revisionDate )
)
ENDSQL
my $assets = $session->config->get( "assets" );
$assets->{ "WebGUI::Asset::Wobject::DataTable" } = { category => "basic" };
$session->config->set( "assets", $assets );
print "DONE!\n" unless $quiet;
}
#----------------------------------------------------------------------------
# installDataTableAsset
# Install the content handler by adding it to the config file
sub installAjaxI18N {
my $session = shift;
print "\tInstalling the AjaxI18N content handler... " unless $quiet;
my $oldHandlers = $session->config->get( "contentHandlers" );
my @newHandlers;
for my $handler ( @{ $oldHandlers } ) {
if ( $handler eq "WebGUI::Content::Operation" ) {
push @newHandlers, "WebGUI::Content::AjaxI18N";
}
push @newHandlers, $handler;
}
$session->config->set( "contentHandlers", \@newHandlers );
print "DONE!\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub upgradeToYui26 {
my $session = shift;
@ -195,6 +241,7 @@ sub updateTemplates {
my @files = readdir(DIR);
closedir(DIR);
my $newFolder = undef;
$session->db->write( "UPDATE asset SET parentId='infinityandbeyond' WHERE assetId='pbversion0000000000001'" );
foreach my $file (@files) {
next unless ($file =~ /\.wgpkg$/);
# Fix the filename to include a path

View file

@ -0,0 +1,296 @@
package WebGUI::Asset::Wobject::DataTable;
$VERSION = "1.0.0";
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2008 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use strict;
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
use base 'WebGUI::Asset::Wobject';
#-------------------------------------------------------------------
=head2 definition ( session, definition )
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Asset_DataTable');
tie my %properties, 'Tie::IxHash', (
data => {
fieldType => 'DataTable',
defaultValue => undef,
autoGenerate => 0,
},
templateId => {
tab => "display",
fieldType => "template",
namespace => "DataTable",
defaultValue => "3rjnBVJRO6ZSkxlFkYh_ug",
label => $i18n->get( "editForm templateId label" ),
hoverHelp => $i18n->get( "editForm templateId description" ),
},
);
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'Asset.gif',
autoGenerateForms => 1,
tableName => 'DataTable',
className => 'WebGUI::Asset::Wobject::DataTable',
properties => \%properties,
};
return $class->SUPER::definition($session, $definition);
}
#----------------------------------------------------------------------------
=head2 getDataJson ( )
Get the data as a JSON object with the following structure:
{
columns => [
{
key => "Column Key",
formatter => "Column Format",
},
...
],
rows => [
{
"Column Key" => "Column Value",
...
},
...
],
}
=cut
sub getDataJson {
my $self = shift;
return $self->get( "data" );
}
#----------------------------------------------------------------------------
=head2 getDataTable ( )
Get the YUI DataTable markup and script for this DataTable.
=cut
# TODO Have this method get from a WebGUI::DataSource object
sub getDataTable {
my $self = shift;
# This method assumes the form control has been prepared
return $self->{ _datatable }->getValueAsHtml;
}
#----------------------------------------------------------------------------
=head2 getDataTemplateVars ( )
Get the template variables for the raw data. Returns a hash reference with
"rows" and "columns" keys.
=cut
# TODO Have this method get from a WebGUI::DataSource object
sub getDataTemplateVars {
my $self = shift;
# This method assumes the form control has been prepared
my $json = $self->{ _datatable }->getValue;
my $dt = JSON->new->decode( $json );
# Make row data more friendly to templates
my %cols = map { $_->{ key } => $_ } @{ $dt->{ columns } };
for my $row ( @{ $dt->{ rows } } ) {
# Create the column loop for the row
for my $col ( @{ $dt->{ columns } } ) {
push @{ $row->{ row_columns } }, {
%{ $col },
value => $row->{ $col->{ key } },
};
}
}
return $dt;
}
#----------------------------------------------------------------------------
=head2 getEditForm ( )
Add the data table to the edit form.
=cut
# TODO Get the DataSource's edit form
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm( @_ );
$tabform->getTab( "data" )->raw(
WebGUI::Form::DataTable->new( $self->session, {
name => "data",
value => $self->get( "data" ),
defaultValue => undef,
showEdit => 1,
} )->toHtml
);
return $tabform;
}
#----------------------------------------------------------------------------
=head2 getEditTabs ( )
Add a tab for the data table.
=cut
sub getEditTabs {
my $self = shift;
my $i18n = WebGUI::International->new( $self->session, "Asset_DataTable" );
return (
$self->SUPER::getEditTabs,
[ "data" => $i18n->get( "tab label data" ) ],
);
}
#----------------------------------------------------------------------------
=head2 getTemplateVars ( )
Get the template vars for this asset.
=cut
sub getTemplateVars {
my $self = shift;
my $var = $self->get;
$var->{ url } = $self->getUrl;
$var->{ dataTable } = $self->getDataTable;
$var->{ dataJson } = $self->getDataJson;
%{ $var } = (
%{ $var },
%{ $self->getDataTemplateVars },
);
return $var;
}
#----------------------------------------------------------------------------
=head2 prepareView ( )
Prepare the view. Add stuff to HEAD.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView( @_ );
my $session = $self->session;
# For now, prepare the form control.
# TODO Use a WebGUI::DataSource
my $dt = WebGUI::Form::DataTable->new( $session, {
name => "data",
value => $self->get( 'data' ),
defaultValue => undef,
} );
$dt->prepare;
$self->{ _datatable } = $dt;
# Prepare the template
my $template = WebGUI::Asset::Template->new( $session, $self->get( "templateId" ) );
$template->prepare;
$self->{ _template } = $template;
return;
}
#----------------------------------------------------------------------------
=head2 view ( )
method called by the www_view method. Returns a processed template
to be displayed within the page style.
=cut
sub view {
my $self = shift;
my $session = $self->session;
my $var = $self->getTemplateVars;
my $dt = $self->{ _datatable };
my $template = $self->{ _template };
return $self->processTemplate( $var, undef, $template );
}
#----------------------------------------------------------------------------
=head2 www_ajaxGetData ( )
Get the data asynchronously.
=cut
sub www_ajaxGetData {
my $self = shift;
$self->session->http->setMimeType( "application/json" );
return $self->getDataJson;
}
#----------------------------------------------------------------------------
=head2 www_ajaxUpdateData ( )
Update the data table asynchronously.
=cut
sub www_ajaxUpdateData {
my $self = shift;
my $data = $self->session->form->get( "data" );
if ( $data && $self->canEdit ) {
$self->update( { data => $data } );
}
$data ||= $self->get( "data" );
$self->session->http->setMimeType( "application/json" );
return $data;
}
1;

View file

@ -0,0 +1,68 @@
package WebGUI::Content::AjaxI18N;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2008 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use JSON;
=head1 NAME
Package WebGUI::Content::AjaxI18N
=head1 DESCRIPTION
A content handler to get i18n data using the WebGUI.i18n JavaScript object.
=head1 SYNOPSIS
use WebGUI::Content::AjaxI18N
my $output = WebGUI::Content::AjaxI18N::handler($session);
=head1 SUBROUTINES
These subroutines are available from this package:
=cut
#-------------------------------------------------------------------
=head2 handler ( session )
The content handler for this package.
=cut
sub handler {
my ( $session ) = @_;
# Only handle op=ajaxGetI18N
return undef unless ( $session->form->get( "op" ) eq "ajaxGetI18N" );
my $json = $session->form->get( "request" );
my $namespaces = JSON->new->decode( $json );
my $i18n = WebGUI::International->new( $session );
my $response = {};
for my $ns ( keys %{ $namespaces } ) {
for my $key ( @{ $namespaces->{ $ns } } ) {
$response->{ $ns }->{ $key } = $i18n->get( $key, $ns );
}
}
$session->http->setMimeType( "application/json" );
return JSON->new->encode( $response );
}
1;

View file

@ -0,0 +1,407 @@
package WebGUI::Form::DataTable;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2008 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use base 'WebGUI::Form::Control';
use WebGUI::International;
=head1 NAME
Package WebGUI::Form::DataTable
=head1 DESCRIPTION
Create an editable table. Users can add columns and rows to the table.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::Control.
=head1 METHODS
The following methods are specifically available from this class. Check the superclass for additional methods.
=cut
#-------------------------------------------------------------------
=head2 definition ( [ additionalTerms ] )
See the super class for additional details.
=head3 additionalTerms
The following additional parameters have been added via this sub class.
=head4 ajaxDataUrl
This is the URL to get the data from, AJAX-style.
=head4 ajaxDataFunc
This is the ?func= to get the data from.
=head4 ajaxSaveUrl
This is the URL to send AJAX requests to. If this exists, will send
updates to the table asyncronously.
=head4 ajaxSaveFunc
This is the ?func= to send AJAX requests to.
=head4 showEdit
If true, will enable the table for editing. This is only necessary when
displaying the table with getValueAsHtml().
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift || [];
push @{$definition}, {
showEdit => {
defaultValue => 0,
},
ajaxDataUrl => {
defaultValue => undef,
},
ajaxDataFunc => {
defaultValue => "view",
},
ajaxSaveUrl => {
defaultValue => undef,
},
ajaxSaveFunc => {
defaultValue => "view",
},
};
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getDataTableHtml ( )
Render the DataTable
=cut
sub getDataTableHtml {
my $self = shift;
$self->prepare unless $self->{_prepared};
my $data = JSON->new->decode( $self->getOriginalValue );
my $id = $self->get( 'id' );
# Get the HTML for the table
my $html = $self->getTableHtml( $data );
### Prepare the columns data
my %parsers = (
date => "YAHOO.util.DataSource.parseDate",
number => "YAHOO.util.DataSource.parseNumber",
);
my %editors = (
date => "date",
textbox => "textbox",
);
my @columnsJson = ();
for my $column ( @{ $data->{ columns } } ) {
# Not using a datastructure and JSON.pm because of function references for "parser"
my $columnDef = '{'
. qq{"key" : "$column->{ key }", }
. qq{"abbr" : "$column->{ key }", }
. qq{"formatter" : "$column->{ formatter }", }
. qq{"resizable" : 1, }
. qq{"sortable" : 1}
;
# Automatically determine the parser to use
if ( $parsers{ $column->{ formatter } } ) {
$columnDef .= qq{, "parser" : $parsers{ $column->{ formatter } }};
};
# If we can edit
if ( $self->get( 'showEdit' ) ) {
# Set the editor
my $editor = $editors{ $column->{ formatter } }
|| $editors{ "textbox" }
;
$columnDef .= qq{, "editor" : "$editor"};
}
$columnDef .= '}';
push @columnsJson, $columnDef;
}
my $columnsJson = "[" . join( ",", @columnsJson ) . "]";
### Prepare any options
my $options = {
"showEdit" => $self->get( 'showEdit' ),
"inputName" => $self->get( 'name' ),
"ajaxDataUrl" => $self->get( 'ajaxDataUrl' ),
"ajaxDataFunc" => $self->get( 'ajaxDataFunc' ),
"ajaxSaveUrl" => $self->get( 'ajaxSaveUrl' ),
"ajaxSaveFunc" => $self->get( 'ajaxSaveFunc' ),
};
my $optionsJson = JSON->new->encode( $options );
# Progressively enhance the bejesus out of it
$html .= <<"ENDJS";
<script type="text/javascript">
var myDataTable = WebGUI.Form.DataTable( "$id-container", $columnsJson, $optionsJson );
</script>
ENDJS
return $html;
}
#-------------------------------------------------------------------
=head2 getDefaultValue ( )
Get the default value. If none exists, return at least an appropriate
data structure.
=cut
sub getDefaultValue {
my $self = shift;
my $value = $self->SUPER::getDefaultValue( @_ );
if ( !$value ) {
$value = JSON->new->encode(
{
columns => [
{
key => "New Column",
formatter => "text",
},
],
rows => [
{
"New Column" => "Value",
},
],
}
);
}
return $value;
}
#-------------------------------------------------------------------
=head2 getName ( session )
Returns the name of the form control.
=cut
sub getName {
my ($class, $session) = @_;
return WebGUI::International->new($session, "Form_DataTable")->get("topicName");
}
#-------------------------------------------------------------------
=head2 getOriginalValue ( )
Get the original value, or the default value.
=cut
sub getOriginalValue {
my $self = shift;
my $value = $self->SUPER::getOriginalValue( @_ );
if ( !$value ) {
$value = $self->getDefaultValue;
}
return $value;
}
#-------------------------------------------------------------------
=head2 getValue ( [ value ] )
Return the data for the table in a serialized JSON object with the following
structure.
$VAR1 = {
columns => [
{
key => column name,
formatter => "", one of "button", "checkbox", "currency", "date", "dropdown",
"email", "link", "number", "radio", "text", "textarea", "textbox"
// FUTURE ENHANCEMENTS
editor => "", one of "text", "date", "dropdown", "radio", "check"
editorOptions => [ ... ], needed for "dropdown", "radio", "check"
resizable => 1,
sortable => 1,
},
],
rows => [
{
column name => value,
column name => value,
...
},
]
}
=cut
sub getValue {
my $self = shift;
my $value = $self->SUPER::getValue(@_);
# If passing in a data structure, encode to JSON
if ( ref $value eq "HASH" ) {
$value = JSON->new->encode( $value );
}
return $value;
}
#-------------------------------------------------------------------
=head2 getValueAsHtml ( )
Get the value as HTML. Render the datatable in a non-editable form.
=cut
sub getValueAsHtml {
my $self = shift;
return $self->getDataTableHtml;
}
#-------------------------------------------------------------------
=head2 getTableHtml ( [data] )
Get the HTML to render the table. C<data> is the data structure with
columns and rows to render. Defaults to C<getValue>.
=cut
sub getTableHtml {
my $self = shift;
my $data = shift;
my $html = '<div id="' . $self->get('id') . '-container" class="yui-skin-sam">';
# Only insert the table if we're not getting AJAX Data
if ( !$self->get( "ajaxDataUrl" ) ) {
$html .= '<table id="' . $self->get('id') . '-container-table"><thead><tr>';
for my $column ( @{ $data->{ columns } } ) {
$html .= '<th>' . $column->{ key } . '</th>';
}
# TODO: Add table footer
$html .= '</tr></thead><tbody>';
for my $row ( @{ $data->{ rows } } ) {
$html .= '<tr>';
for my $column ( @{ $data->{ columns } } ) {
$html .= '<td>' . $row->{ $column->{ key } } . '</td>';
}
$html .= '</tr>';
}
$html .= '</tbody></table>';
}
$html .= '</div>';
# Add hidden form element to hold JSON
if ( $self->get( 'showEdit' ) ) {
$html .= '<input type="hidden" name="' . $self->get( 'name' ) . '" />';
}
return $html;
}
#-------------------------------------------------------------------
=head2 prepare ( )
Load all the script and css files we need. Call this in prepareView() if needed.
Otherwise, is called automatically.
=cut
sub prepare {
my $self = shift;
# Source in the scripts
my $style = $self->session->style;
my $url = $self->session->url;
$style->setLink( $url->extras( 'yui/build/datatable/assets/skins/sam/datatable.css' ), { rel => "stylesheet", type => "text/css" } );
$style->setScript( $url->extras( 'yui/build/yahoo-dom-event/yahoo-dom-event.js' ) );
$style->setScript( $url->extras( 'yui/build/element/element-beta.js' ) );
$style->setScript( $url->extras( 'yui/build/dragdrop/dragdrop-min.js' ) );
$style->setScript( $url->extras( 'yui/build/connection/connection-min.js' ) );
$style->setScript( $url->extras( 'yui/build/json/json-min.js' ) );
# Prepare the editors
if ( $self->get( 'showEdit' ) ) {
$style->setLink( $url->extras( 'yui/build/button/assets/skins/sam/button.css', { rel => "stylesheet", type => "text/css" } ) );
$style->setLink( $url->extras( 'yui/build/calendar/assets/skins/sam/calendar.css', { rel => "stylesheet", type => "text/css" } ) );
$style->setLink( $url->extras( 'yui/build/container/assets/skins/sam/container.css' ), { rel => "stylesheet", type => "text/css" } );
$style->setScript( $url->extras( 'yui/build/container/container-min.js' ) );
$style->setScript( $url->extras( 'yui/build/button/button-min.js' ) );
$style->setScript( $url->extras( 'yui/build/calendar/calendar-min.js' ) );
}
$style->setScript( $url->extras( 'yui-webgui/build/i18n/i18n.js' ) );
$style->setScript( $url->extras( 'yui/build/datasource/datasource.js' ) );
$style->setScript( $url->extras( 'yui/build/datatable/datatable.js' ) );
$style->setScript( $url->extras( 'yui-webgui/build/form/datatable.js' ) );
$self->{_prepared} = 1;
return;
}
#-------------------------------------------------------------------
=head2 toHtml ( )
Render the DataTable in an editable format.
=cut
sub toHtml {
my $self = shift;
$self->set( 'showEdit', 1 );
return $self->getDataTableHtml;
}
1;

View file

@ -0,0 +1,19 @@
package WebGUI::i18n::English::Asset_DataTable;
use strict;
our $I18N = {
'assetName' => {
message => q{DataTable},
lastUpdated => 0,
},
'tab label data' => {
message => q{Data Table},
lastUpdated => 0,
context => q{Label for the tab with the editable data table},
},
};
1;

View file

@ -0,0 +1,132 @@
package WebGUI::i18n::English::Form_DataTable;
use strict;
our $I18N = {
'topicName' => {
message => q{DataTable},
lastUpdated => 0,
},
'delete rows' => {
message => q{Delete Selected Rows},
lastUpdated => 0,
context => q{Label for button to delete the selected rows},
},
'save' => {
message => q{Save},
lastUpdated => 0,
context => q{Label for button to Save changes},
},
"add row" => {
message => q{Add Row},
lastUpdated => 0,
context => q{Label for button to Add Row to the table},
},
"help" => {
message => q{Help},
lastUpdated => 0,
context => q{Label for button to open the help dialog},
},
"edit schema" => {
message => q{Edit Schema},
lastUpdated => 0,
context => q{Label for button to edit the table column configuration},
},
"delete confirm" => {
message => q{Are you sure you want to delete these rows?},
lastUpdated => 0,
context => q{Message for pop-up to confirm deleting rows from the table},
},
"format text" => {
message => q{Text},
lastUpdated => 0,
context => q{Format for a plain text column},
},
"format email" => {
message => q{E-mail},
lastUpdated => 0,
context => q{Format for a column for an e-mail address},
},
"format link" => {
message => q{URL},
lastUpdated => 0,
context => q{Format for a column for URLs},
},
"format number" => {
message => q{Number},
lastUpdated => 0,
context => q{Format for a column with numbers},
},
"add column" => {
message => q{Add Column},
lastUpdated => 0,
context => q{Label for button to add a column},
},
"cancel" => {
message => q{Cancel},
lastUpdated => 0,
context => q{Label for button to cancel},
},
"ok" => {
message => q{Ok},
lastUpdated => 0,
context => q{Label for button to close an information dialog},
},
"save success" => {
message => q{Table saved successfully!},
lastUpdated => 0,
context => q{Message shown when the table save succeeds},
},
"save failure" => {
message => q{Save failed! Please try again.},
lastUpdated => 0,
context => q{Message shown when the table save fails},
},
"help edit cell" => {
message => q{Double-click a cell to edit the cell. Hitting tab will save the current cell and open the next.},
lastUpdated => 0,
context => q{How to edit a cell. Shown in the help pop-up},
},
"help select row" => {
message => q{Click on a row to select it. Hold shift or ctrl to select multiple rows.},
lastUpdated => 0,
context => q{How to select a row. Shown in the help pop-up},
},
"help add row" => {
message => q{Clicking Add Row will add a row to the bottom of the table.},
lastUpdated => 0,
context => q{How to add a row. Shown in the help pop-up},
},
"help default sort" => {
message => q{By default, the table will be sorted exactly as it is when it is saved.},
lastUpdated => 0,
context => q{How to set the default sort. Shown in the help pop-up},
},
"help reorder column" => {
message => q{Drag and drop columns to reorder them.},
lastUpdated => 0,
context => q{How to reorder columns. Shown in the help pop-up},
},
};
1;

View file

@ -0,0 +1,583 @@
// Initialize namespace
if (typeof WebGUI == "undefined") {
var WebGUI = {};
}
if (typeof WebGUI.Form == "undefined") {
WebGUI.Form = {};
}
/**
* This object contains scripts for the DataTable form control
*/
/****************************************************************************
* DataTable ( containerId, columns, options )
* Initialize a WebGUI.Form.DataTable.
* containerId is the ID of the container for the table. It is assumed the data
* is already in a table inside of the container (progressive enhancement).
* columns is an array of definitions for YAHOO.widget.Column objects
* rows is an array of key/value pairs
* options is an object literal of options with the following keys
* ajaxDataUrl : The URL to get data from
* ajaxSaveUrl : The URL to save data to
* ajaxSaveFunc : The ?func= to save data to
* ajaxSaveExtras : Any extra name=value pairs necessary to save the data
* inputName : The name of the DataTable when editing the data
* showEdit : Show the edit controls and allow inline editing of values
*
*/
WebGUI.Form.DataTable
= function ( containerId, columns, options ) {
this.containerId = containerId;
this.columns = columns;
this.dataSource = undefined;
this.dataTable = undefined;
this.form = undefined;
this.options = options;
this.schemaDialog = undefined;
/************************************************************************
* addRow ( event, data )
* Add a row to the bottom of the table
*/
this.addRow
= function ( event, data ) {
if ( !data ) {
// Make a blank row
data = {};
var columns = this.dataTable.getColumnSet().getDefinitions();
for ( var i = 0; i < columns.length; i++ ) {
data[ columns[ i ].key ] = "";
}
}
this.dataTable.addRow( data );
};
/************************************************************************
* deleteSelectedRows ( )
* Delete the selected rows after confirming
*/
this.deleteSelectedRows
= function ( ) {
if ( confirm( this.i18n.get( "Form_DataTable", "delete confirm" ) ) ) {
var rows = this.dataTable.getSelectedRows();
for ( var i = 0; i < rows.length; i++ ) {
this.dataTable.deleteRow( this.dataTable.getRecord( rows[i] ) );
}
}
};
/************************************************************************
* getJson ( )
* Get the JSON data structure for the current state of the datatable
*/
this.getJson
= function () {
var data = { rows : [], columns : [] };
// Get the rows
var rows = this.dataTable.getRecordSet().getRecords();
for ( var i = 0; i < rows.length; i++ ) {
data.rows[ i ] = rows[i].getData();
}
// Get the columns
var cols = this.dataTable.getColumnSet().getDefinitions();
for ( var i = 0; i < cols.length; i++ ) {
data.columns[ i ] = cols[i];
delete data.columns[ i ].editor;
delete data.columns[ i ].editorOptions;
}
return YAHOO.lang.JSON.stringify( data );
};
/************************************************************************
* handleEditorKeyEvent ( obj )
* Handle a keypress when the Cell Editor is open
* Enter will close the editor and move down
* Tab will close the editor and move right.
* Use the handleTableKeyEvent() to handle the moving
* Open a new cell editor on the newly focused cell
*/
this.handleEditorKeyEvent
= function ( obj ) {
// 9 = tab, 13 = enter
var e = obj.event;
if ( e.keyCode == 9 || e.keyCode == 13 ) {
var cell = this.dataTable.getCellEditor().getTdEl();
var nextCell = this.dataTable.getNextTdEl( cell );
this.dataTable.saveCellEditor();
if ( nextCell ) {
this.dataTable.showCellEditor( nextCell );
e.returnValue = false;
e.preventDefault();
return false;
}
else {
// No next cell, make a new row and open the editor for that one
this.dataTable.addRow( {} );
}
// BUG: If pressing Enter, editor gets hidden right away due to YUI default event
// putting e.preventDefault() and return false here makes no difference
}
};
/************************************************************************
* handleEditorShowEvent ( editor )
* Give the focus to the editor
*/
this.handleEditorShowEvent
= function ( obj ) {
obj.editor.focus();
setTimeout( obj.editor.focus, 500 );
};
/************************************************************************
* handleRowAdd ( )
* Open the cell editor for the newly-added row
*/
this.handleRowAdd
= function () {
var row = this.dataTable.getLastTrEl();
this.dataTable.showCellEditor( row.firstChild );
this.dataTable.getCellEditor().focus();
};
/************************************************************************
* handleTableKeyEvent ( obj )
* Handle a keypress inside the DataTable
* Space will open the cell editor
*/
this.handleTableKeyEvent
= function ( obj ) {
// 9 = tab, 13 = enter, 32 = space
var e = obj.event;
if ( e.keyCode == 32 ) {
var cell = this.dataTable.getSelectedTdEls()[0];
if ( cell ) {
this.dataTable.showCellEditor(cell);
}
}
};
/************************************************************************
* hideSchemaDialog ( )
* Hide the schema dialog without saving changed
*/
this.hideSchemaDialog
= function () {
this.schemaDialog.cancel();
};
/************************************************************************
* initDataTable
* Initialize the data table. Called automatically when the DOM is ready
*/
this.initDataTable
= function () {
var container = document.getElementById( this.containerId );
// If we have an ajax datasource
if ( this.options.ajaxDataUrl ) {
this.dataSource
= new YAHOO.util.DataSource( this.options.ajaxDataUrl );
this.dataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
this.dataSource.responseSchema = {
resultsList : "rows",
fields : this.columns
};
}
else {
// Initialize a datasource with the table
this.dataSource
= new YAHOO.util.DataSource( YAHOO.util.Dom.get( this.containerId + "-table" ) );
this.dataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
this.dataSource.responseSchema = {
fields : this.columns
};
YAHOO.util.Dom.get( this.containerId + "-table" ).style.display = "none";
}
var dataTableOptions = { };
if ( this.options.showEdit ) {
dataTableOptions.draggableColumns = true;
}
if ( this.options.ajaxDataUrl ) {
dataTableOptions.initialLoad = true;
dataTableOptions.initialRequest = "";
}
this.dataTable = new YAHOO.widget.DataTable(
this.containerId,
this.columns,
this.dataSource,
dataTableOptions
);
if ( this.options.showEdit ) {
// Add the class so our editors get the right skin
YAHOO.util.Dom.addClass( document.body, "yui-skin-sam" );
this.dataTable.subscribe( "cellDblclickEvent", this.dataTable.onEventShowCellEditor );
this.dataTable.subscribe( "rowClickEvent", this.dataTable.onEventSelectRow );
this.dataTable.subscribe( "tableKeyEvent", this.handleTableKeyEvent, this, true );
this.dataTable.subscribe( "editorKeydownEvent", this.handleEditorKeyEvent, this, true );
this.dataTable.subscribe( "editorShowEvent", this.handleEditorShowEvent, this, true );
this.dataTable.subscribe( "rowAddEvent", this.handleRowAdd, this, true );
// Add the Help button
var help = new YAHOO.widget.Button( {
id : "help",
type : "push",
label : this.i18n.get( "Form_DataTable", "help" ),
container : this.containerId,
onclick : {
fn : this.showHelp,
scope : this
}
});
help.setStyle( "float", "left" );
// Add the Edit Schema button
var editSchema = new YAHOO.widget.Button( {
id : "editSchema",
type : "push",
label : this.i18n.get( "Form_DataTable", "edit schema" ),
container : this.containerId,
onclick : {
fn : this.showSchemaDialog,
scope : this
}
} );
editSchema.setStyle( "float", "right" );
// Add the Add Row and Delete Row buttons
var addRow = new YAHOO.widget.Button( {
id : "addRow",
type : "push",
label : this.i18n.get( "Form_DataTable", "add row" ),
container : this.containerId,
onclick : {
fn : this.addRow,
scope : this
}
} );
var deleteRow = new YAHOO.widget.Button( {
id : "deleteRow",
type : "push",
label : this.i18n.get( "Form_DataTable", "delete rows" ),
container : this.containerId,
onclick : {
fn : this.deleteSelectedRows,
scope : this
}
} );
// This data table will be submitted async
if ( this.options.ajaxSaveUrl ) {
var save = new YAHOO.widget.Button( {
id : "saveTable",
type : "push",
label : this.i18n.get( "Form_DataTable", "save" ),
container : this.containerId,
onclick : {
fn : this.submitToAjax,
scope : this
}
} );
}
// This data table will be submitted with a form
if ( this.options.inputName && !this.options.ajaxSaveUrl ) {
// Find our form
var form = document.getElementById( this.containerId );
while ( form.nodeName != "FORM" ) {
form = form.parentNode;
}
this.form = form;
// When form is submitted, compile the JSON
YAHOO.util.Event.addListener( form, "submit", this.submitToForm, this, true );
}
}
};
/************************************************************************
* initI18N ( )
* Initialize the I18N that we need. Then initialize the datatable.
*/
this.initI18N
= function ( ) {
this.i18n = new WebGUI.i18n( {
namespaces : {
'Form_DataTable' : [
"topicName",
"save",
"delete rows",
"add row",
"help",
"edit schema",
"delete confirm",
"format text",
"format email",
"format link",
"format number",
"add column",
"cancel",
"ok",
"save success",
"save failure",
"help edit cell",
"help select row",
"help add row",
"help default sort",
"help reorder column"
]
},
onpreload : {
fn : this.initDataTable,
obj : this,
override : true
}
} );
};
// Run this automatically
YAHOO.util.Event.onDOMReady( this.initI18N, undefined, this );
/************************************************************************
* showHelp ( event )
* Show the help dialog, creating it if necessary
*/
this.showHelp
= function ( e ) {
if ( !this.helpDialog ) {
var helpDialog = new YAHOO.widget.Panel( "helpWindow", {
modal : false,
draggable : true,
zIndex : 1000
} );
helpDialog.setHeader( "DataTable Help" );
helpDialog.setBody(
"<ul>"
+ "<li>" + this.i18n.get( "Form_DataTable", "help edit cell" ) + "</li>"
+ "<li>" + this.i18n.get( "Form_DataTable", "help select row" ) + "</li>"
+ "<li>" + this.i18n.get( "Form_DataTable", "help add row" ) + "</li>"
+ "<li>" + this.i18n.get( "Form_DataTable", "help default sort" ) + "</li>"
+ "<li>" + this.i18n.get( "Form_DataTable", "help reorder column" ) + "</li>"
+ "</ul>"
);
helpDialog.render( document.body );
this.helpDialog = helpDialog;
}
this.helpDialog.show();
};
/************************************************************************
* showSchemaDialog ( event )
* Show the Edit Schema YUI Dialog. Markup is in WebGUI::Form::DataTable
*/
this.showSchemaDialog
= function ( e ) {
var dg = new YAHOO.widget.Dialog( "editSchemaDialog", {
modal : true,
fixedcenter : true
});
dg.setHeader( this.i18n.get( "Form_DataTable", "edit schema" ) );
var body = '<form>';
var cols = this.dataTable.getColumnSet().keys;
for ( var i = 0; i < cols.length; i++ ) {
body = body
+ '<input name="oldKey_' + i + '" type="hidden" value="' + cols[ i ].key + '" />'
+ '<input name="newKey_' + i + '" value="' + cols[ i ].key + '"/>'
+ '<select name="format_' + i + '">'
+ '<option value="text"' + ( cols[ i ].formatter == "text" ? ' selected="selected"' : '' ) + '>'
+ this.i18n.get( "Form_DataTable", "format text" ) + '</option>'
+ '<option value="number"' + ( cols[ i ].formatter == "number" ? ' selected="selected"' : '' ) + '>'
+ this.i18n.get( "Form_DataTable", "format number" ) + '</option>'
+ '<option value="link"' + ( cols[ i ].formatter == "link" ? ' selected="selected"' : '' ) + '>'
+ this.i18n.get( "Form_DataTable", "format link" ) + '</option>'
+ '<option value="email"' + ( cols[ i ].formatter == "email" ? ' selected="selected"' : '' ) + '>'
+ this.i18n.get( "Form_DataTable", "format email" ) + '</option>'
+ '</select><br/>'
}
body += '</form>';
dg.setBody( body );
// Function to add a column
var addColumn = function ( e ) {
// this is the dialog
var form = this.element.getElementsByTagName( "form" )[0];
var newIdx = 0;
while ( form.elements[ "oldKey_" + newIdx ] ) { newIdx++; }
var oldKey = form.elements[ "oldKey_" + (newIdx - 1) ].cloneNode(true);
oldKey.name = "oldKey_" + newIdx;
oldKey.value = "";
form.appendChild( oldKey );
var newKey = form.elements[ "newKey_" + (newIdx - 1) ].cloneNode(true);
newKey.name = "newKey_" + newIdx;
newKey.value = "New Column " + newIdx;
form.appendChild( newKey );
var format = form.elements[ "format_" + (newIdx - 1) ].cloneNode(true);
format.name = "format_" + newIdx;
format.selectedIndex = 0;
form.appendChild( format );
form.appendChild( document.createElement( "br" ) );
};
dg.cfg.queueProperty( "buttons", [
{ text: this.i18n.get( "Form_DataTable", "add column" ), handler: addColumn },
{ text: this.i18n.get( "Form_DataTable", "cancel" ), handler: { fn: this.hideSchemaDialog, scope: this } },
{ text: this.i18n.get( "Form_DataTable", "save" ), handler: { fn: this.updateSchema, scope: this }, isDefault: true }
] );
dg.render( document.body );
this.schemaDialog = dg;
};
/************************************************************************
* submitToAjax ( event )
* Save the data table to the AJAX URL
*/
this.submitToAjax
= function ( e ) {
if ( this.options.ajaxSaveUrl ) {
var callback = {
success : function ( o ) {
var dialog = new YAHOO.widget.Panel( "savedMessage", {
modal : true,
fixedcenter : true
} );
dialog.setBody( this.i18n.get( "Form_DataTable", "save success" ) + "<br/>" );
new YAHOO.widget.Button( {
id : "ok",
type : "push",
label : this.i18n.get( "Form_DataTable", "ok" ),
container : dialog.body,
onclick : {
fn : function () { this.destroy() },
scope : dialog
}
} );
dialog.render( document.body );
},
failure : function ( o ) {
var dialog = new YAHOO.widget.Panel( el, {
modal : true,
fixedcenter : true
} );
dialog.setBody( this.i18n.get( "Form_DataTable", "save failure" ) + "<br/>" );
new YAHOO.widget.Button( {
id : "ok",
type : "push",
label : this.i18n.get( "Form_DataTable", "ok" ),
container : dialog.body,
onclick : {
fn : function () { this.destroy() },
scope : dialog
}
} );
dialog.render( document.body );
},
scope : this
};
var data = this.getJson();
var postdata = "func=" + this.options.ajaxSaveFunc + ";"
+ this.options.ajaxSaveExtras + ";"
+ this.options.inputName + "=" + escape( data )
;
YAHOO.util.Connect.asyncRequest(
"POST",
this.options.ajaxSaveUrl,
callback,
postdata
);
}
};
/************************************************************************
* submitToForm ( event )
* Compile the DataTable data into the right form element
*/
this.submitToForm
= function (e) {
var elem = this.form.elements[ this.options.inputName ];
elem.value = this.getJson();
};
/************************************************************************
* updateSchema callback
*/
this.updateSchema
= function () {
var i = 0;
var data = this.schemaDialog.getData();
while ( data[ "newKey_" + i ] ) {
var oldKey = data[ "oldKey_" + i ];
var newKey = data[ "newKey_" + i ];
var format = data[ "format_" + i ][0];
var col = this.dataTable.getColumn( oldKey );
// Don't allow adding multiple columns with same key
if ( oldKey != newKey && this.dataTable.getColumn( newKey ) ) {
// TODO: Log an error
i++;
continue;
}
// If the key has changed, update the row data
if ( col && col.key != newKey ) {
var rows = this.dataTable.getRecordSet().getRecords();
for ( var i = 0; i < rows.length; i++ ) {
rows[ i ].setData( newKey, rows[ i ].getData( oldKey ) );
rows[ i ].setData( oldKey, undefined );
}
}
// Change the column info
var newCol = {
key : newKey,
formatter : format,
resizeable : ( col ? col.resizeable : 1 ),
sortable : ( col ? col.sortable : 1 )
};
var newIndex = col ? col.getKeyIndex() : undefined;
// Set the editor
if ( format == "date" ) {
newCol.editor = "date";
}
else {
newCol.editor = "textbox";
}
this.dataTable.insertColumn( newCol, newIndex );
if ( col ) {
// Get a new reference so we remove the right column
var delCol;
if ( oldKey == newKey ) {
delCol = this.dataTable.getColumn( oldKey )[1];
}
else {
delCol = this.dataTable.getColumn( oldKey );
}
this.dataTable.removeColumn( delCol );
}
i++;
}
this.dataTable.render();
this.schemaDialog.cancel();
}
};

View file

@ -0,0 +1,86 @@
// Initialize namespace
if (typeof WebGUI == "undefined") {
var WebGUI = {};
}
/****************************************************************************
* WebGUI.i18n ( { options } )
* Initialize an i18n object. Options is an object with the following keys:
* - url : (Optional) Specify a URL to a WebGUI site. Do not include the query string
* - namespaces : (Optional) Object of arrays of namespaces to preload. Keys are namespace names,
* and array members are i18n entries to get.
* - onpreload : (Optional) Callback object for after the i18n is loaded.
* fn : The function to call
* obj : An object to pass to the function
* override : If true, the function will be called in "obj" scope
* Requires: YUI Event, YUI Connect, and YUI JSON
* Events:
* - preload : Fired when preloading is complete
*/
WebGUI.i18n
= function ( opt ) {
this.url = opt.url || "";
this.namespaces = {};
this.evPreload = this.createEvent( "preload" );
if ( opt.onpreload ) {
this.subscribe( "preload", opt.onpreload.fn, opt.onpreload.obj, opt.onpreload.override );
}
if ( opt.namespaces ) {
this.load( opt.namespaces, true );
}
};
YAHOO.lang.augmentProto( WebGUI.i18n, YAHOO.util.EventProvider );
/****************************************************************************
* get( ns, key )
* Return the string referenced by namespace and key. If the namespace and key
* have not yet been retrieved, get it from the WebGUI server.
*/
WebGUI.i18n.prototype.get
= function ( ns, key ) {
if ( typeof this.namespaces[ ns ][ key ] == "undefined" ) {
this.load( ns, [ key ] );
}
return this.namespaces[ ns ][ key ];
};
/****************************************************************************
* load( { ns : keys, ns : keys, ... }, preload )
* Grab the requested namespace / keys from the WebGUI server.
* keys is an array of keys to get.
* If preload is defined, will fire off the preload event
*/
WebGUI.i18n.prototype.load
= function ( obj, preload ) {
var requestUrl = this.url + "?op=ajaxGetI18N"
var callback = {
failure : function ( o, preload ) {
// TODO: YUI logger for this
console.log( "Could not load i18n" );
},
success : function ( o ) {
var responseObj = YAHOO.lang.JSON.parse( o.responseText );
for ( var ns in responseObj ) {
for ( var key in responseObj[ ns ] ) {
if ( !this.namespaces[ ns ] ) {
this.namespaces[ ns ] = {};
}
this.namespaces[ ns ][ key ] = responseObj[ ns ][ key ];
}
}
if ( o.argument.preload ) {
this.fireEvent( "preload" );
}
},
scope : this,
argument : { preload : preload }
};
var postJson = 'request=' + YAHOO.lang.JSON.stringify( obj );
YAHOO.util.Connect.asyncRequest( "POST", requestUrl, callback, postJson );
};