PHP Code:
/**
* @class Ext.grid.SmartCheckboxSelectionModel
* @extends Ext.grid.RowSelectionModel
*
* A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
* By passing in a dataIndex and a store, it can pre-check (and select) rows after it renders.
* Included are all the standard navigation options of a RowSelectionModel, including Up/Down arrow keyMaps and Ctrl/Shift selections.
*
* @param (object) config The configuration options, as highlighted below
* @param (string) dataIndex The field that contains the boolean true/false value for checked/selected rows
*
* @copyright June 4, 2008 <last updated: March 23, 2011>
* @author NoahK17
* @version 1.8
*/
Ext.grid.SmartCheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
/**
* @width (int) The default width in pixels of the checkbox column (defaults to 20).
*/
width: 20,
/**
* @sortable (bool) Set to true if you want the checkbox column to be sortable.
*/
sortable: false,
/**
* @email (bool) Will mimic email client functionality by separating out the selection of rows
* with the checking of rows, similar to how Yahoo! or Gmail works. One could then
* apply different actions on checked rows vs. selected rows.
* Defaults to false.
*/
email: false,
/**
* @excel (bool) Mimics excel functionality when clicking on rows or checkboxes. If set to true,
* all other rows will be deselected and unchecked except the row you most recently
* clicked. If set to false, all previous selections will remain selected and/or checked
* as you click around the grid (as if you were holding down CTRL and clicking).
* Defaults to true.
*/
excel: true,
/**
* @alwaysSelectOnCheck
* (bool) If set to true, clicking a checkbox will always select the row, working in conjunction
* with the email option.
* Defaults to false.
*
*/
alwaysSelectOnCheck: false,
/**
* @userStateProvider
* (bool) If set to true, the selection model will attempt to use the CookieState Provider by
* default, unless you are already using a different provider, such as Saki's HttpProvider,
* in which case the already existing provider will be used. If set to false, no provider
* will be used, which may allow for better compatibility.
* Defaults to true.
*
*/
useStateProvider: true,
// private
menuDisabled:true,
fixed:true,
id: 'checker',
dataIndex: '', // Define the dataIndex when you construct the selectionModel, not here
constructor: function()
{
// Define the header using a unique Id (e.g., ext-gen12)
this.headerId = Ext.id();
this.header = '<div id="'+ this.headerId +'" class="x-grid3-hd-checker">Â </div>';
// Add some events
this.addEvents(
/**
* @event check
* Fires when the checkbox is checked or unchecked.
* @param {SelectionModel} this
* @param {Number} rowIndex The checked row index
* @param {Ext.data.Record} r The checked record
* @param {Bool} checked The new checked value
*/
"check", // courtesy of Sencha forum user renku
/**
* @event beforerowdeselect
* Fires just before a row is de-selected.
* @param {SelectionModel} this
* @param {Number} rowIndex The row's row index
* @param {Ext.data.Record} r The row's record
*/
"beforerowdeselect" // user requested
);
// Call the parent
Ext.grid.SmartCheckboxSelectionModel.superclass.constructor.apply(this,arguments);
},
// private
initEvents : function(){
// Call the parent
Ext.grid.SmartCheckboxSelectionModel.superclass.initEvents.call(this);
// Assign the handlers for the mousedown events
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
}, this);
// Disable the rowNav created in our parent object, otherwise pressing DOWN will go down two rows!
this.rowNav.disable();
// Create our new rowNav that controls checkboxes as well
this.rowNav2 = new Ext.KeyNav(this.grid.getGridEl(), {
"up" : function(e){
if(!e.shiftKey){
if (!this.email) {
this.selectPreviousChecked(e.shiftKey);
} else {
this.selectPrevious(e.shiftKey);
}
} else if(this.last !== false && this.lastActive !== false) {
var last = this.last;
if (!this.email) {
this.selectRangeChecked(this.last, this.lastActive-1);
} else {
this.selectRange(this.last, this.lastActive-1);
}
this.grid.getView().focusRow(this.lastActive);
if (last !== false) {
this.last = last;
}
} else {
this.selectFirstRow();
if (!this.email) { this.toggleChecked(0, true); }
}
},
"down" : function(e){
if(!e.shiftKey){
if (!this.email) {
this.selectNextChecked(e.shiftKey);
} else {
this.selectNext(e.shiftKey);
}
} else if (this.last !== false && this.lastActive !== false) {
var last = this.last;
if (!this.email) {
this.selectRangeChecked(this.last, this.lastActive+1);
} else {
this.selectRange(this.last, this.lastActive+1);
}
this.grid.getView().focusRow(this.lastActive,true);
if (last !== false) {
this.last = last;
}
} else {
this.selectFirstRow();
if(!this.email){ this.toggleChecked(0, true); }
}
},
scope: this
});
// Listen for the movement of the columns
this.grid.on('columnmove', function(p){
var t = Ext.get(this.headerId);
if (t != null) {
if(t.dom.className != 'x-grid3-hd-checker'){
Ext.fly(t.dom.parentNode).removeClass('x-grid3-hd-checker');
}
}
});
// If we sent a store to the selModel, auto-select rows based on dataIndex
if (this.grid.store){
this.grid.store.on('load', function(p){
// This block of code checks the status of the checkbox header,
// and if checked, will check all other checkboxes (but not on the initial load)
var t = Ext.get(this.headerId);
if (t != null) {
if(t.dom.className == 'x-grid3-hd-checker'){
if (this.useStateProvider) {
if (Ext.state.Manager.loaded) {
var hd = Ext.fly(t.dom.parentNode);
var isChecked = hd.hasClass('x-grid3-hd-checker-on');
if (isChecked) {
hd.addClass('x-grid3-hd-checker-on');
if(!this.email){ this.selectAll(); }
this.selectAllChecked(true);
}
}
} else {
var hd = Ext.fly(t.dom.parentNode);
var isChecked = hd.hasClass('x-grid3-hd-checker-on');
if (isChecked) {
hd.addClass('x-grid3-hd-checker-on');
if(!this.email){ this.selectAll(); }
this.selectAllChecked(true);
}
}
} else {
Ext.fly(t.dom.parentNode).removeClass('x-grid3-hd-checker');
}
}
// This block of code will pre-select checkboxes based on the dataIndex supplied,
// but only on the initial load.
var dataIndex = this.grid.getSelectionModel().dataIndex; // the dataIndex for the selectionModel
var count = this.grid.store.getCount();
for (var i = 0, len = count; i < len; i++) {
var dataIndexValue = this.parseBoolean(p.data.items[i].data[dataIndex]); // the value of the dataIndex for each row
var isSelected = this.isSelected(i);
if (dataIndexValue == true || isSelected) {
if (this.useStateProvider) {
if (!Ext.state.Manager.loaded) {
// This code will only run the first time a grid is loaded
// Make sure that any "checked" rows are also selected
if (!this.email || this.alwaysSelectOnCheck) { this.grid.getSelectionModel().selectRow(i, true); }
}
} else {
if (!this.email || this.alwaysSelectOnCheck) { this.grid.getSelectionModel().selectRow(i, true); }
}
} else if(isSelected) {
// Let the state.Manager check the correct rows now
if(!this.email){ this.toggleChecked(i, true); }
} else {
// Uncheck everything else
if(!this.email){ this.toggleChecked(i, false); }
}
}
}, this);
}
},
/**
* private custom function written by Sencha forum user Greffin
*
* this ensures that the true/false value of the dataIndex
* is actually a boolean value and not a string
*
* @param (mixed) value the value you want to check
*
* @access private
*/
parseBoolean : function(value){
if (Ext.isDefined(value) && Ext.isString(value)) {
return (value.toLowerCase() == 'true' ? true:false);
}
return value;
},
/**
* private function that controls the checkboxes
*
* @param (int) rowIndex the row you want to toggle
* @param (bool) c optional flag set to either true (to check) or false (to uncheck)
* if no second param, the checkbox will toggle itself
*
* @access private
*/
toggleChecked : function(rowIndex, c){
if (this.locked) { return; }
var record = this.grid.store.getAt(rowIndex);
// Determine if we are checking, unchecking, or toggling
var checked = (typeof c === "boolean") ? c : !record.data[this.dataIndex];
// Set the record
record.set(this.dataIndex, checked);
// Fire the "check" event
this.fireEvent("check", this, rowIndex, record, checked);
},
/**
* private functions that toggles all checkboxes on or off depending on param
*
* @param (bool) c true to check all checkboxes, false to uncheck all checkboxes
* @param (int) e (optional) if an exception is given, all rows will be checked/unchecked except this row
*/
selectAllChecked : function(c, e){
if (this.locked) { return; }
var count = this.grid.store.getCount();
for (var i = 0, len = count; i < len; i++) {
if(c){
if(i !== e){
this.toggleChecked(i, true);
}
} else {
if(i !== e){
this.toggleChecked(i, false);
}
}
}
},
/**
* private function that clears all checkboxes
* specifically used to deal with shift+arrow keys,
* but can also be called with fast param to quickly uncheck everything
*
* @param (bool) fast true to quickly deselect everything with no exceptions
*
* @access private
*/
clearChecked : function(fast){
if (this.locked) { return; }
if (fast !== true) {
var count = this.grid.store.getCount();
for(var i = 0, len = count; i < len; i++){
var isSelected = this.isSelected(i);
if(!isSelected){
this.toggleChecked(i, false);
}
}
} else {
// Quick and dirty method to uncheck everything
this.selectAllChecked(false);
}
this.last = false;
},
/**
* private function used in conjuction with the shift key for checking multiple rows at once
*
* @access private
*/
selectRangeChecked : function(startRow, endRow, keepExisting){
if (this.locked) { return; }
if (!keepExisting) {
if(!this.email || this.alwaysSelectOnCheck){ this.clearSelections(); }
this.clearChecked();
}
if (startRow <= endRow) {
for(var i = startRow; i <= endRow; i++){
if(this.grid.store.getAt(i)){
this.toggleChecked(i, true);
if(!this.email || this.alwaysSelectOnCheck){ this.selectRow(i, true); }
}
}
} else {
for (var i = startRow; i >= endRow; i--) {
if (this.grid.store.getAt(i)) {
this.toggleChecked(i, true);
if(!this.email || this.alwaysSelectOnCheck){ this.selectRow(i, true); }
}
}
}
},
/**
* private function that is used with the UP arrow keyMap
*
* @access private
*/
selectPreviousChecked : function(keepExisting){
if(this.hasPrevious()){
// Select the next row
this.selectRow(this.last-1, keepExisting);
// Set the focus
this.grid.getView().focusRow(this.last);
if(!this.email){
// Check the current (selected) row
this.toggleChecked(this.last, true);
// Uncheck all other rows
this.selectAllChecked(false, this.last);
}
return true;
}
return false;
},
/**
* private function that is used with the DOWN arrow keyMap
*
* @access private
*/
selectNextChecked : function(keepExisting){
if(this.hasNext()){
// Select the next row
if(!this.email){ this.selectRow(this.last+1, keepExisting); }
// Set the focus
this.grid.getView().focusRow(this.last);
if(!this.email){
// Check the current (selected) row
this.toggleChecked(this.last, true);
// Uncheck all other rows
this.selectAllChecked(false, this.last);
}
return true;
}
return false;
},
/**
* private function that executes when you click on any row
* will keep other row selections active as you click around
*
* @access private
*/
handleMouseDown : function(g, rowIndex, e){
var t = e.getTarget('.ux-row-action-item');
if (!t) {
if(e.button !== 0 || this.isLocked()){
return;
};
var view = this.grid.getView();
var record = this.grid.store.getAt(rowIndex);
if (e.shiftKey && this.last !== false) {
var last = this.last;
this.selectRange(last, rowIndex, e.ctrlKey);
if(!this.email){ this.selectRangeChecked(last, rowIndex, e.ctrlKey); }
this.last = last; // reset the last
view.focusRow(rowIndex);
} else {
var isChecked = this.parseBoolean(record.data[this.dataIndex]);
var isSelected = this.isSelected(rowIndex);
if (isSelected) {
this.fireEvent("beforerowdeselect", this, rowIndex, record);
this.deselectRow(rowIndex);
if(!this.email){ this.toggleChecked(rowIndex, false); }
} else {
if (!this.excel) {
this.selectRow(rowIndex, true);
if(!this.email){
this.toggleChecked(rowIndex, true);
}
} else {
this.selectRow(rowIndex, e.ctrlKey);
if(!this.email){
this.selectRangeChecked(rowIndex, rowIndex, e.ctrlKey);
}
}
view.focusRow(rowIndex);
}
}
}
},
/**
* private function restricted to execute when you click a checkbox itself
*
* @access private
*/
onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
// Define variables
var view = this.grid.getView();
var rowIndex = view.findRowIndex(t);
var record = this.grid.store.getAt(rowIndex);
var isSelected = this.isSelected(rowIndex);
var isChecked = this.parseBoolean(record.data[this.dataIndex]);
// Logic to select/de-select rows and the checkboxes
if (!this.email || this.alwaysSelectOnCheck) {
if (isSelected){
if (!isChecked && this.alwaysSelectOnCheck) {
this.toggleChecked(rowIndex, true);
} else {
this.fireEvent("beforerowdeselect", this, rowIndex, record);
this.deselectRow(rowIndex);
this.toggleChecked(rowIndex, false);
}
} else {
this.selectRow(rowIndex, true);
this.toggleChecked(rowIndex, true);
view.focusRow(rowIndex);
}
} else {
if (isChecked){
this.toggleChecked(rowIndex, false);
} else {
this.toggleChecked(rowIndex, true);
}
}
view.focusRow(rowIndex);
}
// Load the state manager if one does not already exist
if (this.useStateManager) {
if (typeof Ext.state.Manager.getProvider().path == 'undefined') {
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
}
Ext.state.Manager.loaded = true;
}
},
/**
* private function that executes when you click the checkbox header
*
* @access private
*/
onHdMouseDown : function(e, t){
if(t.className == 'x-grid3-hd-checker'){
e.stopEvent();
var hd = Ext.fly(t.parentNode);
var isChecked = hd.hasClass('x-grid3-hd-checker-on');
if(isChecked){
hd.removeClass('x-grid3-hd-checker-on');
if(!this.email || this.alwaysSelectOnCheck){ this.clearSelections(); }
this.clearChecked(true); // the true param enables fast mode
} else {
hd.addClass('x-grid3-hd-checker-on');
if(!this.email || this.alwaysSelectOnCheck){ this.selectAll(); }
this.selectAllChecked(true);
}
}
// Load the state manager if one does not already exist
if (this.useStateManager) {
if (typeof Ext.state.Manager.getProvider().path == 'undefined') {
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
}
Ext.state.Manager.loaded = true;
}
},
/**
* private function that renders the proper checkbox based on your dataIndex variable
*
* @param (varchar) v the dataIndex passed into the selectionModel that contains whether a row is checked by default or not
*
* @access private
*/
renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
v = this.parseBoolean(v); // make sure the v variable is a boolean value
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'"> </div>';
}
});
/* eof */