summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions/grid
Side-by-side diff
Diffstat (limited to 'frontend/beta/js/YUI-extensions/grid') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js131
-rw-r--r--frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js325
-rw-r--r--frontend/beta/js/YUI-extensions/grid/EditorGrid.js16
-rw-r--r--frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js182
-rw-r--r--frontend/beta/js/YUI-extensions/grid/Grid.js965
-rw-r--r--frontend/beta/js/YUI-extensions/grid/GridDD.js101
-rw-r--r--frontend/beta/js/YUI-extensions/grid/GridView.js790
-rw-r--r--frontend/beta/js/YUI-extensions/grid/PagedGridView.js194
-rw-r--r--frontend/beta/js/YUI-extensions/grid/SelectionModel.js445
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js91
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js60
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js268
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js166
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js37
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js110
15 files changed, 3881 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js b/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js
new file mode 100644
index 0000000..1f93590
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js
@@ -0,0 +1,131 @@
+/**
+ * @class YAHOO.ext.grid.AbstractColumnModel
+ * @extends YAHOO.ext.util.Observable
+ * This abstract class defines the ColumnModel interface and provides default implementations of the events required by the Grid.
+ * @constructor
+*/
+YAHOO.ext.grid.AbstractColumnModel = function(){
+ // legacy events
+ this.onWidthChange = new YAHOO.util.CustomEvent('widthChanged');
+ this.onHeaderChange = new YAHOO.util.CustomEvent('headerChanged');
+ this.onHiddenChange = new YAHOO.util.CustomEvent('hiddenChanged');
+
+ this.events = {
+ /**
+ * @event widthchange
+ * Fires when the width of a column changes
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Number} newWidth The new width
+ */
+ 'widthchange': this.onWidthChange,
+ /**
+ * @event headerchange
+ * Fires when the text of a header changes
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Number} newText The new header text
+ */
+ 'headerchange': this.onHeaderChange,
+ /**
+ * @event hiddenchange
+ * Fires when a column is hidden or "unhidden"
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Number} hidden true if hidden, false otherwise
+ */
+ 'hiddenchange': this.onHiddenChange
+ };
+};
+
+YAHOO.ext.grid.AbstractColumnModel.prototype = {
+ fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
+ on : YAHOO.ext.util.Observable.prototype.on,
+ addListener : YAHOO.ext.util.Observable.prototype.addListener,
+ delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
+ removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
+ purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
+ bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
+
+ fireWidthChange : function(colIndex, newWidth){
+ this.onWidthChange.fireDirect(this, colIndex, newWidth);
+ },
+
+ fireHeaderChange : function(colIndex, newHeader){
+ this.onHeaderChange.fireDirect(this, colIndex, newHeader);
+ },
+
+ fireHiddenChange : function(colIndex, hidden){
+ this.onHiddenChange.fireDirect(this, colIndex, hidden);
+ },
+
+ /**
+ * Interface method - Returns the number of columns.
+ * @return {Number}
+ */
+ getColumnCount : function(){
+ return 0;
+ },
+
+ /**
+ * Interface method - Returns true if the specified column is sortable.
+ * @param {Number} col The column index
+ * @return {Boolean}
+ */
+ isSortable : function(col){
+ return false;
+ },
+
+ /**
+ * Interface method - Returns true if the specified column is hidden.
+ * @param {Number} col The column index
+ * @return {Boolean}
+ */
+ isHidden : function(col){
+ return false;
+ },
+
+ /**
+ * Interface method - Returns the sorting comparison function defined for the column (defaults to sortTypes.none).
+ * @param {Number} col The column index
+ * @return {Function}
+ */
+ getSortType : function(col){
+ return YAHOO.ext.grid.DefaultColumnModel.sortTypes.none;
+ },
+
+ /**
+ * Interface method - Returns the rendering (formatting) function defined for the column.
+ * @param {Number} col The column index
+ * @return {Function}
+ */
+ getRenderer : function(col){
+ return YAHOO.ext.grid.DefaultColumnModel.defaultRenderer;
+ },
+
+ /**
+ * Interface method - Returns the width for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getColumnWidth : function(col){
+ return 0;
+ },
+
+ /**
+ * Interface method - Returns the total width of all columns.
+ * @return {Number}
+ */
+ getTotalWidth : function(){
+ return 0;
+ },
+
+ /**
+ * Interface method - Returns the header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return '';
+ }
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js b/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js
new file mode 100644
index 0000000..fbdba26
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js
@@ -0,0 +1,325 @@
+/**
+ * @class YAHOO.ext.grid.DefaultColumnModel
+ * @extends YAHOO.ext.grid.AbstractColumnModel
+ * This is the default implementation of a ColumnModel used by the Grid. It defines
+ * the columns in the grid.
+ * <br>Usage:<br>
+ <pre><code>
+ var sort = YAHOO.ext.grid.DefaultColumnModel.sortTypes;
+ var myColumns = [
+ {header: "Ticker", width: 60, sortable: true, sortType: sort.asUCString},
+ {header: "Company Name", width: 150, sortable: true, sortType: sort.asUCString},
+ {header: "Market Cap.", width: 100, sortable: true, sortType: sort.asFloat},
+ {header: "$ Sales", width: 100, sortable: true, sortType: sort.asFloat, renderer: money},
+ {header: "Employees", width: 100, sortable: true, sortType: sort.asFloat}
+ ];
+ var colModel = new YAHOO.ext.grid.DefaultColumnModel(myColumns);
+ </code></pre>
+ * @constructor
+ * @param {Object} config The config object
+*/
+YAHOO.ext.grid.DefaultColumnModel = function(config){
+ YAHOO.ext.grid.DefaultColumnModel.superclass.constructor.call(this);
+ /**
+ * The config passed into the constructor
+ */
+ this.config = config;
+
+ /**
+ * The width of columns which have no width specified (defaults to 100)
+ * @type Number
+ */
+ this.defaultWidth = 100;
+ /**
+ * Default sortable of columns which have no sortable specified (defaults to false)
+ * @type Boolean
+ */
+ this.defaultSortable = false;
+};
+YAHOO.extendX(YAHOO.ext.grid.DefaultColumnModel, YAHOO.ext.grid.AbstractColumnModel, {
+
+ /**
+ * Returns the number of columns.
+ * @return {Number}
+ */
+ getColumnCount : function(){
+ return this.config.length;
+ },
+
+ /**
+ * Returns true if the specified column is sortable.
+ * @param {Number} col The column index
+ * @return {Boolean}
+ */
+ isSortable : function(col){
+ if(typeof this.config[col].sortable == 'undefined'){
+ return this.defaultSortable;
+ }
+ return this.config[col].sortable;
+ },
+
+ /**
+ * Returns the sorting comparison function defined for the column (defaults to sortTypes.none).
+ * @param {Number} col The column index
+ * @return {Function}
+ */
+ getSortType : function(col){
+ if(!this.dataMap){
+ // build a lookup so we don't search every time
+ var map = [];
+ for(var i = 0, len = this.config.length; i < len; i++){
+ map[this.getDataIndex(i)] = i;
+ }
+ this.dataMap = map;
+ }
+ col = this.dataMap[col];
+ if(!this.config[col].sortType){
+ return YAHOO.ext.grid.DefaultColumnModel.sortTypes.none;
+ }
+ return this.config[col].sortType;
+ },
+
+ /**
+ * Sets the sorting comparison function for a column.
+ * @param {Number} col The column index
+ * @param {Function} fn
+ */
+ setSortType : function(col, fn){
+ this.config[col].sortType = fn;
+ },
+
+
+ /**
+ * Returns the rendering (formatting) function defined for the column.
+ * @param {Number} col The column index
+ * @return {Function}
+ */
+ getRenderer : function(col){
+ if(!this.config[col].renderer){
+ return YAHOO.ext.grid.DefaultColumnModel.defaultRenderer;
+ }
+ return this.config[col].renderer;
+ },
+
+ /**
+ * Sets the rendering (formatting) function for a column.
+ * @param {Number} col The column index
+ * @param {Function} fn
+ */
+ setRenderer : function(col, fn){
+ this.config[col].renderer = fn;
+ },
+
+ /**
+ * Returns the width for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getColumnWidth : function(col){
+ return this.config[col].width || this.defaultWidth;
+ },
+
+ /**
+ * Sets the width for a column.
+ * @param {Number} col The column index
+ * @param {Number} width The new width
+ */
+ setColumnWidth : function(col, width, suppressEvent){
+ this.config[col].width = width;
+ this.totalWidth = null;
+ if(!suppressEvent){
+ this.onWidthChange.fireDirect(this, col, width);
+ }
+ },
+
+ /**
+ * Returns the total width of all columns.
+ * @param {Boolean} includeHidden True to include hidden column widths
+ * @return {Number}
+ */
+ getTotalWidth : function(includeHidden){
+ if(!this.totalWidth){
+ this.totalWidth = 0;
+ for(var i = 0; i < this.config.length; i++){
+ if(includeHidden || !this.isHidden(i)){
+ this.totalWidth += this.getColumnWidth(i);
+ }
+ }
+ }
+ return this.totalWidth;
+ },
+
+ /**
+ * Returns the header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return this.config[col].header;
+ },
+
+ /**
+ * Sets the header for a column.
+ * @param {Number} col The column index
+ * @param {String} header The new header
+ */
+ setColumnHeader : function(col, header){
+ this.config[col].header = header;
+ this.onHeaderChange.fireDirect(this, col, header);
+ },
+
+ /**
+ * Returns the tooltip for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnTooltip : function(col){
+ return this.config[col].tooltip;
+ },
+ /**
+ * Sets the tooltip for a column.
+ * @param {Number} col The column index
+ * @param {String} tooltip The new tooltip
+ */
+ setColumnTooltip : function(col, header){
+ this.config[col].tooltip = tooltip;
+ },
+
+ /**
+ * Returns the dataIndex for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getDataIndex : function(col){
+ if(typeof this.config[col].dataIndex != 'number'){
+ return col;
+ }
+ return this.config[col].dataIndex;
+ },
+
+ /**
+ * Sets the dataIndex for a column.
+ * @param {Number} col The column index
+ * @param {Number} dataIndex The new dataIndex
+ */
+ setDataIndex : function(col, dataIndex){
+ this.config[col].dataIndex = dataIndex;
+ },
+ /**
+ * Returns true if the cell is editable.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index
+ * @return {Boolean}
+ */
+ isCellEditable : function(colIndex, rowIndex){
+ return this.config[colIndex].editable || (typeof this.config[colIndex].editable == 'undefined' && this.config[colIndex].editor);
+ },
+
+ /**
+ * Returns the editor defined for the cell/column.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index
+ * @return {Object}
+ */
+ getCellEditor : function(colIndex, rowIndex){
+ return this.config[colIndex].editor;
+ },
+
+ /**
+ * Sets if a column is editable.
+ * @param {Number} col The column index
+ * @param {Boolean} editable True if the column is editable
+ */
+ setEditable : function(col, editable){
+ this.config[col].editable = editable;
+ },
+
+
+ /**
+ * Returns true if the column is hidden.
+ * @param {Number} colIndex The column index
+ * @return {Boolean}
+ */
+ isHidden : function(colIndex){
+ return this.config[colIndex].hidden;
+ },
+
+
+ /**
+ * Returns true if the column width cannot be changed
+ */
+ isFixed : function(colIndex){
+ return this.config[colIndex].fixed;
+ },
+
+ /**
+ * Returns true if the column cannot be resized
+ * @return {Boolean}
+ */
+ isResizable : function(colIndex){
+ return this.config[colIndex].resizable !== false;
+ },
+ /**
+ * Sets if a column is hidden.
+ * @param {Number} colIndex The column index
+ */
+ setHidden : function(colIndex, hidden){
+ this.config[colIndex].hidden = hidden;
+ this.totalWidth = null;
+ this.fireHiddenChange(colIndex, hidden);
+ },
+
+ /**
+ * Sets the editor for a column.
+ * @param {Number} col The column index
+ * @param {Object} editor The editor object
+ */
+ setEditor : function(col, editor){
+ this.config[col].editor = editor;
+ }
+});
+
+/**
+ * Defines the default sorting (casting?) comparison functions used when sorting data:
+ * <br>&nbsp;&nbsp;sortTypes.none - sorts data as it is without casting or parsing (the default)
+ * <br>&nbsp;&nbsp;sortTypes.asUCString - case insensitive string
+ * <br>&nbsp;&nbsp;sortTypes.asDate - attempts to parse data as a date
+ * <br>&nbsp;&nbsp;sortTypes.asFloat
+ * <br>&nbsp;&nbsp;sortTypes.asInt
+ * @static
+ */
+YAHOO.ext.grid.DefaultColumnModel.sortTypes = {
+ none : function(s) {
+ return s;
+ },
+
+ asUCString : function(s) {
+ return String(s).toUpperCase();
+ },
+
+ asDate : function(s) {
+ if(s instanceof Date){
+ return s.getTime();
+ }
+ return Date.parse(String(s));
+ },
+
+ asFloat : function(s) {
+ var val = parseFloat(String(s).replace(/,/g, ''));
+ if(isNaN(val)) val = 0;
+ return val;
+ },
+
+ asInt : function(s) {
+ var val = parseInt(String(s).replace(/,/g, ''));
+ if(isNaN(val)) val = 0;
+ return val;
+ }
+};
+
+YAHOO.ext.grid.DefaultColumnModel.defaultRenderer = function(value){
+ if(typeof value == 'string' && value.length < 1){
+ return '&#160;';
+ }
+ return value;
+}
diff --git a/frontend/beta/js/YUI-extensions/grid/EditorGrid.js b/frontend/beta/js/YUI-extensions/grid/EditorGrid.js
new file mode 100644
index 0000000..e7405a0
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/EditorGrid.js
@@ -0,0 +1,16 @@
+/**
+ * @class YAHOO.ext.grid.EditorGrid
+ * @extends YAHOO.ext.grid.Grid
+ * Shortcut class for creating and editable grid.
+ * @param {String/HTMLElement/YAHOO.ext.Element} container The element into which this grid will be rendered -
+ * The container MUST have some type of size defined for the grid to fill. The container will be
+ * automatically set to position relative if it isn't already.
+ * @param {Object} dataModel The data model to bind to
+ * @param {Object} colModel The column model with info about this grid's columns
+ */
+YAHOO.ext.grid.EditorGrid = function(container, dataModel, colModel){
+ YAHOO.ext.grid.EditorGrid.superclass.constructor.call(this, container, dataModel,
+ colModel, new YAHOO.ext.grid.EditorSelectionModel());
+ this.container.addClass('yeditgrid');
+};
+YAHOO.extendX(YAHOO.ext.grid.EditorGrid, YAHOO.ext.grid.Grid);
diff --git a/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js b/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js
new file mode 100644
index 0000000..c1cb240
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js
@@ -0,0 +1,182 @@
+
+/**
+ @class YAHOO.ext.grid.EditorSelectionModel
+ * Extends {@link YAHOO.ext.grid.DefaultSelectionModel} to enable cell navigation. <br><br>
+ @extends YAHOO.ext.grid.DefaultSelectionModel
+ @constructor
+ */
+YAHOO.ext.grid.EditorSelectionModel = function(){
+ YAHOO.ext.grid.EditorSelectionModel.superclass.constructor.call(this);
+ /** Number of clicks to activate a cell (for editing) - valid values are 1 or 2
+ * @type Number */
+ this.clicksToActivateCell = 1;
+ this.events['cellactivate'] = new YAHOO.util.CustomEvent('cellactivate');
+};
+
+YAHOO.extendX(YAHOO.ext.grid.EditorSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.disableArrowNavigation = false;
+YAHOO.ext.grid.EditorSelectionModel.prototype.controlForArrowNavigation = false;
+
+/** @ignore */
+YAHOO.ext.grid.EditorSelectionModel.prototype.initEvents = function(){
+ this.grid.addListener("cellclick", this.onCellClick, this, true);
+ this.grid.addListener("celldblclick", this.onCellDblClick, this, true);
+ this.grid.addListener("keydown", this.keyDown, this, true);
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.onCellClick = function(grid, rowIndex, colIndex){
+ if(this.clicksToActivateCell == 1){
+ var row = this.grid.getRow(rowIndex);
+ var cell = row.childNodes[colIndex];
+ if(cell){
+ this.activate(row, cell);
+ }
+ }
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.activate = function(row, cell){
+ this.fireEvent('cellactivate', this, row, cell);
+ this.grid.doEdit(row, cell);
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.onCellDblClick = function(grid, rowIndex, colIndex){
+ if(this.clicksToActivateCell == 2){
+ var row = this.grid.getRow(rowIndex);
+ var cell = row.childNodes[colIndex];
+ if(cell){
+ this.activate(row, cell);
+ }
+ }
+};
+
+/** @ignore */
+YAHOO.ext.grid.EditorSelectionModel.prototype.setRowState = function(row, selected){
+ YAHOO.ext.grid.EditorSelectionModel.superclass.setRowState.call(this, row, false, false);
+};
+/** @ignore */
+YAHOO.ext.grid.EditorSelectionModel.prototype.focusRow = function(row, selected){
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.getEditorCellAfter = function(cell, spanRows){
+ var g = this.grid;
+ var next = g.getCellAfter(cell);
+ while(next && !g.colModel.isCellEditable(next.columnIndex)){
+ next = g.getCellAfter(next);
+ }
+ if(!next && spanRows){
+ var row = g.getRowAfter(g.getRowFromChild(cell));
+ if(row){
+ next = g.getFirstCell(row);
+ if(!g.colModel.isCellEditable(next.columnIndex)){
+ next = this.getEditorCellAfter(next);
+ }
+ }
+ }
+ return next;
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.getEditorCellBefore = function(cell, spanRows){
+ var g = this.grid;
+ var prev = g.getCellBefore(cell);
+ while(prev && !g.colModel.isCellEditable(prev.columnIndex)){
+ prev = g.getCellBefore(prev);
+ }
+ if(!prev && spanRows){
+ var row = g.getRowBefore(g.getRowFromChild(cell));
+ if(row){
+ prev = g.getLastCell(row);
+ if(!g.colModel.isCellEditable(prev.columnIndex)){
+ prev = this.getEditorCellBefore(prev);
+ }
+ }
+ }
+ return prev;
+};
+
+YAHOO.ext.grid.EditorSelectionModel.prototype.allowArrowNav = function(e){
+ return (!this.disableArrowNavigation && (!this.controlForArrowNavigation || e.ctrlKey));
+}
+/** @ignore */
+YAHOO.ext.grid.EditorSelectionModel.prototype.keyDown = function(e){
+ var g = this.grid, cm = g.colModel, cell = g.getEditingCell();
+ if(!cell) return;
+ var newCell;
+ switch(e.browserEvent.keyCode){
+ case e.TAB:
+ if(e.shiftKey){
+ newCell = this.getEditorCellBefore(cell, true);
+ }else{
+ newCell = this.getEditorCellAfter(cell, true);
+ }
+ e.preventDefault();
+ break;
+ case e.DOWN:
+ if(this.allowArrowNav(e)){
+ var next = g.getRowAfter(g.getRowFromChild(cell));
+ if(next){
+ newCell = next.childNodes[cell.columnIndex];
+ }
+ }
+ break;
+ case e.UP:
+ if(this.allowArrowNav(e)){
+ var prev = g.getRowBefore(g.getRowFromChild(cell));
+ if(prev){
+ newCell = prev.childNodes[cell.columnIndex];
+ }
+ }
+ break;
+ case e.RETURN:
+ if(e.shiftKey){
+ var prev = g.getRowBefore(g.getRowFromChild(cell));
+ if(prev){
+ newCell = prev.childNodes[cell.columnIndex];
+ }
+ }else{
+ var next = g.getRowAfter(g.getRowFromChild(cell));
+ if(next){
+ newCell = next.childNodes[cell.columnIndex];
+ }
+ }
+ break;
+ case e.RIGHT:
+ if(this.allowArrowNav(e)){
+ newCell = this.getEditorCellAfter(cell);
+ }
+ break;
+ case e.LEFT:
+ if(this.allowArrowNav(e)){
+ newCell = this.getEditorCellBefore(cell);
+ }
+ break;
+ };
+ if(newCell){
+ this.activate(g.getRowFromChild(newCell), newCell);
+ e.stopEvent();
+ }
+};
+
+/**
+ * @class YAHOO.ext.grid.EditorAndSelectionModel
+ */
+YAHOO.ext.grid.EditorAndSelectionModel = function(){
+ YAHOO.ext.grid.EditorAndSelectionModel.superclass.constructor.call(this);
+ this.events['cellactivate'] = new YAHOO.util.CustomEvent('cellactivate');
+};
+
+YAHOO.extendX(YAHOO.ext.grid.EditorAndSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
+
+YAHOO.ext.grid.EditorAndSelectionModel.prototype.initEvents = function(){
+ YAHOO.ext.grid.EditorAndSelectionModel.superclass.initEvents.call(this);
+ this.grid.addListener("celldblclick", this.onCellDblClick, this, true);
+};
+
+YAHOO.ext.grid.EditorAndSelectionModel.prototype.onCellDblClick = function(grid, rowIndex, colIndex){
+ var row = this.grid.getRow(rowIndex);
+ var cell = row.childNodes[colIndex];
+ if(cell){
+ this.fireEvent('cellactivate', this, row, cell);
+ this.grid.doEdit(row, cell);
+ }
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/Grid.js b/frontend/beta/js/YUI-extensions/grid/Grid.js
new file mode 100644
index 0000000..46d5de4
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/Grid.js
@@ -0,0 +1,965 @@
+/**
+ * @class YAHOO.ext.grid.Grid
+ * @extends YAHOO.ext.util.Observable
+ * This class represents the primary interface of a component based grid control.
+ * <br><br>Usage:<pre><code>
+ var grid = new YAHOO.ext.grid.Grid('my-container-id', dataModel, columnModel);
+ // set any options
+ grid.render();
+ // or using a config
+ var grid = new YAHOO.ext.grid.Grid('my-container-id', {
+ dataModel: myDataModel,
+ colModel: myColModel,
+ selModel: mySelectionModel,
+ autoSizeColumns: true,
+ monitorWindowResize: false,
+ trackMouseOver: true
+ }).render();
+ * </code></pre>
+ * <b>Common Problems:</b><br/>
+ * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
+ * element will correct this<br/>
+ * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
+ * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
+ * are unpredictable.<br/>
+ * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
+ * grid to calculate dimensions/offsets.<br/>
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @requires YAHOO.ext.Element
+ * @requires YAHOO.ext.util.Browser
+ * @requires YAHOO.ext.util.CSS
+ * @requires YAHOO.ext.SplitBar
+ * @requires YAHOO.ext.EventObject
+ * @constructor
+ * @param {String/HTMLElement/YAHOO.ext.Element} container The element into which this grid will be rendered -
+ * The container MUST have some type of size defined for the grid to fill. The container will be
+ * automatically set to position relative if it isn't already.
+ * @param {Object} config A config object that sets properties on this grid OR the data model to bind to
+ * @param {Object} colModel (optional) The column model with info about this grid's columns
+ * @param {Object} selectionModel (optional) The selection model for this grid (defaults to DefaultSelectionModel)
+ */
+YAHOO.ext.grid.Grid = function(container, config, colModel, selectionModel){
+ /** @private */
+ this.container = YAHOO.ext.Element.get(container);
+ this.container.update('');
+ this.container.setStyle('overflow', 'hidden');
+ this.id = this.container.id;
+ this.rows = [];
+ this.rowCount = 0;
+ this.fieldId = null;
+ var dataModel = config; // for legacy pre config support
+ this.dataModel = dataModel;
+ this.colModel = colModel;
+ this.selModel = selectionModel;
+ this.activeEditor = null;
+ this.editingCell = null;
+
+
+ if(typeof config == 'object' && !config.getRowCount){// must be config object
+ YAHOO.ext.util.Config.apply(this, config);
+ }
+
+ /** @private */
+ this.setValueDelegate = this.setCellValue.createDelegate(this);
+
+ /** @private */
+ this.events = {
+ // raw events
+ /**
+ * @event click
+ * The raw click event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'click' : true,
+ /**
+ * @event dblclick
+ * The raw dblclick event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'dblclick' : true,
+ /**
+ * @event mousedown
+ * The raw mousedown event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'mousedown' : true,
+ /**
+ * @event mouseup
+ * The raw mouseup event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'mouseup' : true,
+ /**
+ * @event mouseover
+ * The raw mouseover event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'mouseover' : true,
+ /**
+ * @event mouseout
+ * The raw mouseout event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'mouseout' : true,
+ /**
+ * @event keypress
+ * The raw keypress event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'keypress' : true,
+ /**
+ * @event keydown
+ * The raw keydown event for the entire grid.
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'keydown' : true,
+
+ // custom events
+
+ /**
+ * @event cellclick
+ * Fires when a cell is clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'cellclick' : true,
+ /**
+ * @event celldblclick
+ * Fires when a cell is double clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'celldblclick' : true,
+ /**
+ * @event rowclick
+ * Fires when a row is clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'rowclick' : true,
+ /**
+ * @event rowdblclick
+ * Fires when a row is double clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'rowdblclick' : true,
+ /**
+ * @event headerclick
+ * Fires when a header is clicked
+ * @param {Grid} this
+ * @param {Number} columnIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'headerclick' : true,
+ /**
+ * @event rowcontextmenu
+ * Fires when a row is right clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'rowcontextmenu' : true,
+ /**
+ * @event cellcontextmenu
+ * Fires when a cell is right clicked
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {Number} cellIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'cellcontextmenu' : true,
+ /**
+ * @event headercontextmenu
+ * Fires when a header is right clicked
+ * @param {Grid} this
+ * @param {Number} columnIndex
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'headercontextmenu' : true,
+ /**
+ * @event beforeedit
+ * Fires before a cell is edited
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ */
+ 'beforeedit' : true,
+ /**
+ * @event afteredit
+ * Fires after a cell is edited
+ * @param {Grid} this
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ */
+ 'afteredit' : true,
+ /**
+ * @event bodyscroll
+ * Fires when the body element is scrolled
+ * @param {Number} scrollLeft
+ * @param {Number} scrollTop
+ */
+ 'bodyscroll' : true,
+ /**
+ * @event columnresize
+ * Fires when the user resizes a column
+ * @param {Number} columnIndex
+ * @param {Number} newSize
+ */
+ 'columnresize' : true,
+ /**
+ * @event startdrag
+ * Fires when row(s) start being dragged
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'startdrag' : true,
+ /**
+ * @event enddrag
+ * Fires when a drag operation is complete
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'enddrag' : true,
+ /**
+ * @event dragdrop
+ * Fires when dragged row(s) are dropped on a valid DD target
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {String} targetId The target drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'dragdrop' : true,
+ /**
+ * @event dragover
+ * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {String} targetId The target drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'dragover' : true,
+ /**
+ * @event dragenter
+ * Fires when the dragged row(s) first cross another DD target while being dragged
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {String} targetId The target drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'dragenter' : true,
+ /**
+ * @event dragout
+ * Fires when the dragged row(s) leave another DD target while being dragged
+ * @param {Grid} this
+ * @param {YAHOO.ext.GridDD} dd The drag drop object
+ * @param {String} targetId The target drag drop object
+ * @param {event} e The raw browser event
+ */
+ 'dragout' : true
+ };
+};
+
+YAHOO.ext.grid.Grid.prototype = {
+ /** The minimum width a column can be resized to. (Defaults to 25)
+ * @type Number */
+ minColumnWidth : 25,
+
+ /** True to automatically resize the columns to fit their content <b>on initial render</b>
+ * @type Boolean */
+ autoSizeColumns : false,
+
+ /** True to measure headers with column data when auto sizing columns
+ * @type Boolean */
+ autoSizeHeaders : false,
+
+ /**
+ * True to autoSize the grid when the window resizes - defaults to true
+ */
+ monitorWindowResize : true,
+
+ /** If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
+ * rows measured to get a columns size - defaults to 0 (all rows).
+ * @type Number */
+ maxRowsToMeasure : 0,
+
+ /** True to highlight rows when the mouse is over (default is false)
+ * @type Boolean */
+ trackMouseOver : false,
+
+ /** True to enable drag and drop of rows
+ * @type Boolean */
+ enableDragDrop : false,
+
+ /** True to stripe the rows (default is true)
+ * @type Boolean */
+ stripeRows : true,
+ /** True to fit the height of the grid container to the height of the data (defaults to false)
+ * @type Boolean */
+ autoHeight : false,
+
+ /** True to fit the width of the grid container to the width of the columns (defaults to false)
+ * @type Boolean */
+ autoWidth : false,
+
+ /**
+ * The view used by the grid. This can be set before a call to render().
+ * Defaults to a YAHOO.ext.grid.GridView or PagedGridView depending on the data model.
+ * @type Object
+ */
+ view : null,
+
+ /** A regular expression defining tagNames
+ * allowed to have text selection (Defaults to <code>/INPUT|TEXTAREA|SELECT/i</code>) */
+ allowTextSelectionPattern : /INPUT|TEXTAREA|SELECT/i,
+
+ /**
+ * Called once after all setup has been completed and the grid is ready to be rendered.
+ * @return {YAHOO.ext.grid.Grid} this
+ */
+ render : function(){
+ if((!this.container.dom.offsetHeight || this.container.dom.offsetHeight < 20)
+ || this.container.getStyle('height') == 'auto'){
+ this.autoHeight = true;
+ }
+ if((!this.container.dom.offsetWidth || this.container.dom.offsetWidth < 20)){
+ this.autoWidth = true;
+ }
+ if(!this.view){
+ if(this.dataModel.isPaged()){
+ this.view = new YAHOO.ext.grid.PagedGridView();
+ }else{
+ this.view = new YAHOO.ext.grid.GridView();
+ }
+ }
+ this.view.init(this);
+ this.el = getEl(this.view.render(), true);
+ var c = this.container;
+ c.mon("click", this.onClick, this, true);
+ c.mon("dblclick", this.onDblClick, this, true);
+ c.mon("contextmenu", this.onContextMenu, this, true);
+ c.mon("selectstart", this.cancelTextSelection, this, true);
+ c.mon("mousedown", this.cancelTextSelection, this, true);
+ c.mon("mousedown", this.onMouseDown, this, true);
+ c.mon("mouseup", this.onMouseUp, this, true);
+ if(this.trackMouseOver){
+ this.el.mon("mouseover", this.onMouseOver, this, true);
+ this.el.mon("mouseout", this.onMouseOut, this, true);
+ }
+ c.mon("keypress", this.onKeyPress, this, true);
+ c.mon("keydown", this.onKeyDown, this, true);
+ this.init();
+ return this;
+ },
+
+ init : function(){
+ this.rows = this.el.dom.rows;
+ if(!this.disableSelection){
+ if(!this.selModel){
+ this.selModel = new YAHOO.ext.grid.DefaultSelectionModel(this);
+ }
+ this.selModel.init(this);
+ this.selModel.onSelectionChange.subscribe(this.updateField, this, true);
+ }else{
+ this.selModel = new YAHOO.ext.grid.DisableSelectionModel(this);
+ this.selModel.init(this);
+ }
+
+ if(this.enableDragDrop){
+ this.dd = new YAHOO.ext.grid.GridDD(this, this.container.dom);
+ }
+ },
+
+ /**
+ * Resets the grid for use with a new configuration and/or data and column models. After calling this function
+ * you will need to call render() again. Any listeners for this grid will be retained.
+ * Warning: any listeners manually attached (not through the grid) to the grid's container
+ * element will be removed.
+ * @param {Object} config Standard config object with properties to set on this grid
+ * @return {YAHOO.ext.grid.Grid} this
+ */
+ reset : function(config){
+ this.destroy(false, true);
+ YAHOO.ext.util.Config.apply(this, config);
+ return this;
+ },
+
+ /**
+ * Destroy this grid.
+ * @param {Boolean} removeEl True to remove the element
+ */
+ destroy : function(removeEl, keepListeners){
+ var c = this.container;
+ c.removeAllListeners();
+ this.view.destroy();
+ YAHOO.ext.EventManager.removeResizeListener(this.view.onWindowResize, this.view);
+ this.view = null;
+ this.colModel.purgeListeners();
+ if(!keepListeners){
+ this.purgeListeners();
+ }
+ c.update('');
+ if(removeEl === true){
+ c.remove();
+ }
+ },
+
+ /**
+ * Replace the current data model with a new one (experimental)
+ * @param {DataModel} dm The new data model
+ * @pram {Boolean} rerender true to render the grid rows from scratch
+ */
+ setDataModel : function(dm, rerender){
+ this.view.unplugDataModel(this.dataModel);
+ this.dataModel = dm;
+ this.view.plugDataModel(dm);
+ if(rerender){
+ dm.fireEvent('datachanged');
+ }
+ },
+
+ onMouseDown : function(e){
+ this.fireEvent('mousedown', e);
+ },
+
+ onMouseUp : function(e){
+ this.fireEvent('mouseup', e);
+ },
+
+ onMouseOver : function(e){
+ this.fireEvent('mouseover', e);
+ },
+
+ onMouseOut : function(e){
+ this.fireEvent('mouseout', e);
+ },
+
+ onKeyPress : function(e){
+ this.fireEvent('keypress', e);
+ },
+
+ onKeyDown : function(e){
+ this.fireEvent('keydown', e);
+ },
+
+ fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
+ on : YAHOO.ext.util.Observable.prototype.on,
+ addListener : YAHOO.ext.util.Observable.prototype.addListener,
+ delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
+ removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
+ purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
+ bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
+
+ onClick : function(e){
+ this.fireEvent('click', e);
+ var target = e.getTarget();
+ var row = this.getRowFromChild(target);
+ var cell = this.getCellFromChild(target);
+ var header = this.getHeaderFromChild(target);
+ if(cell){
+ this.fireEvent('cellclick', this, row.rowIndex, cell.columnIndex, e);
+ }
+ if(row){
+ this.fireEvent('rowclick', this, row.rowIndex, e);
+ }
+ if(header){
+ this.fireEvent('headerclick', this, header.columnIndex, e);
+ }
+ },
+
+ onContextMenu : function(e){
+ var target = e.getTarget();
+ var row = this.getRowFromChild(target);
+ var cell = this.getCellFromChild(target);
+ var header = this.getHeaderFromChild(target);
+ if(cell){
+ this.fireEvent('cellcontextmenu', this, row.rowIndex, cell.columnIndex, e);
+ }
+ if(row){
+ this.fireEvent('rowcontextmenu', this, row.rowIndex, e);
+ }
+ if(header){
+ this.fireEvent('headercontextmenu', this, header.columnIndex, e);
+ }
+ e.preventDefault();
+ },
+
+ onDblClick : function(e){
+ this.fireEvent('dblclick', e);
+ var target = e.getTarget();
+ var row = this.getRowFromChild(target);
+ var cell = this.getCellFromChild(target);
+ if(row){
+ this.fireEvent('rowdblclick', this, row.rowIndex, e);
+ }
+ if(cell){
+ this.fireEvent('celldblclick', this, row.rowIndex, cell.columnIndex, e);
+ }
+ },
+
+ /**
+ * Starts editing the specified for the specified row/column
+ * @param {Number} rowIndex
+ * @param {Number} colIndex
+ */
+ startEditing : function(rowIndex, colIndex){
+ var row = this.rows[rowIndex];
+ var cell = row.childNodes[colIndex];
+ this.stopEditing();
+ setTimeout(this.doEdit.createDelegate(this, [row, cell]), 10);
+ },
+
+ /**
+ * Stops any active editing
+ */
+ stopEditing : function(){
+ if(this.activeEditor){
+ this.activeEditor.stopEditing();
+ }
+ },
+
+ /** @ignore */
+ doEdit : function(row, cell){
+ if(!row || !cell) return;
+ var cm = this.colModel;
+ var dm = this.dataModel;
+ var colIndex = cell.columnIndex;
+ var rowIndex = row.rowIndex;
+ if(cm.isCellEditable(colIndex, rowIndex)){
+ var ed = cm.getCellEditor(colIndex, rowIndex);
+ if(ed){
+ if(this.activeEditor){
+ this.activeEditor.stopEditing();
+ }
+ this.fireEvent('beforeedit', this, rowIndex, colIndex);
+ this.activeEditor = ed;
+ this.editingCell = cell;
+ this.view.ensureVisible(row, true);
+ try{
+ cell.focus();
+ }catch(e){}
+ ed.init(this, this.el.dom.parentNode, this.setValueDelegate);
+ var value = dm.getValueAt(rowIndex, cm.getDataIndex(colIndex));
+ // set timeout so firefox stops editing before starting a new edit
+ setTimeout(ed.startEditing.createDelegate(ed, [value, row, cell]), 1);
+ }
+ }
+ },
+
+ setCellValue : function(value, rowIndex, colIndex){
+ this.dataModel.setValueAt(value, rowIndex, this.colModel.getDataIndex(colIndex));
+ this.fireEvent('afteredit', this, rowIndex, colIndex);
+ },
+
+ /** @ignore Called when text selection starts or mousedown to prevent default */
+ cancelTextSelection : function(e){
+ var target = e.getTarget();
+ if(target && target != this.el.dom.parentNode && !this.allowTextSelectionPattern.test(target.tagName)){
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Causes the grid to manually recalculate it's dimensions. Generally this is done automatically,
+ * but if manual update is required this method will initiate it.
+ */
+ autoSize : function(){
+ this.view.updateWrapHeight();
+ this.view.adjustForScroll();
+ },
+
+ /**
+ * Scrolls the grid to the specified row
+ * @param {Number/HTMLElement} row The row object or index of the row
+ */
+ scrollTo : function(row){
+ if(typeof row == 'number'){
+ row = this.rows[row];
+ }
+ this.view.ensureVisible(row, true);
+ },
+
+ /** @private */
+ getEditingCell : function(){
+ return this.editingCell;
+ },
+
+ /**
+ * Binds this grid to the field with the specified id. Initially reads and parses the comma
+ * delimited ids in the field and selects those items. All selections made in the grid
+ * will be persisted to the field by their ids comma delimited.
+ * @param {String} The id of the field to bind to
+ */
+ bindToField : function(fieldId){
+ this.fieldId = fieldId;
+ this.readField();
+ },
+
+ /** @private */
+ updateField : function(){
+ if(this.fieldId){
+ var field = YAHOO.util.Dom.get(this.fieldId);
+ field.value = this.getSelectedRowIds().join(',');
+ }
+ },
+
+ /**
+ * Causes the grid to read and select the ids from the bound field - See {@link #bindToField}.
+ */
+ readField : function(){
+ if(this.fieldId){
+ var field = YAHOO.util.Dom.get(this.fieldId);
+ var values = field.value.split(',');
+ var rows = this.getRowsById(values);
+ this.selModel.selectRows(rows, false);
+ }
+ },
+
+ /**
+ * Returns the table row at the specified index
+ * @param {Number} index
+ * @return {HTMLElement}
+ */
+ getRow : function(index){
+ return this.rows[index];
+ },
+
+ /**
+ * Returns the rows that have the specified id(s). The id value for a row is provided
+ * by the DataModel. See {@link YAHOO.ext.grid.DefaultDataModel#getRowId}.
+ * @param {String/Array} An id to find or an array of ids
+ * @return {HtmlElement/Array} If one id was passed in, it returns one result.
+ * If an array of ids was specified, it returns an Array of HTMLElements
+ */
+ getRowsById : function(id){
+ var dm = this.dataModel;
+ if(!(id instanceof Array)){
+ for(var i = 0; i < this.rows.length; i++){
+ if(dm.getRowId(i) == id){
+ return this.rows[i];
+ }
+ }
+ return null;
+ }
+ var found = [];
+ var re = "^(?:";
+ for(var i = 0; i < id.length; i++){
+ re += id[i];
+ if(i != id.length-1) re += "|";
+ }
+ var regex = new RegExp(re + ")$");
+ for(var i = 0; i < this.rows.length; i++){
+ if(regex.test(dm.getRowId(i))){
+ found.push(this.rows[i]);
+ }
+ }
+ return found;
+ },
+
+ /**
+ * Returns the row that comes after the specified row - text nodes are skipped.
+ * @param {HTMLElement} row
+ * @return {HTMLElement}
+ */
+ getRowAfter : function(row){
+ return this.getSibling('next', row);
+ },
+
+ /**
+ * Returns the row that comes before the specified row - text nodes are skipped.
+ * @param {HTMLElement} row
+ * @return {HTMLElement}
+ */
+ getRowBefore : function(row){
+ return this.getSibling('previous', row);
+ },
+
+ /**
+ * Returns the cell that comes after the specified cell - text nodes are skipped.
+ * @param {HTMLElement} cell
+ * @param {Boolean} includeHidden
+ * @return {HTMLElement}
+ */
+ getCellAfter : function(cell, includeHidden){
+ var next = this.getSibling('next', cell);
+ if(next && !includeHidden && this.colModel.isHidden(next.columnIndex)){
+ return this.getCellAfter(next);
+ }
+ return next;
+ },
+
+ /**
+ * Returns the cell that comes before the specified cell - text nodes are skipped.
+ * @param {HTMLElement} cell
+ * @param {Boolean} includeHidden
+ * @return {HTMLElement}
+ */
+ getCellBefore : function(cell, includeHidden){
+ var prev = this.getSibling('previous', cell);
+ if(prev && !includeHidden && this.colModel.isHidden(prev.columnIndex)){
+ return this.getCellBefore(prev);
+ }
+ return prev;
+ },
+
+ /**
+ * Returns the last cell for the row - text nodes and hidden columns are skipped.
+ * @param {HTMLElement} row
+ * @param {Boolean} includeHidden
+ * @return {HTMLElement}
+ */
+ getLastCell : function(row, includeHidden){
+ var cell = this.getElement('previous', row.lastChild);
+ if(cell && !includeHidden && this.colModel.isHidden(cell.columnIndex)){
+ return this.getCellBefore(cell);
+ }
+ return cell;
+ },
+
+ /**
+ * Returns the first cell for the row - text nodes and hidden columns are skipped.
+ * @param {HTMLElement} row
+ * @param {Boolean} includeHidden
+ * @return {HTMLElement}
+ */
+ getFirstCell : function(row, includeHidden){
+ var cell = this.getElement('next', row.firstChild);
+ if(cell && !includeHidden && this.colModel.isHidden(cell.columnIndex)){
+ return this.getCellAfter(cell);
+ }
+ return cell;
+ },
+
+ /**
+ * @private
+ * Gets siblings, skipping text nodes
+ * @param {String} type The direction to walk: 'next' or 'previous'
+ * @param {HTMLElement} node
+ */
+ getSibling : function(type, node){
+ if(!node) return null;
+ type += 'Sibling';
+ var n = node[type];
+ while(n && n.nodeType != 1){
+ n = n[type];
+ }
+ return n;
+ },
+
+ /**
+ * Returns node if node is an HTMLElement else walks the siblings in direction looking for
+ * a node that is an element
+ * @param {String} direction The direction to walk: 'next' or 'previous'
+ * @private
+ */
+ getElement : function(direction, node){
+ if(!node || node.nodeType == 1) return node;
+ else return this.getSibling(direction, node);
+ },
+
+ /**
+ * @private
+ */
+ getElementFromChild : function(childEl, parentClass){
+ if(!childEl || (YAHOO.util.Dom.hasClass(childEl, parentClass))){
+ return childEl;
+ }
+ var p = childEl.parentNode;
+ var b = document.body;
+ while(p && p != b){
+ if(YAHOO.util.Dom.hasClass(p, parentClass)){
+ return p;
+ }
+ p = p.parentNode;
+ }
+ return null;
+ },
+
+ /**
+ * Returns the row that contains the specified child element.
+ * @param {HTMLElement} childEl
+ * @return {HTMLElement}
+ */
+ getRowFromChild : function(childEl){
+ return this.getElementFromChild(childEl, 'ygrid-row');
+ },
+
+ /**
+ * Returns the cell that contains the specified child element.
+ * @param {HTMLElement} childEl
+ * @return {HTMLElement}
+ */
+ getCellFromChild : function(childEl){
+ return this.getElementFromChild(childEl, 'ygrid-col');
+ },
+
+
+ /**
+ * Returns the header element that contains the specified child element.
+ * @param {HTMLElement} childEl
+ * @return {HTMLElement}
+ */
+ getHeaderFromChild : function(childEl){
+ return this.getElementFromChild(childEl, 'ygrid-hd');
+ },
+
+ /**
+ * Convenience method for getSelectionModel().getSelectedRows() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRows}</small> for more details.
+ * @return {Array}
+ */
+ getSelectedRows : function(){
+ return this.selModel.getSelectedRows();
+ },
+
+ /**
+ * Convenience method for getSelectionModel().getSelectedRows()[0] -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRows}</small> for more details.
+ * @return {HTMLElement}
+ */
+ getSelectedRow : function(){
+ if(this.selModel.hasSelection()){
+ return this.selModel.getSelectedRows()[0];
+ }
+ return null;
+ },
+
+ /**
+ * Get the selected row indexes
+ * @return {Array} Array of indexes
+ */
+ getSelectedRowIndexes : function(){
+ var a = [];
+ var rows = this.selModel.getSelectedRows();
+ for(var i = 0; i < rows.length; i++) {
+ a[i] = rows[i].rowIndex;
+ }
+ return a;
+ },
+
+ /**
+ * Gets the first selected row or -1 if none are selected
+ * @return {Number}
+ */
+ getSelectedRowIndex : function(){
+ if(this.selModel.hasSelection()){
+ return this.selModel.getSelectedRows()[0].rowIndex;
+ }
+ return -1;
+ },
+
+ /**
+ * Convenience method for getSelectionModel().getSelectedRowIds()[0] -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRowIds}</small> for more details.
+ * @return {String}
+ */
+ getSelectedRowId : function(){
+ if(this.selModel.hasSelection()){
+ return this.selModel.getSelectedRowIds()[0];
+ }
+ return null;
+ },
+
+ /**
+ * Convenience method for getSelectionModel().getSelectedRowIds() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRowIds}</small> for more details.
+ * @return {Array}
+ */
+ getSelectedRowIds : function(){
+ return this.selModel.getSelectedRowIds();
+ },
+
+ /**
+ * Convenience method for getSelectionModel().clearSelections() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#clearSelections}</small> for more details.
+ */
+ clearSelections : function(){
+ this.selModel.clearSelections();
+ },
+
+
+ /**
+ * Convenience method for getSelectionModel().selectAll() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#selectAll}</small> for more details.
+ */
+ selectAll : function(){
+ this.selModel.selectAll();
+ },
+
+
+ /**
+ * Convenience method for getSelectionModel().getCount() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getCount}</small> for more details.
+ * @return {Number}
+ */
+ getSelectionCount : function(){
+ return this.selModel.getCount();
+ },
+
+ /**
+ * Convenience method for getSelectionModel().hasSelection() -
+ * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#hasSelection}</small> for more details.
+ * @return {Boolean}
+ */
+ hasSelection : function(){
+ return this.selModel.hasSelection();
+ },
+
+ /**
+ * Returns the grid's SelectionModel.
+ * @return {SelectionModel}
+ */
+ getSelectionModel : function(){
+ if(!this.selModel){
+ this.selModel = new DefaultSelectionModel();
+ }
+ return this.selModel;
+ },
+
+ /**
+ * Returns the grid's DataModel.
+ * @return {DataModel}
+ */
+ getDataModel : function(){
+ return this.dataModel;
+ },
+
+ /**
+ * Returns the grid's ColumnModel.
+ * @return {ColumnModel}
+ */
+ getColumnModel : function(){
+ return this.colModel;
+ },
+
+ /**
+ * Returns the grid's GridView object.
+ * @return {GridView}
+ */
+ getView : function(){
+ return this.view;
+ },
+ /**
+ * Called to get grid's drag proxy text, by default returns this.ddText.
+ * @return {String}
+ */
+ getDragDropText : function(){
+ return this.ddText.replace('%0', this.selModel.getCount());
+ }
+};
+/**
+ * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
+ * %0 is replaced with the number of selected rows.
+ * @type String
+ */
+YAHOO.ext.grid.Grid.prototype.ddText = "%0 selected row(s)";
diff --git a/frontend/beta/js/YUI-extensions/grid/GridDD.js b/frontend/beta/js/YUI-extensions/grid/GridDD.js
new file mode 100644
index 0000000..cdcaf39
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/GridDD.js
@@ -0,0 +1,101 @@
+
+// kill dependency issue
+if(YAHOO.util.DDProxy){
+/**
+ * @class YAHOO.ext.grid.GridDD
+ * Custom implementation of YAHOO.util.DDProxy used internally by the grid
+ * @extends YAHOO.util.DDProxy
+ */
+YAHOO.ext.grid.GridDD = function(grid, bwrap){
+ this.grid = grid;
+ var ddproxy = document.createElement('div');
+ ddproxy.id = grid.container.id + '-ddproxy';
+ ddproxy.className = 'ygrid-drag-proxy';
+ document.body.insertBefore(ddproxy, document.body.firstChild);
+ YAHOO.util.Dom.setStyle(ddproxy, 'opacity', .80);
+ var ddicon = document.createElement('span');
+ ddicon.className = 'ygrid-drop-icon ygrid-drop-nodrop';
+ ddproxy.appendChild(ddicon);
+ var ddtext = document.createElement('span');
+ ddtext.className = 'ygrid-drag-text';
+ ddtext.innerHTML = "&#160;";
+ ddproxy.appendChild(ddtext);
+
+ this.ddproxy = ddproxy;
+ this.ddtext = ddtext;
+ this.ddicon = ddicon;
+ YAHOO.util.Event.on(bwrap, 'click', this.handleClick, this, true);
+ YAHOO.ext.grid.GridDD.superclass.constructor.call(this, bwrap.id, 'GridDD',
+ {dragElId : ddproxy.id, resizeFrame: false});
+
+ this.unlockDelegate = grid.selModel.unlock.createDelegate(grid.selModel);
+};
+YAHOO.extendX(YAHOO.ext.grid.GridDD, YAHOO.util.DDProxy);
+
+YAHOO.ext.grid.GridDD.prototype.handleMouseDown = function(e){
+ var row = this.grid.getRowFromChild(YAHOO.util.Event.getTarget(e));
+ if(!row) return;
+ if(this.grid.selModel.isSelected(row)){
+ YAHOO.ext.grid.GridDD.superclass.handleMouseDown.call(this, e);
+ }else {
+ this.grid.selModel.unlock();
+ YAHOO.ext.EventObject.setEvent(e);
+ this.grid.selModel.rowClick(this.grid, row.rowIndex, YAHOO.ext.EventObject);
+ YAHOO.ext.grid.GridDD.superclass.handleMouseDown.call(this, e);
+ this.grid.selModel.lock();
+ }
+};
+
+YAHOO.ext.grid.GridDD.prototype.handleClick = function(e){
+ if(this.grid.selModel.isLocked()){
+ setTimeout(this.unlockDelegate, 1);
+ YAHOO.util.Event.stopEvent(e);
+ }
+};
+
+/**
+ * Updates the DD visual element to allow/not allow a drop
+ * @param {Boolean} dropStatus True if drop is allowed on the target
+ */
+YAHOO.ext.grid.GridDD.prototype.setDropStatus = function(dropStatus){
+ if(dropStatus === true){
+ YAHOO.util.Dom.replaceClass(this.ddicon, 'ygrid-drop-nodrop', 'ygrid-drop-ok');
+ }else{
+ YAHOO.util.Dom.replaceClass(this.ddicon, 'ygrid-drop-ok', 'ygrid-drop-nodrop');
+ }
+};
+
+YAHOO.ext.grid.GridDD.prototype.startDrag = function(e){
+ this.ddtext.innerHTML = this.grid.getDragDropText();
+ this.setDropStatus(false);
+ this.grid.selModel.lock();
+ this.grid.fireEvent('startdrag', this.grid, this, e);
+};
+
+YAHOO.ext.grid.GridDD.prototype.endDrag = function(e){
+ YAHOO.util.Dom.setStyle(this.ddproxy, 'visibility', 'hidden');
+ this.grid.fireEvent('enddrag', this.grid, this, e);
+};
+
+YAHOO.ext.grid.GridDD.prototype.autoOffset = function(iPageX, iPageY) {
+ this.setDelta(-12, -20);
+};
+
+YAHOO.ext.grid.GridDD.prototype.onDragEnter = function(e, id) {
+ this.setDropStatus(true);
+ this.grid.fireEvent('dragenter', this.grid, this, id, e);
+};
+
+YAHOO.ext.grid.GridDD.prototype.onDragDrop = function(e, id) {
+ this.grid.fireEvent('dragdrop', this.grid, this, id, e);
+};
+
+YAHOO.ext.grid.GridDD.prototype.onDragOver = function(e, id) {
+ this.grid.fireEvent('dragover', this.grid, this, id, e);
+};
+
+YAHOO.ext.grid.GridDD.prototype.onDragOut = function(e, id) {
+ this.setDropStatus(false);
+ this.grid.fireEvent('dragout', this.grid, this, id, e);
+};
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/GridView.js b/frontend/beta/js/YUI-extensions/grid/GridView.js
new file mode 100644
index 0000000..dbd47e3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/GridView.js
@@ -0,0 +1,790 @@
+/**
+ * @class YAHOO.ext.grid.GridView
+ * Default UI code used internally by the Grid. This is the object returned by {@link YAHOO.ext.grid.Grid#getView}.
+ * @constructor
+ */
+YAHOO.ext.grid.GridView = function(){
+ this.grid = null;
+ this.lastFocusedRow = null;
+ this.onScroll = new YAHOO.util.CustomEvent('onscroll');
+ this.adjustScrollTask = new YAHOO.ext.util.DelayedTask(this._adjustForScroll, this);
+ this.ensureVisibleTask = new YAHOO.ext.util.DelayedTask();
+};
+
+YAHOO.ext.grid.GridView.prototype = {
+ init: function(grid){
+ this.grid = grid;
+ },
+
+ fireScroll: function(scrollLeft, scrollTop){
+ this.onScroll.fireDirect(this.grid, scrollLeft, scrollTop);
+ },
+
+ /**
+ * @private
+ * Utility method that gets an array of the cell renderers
+ */
+ getColumnRenderers : function(){
+ var renderers = [];
+ var cm = this.grid.colModel;
+ var colCount = cm.getColumnCount();
+ for(var i = 0; i < colCount; i++){
+ renderers.push(cm.getRenderer(i));
+ }
+ return renderers;
+ },
+
+ buildIndexMap : function(){
+ var colToData = {};
+ var dataToCol = {};
+ var cm = this.grid.colModel;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ var di = cm.getDataIndex(i);
+ colToData[i] = di;
+ dataToCol[di] = i;
+ }
+ return {'colToData': colToData, 'dataToCol': dataToCol};
+ },
+
+ getDataIndexes : function(){
+ if(!this.indexMap){
+ this.indexMap = this.buildIndexMap();
+ }
+ return this.indexMap.colToData;
+ },
+
+ getColumnIndexByDataIndex : function(dataIndex){
+ if(!this.indexMap){
+ this.indexMap = this.buildIndexMap();
+ }
+ return this.indexMap.dataToCol[dataIndex];
+ },
+
+ updateHeaders : function(){
+ var colModel = this.grid.colModel;
+ var hcells = this.headers;
+ var colCount = colModel.getColumnCount();
+ for(var i = 0; i < colCount; i++){
+ hcells[i].textNode.innerHTML = colModel.getColumnHeader(i);
+ }
+ },
+
+ adjustForScroll : function(disableDelay){
+ if(!disableDelay){
+ this.adjustScrollTask.delay(50);
+ }else{
+ this._adjustForScroll();
+ }
+ },
+
+ /**
+ * Returns the rowIndex/columnIndex of the cell found at the passed page coordinates
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Array} [rowIndex, columnIndex]
+ */
+ getCellAtPoint : function(x, y){
+ var colIndex = null;
+ var rowIndex = null;
+
+ // translate page coordinates to local coordinates
+ var xy = YAHOO.util.Dom.getXY(this.wrap);
+ x = (x - xy[0]) + this.wrap.scrollLeft;
+ y = (y - xy[1]) + this.wrap.scrollTop;
+
+ var colModel = this.grid.colModel;
+ var pos = 0;
+ var colCount = colModel.getColumnCount();
+ for(var i = 0; i < colCount; i++){
+ if(colModel.isHidden(i)) continue;
+ var width = colModel.getColumnWidth(i);
+ if(x >= pos && x < pos+width){
+ colIndex = i;
+ break;
+ }
+ pos += width;
+ }
+ if(colIndex != null){
+ rowIndex = (y == 0 ? 0 : Math.floor(y / this.getRowHeight()));
+ if(rowIndex >= this.grid.dataModel.getRowCount()){
+ return null;
+ }
+ return [colIndex, rowIndex];
+ }
+ return null;
+ },
+
+ /** @private */
+ _adjustForScroll : function(){
+ this.forceScrollUpdate();
+ if(this.scrollbarMode == YAHOO.ext.grid.GridView.SCROLLBARS_OVERLAP){
+ var adjustment = 0;
+ if(this.wrap.clientWidth && this.wrap.clientWidth !== 0){
+ adjustment = this.wrap.offsetWidth - this.wrap.clientWidth;
+ }
+ this.hwrap.setWidth(this.wrap.offsetWidth-adjustment);
+ }else{
+ this.hwrap.setWidth(this.wrap.offsetWidth);
+ }
+ this.bwrap.setWidth(Math.max(this.grid.colModel.getTotalWidth(), this.wrap.clientWidth));
+ },
+
+ /**
+ * Focuses the specified row. The preferred way to scroll to a row is {@link #ensureVisible}.
+ * @param {Number/HTMLElement} row The index of a row or the row itself
+ */
+ focusRow : function(row){
+ if(typeof row == 'number'){
+ row = this.getBodyTable().childNodes[row];
+ }
+ if(!row) return;
+ var left = this.wrap.scrollLeft;
+ try{ // try catch for IE occasional focus bug
+ row.childNodes.item(0).hideFocus = true;
+ row.childNodes.item(0).focus();
+ }catch(e){}
+ this.ensureVisible(row);
+ this.wrap.scrollLeft = left;
+ this.handleScroll();
+ this.lastFocusedRow = row;
+ },
+
+ /**
+ * Scrolls the specified row into view. This call is automatically buffered (delayed), to disable
+ * the delay, pass true for disableDelay.
+ * @param {Number/HTMLElement} row The index of a row or the row itself
+ * @param {Boolean} disableDelay
+ */
+ ensureVisible : function(row, disableDelay){
+ if(!disableDelay){
+ this.ensureVisibleTask.delay(50, this._ensureVisible, this, [row]);
+ }else{
+ this._ensureVisible(row);
+ }
+ },
+
+ /** @ignore */
+ _ensureVisible : function(row){
+ if(typeof row == 'number'){
+ row = this.getBodyTable().childNodes[row];
+ }
+ if(!row) return;
+ var left = this.wrap.scrollLeft;
+ var rowTop = parseInt(row.offsetTop, 10); // parseInt for safari bug
+ var rowBottom = rowTop + row.offsetHeight;
+ var clientTop = parseInt(this.wrap.scrollTop, 10); // parseInt for safari bug
+ var clientBottom = clientTop + this.wrap.clientHeight;
+ if(rowTop < clientTop){
+ this.wrap.scrollTop = rowTop;
+ }else if(rowBottom > clientBottom){
+ this.wrap.scrollTop = rowBottom-this.wrap.clientHeight;
+ }
+ this.wrap.scrollLeft = left;
+ this.handleScroll();
+ },
+
+ updateColumns : function(){
+ this.grid.stopEditing();
+ var colModel = this.grid.colModel;
+ var hcols = this.headers;
+ var colCount = colModel.getColumnCount();
+ var pos = 0;
+ var totalWidth = colModel.getTotalWidth();
+ for(var i = 0; i < colCount; i++){
+ if(colModel.isHidden(i)) continue;
+ var width = colModel.getColumnWidth(i);
+ hcols[i].style.width = width + 'px';
+ hcols[i].style.left = pos + 'px';
+ hcols[i].split.style.left = (pos+width-3) + 'px';
+ this.setCSSWidth(i, width, pos);
+ pos += width;
+ }
+ this.lastWidth = totalWidth;
+ if(this.grid.autoWidth){
+ this.grid.container.setWidth(totalWidth+this.grid.container.getBorderWidth('lr'));
+ this.grid.autoSize();
+ }
+ this.bwrap.setWidth(Math.max(totalWidth, this.wrap.clientWidth));
+ if(!YAHOO.ext.util.Browser.isIE){ // fix scrolling prob in gecko and opera
+ this.wrap.scrollLeft = this.hwrap.dom.scrollLeft;
+ }
+ this.syncScroll();
+ this.forceScrollUpdate();
+ if(this.grid.autoHeight){
+ this.autoHeight();
+ this.updateWrapHeight();
+ }
+ },
+
+ setCSSWidth : function(colIndex, width, pos){
+ var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
+ YAHOO.ext.util.CSS.updateRule(selector, 'width', width + 'px');
+ if(typeof pos == 'number'){
+ YAHOO.ext.util.CSS.updateRule(selector, 'left', pos + 'px');
+ }
+ },
+
+ /**
+ * Set a css style for a column dynamically.
+ * @param {Number} colIndex The index of the column
+ * @param {String} name The css property name
+ * @param {String} value The css value
+ */
+ setCSSStyle : function(colIndex, name, value){
+ var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
+ YAHOO.ext.util.CSS.updateRule(selector, name, value);
+ },
+
+ handleHiddenChange : function(colModel, colIndex, hidden){
+ if(hidden){
+ this.hideColumn(colIndex);
+ }else{
+ this.unhideColumn(colIndex);
+ }
+ this.updateColumns();
+ },
+
+ hideColumn : function(colIndex){
+ var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
+ YAHOO.ext.util.CSS.updateRule(selector, 'position', 'absolute');
+ YAHOO.ext.util.CSS.updateRule(selector, 'visibility', 'hidden');
+
+ this.headers[colIndex].style.display = 'none';
+ this.headers[colIndex].split.style.display = 'none';
+ },
+
+ unhideColumn : function(colIndex){
+ var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
+ YAHOO.ext.util.CSS.updateRule(selector, 'position', '');
+ YAHOO.ext.util.CSS.updateRule(selector, 'visibility', 'visible');
+
+ this.headers[colIndex].style.display = '';
+ this.headers[colIndex].split.style.display = '';
+ },
+
+ getBodyTable : function(){
+ return this.bwrap.dom;
+ },
+
+ updateRowIndexes : function(firstRow, lastRow){
+ var stripeRows = this.grid.stripeRows;
+ var bt = this.getBodyTable();
+ var nodes = bt.childNodes;
+ firstRow = firstRow || 0;
+ lastRow = lastRow || nodes.length-1;
+ var re = /^(?:ygrid-row ygrid-row-alt|ygrid-row)/;
+ for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
+ var node = nodes[rowIndex];
+ if(stripeRows && (rowIndex+1) % 2 == 0){
+ node.className = node.className.replace(re, 'ygrid-row ygrid-row-alt');
+ }else{
+ node.className = node.className.replace(re, 'ygrid-row');
+ }
+ node.rowIndex = rowIndex;
+ nodes[rowIndex].style.top = (rowIndex * this.rowHeight) + 'px';
+ }
+ },
+
+ insertRows : function(dataModel, firstRow, lastRow){
+ this.updateBodyHeight();
+ this.adjustForScroll(true);
+ var renderers = this.getColumnRenderers();
+ var dindexes = this.getDataIndexes();
+ var colCount = this.grid.colModel.getColumnCount();
+ var beforeRow = null;
+ var bt = this.getBodyTable();
+ if(firstRow < bt.childNodes.length){
+ beforeRow = bt.childNodes[firstRow];
+ }
+ for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
+ var row = document.createElement('span');
+ row.className = 'ygrid-row';
+ row.style.top = (rowIndex * this.rowHeight) + 'px';
+ this.renderRow(dataModel, row, rowIndex, colCount, renderers, dindexes);
+ if(beforeRow){
+ bt.insertBefore(row, beforeRow);
+ }else{
+ bt.appendChild(row);
+ }
+ }
+ this.updateRowIndexes(firstRow);
+ this.adjustForScroll(true);
+ },
+
+ renderRow : function(dataModel, row, rowIndex, colCount, renderers, dindexes){
+ for(var colIndex = 0; colIndex < colCount; colIndex++){
+ var td = document.createElement('span');
+ td.className = 'ygrid-col ygrid-col-' + colIndex + (colIndex == colCount-1 ? ' ygrid-col-last' : '');
+ td.columnIndex = colIndex;
+ td.tabIndex = 0;
+ var span = document.createElement('span');
+ span.className = 'ygrid-cell-text';
+ td.appendChild(span);
+ var val = renderers[colIndex](dataModel.getValueAt(rowIndex, dindexes[colIndex]), rowIndex, colIndex, td, dataModel);
+ if(typeof val == 'undefined' || val === '') val = '&#160;';
+ span.innerHTML = val;
+ row.appendChild(td);
+ }
+ },
+
+ deleteRows : function(dataModel, firstRow, lastRow){
+ this.updateBodyHeight();
+ // first make sure they are deselected
+ this.grid.selModel.deselectRange(firstRow, lastRow);
+ var bt = this.getBodyTable();
+ var rows = []; // get references because the rowIndex will change
+ for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
+ rows.push(bt.childNodes[rowIndex]);
+ }
+ for(var i = 0; i < rows.length; i++){
+ bt.removeChild(rows[i]);
+ rows[i] = null;
+ }
+ rows = null;
+ this.updateRowIndexes(firstRow);
+ this.adjustForScroll();
+ },
+
+ updateRows : function(dataModel, firstRow, lastRow){
+ var bt = this.getBodyTable();
+ var dindexes = this.getDataIndexes();
+ var renderers = this.getColumnRenderers();
+ var colCount = this.grid.colModel.getColumnCount();
+ for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
+ var row = bt.rows[rowIndex];
+ var cells = row.childNodes;
+ for(var colIndex = 0; colIndex < colCount; colIndex++){
+ var td = cells[colIndex];
+ var val = renderers[colIndex](dataModel.getValueAt(rowIndex, dindexes[colIndex]), rowIndex, colIndex, td, dataModel);
+ if(typeof val == 'undefined' || val === '') val = '&#160;';
+ td.firstChild.innerHTML = val;
+ }
+ }
+ },
+
+ handleSort : function(dataModel, sortColumnIndex, sortDir, noRefresh){
+ var selectedRows;
+ this.grid.selModel.syncSelectionsToIds();
+ if(!noRefresh){
+ this.updateRows(dataModel, 0, dataModel.getRowCount()-1);
+ }
+ this.updateHeaderSortState();
+ selectedRows = this.grid.selModel.getSelectedRows();
+ if (selectedRows.length > 0) {
+ this.focusRow(selectedRows[0]);
+ }
+ },
+
+ syncScroll : function(){
+ this.hwrap.dom.scrollLeft = this.wrap.scrollLeft;
+ },
+
+ handleScroll : function(){
+ this.syncScroll();
+ this.fireScroll(this.wrap.scrollLeft, this.wrap.scrollTop);
+ this.grid.fireEvent('bodyscroll', this.wrap.scrollLeft, this.wrap.scrollTop);
+ },
+
+ getRowHeight : function(){
+ if(!this.rowHeight){
+ var rule = YAHOO.ext.util.CSS.getRule(["#" + this.grid.id + " .ygrid-row", ".ygrid-row"]);
+ if(rule && rule.style.height){
+ this.rowHeight = parseInt(rule.style.height, 10);
+ }else{
+ this.rowHeight = 21;
+ }
+ }
+ return this.rowHeight;
+ },
+
+ renderRows : function(dataModel){
+ this.grid.stopEditing();
+ if(this.grid.selModel){
+ this.grid.selModel.clearSelections();
+ }
+ var bt = this.getBodyTable();
+ bt.innerHTML = '';
+ this.rowHeight = this.getRowHeight();
+ this.insertRows(dataModel, 0, dataModel.getRowCount()-1);
+ },
+
+ updateCell : function(dataModel, rowIndex, dataIndex){
+ var colIndex = this.getColumnIndexByDataIndex(dataIndex);
+ if(typeof colIndex == 'undefined'){ // not present in grid
+ return;
+ }
+ var bt = this.getBodyTable();
+ var row = bt.childNodes[rowIndex];
+ var cell = row.childNodes[colIndex];
+ var renderer = this.grid.colModel.getRenderer(colIndex);
+ var val = renderer(dataModel.getValueAt(rowIndex, dataIndex), rowIndex, colIndex, cell, dataModel);
+ if(typeof val == 'undefined' || val === '') val = '&#160;';
+ cell.firstChild.innerHTML = val;
+ },
+
+ calcColumnWidth : function(colIndex, maxRowsToMeasure){
+ var maxWidth = 0;
+ var bt = this.getBodyTable();
+ var rows = bt.childNodes;
+ var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
+ if(this.grid.autoSizeHeaders){
+ var h = this.headers[colIndex];
+ var curWidth = h.style.width;
+ h.style.width = this.grid.minColumnWidth+'px';
+ maxWidth = Math.max(maxWidth, h.scrollWidth);
+ h.style.width = curWidth;
+ }
+ for(var i = 0; i < stopIndex; i++){
+ var cell = rows[i].childNodes[colIndex].firstChild;
+ maxWidth = Math.max(maxWidth, cell.scrollWidth);
+ }
+ return maxWidth + /*margin for error in IE*/ 5;
+ },
+
+ /**
+ * Autofit a column to it's content.
+ * @param {Number} colIndex
+ * @param {Boolean} forceMinSize true to force the column to go smaller if possible
+ */
+ autoSizeColumn : function(colIndex, forceMinSize){
+ if(forceMinSize){
+ this.setCSSWidth(colIndex, this.grid.minColumnWidth);
+ }
+ var newWidth = this.calcColumnWidth(colIndex);
+ this.grid.colModel.setColumnWidth(colIndex,
+ Math.max(this.grid.minColumnWidth, newWidth));
+ this.grid.fireEvent('columnresize', colIndex, newWidth);
+ },
+
+ /**
+ * Autofits all columns to their content and then expands to fit any extra space in the grid
+ */
+ autoSizeColumns : function(){
+ var colModel = this.grid.colModel;
+ var colCount = colModel.getColumnCount();
+ var wrap = this.wrap;
+ for(var i = 0; i < colCount; i++){
+ this.setCSSWidth(i, this.grid.minColumnWidth);
+ colModel.setColumnWidth(i, this.calcColumnWidth(i, this.grid.maxRowsToMeasure), true);
+ }
+ if(colModel.getTotalWidth() < wrap.clientWidth){
+ var diff = Math.floor((wrap.clientWidth - colModel.getTotalWidth()) / colCount);
+ for(var i = 0; i < colCount; i++){
+ colModel.setColumnWidth(i, colModel.getColumnWidth(i) + diff, true);
+ }
+ }
+ this.updateColumns();
+ },
+
+ /**
+ * Autofits all columns to the grid's width proportionate with their current size
+ */
+ fitColumns : function(){
+ var cm = this.grid.colModel;
+ var colCount = cm.getColumnCount();
+ var cols = [];
+ var width = 0;
+ var i, w;
+ for (i = 0; i < colCount; i++){
+ if(!cm.isHidden(i) && !cm.isFixed(i)){
+ w = cm.getColumnWidth(i);
+ cols.push(i);
+ cols.push(w);
+ width += w;
+ }
+ }
+ var frac = (this.wrap.clientWidth - cm.getTotalWidth())/width;
+ while (cols.length){
+ w = cols.pop();
+ i = cols.pop();
+ cm.setColumnWidth(i, Math.floor(w + w*frac), true);
+ }
+ this.updateColumns();
+ },
+
+ onWindowResize : function(){
+ if(this.grid.monitorWindowResize){
+ this.adjustForScroll();
+ this.updateWrapHeight();
+ this.adjustForScroll();
+ }
+ },
+
+ updateWrapHeight : function(){
+ this.grid.container.beginMeasure();
+ this.autoHeight();
+ var box = this.grid.container.getSize(true);
+ this.wrapEl.setHeight(box.height-this.footerHeight-parseInt(this.wrap.offsetTop, 10));
+ this.pwrap.setSize(box.width, box.height);
+ this.grid.container.endMeasure();
+ },
+
+ forceScrollUpdate : function(){
+ var wrap = this.wrapEl;
+ wrap.setWidth(wrap.getWidth(true));
+ setTimeout(function(){ // set timeout so FireFox works
+ wrap.setWidth('');
+ }, 1);
+ },
+
+ updateHeaderSortState : function(){
+ var state = this.grid.dataModel.getSortState();
+ if(!state || typeof state.column == 'undefined') return;
+ var sortColumn = this.getColumnIndexByDataIndex(state.column);
+ var sortDir = state.direction;
+ for(var i = 0, len = this.headers.length; i < len; i++){
+ var h = this.headers[i];
+ if(i != sortColumn){
+ h.sortDesc.style.display = 'none';
+ h.sortAsc.style.display = 'none';
+ YAHOO.util.Dom.removeClass(h, 'ygrid-sort-col');
+ }else{
+ h.sortDesc.style.display = sortDir == 'DESC' ? 'block' : 'none';
+ h.sortAsc.style.display = sortDir == 'ASC' ? 'block' : 'none';
+ YAHOO.util.Dom.addClass(h, 'ygrid-sort-col');
+ }
+ }
+ },
+
+ unplugDataModel : function(dm){
+ dm.removeListener('cellupdated', this.updateCell, this);
+ dm.removeListener('datachanged', this.renderRows, this);
+ dm.removeListener('rowsdeleted', this.deleteRows, this);
+ dm.removeListener('rowsinserted', this.insertRows, this);
+ dm.removeListener('rowsupdated', this.updateRows, this);
+ dm.removeListener('rowssorted', this.handleSort, this);
+ },
+
+ plugDataModel : function(dm){
+ dm.on('cellupdated', this.updateCell, this, true);
+ dm.on('datachanged', this.renderRows, this, true);
+ dm.on('rowsdeleted', this.deleteRows, this, true);
+ dm.on('rowsinserted', this.insertRows, this, true);
+ dm.on('rowsupdated', this.updateRows, this, true);
+ dm.on('rowssorted', this.handleSort, this, true);
+ },
+
+ destroy : function(){
+ this.unplugDataModel(this.grid.dataModel);
+ var sp = this.splitters;
+ if(sp){
+ for(var i in sp){
+ if(sp[i] && typeof sp[i] != 'function'){
+ sp[i].destroy(true);
+ }
+ }
+ }
+ },
+
+ render : function(){
+ var grid = this.grid;
+ var container = grid.container.dom;
+ var dataModel = grid.dataModel;
+ this.plugDataModel(dataModel);
+
+ var colModel = grid.colModel;
+ colModel.onWidthChange.subscribe(this.updateColumns, this, true);
+ colModel.onHeaderChange.subscribe(this.updateHeaders, this, true);
+ colModel.onHiddenChange.subscribe(this.handleHiddenChange, this, true);
+
+ if(grid.monitorWindowResize === true){
+ YAHOO.ext.EventManager.onWindowResize(this.onWindowResize, this, true);
+ }
+ var autoSizeDelegate = this.autoSizeColumn.createDelegate(this);
+
+ var colCount = colModel.getColumnCount();
+
+ var dh = YAHOO.ext.DomHelper;
+ this.pwrap = dh.append(container,
+ {tag: 'div', cls: 'ygrid-positioner',
+ style: 'position:relative;width:100%;height:100%;left:0;top:0;overflow:hidden;'}, true);
+ var pos = this.pwrap.dom;
+
+ //create wrapper elements that handle offsets and scrolling
+ var wrap = dh.append(pos, {tag: 'div', cls: 'ygrid-wrap'});
+ this.wrap = wrap;
+ this.wrapEl = getEl(wrap, true);
+ YAHOO.ext.EventManager.on(wrap, 'scroll', this.handleScroll, this, true);
+
+ var hwrap = dh.append(pos, {tag: 'div', cls: 'ygrid-wrap-headers'});
+ this.hwrap = getEl(hwrap, true);
+
+ var bwrap = dh.append(wrap, {tag: 'div', cls: 'ygrid-wrap-body', id: container.id + '-body'});
+ this.bwrap = getEl(bwrap, true);
+ this.bwrap.setWidth(colModel.getTotalWidth());
+ bwrap.rows = bwrap.childNodes;
+
+ this.footerHeight = 0;
+ var foot = this.appendFooter(this.pwrap.dom);
+ if(foot){
+ this.footer = getEl(foot, true);
+ this.footerHeight = this.footer.getHeight();
+ }
+ this.updateWrapHeight();
+
+ var hrow = dh.append(hwrap, {tag: 'span', cls: 'ygrid-hrow'});
+ this.hrow = hrow;
+
+ if(!YAHOO.ext.util.Browser.isGecko){
+ // IE doesn't like iframes, we will leave this alone
+ var iframe = document.createElement('iframe');
+ iframe.className = 'ygrid-hrow-frame';
+ iframe.frameBorder = 0;
+ iframe.src = YAHOO.ext.SSL_SECURE_URL;
+ hwrap.appendChild(iframe);
+ }
+ this.headerCtrl = new YAHOO.ext.grid.HeaderController(this.grid);
+ this.headers = [];
+ this.cols = [];
+ this.splitters = [];
+
+ var htemplate = dh.createTemplate({
+ tag: 'span', cls: 'ygrid-hd ygrid-header-{0}', children: [{
+ tag: 'span',
+ cls: 'ygrid-hd-body',
+ html: '<table border="0" cellpadding="0" cellspacing="0" title="{2}">' +
+ '<tbody><tr><td><span>{1}</span></td>' +
+ '<td><span class="sort-desc"></span><span class="sort-asc"></span></td>' +
+ '</tr></tbody></table>'
+ }]
+ });
+ htemplate.compile();
+
+ var ruleBuf = [];
+
+ for(var i = 0; i < colCount; i++){
+ var hd = htemplate.append(hrow, [i, colModel.getColumnHeader(i), colModel.getColumnTooltip(i) || '']);
+ var spans = hd.getElementsByTagName('span');
+ hd.textNode = spans[1];
+ hd.sortDesc = spans[2];
+ hd.sortAsc = spans[3];
+ hd.columnIndex = i;
+ this.headers.push(hd);
+ if(colModel.isSortable(i)){
+ this.headerCtrl.register(hd);
+ }
+ var split = dh.append(hrow, {tag: 'span', cls: 'ygrid-hd-split'});
+ hd.split = split;
+
+ if(colModel.isResizable(i) && !colModel.isFixed(i)){
+ YAHOO.util.Event.on(split, 'dblclick', autoSizeDelegate.createCallback(i+0, true));
+ var sb = new YAHOO.ext.SplitBar(split, hd, null, YAHOO.ext.SplitBar.LEFT);
+ sb.columnIndex = i;
+ sb.minSize = grid.minColumnWidth;
+ sb.onMoved.subscribe(this.onColumnSplitterMoved, this, true);
+ YAHOO.util.Dom.addClass(sb.proxy, 'ygrid-column-sizer');
+ YAHOO.util.Dom.setStyle(sb.proxy, 'background-color', '');
+ sb.dd._resizeProxy = function(){
+ var el = this.getDragEl();
+ YAHOO.util.Dom.setStyle(el, 'height', (hwrap.clientHeight+wrap.clientHeight-2) +'px');
+ };
+ this.splitters[i] = sb;
+ }else{
+ split.style.cursor = 'default';
+ }
+ ruleBuf.push('#', container.id, ' .ygrid-col-', i, ' {\n}\n');
+ }
+
+ YAHOO.ext.util.CSS.createStyleSheet(ruleBuf.join(''));
+
+ if(grid.autoSizeColumns){
+ this.renderRows(dataModel);
+ this.autoSizeColumns();
+ }else{
+ this.updateColumns();
+ this.renderRows(dataModel);
+ }
+
+ for(var i = 0; i < colCount; i++){
+ if(colModel.isHidden(i)){
+ this.hideColumn(i);
+ }
+ }
+ this.updateHeaderSortState();
+ return this.bwrap;
+ },
+
+ onColumnSplitterMoved : function(splitter, newSize){
+ this.grid.colModel.setColumnWidth(splitter.columnIndex, newSize);
+ this.grid.fireEvent('columnresize', splitter.columnIndex, newSize);
+ },
+
+ appendFooter : function(parentEl){
+ return null;
+ },
+
+ autoHeight : function(){
+ if(this.grid.autoHeight){
+ var h = this.getBodyHeight();
+ var c = this.grid.container;
+ var total = h + (parseInt(this.wrap.offsetTop, 10)||0) +
+ this.footerHeight + c.getBorderWidth('tb') + c.getPadding('tb')
+ + (this.wrap.offsetHeight - this.wrap.clientHeight);
+ c.setHeight(total);
+
+ }
+ },
+
+ getBodyHeight : function(){
+ return this.grid.dataModel.getRowCount() * this.getRowHeight();;
+ },
+
+ updateBodyHeight : function(){
+ this.getBodyTable().style.height = this.getBodyHeight() + 'px';
+ if(this.grid.autoHeight){
+ this.autoHeight();
+ this.updateWrapHeight();
+ }
+ }
+};
+YAHOO.ext.grid.GridView.SCROLLBARS_UNDER = 0;
+YAHOO.ext.grid.GridView.SCROLLBARS_OVERLAP = 1;
+YAHOO.ext.grid.GridView.prototype.scrollbarMode = YAHOO.ext.grid.GridView.SCROLLBARS_UNDER;
+
+YAHOO.ext.grid.GridView.prototype.fitColumnsToContainer = YAHOO.ext.grid.GridView.prototype.fitColumns;
+
+YAHOO.ext.grid.HeaderController = function(grid){
+ this.grid = grid;
+ this.headers = [];
+};
+
+YAHOO.ext.grid.HeaderController.prototype = {
+ register : function(header){
+ this.headers.push(header);
+ YAHOO.ext.EventManager.on(header, 'selectstart', this.cancelTextSelection, this, true);
+ YAHOO.ext.EventManager.on(header, 'mousedown', this.cancelTextSelection, this, true);
+ YAHOO.ext.EventManager.on(header, 'mouseover', this.headerOver, this, true);
+ YAHOO.ext.EventManager.on(header, 'mouseout', this.headerOut, this, true);
+ YAHOO.ext.EventManager.on(header, 'click', this.headerClick, this, true);
+ },
+
+ headerClick : function(e){
+ var grid = this.grid, cm = grid.colModel, dm = grid.dataModel;
+ grid.stopEditing();
+ var header = grid.getHeaderFromChild(e.getTarget());
+ var state = dm.getSortState();
+ var direction = header.sortDir || 'ASC';
+ if(typeof state.column != 'undefined' &&
+ grid.getView().getColumnIndexByDataIndex(state.column) == header.columnIndex){
+ direction = (state.direction == 'ASC' ? 'DESC' : 'ASC');
+ }
+ header.sortDir = direction;
+ dm.sort(cm, cm.getDataIndex(header.columnIndex), direction);
+ },
+
+ headerOver : function(e){
+ var header = this.grid.getHeaderFromChild(e.getTarget());
+ YAHOO.util.Dom.addClass(header, 'ygrid-hd-over');
+ //YAHOO.ext.util.CSS.applyFirst(header, this.grid.id, '.ygrid-hd-over');
+ },
+
+ headerOut : function(e){
+ var header = this.grid.getHeaderFromChild(e.getTarget());
+ YAHOO.util.Dom.removeClass(header, 'ygrid-hd-over');
+ //YAHOO.ext.util.CSS.revertFirst(header, this.grid.id, '.ygrid-hd-over');
+ },
+
+ cancelTextSelection : function(e){
+ e.preventDefault();
+ }
+}; \ No newline at end of file
diff --git a/frontend/beta/js/YUI-extensions/grid/PagedGridView.js b/frontend/beta/js/YUI-extensions/grid/PagedGridView.js
new file mode 100644
index 0000000..ecaece2
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/PagedGridView.js
@@ -0,0 +1,194 @@
+/**
+ * @class YAHOO.ext.grid.PagedGridView
+ * @extends YAHOO.ext.grid.GridView
+ * Extends the default GridView to add a paging interface.
+ * @constructor
+ * This class is created for you automatically if your data model is set to use paging.
+ */
+YAHOO.ext.grid.PagedGridView = function(){
+ YAHOO.ext.grid.PagedGridView.superclass.constructor.call(this);
+ this.cursor = 1;
+};
+
+YAHOO.extendX(YAHOO.ext.grid.PagedGridView, YAHOO.ext.grid.GridView, {
+ appendFooter : function(parentEl){
+ var fwrap = document.createElement('div');
+ fwrap.className = 'ygrid-wrap-footer';
+ var fbody = document.createElement('span');
+ fbody.className = 'ygrid-footer';
+ fwrap.appendChild(fbody);
+ parentEl.appendChild(fwrap);
+ this.createPagingToolbar(fbody);
+ return fwrap;
+ },
+
+ createPagingToolbar : function(container){
+ var tb = new YAHOO.ext.Toolbar(container);
+ this.pageToolbar = tb;
+ this.first = tb.addButton({
+ tooltip: this.firstText,
+ className: 'ygrid-page-first',
+ disabled: true,
+ click: this.onClick.createDelegate(this, ['first'])
+ });
+ this.prev = tb.addButton({
+ tooltip: this.prevText,
+ className: 'ygrid-page-prev',
+ disabled: true,
+ click: this.onClick.createDelegate(this, ['prev'])
+ });
+ tb.addSeparator();
+ tb.add(this.beforePageText);
+ var pageBox = document.createElement('input');
+ pageBox.type = 'text';
+ pageBox.size = 3;
+ pageBox.value = '1';
+ pageBox.className = 'ygrid-page-number';
+ tb.add(pageBox);
+ this.field = getEl(pageBox, true);
+ this.field.mon('keydown', this.onEnter, this, true);
+ this.field.on('focus', function(){pageBox.select();});
+ this.afterTextEl = tb.addText(this.afterPageText.replace('%0', '1'));
+ this.field.setHeight(18);
+ tb.addSeparator();
+ this.next = tb.addButton({
+ tooltip: this.nextText,
+ className: 'ygrid-page-next',
+ disabled: true,
+ click: this.onClick.createDelegate(this, ['next'])
+ });
+ this.last = tb.addButton({
+ tooltip: this.lastText,
+ className: 'ygrid-page-last',
+ disabled: true,
+ click: this.onClick.createDelegate(this, ['last'])
+ });
+ tb.addSeparator();
+ this.loading = tb.addButton({
+ tooltip: this.refreshText,
+ className: 'ygrid-loading',
+ disabled: true,
+ click: this.onClick.createDelegate(this, ['refresh'])
+ });
+ this.onPageLoaded(1, this.grid.dataModel.getTotalPages());
+ },
+
+ /**
+ * Returns the toolbar used for paging so you can add new buttons.
+ * @return {YAHOO.ext.Toolbar}
+ */
+ getPageToolbar : function(){
+ return this.pageToolbar;
+ },
+
+ onPageLoaded : function(pageNum, totalPages){
+ this.cursor = pageNum;
+ this.lastPage = totalPages;
+ this.afterTextEl.innerHTML = this.afterPageText.replace('%0', totalPages);
+ this.field.dom.value = pageNum;
+ this.first.setDisabled(pageNum == 1);
+ this.prev.setDisabled(pageNum == 1);
+ this.next.setDisabled(pageNum == totalPages);
+ this.last.setDisabled(pageNum == totalPages);
+ this.loading.enable();
+ },
+
+ onLoadError : function(){
+ this.loading.enable();
+ },
+
+ onEnter : function(e){
+ if(e.browserEvent.keyCode == e.RETURN){
+ var v = this.field.dom.value;
+ if(!v){
+ this.field.dom.value = this.cursor;
+ return;
+ }
+ var pageNum = parseInt(v, 10);
+ if(isNaN(pageNum)){
+ this.field.dom.value = this.cursor;
+ return;
+ }
+ pageNum = Math.min(Math.max(1, pageNum), this.lastPage);
+ this.grid.dataModel.loadPage(pageNum);
+ e.stopEvent();
+ }
+ },
+
+ beforeLoad : function(){
+ this.grid.stopEditing();
+ if(this.loading){
+ this.loading.disable();
+ }
+ },
+
+ onClick : function(which){
+ switch(which){
+ case 'first':
+ this.grid.dataModel.loadPage(1);
+ break;
+ case 'prev':
+ this.grid.dataModel.loadPage(this.cursor -1);
+ break;
+ case 'next':
+ this.grid.dataModel.loadPage(this.cursor + 1);
+ break;
+ case 'last':
+ this.grid.dataModel.loadPage(this.lastPage);
+ break;
+ case 'refresh':
+ this.grid.dataModel.loadPage(this.cursor);
+ break;
+ }
+ },
+
+ unplugDataModel : function(dm){
+ dm.removeListener('beforeload', this.beforeLoad, this);
+ dm.removeListener('load', this.onPageLoaded, this);
+ dm.removeListener('loadexception', this.onLoadError, this);
+ YAHOO.ext.grid.PagedGridView.superclass.unplugDataModel.call(this, dm);
+ },
+
+ plugDataModel : function(dm){
+ dm.on('beforeload', this.beforeLoad, this, true);
+ dm.on('load', this.onPageLoaded, this, true);
+ dm.on('loadexception', this.onLoadError, this);
+ YAHOO.ext.grid.PagedGridView.superclass.plugDataModel.call(this, dm);
+ },
+
+ /**
+ * Customizable piece of the default paging text (defaults to "Page")
+ * @type String
+ */
+ beforePageText : "Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "of %0")
+ * @type String
+ */
+ afterPageText : "of %0",
+ /**
+ * Customizable piece of the default paging text (defaults to "First Page")
+ * @type String
+ */
+ firstText : "First Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Previous Page")
+ * @type String
+ */
+ prevText : "Previous Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Next Page")
+ * @type String
+ */
+ nextText : "Next Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Last Page")
+ * @type String
+ */
+ lastText : "Last Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Refresh")
+ * @type String
+ */
+ refreshText : "Refresh"
+});
diff --git a/frontend/beta/js/YUI-extensions/grid/SelectionModel.js b/frontend/beta/js/YUI-extensions/grid/SelectionModel.js
new file mode 100644
index 0000000..6981440
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/SelectionModel.js
@@ -0,0 +1,445 @@
+/**
+ @class YAHOO.ext.grid.DefaultSelectionModel
+ * @extends YAHOO.ext.util.Observable
+ * The default SelectionModel used by {@link YAHOO.ext.grid.Grid}.
+ It supports multiple selections and keyboard selection/navigation. <br><br>
+ @constructor
+ */
+YAHOO.ext.grid.DefaultSelectionModel = function(){
+ this.selectedRows = [];
+ this.selectedRowIds = [];
+ this.lastSelectedRow = null;
+
+ this.onRowSelect = new YAHOO.util.CustomEvent('SelectionTable.rowSelected');
+ this.onSelectionChange = new YAHOO.util.CustomEvent('SelectionTable.selectionChanged');
+
+ this.events = {
+ /**
+ * @event selectionchange
+ * Fires when the selection changes
+ * @param {SelectionModel} this
+ * @param {Array} rows Array of row elements that are selected
+ * @param {String} ids Array of ids that are selected
+ */
+ 'selectionchange' : this.onSelectionChange,
+ /**
+ * @event rowselect
+ * Fires when a row is selected or deselected
+ * @param {SelectionModel} this
+ * @param {HTMLElement} row The row element
+ * @param {Boolean} selected true if the row was selected, false if deselected
+ */
+ 'rowselect' : this.onRowSelect
+ };
+
+ this.locked = false;
+};
+
+YAHOO.ext.grid.DefaultSelectionModel.prototype = {
+ /** @ignore Called by the grid automatically. Do not call directly. */
+ init : function(grid){
+ this.grid = grid;
+ this.initEvents();
+ },
+
+ /**
+ * Lock the selections
+ */
+ lock : function(){
+ this.locked = true;
+ },
+
+ /**
+ * Unlock the selections
+ */
+ unlock : function(){
+ this.locked = false;
+ },
+
+ /**
+ * Returns true if the selections are locked
+ * @return {Boolean}
+ */
+ isLocked : function(){
+ return this.locked;
+ },
+
+ /** @ignore */
+ initEvents : function(){
+ if(this.grid.trackMouseOver){
+ this.grid.addListener("mouseover", this.handleOver, this, true);
+ this.grid.addListener("mouseout", this.handleOut, this, true);
+ }
+ this.grid.addListener("rowclick", this.rowClick, this, true);
+ this.grid.addListener("keydown", this.keyDown, this, true);
+ },
+
+ fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
+ on : YAHOO.ext.util.Observable.prototype.on,
+ addListener : YAHOO.ext.util.Observable.prototype.addListener,
+ delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
+ removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
+ purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
+ bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
+
+ /** @ignore Syncs selectedRows with the correct row by looking it up by id.
+ Used after a sort moves data around. */
+ syncSelectionsToIds : function(){
+ if(this.getCount() > 0){
+ var ids = this.selectedRowIds.concat();
+ this.clearSelections();
+ this.selectRowsById(ids, true);
+ }
+ },
+
+ /**
+ * Set the selected rows by their ID(s). IDs must match what is returned by the DataModel getRowId(index).
+ * @param {String/Array} id The id(s) to select
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectRowsById : function(id, keepExisting){
+ var rows = this.grid.getRowsById(id);
+ if (!(rows instanceof Array)){
+ this.selectRow(rows, keepExisting);
+ return;
+ }
+ this.selectRows(rows, keepExisting);
+ },
+
+ /**
+ * Gets the number of selected rows.
+ * @return {Number}
+ */
+ getCount : function(){
+ return this.selectedRows.length;
+ },
+
+ /**
+ * Selects the first row in the grid.
+ */
+ selectFirstRow : function(){
+ for(var j = 0; j < this.grid.rows.length; j++){
+ if(this.isSelectable(this.grid.rows[j])){
+ this.focusRow(this.grid.rows[j]);
+ this.setRowState(this.grid.rows[j], true);
+ return;
+ }
+ }
+ },
+
+ /**
+ * Selects the row immediately following the last selected row.
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectNext : function(keepExisting){
+ if(this.lastSelectedRow){
+ for(var j = (this.lastSelectedRow.rowIndex+1); j < this.grid.rows.length; j++){
+ var row = this.grid.rows[j];
+ if(this.isSelectable(row)){
+ this.focusRow(row);
+ this.setRowState(row, true, keepExisting);
+ return;
+ }
+ }
+ }
+ },
+
+ /**
+ * Selects the row that precedes the last selected row.
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectPrevious : function(keepExisting){
+ if(this.lastSelectedRow){
+ for(var j = (this.lastSelectedRow.rowIndex-1); j >= 0; j--){
+ var row = this.grid.rows[j];
+ if(this.isSelectable(row)){
+ this.focusRow(row);
+ this.setRowState(row, true, keepExisting);
+ return;
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the selected rows.
+ * @return {Array} Array of DOM row elements
+ */
+ getSelectedRows : function(){
+ return this.selectedRows;
+ },
+
+ /**
+ * Returns the selected row ids.
+ * @return {Array} Array of String ids
+ */
+ getSelectedRowIds : function(){
+ return this.selectedRowIds;
+ },
+
+ /**
+ * Clears all selections.
+ */
+ clearSelections : function(){
+ if(this.isLocked()) return;
+ var oldSelections = this.selectedRows.concat();
+ for(var j = 0; j < oldSelections.length; j++){
+ this.setRowState(oldSelections[j], false);
+ }
+ this.selectedRows = [];
+ this.selectedRowIds = [];
+ },
+
+
+ /**
+ * Selects all rows.
+ */
+ selectAll : function(){
+ if(this.isLocked()) return;
+ this.selectedRows = [];
+ this.selectedRowIds = [];
+ for(var j = 0, len = this.grid.rows.length; j < len; j++){
+ this.setRowState(this.grid.rows[j], true, true);
+ }
+ },
+
+ /**
+ * Returns True if there is a selection.
+ * @return {Boolean}
+ */
+ hasSelection : function(){
+ return this.selectedRows.length > 0;
+ },
+
+ /**
+ * Returns True if the specified row is selected.
+ * @param {HTMLElement} row The row to check
+ * @return {Boolean}
+ */
+ isSelected : function(row){
+ return row && (row.selected === true || row.getAttribute('selected') == 'true');
+ },
+
+ /**
+ * Returns True if the specified row is selectable.
+ * @param {HTMLElement} row The row to check
+ * @return {Boolean}
+ */
+ isSelectable : function(row){
+ return row && row.getAttribute('selectable') != 'false';
+ },
+
+ /** @ignore */
+ rowClick : function(grid, rowIndex, e){
+ if(this.isLocked()) return;
+ var row = grid.getRow(rowIndex);
+ if(this.isSelectable(row)){
+ if(e.shiftKey && this.lastSelectedRow){
+ var lastIndex = this.lastSelectedRow.rowIndex;
+ this.selectRange(this.lastSelectedRow, row, e.ctrlKey);
+ this.lastSelectedRow = this.grid.el.dom.rows[lastIndex];
+ }else{
+ this.focusRow(row);
+ var rowState = e.ctrlKey ? !this.isSelected(row) : true;
+ this.setRowState(row, rowState, e.hasModifier());
+ }
+ }
+ },
+
+ /**
+ * Deprecated. Tries to focus the row and scroll it into view - Use grid.scrollTo or grid.getView().focusRow() instead.
+ * @deprecated
+ * @param {HTMLElement} row The row to focus
+ */
+ focusRow : function(row){
+ this.grid.view.focusRow(row);
+ },
+
+ /**
+ * Selects a row.
+ * @param {Number/HTMLElement} row The row or index of the row to select
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectRow : function(row, keepExisting){
+ this.setRowState(this.getRow(row), true, keepExisting);
+ },
+
+ /**
+ * Selects multiple rows.
+ * @param {Array} rows Array of the rows or indexes of the row to select
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectRows : function(rows, keepExisting){
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ for(var i = 0; i < rows.length; i++){
+ this.selectRow(rows[i], true);
+ }
+ },
+
+ /**
+ * Deselects a row.
+ * @param {Number/HTMLElement} row The row or index of the row to deselect
+ */
+ deselectRow : function(row){
+ this.setRowState(this.getRow(row), false);
+ },
+
+ /** @ignore */
+ getRow : function(row){
+ if(typeof row == 'number'){
+ row = this.grid.rows[row];
+ }
+ return row;
+ },
+
+ /**
+ * Selects a range of rows. All rows in between startRow and endRow are also selected.
+ * @param {Number/HTMLElement} startRow The row or index of the first row in the range
+ * @param {Number/HTMLElement} endRow The row or index of the last row in the range
+ * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
+ */
+ selectRange : function(startRow, endRow, keepExisting){
+ startRow = this.getRow(startRow);
+ endRow = this.getRow(endRow);
+ this.setRangeState(startRow, endRow, true, keepExisting);
+ },
+
+ /**
+ * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
+ * @param {Number/HTMLElement} startRow The row or index of the first row in the range
+ * @param {Number/HTMLElement} endRow The row or index of the last row in the range
+ */
+ deselectRange : function(startRow, endRow){
+ startRow = this.getRow(startRow);
+ endRow = this.getRow(endRow);
+ this.setRangeState(startRow, endRow, false, true);
+ },
+
+ /** @ignore */
+ setRowStateFromChild : function(childEl, selected, keepExisting){
+ var row = this.grid.getRowFromChild(childEl);
+ this.setRowState(row, selected, keepExisting);
+ },
+
+ /** @ignore */
+ setRangeState : function(startRow, endRow, selected, keepExisting){
+ if(this.isLocked()) return;
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ var curRow = startRow;
+ while(curRow.rowIndex != endRow.rowIndex){
+ this.setRowState(curRow, selected, true);
+ curRow = (startRow.rowIndex < endRow.rowIndex ?
+ this.grid.getRowAfter(curRow) : this.grid.getRowBefore(curRow))
+ }
+ this.setRowState(endRow, selected, true);
+ },
+
+ /** @ignore */
+ setRowState : function(row, selected, keepExisting){
+ if(this.isLocked()) return;
+ if(this.isSelectable(row)){
+ if(selected){
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ this.setRowClass(row, 'selected');
+ row.selected = true;
+ this.selectedRows.push(row);
+ this.selectedRowIds.push(this.grid.dataModel.getRowId(row.rowIndex));
+ this.lastSelectedRow = row;
+ }else{
+ this.setRowClass(row, '');
+ row.selected = false;
+ this._removeSelected(row);
+ }
+ this.fireEvent('rowselect', this, row, selected);
+ this.fireEvent('selectionchange', this, this.selectedRows, this.selectedRowIds);
+ }
+ },
+
+ /** @ignore */
+ handleOver : function(e){
+ var row = this.grid.getRowFromChild(e.getTarget());
+ if(this.isSelectable(row) && !this.isSelected(row)){
+ this.setRowClass(row, 'over');
+ }
+ },
+
+ /** @ignore */
+ handleOut : function(e){
+ var row = this.grid.getRowFromChild(e.getTarget());
+ if(this.isSelectable(row) && !this.isSelected(row)){
+ this.setRowClass(row, '');
+ }
+ },
+
+ /** @ignore */
+ keyDown : function(e){
+ if(e.browserEvent.keyCode == e.DOWN){
+ this.selectNext(e.shiftKey);
+ e.preventDefault();
+ }else if(e.browserEvent.keyCode == e.UP){
+ this.selectPrevious(e.shiftKey);
+ e.preventDefault();
+ }
+ },
+
+ /** @ignore */
+ setRowClass : function(row, cssClass){
+ if(this.isSelectable(row)){
+ if(cssClass == 'selected'){
+ YAHOO.util.Dom.removeClass(row, 'ygrid-row-over');
+ YAHOO.util.Dom.addClass(row, 'ygrid-row-selected');
+ }else if(cssClass == 'over'){
+ YAHOO.util.Dom.removeClass(row, 'ygrid-row-selected');
+ YAHOO.util.Dom.addClass(row, 'ygrid-row-over');
+ }else if(cssClass == ''){
+ YAHOO.util.Dom.removeClass(row, 'ygrid-row-selected');
+ YAHOO.util.Dom.removeClass(row, 'ygrid-row-over');
+ }
+ }
+ },
+
+ /** @ignore */
+ _removeSelected : function(row){
+ var sr = this.selectedRows;
+ for (var i = 0; i < sr.length; i++) {
+ if (sr[i] === row){
+ this.selectedRows.splice(i, 1);
+ this.selectedRowIds.splice(i, 1);
+ return;
+ }
+ }
+ }
+};
+
+/**
+ @class YAHOO.ext.grid.SingleSelectionModel
+ @extends YAHOO.ext.grid.DefaultSelectionModel
+ Allows only one row to be selected at a time.
+ @constructor
+ * Create new SingleSelectionModel
+ */
+YAHOO.ext.grid.SingleSelectionModel = function(){
+ YAHOO.ext.grid.SingleSelectionModel.superclass.constructor.call(this);
+};
+
+YAHOO.extendX(YAHOO.ext.grid.SingleSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
+
+/** @ignore */
+YAHOO.ext.grid.SingleSelectionModel.prototype.setRowState = function(row, selected){
+ YAHOO.ext.grid.SingleSelectionModel.superclass.setRowState.call(this, row, selected, false);
+};
+
+YAHOO.ext.grid.DisableSelectionModel = function(){
+ YAHOO.ext.grid.DisableSelectionModel.superclass.constructor.call(this);
+};
+
+YAHOO.extendX(YAHOO.ext.grid.DisableSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
+
+YAHOO.ext.grid.DisableSelectionModel.prototype.initEvents = function(){
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js
new file mode 100644
index 0000000..7c51a48
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js
@@ -0,0 +1,91 @@
+/**
+ * @class YAHOO.ext.grid.CellEditor
+ * Base class for all EditorGrid editors
+ */
+YAHOO.ext.grid.CellEditor = function(element){
+ this.colIndex = null;
+ this.rowIndex = null;
+ this.grid = null;
+ this.editing = false;
+ this.originalValue = null;
+ this.element = getEl(element, true);
+ this.element.addClass('ygrid-editor');
+ this.element.dom.tabIndex = 1;
+ this.initialized = false;
+ this.callback = null;
+};
+
+YAHOO.ext.grid.CellEditor.prototype = {
+ init : function(grid, bodyElement, callback){
+ // there's no way for the grid to know if multiple columns
+ // share the same editor so it will try to initialize the
+ // same one over and over
+ if(this.initialized) return;
+ this.initialized = true;
+ this.callback = callback;
+ this.grid = grid;
+ bodyElement.appendChild(this.element.dom);
+ this.initEvents();
+ },
+
+ initEvents : function(){
+ var stopOnEnter = function(e){
+ if(e.browserEvent.keyCode == e.RETURN){
+ this.stopEditing(true);
+ }else if(e.browserEvent.keyCode == e.ESC){
+ this.setValue(this.originalValue);
+ this.stopEditing(true);
+ }
+ }
+ this.element.mon('keydown', stopOnEnter, this, true);
+ this.element.on('blur', this.stopEditing, this, true);
+ },
+
+ startEditing : function(value, row, cell){
+ this.originalValue = value;
+ this.rowIndex = row.rowIndex;
+ this.colIndex = cell.columnIndex;
+ this.cell = cell;
+ this.setValue(value);
+ var cellbox = getEl(cell, true).getBox();
+ this.fitToCell(cellbox);
+ this.editing = true;
+ this.show();
+ },
+
+ stopEditing : function(focusCell){
+ if(this.editing){
+ this.editing = false;
+ var newValue = this.getValue();
+ this.hide();
+ //if(focusCell){try{this.cell.focus();}catch(e){}}; // try to give the cell focus so keyboard nav still works
+ if(this.originalValue != newValue){
+ this.callback(newValue, this.rowIndex, this.colIndex);
+ }
+ }
+ },
+
+ setValue : function(value){
+ this.element.dom.value = value;
+ },
+
+ getValue : function(){
+ return this.element.dom.value;
+ },
+
+ fitToCell : function(box){
+ this.element.setBox(box, true);
+ },
+
+ show : function(){
+ this.element.show();
+ this.element.focus();
+ },
+
+ hide : function(){
+ try{
+ this.element.dom.blur();
+ }catch(e){}
+ this.element.hide();
+ }
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js
new file mode 100644
index 0000000..681b847
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js
@@ -0,0 +1,60 @@
+/**
+ * @class YAHOO.ext.grid.CheckboxEditor
+ * @extends YAHOO.ext.grid.CellEditor
+Provides a checkbox for editing boolean values. It currently has no configuration options.<br><br>
+For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
+* @constructor
+* Create a new CheckboxEditor
+ */
+YAHOO.ext.grid.CheckboxEditor = function(){
+ var div = document.createElement('span');
+ div.className = 'ygrid-editor ygrid-checkbox-editor';
+ var cb = document.createElement('input');
+ cb.type = 'checkbox';
+ cb.setAttribute('autocomplete', 'off');
+ div.appendChild(cb);
+ document.body.appendChild(div);
+ YAHOO.ext.grid.CheckboxEditor.superclass.constructor.call(this, div);
+ div.tabIndex = '';
+ cb.tabIndex = 1;
+ this.cb = getEl(cb, true);
+};
+
+YAHOO.extendX(YAHOO.ext.grid.CheckboxEditor, YAHOO.ext.grid.CellEditor);
+
+YAHOO.ext.grid.CheckboxEditor.prototype.fitToCell = function(box){
+ this.element.setBox(box, true);
+};
+
+YAHOO.ext.grid.CheckboxEditor.prototype.setValue = function(value){
+ this.cb.dom.checked = (value === true || value === 'true' || value === 1 || value === '1');
+};
+
+YAHOO.ext.grid.CheckboxEditor.prototype.getValue = function(){
+ return this.cb.dom.checked;
+};
+
+YAHOO.ext.grid.CheckboxEditor.prototype.show = function(){
+ this.element.show();
+ this.cb.focus();
+};
+
+YAHOO.ext.grid.CheckboxEditor.prototype.initEvents = function(){
+ var stopOnEnter = function(e){
+ if(e.browserEvent.keyCode == e.RETURN){
+ this.stopEditing(true);
+ }else if(e.browserEvent.keyCode == e.ESC){
+ this.setValue(this.originalValue);
+ this.stopEditing(true);
+ }
+ }
+ this.cb.mon('keydown', stopOnEnter, this, true);
+ this.cb.on('blur', this.stopEditing, this, true);
+};
+
+YAHOO.ext.grid.CheckboxEditor.prototype.hide = function(){
+ try{
+ this.cb.dom.blur();
+ }catch(e){}
+ this.element.hide();
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js
new file mode 100644
index 0000000..303ad2b
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js
@@ -0,0 +1,268 @@
+/**
+ * @class YAHOO.ext.grid.DateEditor
+ * @extends YAHOO.ext.grid.CellEditor
+Provides a date editor field, and optionally a DatePicker. The DateEditor provides a method to override (showCalendar) if you don't want to use the built in DatePicker control. The reason I chose to use my own DatePicker control rather than the nice YUI Calendar component is my control was very easy to override events to make it work well with the grid. It's also only 5k compressed, while the YUI Calendar is 40k compressed. The DatePicker supports left/right keys to move months, up/down keys to move years and the mouse wheel to quickly go through the months. The DateEditor supports the following configuration options:
+<ul class="list">
+<li><i>format</i> - The date format for the editor. The format is identical to <a href="http://www.php.net/date">PHP date()</a> and text is allowed. Credit for that goes to <a style="font-weight:normal;" href="http://www.xaprb.com/blog/2006/05/14/javascript-date-formatting-benchmarks/">this fantastic date library</a>. This format is for the editor only and doesn't affect the rendering of the cell when not in edit mode. Your rendering function can use any date format it wants.</li>
+<li><i>minValue</i> - The minimum allowed date. Can be either a Javascript date object or a string date in the specified format.</li>
+<li><i>maxValue</i> - The maximum allowed date. Can be either a Javascript date object or a string date in the specified format.</li>
+<li><i>minText</i> - The tooltip to display when the date in the cell is before minValue.</li>
+<li><i>maxText</i> - The tooltip to display when the date in the cell is after maxValue.</li>
+<li><i>invalidText</i> - The text to display when the date in the field is invalid (for example: 02/31/06)</li>
+<li><i>disabledDays</i> - An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday.</li>
+<li><i>disabledDaysText</i> - The tooltip to display when the date in the cell (or DatePicker) falls on a disabled day.</li>
+<li><i>disabledDates</i> - An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular expression so they are very powerful. For example, ["03/08/2003", "09/16/2003"] would disable those dates, but ["03/08", "09/16"] would disable them for every year. If you are using short years, you will want to use ^ to tell the regular expression to only match the beginning like ["^03/08"]. To disable March of 2006: ["03/../2006"] or every March ["^03"]. In order to support regular expressions, if you are using a date format that has "." in it, you will have to escape the dot when restricting dates. For example: ["03\\.08\\.03"].</li>
+<li><i>disabledDatesText</i> - The tooltip to display when the date in the cell (or DatePicker) falls on a disabled date.</li>
+<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
+<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
+<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
+<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
+</ul>
+For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
+* @constructor
+* Create a new DateEditor
+* @param {Object} config
+ */
+YAHOO.ext.grid.DateEditor = function(config){
+ var div = document.createElement('span');
+ div.className = 'ygrid-editor ygrid-editor-container';
+
+ var element = document.createElement('input');
+ element.type = 'text';
+ element.tabIndex = 1;
+ element.setAttribute('autocomplete', 'off');
+ div.appendChild(element);
+
+ var pick = document.createElement('span');
+ pick.className = 'pick-button';
+ div.appendChild(pick);
+
+ document.body.appendChild(div);
+
+ this.div = getEl(div, true);
+ this.element = getEl(element, true);
+ this.pick = getEl(pick, true);
+
+ this.colIndex = null;
+ this.rowIndex = null;
+ this.grid = null;
+ this.editing = false;
+ this.originalValue = null;
+ this.initialized = false;
+ this.callback = null;
+
+ this.cal = null;
+ this.mouseDownHandler = YAHOO.ext.EventManager.wrap(this.handleMouseDown, this, true);
+
+ YAHOO.ext.util.Config.apply(this, config);
+ if(typeof this.minValue == 'string') this.minValue = this.parseDate(this.minValue);
+ if(typeof this.maxValue == 'string') this.maxValue = this.parseDate(this.maxValue);
+ this.ddMatch = /ddnone/;
+ if(this.disabledDates){
+ var dd = this.disabledDates;
+ var re = "(?:";
+ for(var i = 0; i < dd.length; i++){
+ re += dd[i];
+ if(i != dd.length-1) re += "|";
+ }
+ this.ddMatch = new RegExp(re + ")");
+ }
+};
+
+YAHOO.ext.grid.DateEditor.prototype = {
+ init : function(grid, bodyElement, callback){
+ if(this.initialized) return;
+
+ this.initialized = true;
+ this.callback = callback;
+ this.grid = grid;
+ bodyElement.appendChild(this.div.dom);
+ this.initEvents();
+ },
+
+ initEvents : function(){
+ var stopOnEnter = function(e){
+ if(e.browserEvent.keyCode == e.RETURN){
+ this.stopEditing(true);
+ }else if(e.browserEvent.keyCode == e.ESC){
+ this.setValue(this.originalValue);
+ this.stopEditing(true);
+ }
+ }
+ this.element.mon('keydown', stopOnEnter, this, true);
+ var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
+ this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
+ this.pick.on('click', this.showCalendar, this, true);
+ },
+
+ startEditing : function(value, row, cell){
+ this.originalValue = value;
+ this.rowIndex = row.rowIndex;
+ this.colIndex = cell.columnIndex;
+ this.cell = cell;
+ this.setValue(value);
+ this.validate();
+ var cellbox = getEl(cell, true).getBox();
+ this.div.setBox(cellbox, true);
+ this.element.setWidth(cellbox.width-this.pick.getWidth());
+ this.editing = true;
+ YAHOO.util.Event.on(document, "mousedown", this.mouseDownHandler);
+ this.show();
+ },
+
+ stopEditing : function(focusCell){
+ if(this.editing){
+ YAHOO.util.Event.removeListener(document, "mousedown", this.mouseDownHandler);
+ this.editing = false;
+ var newValue = this.getValue();
+ this.hide();
+ //if(focusCell){try{this.cell.focus();}catch(e){}}// try to give the cell focus so keyboard nav still works
+ if(this.originalValue != newValue){
+ this.callback(newValue, this.rowIndex, this.colIndex);
+ }
+ }
+ },
+
+ setValue : function(value){
+ this.element.dom.value = this.formatDate(value);
+ this.validate();
+ },
+
+ getValue : function(){
+ if(!this.validate()){
+ return this.originalValue;
+ }else{
+ var value = this.element.dom.value;
+ if(value.length < 1){
+ return value;
+ } else{
+ return this.parseDate(value);
+ }
+ }
+ },
+
+ show : function() {
+ this.div.show();
+ this.element.focus();
+ this.validate();
+ },
+
+ hide : function(){
+ try{
+ this.element.dom.blur();
+ }catch(e){}
+ this.div.hide();
+ },
+
+ validate : function(){
+ var dom = this.element.dom;
+ var value = dom.value;
+ if(value.length < 1){ // if it's blank
+ if(this.allowBlank){
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+ }else{
+ dom.title = this.blankText;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ }
+ value = this.parseDate(value);
+ if(!value){
+ dom.title = this.invalidText.replace('%0', dom.value).replace('%1', this.format);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ var time = value.getTime();
+ if(this.minValue && time < this.minValue.getTime()){
+ dom.title = this.minText.replace('%0', this.formatDate(this.minValue));
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ if(this.maxValue && time > this.maxValue.getTime()){
+ dom.title = this.maxText.replace('%0', this.formatDate(this.maxValue));
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ if(this.disabledDays){
+ var day = value.getDay();
+ for(var i = 0; i < this.disabledDays.length; i++) {
+ if(day === this.disabledDays[i]){
+ dom.title = this.disabledDaysText;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ }
+ }
+ var fvalue = this.formatDate(value);
+ if(this.ddMatch.test(fvalue)){
+ dom.title = this.disabledDatesText.replace('%0', fvalue);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ var msg = this.validator(value);
+ if(msg !== true){
+ dom.title = msg;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+ },
+
+ handleMouseDown : function(e){
+ var t = e.getTarget();
+ var dom = this.div.dom;
+ if(t != dom && !YAHOO.util.Dom.isAncestor(dom, t)){
+ this.stopEditing();
+ }
+ },
+
+ showCalendar : function(value){
+ if(this.cal == null){
+ this.cal = new YAHOO.ext.DatePicker(this.div.dom.parentNode.parentNode);
+ }
+ this.cal.minDate = this.minValue;
+ this.cal.maxDate = this.maxValue;
+ this.cal.disabledDatesRE = this.ddMatch;
+ this.cal.disabledDatesText = this.disabledDatesText;
+ this.cal.disabledDays = this.disabledDays;
+ this.cal.disabledDaysText = this.disabledDaysText;
+ this.cal.format = this.format;
+ if(this.minValue){
+ this.cal.minText = this.minText.replace('%0', this.formatDate(this.minValue));
+ }
+ if(this.maxValue){
+ this.cal.maxText = this.maxText.replace('%0', this.formatDate(this.maxValue));
+ }
+ var r = this.div.getRegion();
+ this.cal.show(r.left, r.bottom, this.getValue(), this.setValue.createDelegate(this));
+ },
+
+ parseDate : function(value){
+ if(!value || value instanceof Date) return value;
+ return Date.parseDate(value, this.format);
+ },
+
+ formatDate : function(date){
+ if(!date || !(date instanceof Date)) return date;
+ return date.format(this.format);
+ }
+};
+
+YAHOO.ext.grid.DateEditor.prototype.format = 'm/d/y';
+YAHOO.ext.grid.DateEditor.prototype.disabledDays = null;
+YAHOO.ext.grid.DateEditor.prototype.disabledDaysText = '';
+YAHOO.ext.grid.DateEditor.prototype.disabledDates = null;
+YAHOO.ext.grid.DateEditor.prototype.disabledDatesText = '';
+YAHOO.ext.grid.DateEditor.prototype.allowBlank = true;
+YAHOO.ext.grid.DateEditor.prototype.minValue = null;
+YAHOO.ext.grid.DateEditor.prototype.maxValue = null;
+YAHOO.ext.grid.DateEditor.prototype.minText = 'The date in this field must be after %0';
+YAHOO.ext.grid.DateEditor.prototype.maxText = 'The date in this field must be before %0';
+YAHOO.ext.grid.DateEditor.prototype.blankText = 'This field cannot be blank';
+YAHOO.ext.grid.DateEditor.prototype.invalidText = '%0 is not a valid date - it must be in the format %1';
+YAHOO.ext.grid.DateEditor.prototype.validationDelay = 200;
+YAHOO.ext.grid.DateEditor.prototype.validator = function(){return true;};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js
new file mode 100644
index 0000000..f74d3d9
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js
@@ -0,0 +1,166 @@
+/**
+ * @class YAHOO.ext.grid.NumberEditor
+ * @extends YAHOO.ext.grid.CellEditor
+Provides a masked editor for numeric values. Invalid keys are ignored. It supports the following configuration options:
+<ul class="list">
+<li><i>allowDecimals</i> - True if the cell can have decimal values.</li>
+<li><i>decimalSeparator</i> - Character(s) to allow as the decimal separator.</li>
+<li><i>decimalPrecision</i> - Set the maximum decimal precision.</li>
+<li><i>decimalPrecisionFcn</i> - Define the function to call to remove extra precision (ie. Math.floor, Math.round, Math.ceil or your own function).</li>
+<li><i>allowNegative</i> - True if the cell allows negative values.</li>
+<li><i>selectOnFocus</i> - True to select the text when the editor is activated.</li>
+<li><i>minValue</i> - The minimum value the cell will allow.</li>
+<li><i>maxValue</i> - The maximum value the cell will allow.</li>
+<li><i>minText</i> - The tooltip to display when the value in the cell is below the minimum.</li>
+<li><i>maxText</i> - The tooltip to display when the value in the cell is above the maximum.</li>
+<li><i>nanText</i> - The tooltip to display when the value in the cell is not a valid number (for example, negatives are allowed and the value in the cell is just "-" with no numbers).</li>
+<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
+<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
+<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
+<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
+</ul>
+For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
+* @constructor
+* Create a new NumberEditor
+* @param {Object} config
+ */
+YAHOO.ext.grid.NumberEditor = function(config){
+ var element = document.createElement('input');
+ element.type = 'text';
+ element.className = 'ygrid-editor ygrid-num-editor';
+ element.setAttribute('autocomplete', 'off');
+ document.body.appendChild(element);
+ YAHOO.ext.grid.NumberEditor.superclass.constructor.call(this, element);
+ YAHOO.ext.util.Config.apply(this, config);
+};
+YAHOO.extendX(YAHOO.ext.grid.NumberEditor, YAHOO.ext.grid.CellEditor);
+
+YAHOO.ext.grid.NumberEditor.prototype.initEvents = function(){
+ var stopOnEnter = function(e){
+ if(e.browserEvent.keyCode == e.RETURN){
+ this.stopEditing(true);
+ }else if(e.browserEvent.keyCode == e.ESC){
+ this.setValue(this.originalValue);
+ this.stopEditing(true);
+ }
+ };
+
+ var allowed = "0123456789";
+ if(this.allowDecimals){
+ allowed += this.decimalSeparator;
+ }
+ if(this.allowNegative){
+ allowed += '-';
+ }
+ var keyPress = function(e){
+ var c = e.getCharCode();
+ if(c != e.BACKSPACE && allowed.indexOf(String.fromCharCode(c)) === -1){
+ e.stopEvent();
+ }
+ };
+ this.element.mon('keydown', stopOnEnter, this, true);
+ var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
+ this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
+ this.element.mon('keypress', keyPress, this, true);
+ this.element.on('blur', this.stopEditing, this, true);
+};
+
+YAHOO.ext.grid.NumberEditor.prototype.validate = function(){
+ var dom = this.element.dom;
+ var value = dom.value;
+ if(value.length < 1){ // if it's blank
+ if(this.allowBlank){
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+ }else{
+ dom.title = this.blankText;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ }
+ if(value.search(/\d+/) === -1){
+ dom.title = this.nanText.replace('%0', value);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ var num = this.parseValue(value);
+ if(num < this.minValue){
+ dom.title = this.minText.replace('%0', this.minValue);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ if(num > this.maxValue){
+ dom.title = this.maxText.replace('%0', this.maxValue);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ var msg = this.validator(value);
+ if(msg !== true){
+ dom.title = msg;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+};
+
+YAHOO.ext.grid.NumberEditor.prototype.show = function(){
+ this.element.dom.title = '';
+ YAHOO.ext.grid.NumberEditor.superclass.show.call(this);
+ if(this.selectOnFocus){
+ try{
+ this.element.dom.select();
+ }catch(e){}
+ }
+ this.validate(this.element.dom.value);
+};
+
+YAHOO.ext.grid.NumberEditor.prototype.getValue = function(){
+ if(!this.validate()){
+ return this.originalValue;
+ }else{
+ var value = this.element.dom.value;
+ if(value.length < 1){
+ return value;
+ } else{
+ return this.fixPrecision(this.parseValue(value));
+ }
+ }
+};
+YAHOO.ext.grid.NumberEditor.prototype.parseValue = function(value){
+ return parseFloat(new String(value).replace(this.decimalSeparator, '.'));
+};
+
+YAHOO.ext.grid.NumberEditor.prototype.fixPrecision = function(value){
+ if(!this.allowDecimals || this.decimalPrecision == -1 || isNaN(value) || value == 0 || !value){
+ return value;
+ }
+ // this should work but doesn't due to precision error in JS
+ // var scale = Math.pow(10, this.decimalPrecision);
+ // var fixed = this.decimalPrecisionFcn(value * scale);
+ // return fixed / scale;
+ //
+ // so here's our workaround:
+ var scale = Math.pow(10, this.decimalPrecision+1);
+ var fixed = this.decimalPrecisionFcn(value * scale);
+ fixed = this.decimalPrecisionFcn(fixed/10);
+ return fixed / (scale/10);
+};
+
+YAHOO.ext.grid.NumberEditor.prototype.allowBlank = true;
+YAHOO.ext.grid.NumberEditor.prototype.allowDecimals = true;
+YAHOO.ext.grid.NumberEditor.prototype.decimalSeparator = '.';
+YAHOO.ext.grid.NumberEditor.prototype.decimalPrecision = 2;
+YAHOO.ext.grid.NumberEditor.prototype.decimalPrecisionFcn = Math.floor;
+YAHOO.ext.grid.NumberEditor.prototype.allowNegative = true;
+YAHOO.ext.grid.NumberEditor.prototype.selectOnFocus = true;
+YAHOO.ext.grid.NumberEditor.prototype.minValue = Number.NEGATIVE_INFINITY;
+YAHOO.ext.grid.NumberEditor.prototype.maxValue = Number.MAX_VALUE;
+YAHOO.ext.grid.NumberEditor.prototype.minText = 'The minimum value for this field is %0';
+YAHOO.ext.grid.NumberEditor.prototype.maxText = 'The maximum value for this field is %0';
+YAHOO.ext.grid.NumberEditor.prototype.blankText = 'This field cannot be blank';
+YAHOO.ext.grid.NumberEditor.prototype.nanText = '%0 is not a valid number';
+YAHOO.ext.grid.NumberEditor.prototype.validationDelay = 100;
+YAHOO.ext.grid.NumberEditor.prototype.validator = function(){return true;};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js
new file mode 100644
index 0000000..200b8e3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js
@@ -0,0 +1,37 @@
+/**
+ * @class YAHOO.ext.grid.SelectEditor
+ * @extends YAHOO.ext.grid.CellEditor
+Creates an editor out of an existing select field. You can create the select element through DOM in Javascript and pass it to the SelectEditor's constructor <b>or</b> an easier way is like this:
+<br><br>
+Define the select field in your document, giving it the ygrid-editor class.
+<pre><code>
+&lt;select id="light" class="ygrid-editor"&gt;
+ &lt;option value="Shade"&gt;Shade&lt;/option&gt;
+ &lt;option value="Mostly Shady"&gt;Mostly Shady&lt;/option&gt;
+ &lt;option value="Sun or Shade"&gt;Sun or Shade&lt;/option&gt;
+ &lt;option value="Mostly Sunny"&gt;Mostly Sunny&lt;/option&gt;
+ &lt;option value="Sunny"&gt;Sunny&lt;/option&gt;
+&lt;/select&gt;
+</code></pre>
+Create the SelectEditor object, passing in the id of your select field.
+<pre><code>
+var editor = new YAHOO.ext.grid.SelectEditor('light');
+</code></pre>
+For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
+* @constructor
+* Create a new SelectEditor
+* @param {HTMLElement/String} element
+ */
+YAHOO.ext.grid.SelectEditor = function(element){
+ element.hideFocus = true;
+ YAHOO.ext.grid.SelectEditor.superclass.constructor.call(this, element);
+ this.element.swallowEvent('click');
+};
+YAHOO.extendX(YAHOO.ext.grid.SelectEditor, YAHOO.ext.grid.CellEditor);
+
+YAHOO.ext.grid.SelectEditor.prototype.fitToCell = function(box){
+ if(YAHOO.ext.util.Browser.isGecko){
+ box.height -= 3;
+ }
+ this.element.setBox(box, true);
+};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js
new file mode 100644
index 0000000..3c97acd
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js
@@ -0,0 +1,110 @@
+/**
+ * @class YAHOO.ext.grid.TextEditor
+ * @extends YAHOO.ext.grid.CellEditor
+Provides basic text editing for a cells and supports the following configuration options:
+<ul class="list">
+<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
+<li><i>minLength</i> - The minimum length the cell will accept.</li>
+<li><i>maxLength</i> - The maximum length the cell will allow.</li>
+<li><i>minText</i> - The tooltip to display when the length of the value in the cell is below the minimum.</li>
+<li><i>maxText</i> - The tooltip to display when the length of the value in the cell is above the maximum.</li>
+<li><i>selectOnFocus</i> - True to select the text when the editor is activated.</li>
+<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
+<li><i>regex</i> - A regular expression to match if the value is valid. If the regex.test(value) returns false, the value is considered invalid.</li>
+<li><i>regexText</i> - The tooltip (error message) to display when regex does not match.</li>
+<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
+<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
+</ul>
+For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
+* @constructor
+* Create a new TextEditor
+* @param {Object} config
+ */
+YAHOO.ext.grid.TextEditor = function(config){
+ var element = document.createElement('input');
+ element.type = 'text';
+ element.className = 'ygrid-editor ygrid-text-editor';
+ element.setAttribute('autocomplete', 'off');
+ document.body.appendChild(element);
+ YAHOO.ext.grid.TextEditor.superclass.constructor.call(this, element);
+ YAHOO.ext.util.Config.apply(this, config);
+};
+YAHOO.extendX(YAHOO.ext.grid.TextEditor, YAHOO.ext.grid.CellEditor);
+
+YAHOO.ext.grid.TextEditor.prototype.validate = function(){
+ var dom = this.element.dom;
+ var value = dom.value;
+ if(value.length < 1){ // if it's blank
+ if(this.allowBlank){
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+ }else{
+ dom.title = this.blankText;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ }
+ if(value.length < this.minLength){
+ dom.title = this.minText.replace('%0', this.minLength);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ if(value.length > this.maxLength){
+ dom.title = this.maxText.replace('%0', this.maxLength);
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ var msg = this.validator(value);
+ if(msg !== true){
+ dom.title = msg;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ if(this.regex && !this.regex.test(value)){
+ dom.title = this.regexText;
+ this.element.addClass('ygrid-editor-invalid');
+ return false;
+ }
+ dom.title = '';
+ this.element.removeClass('ygrid-editor-invalid');
+ return true;
+};
+
+YAHOO.ext.grid.TextEditor.prototype.initEvents = function(){
+ YAHOO.ext.grid.TextEditor.superclass.initEvents.call(this);
+ var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
+ this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
+};
+
+YAHOO.ext.grid.TextEditor.prototype.show = function(){
+ this.element.dom.title = '';
+ YAHOO.ext.grid.TextEditor.superclass.show.call(this);
+ this.element.focus();
+ if(this.selectOnFocus){
+ try{
+ this.element.dom.select();
+ }catch(e){}
+ }
+ this.validate(this.element.dom.value);
+};
+
+YAHOO.ext.grid.TextEditor.prototype.getValue = function(){
+ if(!this.validate()){
+ return this.originalValue;
+ }else{
+ return this.element.dom.value;
+ }
+};
+
+YAHOO.ext.grid.TextEditor.prototype.allowBlank = true;
+YAHOO.ext.grid.TextEditor.prototype.minLength = 0;
+YAHOO.ext.grid.TextEditor.prototype.maxLength = Number.MAX_VALUE;
+YAHOO.ext.grid.TextEditor.prototype.minText = 'The minimum length for this field is %0';
+YAHOO.ext.grid.TextEditor.prototype.maxText = 'The maximum length for this field is %0';
+YAHOO.ext.grid.TextEditor.prototype.selectOnFocus = true;
+YAHOO.ext.grid.TextEditor.prototype.blankText = 'This field cannot be blank';
+YAHOO.ext.grid.TextEditor.prototype.validator = function(){return true;};
+YAHOO.ext.grid.TextEditor.prototype.validationDelay = 200;
+YAHOO.ext.grid.TextEditor.prototype.regex = null;
+YAHOO.ext.grid.TextEditor.prototype.regexText = '';