Skip to content

Instantly share code, notes, and snippets.

@dhoss
Created February 6, 2012 17:32
Show Gist options
  • Save dhoss/1753527 to your computer and use it in GitHub Desktop.
Save dhoss/1753527 to your computer and use it in GitHub Desktop.
datasource fuckery
[% page.load_scripts_at_top = 1 %]
<script type="text/javascript">
YUI.add('datatabledatasource-350-preview-patch', function (Y) {
// Or just put this in your code directly
Y.Plugin.DataTableDataSource.prototype.onDataReturnInitializeTable = function (e) {
this.get('host').set('data', (e.response && e.response.results) || []);
};
}, '0.1', { requires: ['datatable-datasource'] });
YUI({gallery: 'gallery-2012.02.01-21-35'})
.use( 'gallery-datatable-350-preview', 'datatable', 'datatype', 'model', 'view', 'panel', 'dd-plugin', 'button',
'datasource-io', 'datatable-datasource', 'datatable-jsonschema', function (Y) {
// -------------------------
// Define a few DataTable helper methods
// NOTE: These only work with the 3.5.0PR release of DataTable
// -------------------------
/**
Method to take an existing TD or TR Node element as input "target" and scan
the dataset (ModelList) for the underlying data record (a Model).
@method getRecord
@param target {Node} Either a TR or TD Node
@returns {Model} Data record or false (or -1 if not found)
**/
//FIXME: if target is numeric or string, not working yet ... Node only works
Y.DataTable.prototype.getRecord = function( target ) {
var rs = this.get('data')
tag = target.get('tagName').toLowerCase();
var row = ( tag === 'td' ) ? target.ancestor() : ( tag === 'tr' ) ? target : null;
if ( !row ) return false;
if ( Y.Lang.isNumber(row) ) // assume row is rowindex
return rs.item(row) || false;
else if ( row instanceof Y.Node || Y.Lang.isString(row) ) {
var crow = ( Y.Lang.isString(row) ) ? row : row.get('id'); // matches based on DOM id
var rec = -1;
rs.some( function(item) {
if ( item.get('entryid') === crow ) {
rec = item;
return true;
}
});
return rec;
}
return false;
}
/**
Helper method to return the column's key property associated with the current TD.
Uses DataTable's current method of identifying a class on TD as "yui3-datatable-col-XXX"
where XXX is the column 'key' (or 'name')
@method getDTColumnKey
@param tdTarget {Node} The TD cell to return column key to
@returns ckey {String} column key name
**/
Y.DataTable.prototype.getCellColumnKey = function( tdTarget ) {
var DT_COL_CLASS = this.getClassName('col');
var regex = new RegExp( DT_COL_CLASS+'-(.*)'), // currently creates /yui3-datatable-col-(.*) to grab column key
tdclass = tdTarget.get('className').split(" "),
ckey = -1;
//
// Scan through the TD class(es), checking for a match
//
Y.Array.some( tdclass, function(item){
var mitem = item.match( regex );
if ( mitem && mitem[1] ) {
ckey = mitem[1].replace(/^\s+|\s+$/g,""); // trim all spaces
return true;
}
});
return ckey || false;
}
/* https://gist.github.com/1707631
Y.DataTable.prototype.getCellColumnKey = function (node) {
var classRE = new RegExp('\b' + this.getClassName('col') + '-(\W+)'),
name, column;
node = node.ancestor('.' + this.getClassName('cell'), true);
if (node) {
name = (node.get('className').match(classRE) || [])[1];
column = name && this.getColumn(name);
}
return column && column.key;
};
*/
/**
Method to scan the "columns" Array for the target and return the requested column.
The requested "target" can be either of ;
a column index,
or a TD Node,
or a column "key", column "name" or "_yuid" (in that order).
@method getColumn
@param target {Number | Node | String} Either the column index, the TD node or a column ID
@returns {Object} Column
**/
Y.DataTable.prototype.getColumn = function( target ) {
var cs = this.get('columns'),
ckey = null;
if (Y.Lang.isNumber(target) )
return cs[target]; //return cs.keys[col];
else if ( Y.Lang.isString(target) || target instanceof Y.Node ) { // check for 'key' or then 'name', finally '_yuid'
ckey = ( target instanceof Y.Node ) ? ckey = this.getCellColumnKey( target ) : ckey;
col = ( ckey ) ? ckey : target;
// Check if a column "key"
var cm = -1;
Y.Array.some( cs, function(citem) {
if ( citem['key'] === col ) {
cm = citem;
return true;
}
});
if ( cm !== -1) return cm; // found one, bail !!
// If not found, Check if a column "name"
Y.Array.some( cs, function(citem) {
if ( citem.name === col ) {
cm = citem;
return true;
}
});
if ( cm!==-1 ) return cm;
// If not found, Check if a column "_yui" something
Y.Array.some( cs, function(citem) {
if ( citem._yuid === col ) {
cm = citem;
return true;
}
});
return cm;
} else
return false;
}
//==================================================================================================
// Execution Begins
//==================================================================================================
var cols, myDT;
//
// --- Define DataTable custom formatters for this example
//
var fmtPublished = function(o) {
var published = o.value;
return o.value == 1 ?
"yes" :
"no";
}
var fmtBlank = function(o) {
var fclass = o.column.className || null;
if (fclass)
o.className += ' '+fclass;
o.value = ' ';
}
var fmtChkBox = function(o){
var cell = '<input type="checkbox" class="myCheckboxFmtr" />';
o.value = cell;
o.className += ' align-center';
}
cols = [
{ name:'selectBox', label:'Select <input type="checkbox" id="selAll" title="Click to toggle ALL records"/>',
formatter: fmtChkBox, allowHTML:true }, // must use allowHTML:true if we insert HTML with the formatter ...
{ key:"entryid", label:'Entry ID', sortable:true },
{ key:"title", label:"Title", sortable:true },
{ key:"created_at", label:'Created On', sortable:true },
{ key:"published", label:'Published', formatter: fmtPublished, sortable:true },
{ key:"reply_count", label:'Replies', sortable:true },
{ name:'edit', label:'- Edit -', formatter: fmtBlank, className:'align-center cell-edit' },
{ name:'delete', label:'- Delete -', formatter: fmtBlank, className:'align-center cell-delete' }
];
//
// Create the DataTable and render it
//
var ds = new Y.DataSource.GET({
source:'[% c.uri_for_action('/user/manage_entries', [ c.user.name ]) %]',
ioConfig: {
headers: {
'Accept': 'application/json'
}
}
});
ds.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
resultListLocator: "data_table",
resultFields: [
{ key: "entryid" },
{ key: "title" },
{ key: "created_at" },
{ key: "published" },
{ key: "reply_count" },
]
}
});
ds.sendRequest({
callback: {
success: function (e) {
Y.log(e);
}
}
});
myDT = new Y.DataTable({
columns: cols,
});
myDT.plug(Y.PluginDataTableDataSource, {
datasource: ds,
initialRequest: ""
});
myDT.render('#dtable');
// -------------------------
// Create a Panel to serve as the Row Editor box
// ... this uses a template defined in <script> templates below, nice way to
// hide content until we want to display it. (see the beginning of http://yuilibrary.com/yui/docs/app/app-todo.html for an example)
// -------------------------
// var insertFlag = false;
var editorPanel = new Y.Panel({
srcNode : '#idPanel',
width : 320,
xy : [ 750, 170 ],
visible : false,
render : true,
zIndex : 10,
plugins : [Y.Plugin.Drag],
buttons: [
{
value : 'Save',
section: Y.WidgetStdMod.FOOTER,
action : function (e) {
if (e) e.preventDefault();
saveFormData();
this.hide();
}
},
{
value : 'Cancel',
section: Y.WidgetStdMod.FOOTER,
action : function (e) {
e.preventDefault();
this.hide();
}
}
],
on : {
'render' : function() {
Y.one("#main").show(); // render the "main" page elements,
}
}
});
//-----------------
// Function to save the FORM data, based on current values.
// Define a mapping object to help us figure out how to apply INPUT[name=xxx] to what
// column of each record.
// Uses the setting of FORM hidden value "frmInsertFlag" to determine if this is a new
// record or if we are saving an existing record.
// If existing, the record "clientId" is saved in FORM hidden value "frmRecord"
//-----------------
var saveFormData = function() {
var theForm = document.forms[0],
rec_id = theForm.frmRecord.value, // if INSERT, this is disregarded ...
newData = {},
raw_value = 0,
data_value = 0;
//
// Define a mapping between the INPUT 'name' settings and the record "key" names ...
// also, define a parser on a few numeric items
//
var record_map = [
{ field:'entryid', ckey:'entryid', parser: parseInt },
{ field:'title', ckey:'title'},
{ field:'published', ckey:'created_at'},
{ field:'reply_count', ckey:'reply_count', parser: parseInt }
];
//
// Run through the "record_map" FORM variables, inserting data values into "newData"
// that will serve as the data object for DataTable
//
Y.Array.each( record_map, function(item){
raw_value = theForm[item.field].value;
data_value = ( item.parser && Y.Lang.isFunction(item.parser) ) ? item.parser.call(this,raw_value) : raw_value ;
newData[ item.ckey ] = data_value;
});
//
// Now insert the "newData" object into DataTable's data,
// check frmInsertFlag for whether it is "new" or "updated" data
//
if ( parseInt( theForm.frmInsertFlag.value ) === 0 )
myDT.modifyRow( rec_id, newData );
else
myDT.addRow( newData );
}
// trap an ENTER key on the form, save the data ...
editorPanel.get('srcNode').on('key', function() {
saveFormData();
editorPanel.hide();
}, 'enter');
//
// Define DEFAULT data for a "New" inserted row
//
var default_data = {
title : 'New Entry',
body : "",
valRecord : 0,
valInsert : 1
};
// position of "Insert Row" dialog
var default_dialog_xy = [ 220, 130 ];
//-----------------
// Displays the Panel (i.e. Dialog) for either EDIT of existing data or INSERT of new data.
// The passed in "record" and "xy" provide which record to edit and the XY position of the Panel when displayed.
// For a NEW record, these will be null.
//
// If "insert_obj" is defined, then it assumes we are INSERTing a new record, and uses the default_data above.
//-----------------
var showDT_Dialog = function( record, xy, insert_obj ) {
var thePanel, DialogTMPL, body_html, header_html;
//
// Grab the dialog internal content from the <script> template
//
DialogTMPL = Y.one("#dialog-template").getContent();
thePanel = editorPanel;
if ( !insert_obj ) { // we are EDITING and existing row ...
//
// Define the substitution objects to fill in the INPUT default values
//
var form_data = {
valID : record.get('entryid'),
valReplies : record.get('reply_count'),
valTitle : record.get('title'),
valCreatedAt : record.get('created_at'),
valPublished : record.get('published'),
valRecord : record.get('entryid'),
valInsert : 0
}
xy[0] += 50; // offset the dialog a tinch, from the Edit TD ...
header_html = 'Editing Row No. ' + (myDT.get('data').indexOf(record)+1);
body_html = Y.Lang.sub( DialogTMPL, form_data );
} else { // we are INSERTING a new row ...
insertFlag = true; // used
xy = default_dialog_xy;
header_html = 'Inserting NEW Row';
body_html = Y.Lang.sub( DialogTMPL, insert_obj );
}
//
// Fill the Panel content, position it and display it
//
thePanel.set( 'xy', xy );
thePanel.set( 'headerContent', header_html );
thePanel.set( 'bodyContent', body_html );
thePanel.show();
}
// Button click handler for the "Insert New Row" button
new Y.Button({srcNode:"#btnInsert"}).on("click",function() {
editorPanel.hide();
showDT_Dialog( 0, 0, default_data );
});
// -------------------------
// Define a click handler on table cells ...
// Note: use Event Delegation here (instead of just .on() ) because we may be
// deleting rows which may cause problems with just .on
// -------------------------
myDT.delegate("click", function(e) {
var cell = e.currentTarget, // the clicked TD
row = cell.ancestor(), // the parent of TD, which is TR
rec = this.getRecord( cell ), // Call the helper method above to return the "data" record (a Model)
ckey = this.getCellColumnKey( cell ), //
col = this.getColumn( cell ); //
var d_ckey = col.key || col.name || 'not set'; // if column key returned is a yui_id, don't display it
// ... that means we are in the "Select", "Edit" or "Delete" columns
//
// Update status box
//
var StatusTMPL = Y.one("#status-template").getContent(); // this retrieves HTML containing {xxx} tags for substitution
Y.one("#idStatus").setContent( Y.Lang.sub( StatusTMPL, { // ... do the substitution into the template using Y.Lang.sub
rec_id : rec.get('entryid'),
rec_index: this.get('data').indexOf(rec),
col_key : d_ckey,
col_index: Y.Array.indexOf( this.get('columns'), col ), //this.get('columns').indexOf(col),
raw_data : rec.get(ckey) || 'No Data'
} ) );
//
// If a column 'action' is available, process it
//
switch( col.name || null ) {
case 'edit':
showDT_Dialog( rec, cell.getXY() );
break;
case 'delete':
if ( confirm("Are you sure you want to delete this record ?") === true ) {
// DELETE CALL GOES HERE
myDT.removeRow( rec.get('entryid') );
Y.one("#idStatus").setContent("<br/><b>Row was Deleted!</b>");
}
break;
}
}, "tbody tr td", myDT);
// the selector, internal scope
// -------------------------
// Click handler on "Select" TH checkbox, toggle the settings of all rows
// -------------------------
Y.one("#selAll").on("click", function(e){
var selAll = this.get('checked'); // the checked status of the TH checkbox
//
// Get a NodeList of each of INPUT with class="myCheckboxFmtr" in the TBODY
//
var chks = myDT.get('srcNode').all("tbody input.myCheckboxFmtr");
chks.each( function(item){
item.set('checked', selAll); // set the individual "checked" to the TH setting
});
});
// -------------------------
// Handle the "Process Selected Rows" BUTTON press,
// -------------------------
new Y.Button({srcNode:"#btnProcess"}).on("click",function(){
//
// Get a NodeList of all nodes on the DT which have the checkboxes I defined,
// with class="myCheckBoxFmtr" AND that are currently "checked"
//
var chks = this.get("srcNode").all("tbody tr td input.myCheckboxFmtr"); // get all checks
// in a perfect world ... i.e. one without IE 8-, we could just do ...
// var chkd = this.get("srcNode").all("tbody tr td input.myCheckboxFmtr:checked");
//
// Loop over the NodeList (using it's .each method) and append the employee name to the message.
// Note: 'chkd' contains nodes of the INPUT checkboxes, step back twice to get the parent TR node
//
var msg = "The following Employees will be processed;\n\n"; // define the beginning of our message string
chks.each( function(item){
if ( !item.get('checked') ) return;
var rec = this.getRecord( item.ancestor().ancestor() ); // item is INPUT, first parent is TD, second is TR
msg += rec.get('em_id') + ' : ' + rec.get('ename') + "\n";
}, this);
alert(msg);
}, myDT);
});
</script>
<script type="text/x-template" id="status-template"> <!-- used in Y.delegate ... tbody tr td -->
<table id="dt_info" width="250">
<tr><th width="50%">Record clientId :</th><td>{rec_id}</td></tr>
<tr><th>Record Index :</th><td>{rec_index}</td></tr>
<tr><th>Col Key :</th><td>{col_key}</td></tr>
<tr><th>Col Index :</th><td>{col_index}</td></tr>
<tr><th>Raw Data :</th><td>{raw_data}</td></tr>
</table>
</script>
<script type="text/x-template" id="dialog-template"> <!-- used in Function showDT_Dialog -->
<form name="roweditor">
<fieldset id="myfieldset">
<table>
<tr><th>Title :</th><td><input type="text" name="title" value="{title}" /></td></tr>
<tr><th>Published :</th><td><input type="select" name="published" value="{published}" /></td></tr>
<tr><th>Body : </th><td><textarea name="body" rows=10 cols=45>{body}</textarea></td></tr>
</table>
</fieldset>
<input type="hidden" name="frmRecord" value="{valRecord}" />
<input type="hidden" name="frmInsertFlag" value="{valInsert}" />
</form>
</script>
<div id="main">
<h2>Manage Entries</h2>
<table>
<tr valign="top">
<td>
<div id="dtable"></div>
<br/><button id="btnProcess">Process SELECTED Rows</button> &nbsp; &nbsp; <button id="btnInsert">Insert New Row</button>
</td>
<td width="300" align="center">
TD Click Status:<br/>
<div id="idStatus"></div>
</td>
</tr>
</table>
<br/>
<div id="idPanel">
<div class="yui3-widget-hd"></div>
<div class="yui3-widget-bd"></div>
</div>
<div id="idCalendarBox" style="z-index:12;"></div>
</div>
{
"data_table" : [
{
"parent" : null,
"entryid" : 1,
"path" : "1",
"author" : {
"email" : "devin.austin@gmail.com",
"password" : "$2a$08$ZY0mDB7wEHdPZWEtB9YPK.SH332XctfUj77eSnLfQLSXxsr/Encxu",
"created_at" : null,
"userid" : 1,
"entries" : [
{
"title" : "huhuhuhuhuhuh",
"entryid" : 1
}
],
"updated_at" : null,
"name" : "dhoss"
},
"children" : [],
"published" : 1,
"created_at" : "January 31, 2012 at 21:50:10 UTC",
"updated_at" : null,
"title" : "huhuhuhuhuhuh",
"display_title" : "huhuhuhuhuhuh",
"reply_count" : 0
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment