Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some proposed additions [in this message text]: 1) data filtering[sorting friendly] 2) not optimized convinience methods & callbacks #22

Open
IvankoB opened this issue Apr 14, 2015 · 0 comments

Comments

@IvankoB
Copy link

IvankoB commented Apr 14, 2015

////////////////////////////////////
//
// Grid
// MIT-style license. Copyright 2012 Matt V. Murphy
//
////////////////////////////////////

/*

Usage

Create a div element with an id attribute and preferably a fixed width and height:

Initialize the grid using Javascript:

var gridData = {
Head : [["Header 1", "Header 2", "Header 3"]],
Body : [["Row 1, Cell 1", "Row 1, Cell 2", "Row 1, Cell 3"],
["Row 2, Cell 1", "Row 2, Cell 2", "Row 2, Cell 3"],
["Row 3, Cell 1", "Row 3, Cell 2", "Row 3, Cell 3"]]
};

var grd = new Grid("myGrid", {
srcType : "json",
srcData : gridData,
allowGridResize : true,
allowColumnResize : true,
allowClientSideSorting : true,
allowSelections : true,
allowMultipleSelections : true,
showSelectionColumn : true,
fixedCols : 1
});

for (var i=0; i < grd.selectedIndexes.length; i++) {
alert(grd.rawData[grdTest.selectedIndexes[i]][colNum_inclSelectionColumnIfUsed]);
}

Grid data can also be in XML format or retrieved from the DOM:

Header 1Header 2Header 3
Row 1, Cell 1Row 1, Cell 2Row 1, Cell 3
Row 2, Cell 1Row 2, Cell 2Row 2, Cell 3
Row 3, Cell 1Row 3, Cell 2Row 3, Cell 3

Options

srcType
String. Must be either "dom", "json" or "xml". For best performance use "json". If using "dom", make sure to wrap your table rows in , and elements.
Default is "".

srcData
Variable. If srcType is "dom", an HTMLTableElement or string ID for an HTMLTableElement is accepted. If srcType is "json", a json string or object is accepted. If srcType is "xml", an xml string or document is accepted.
Default is "".

allowGridResize
Boolean. Whether or not the user can resize the grid.
Default is false.

allowColumnResize
Boolean. Whether or not the user can resize columns. Disabled automatically in touch devices.
Default is false.

allowClientSideSorting
Boolean. Whether or not the user can sort column data. For ajax-based sorting set this to false and integrate something custom yourself. Note you can use HTML within the grid data input to add ID's, events, etc.
Default is false.

allowSelections
Boolean. Whether or not the user can select rows.
Default is false.

allowMultipleSelections
Boolean. Wether or not the user can select multiple rows. allowSelections must be true if this option is set to true.
Default is false.

showSelectionColumn
Boolean. Whether or not to show a radio or checkbox column in the grid to aid in row selection. allowSelections must be true if this option is set to true.
Default is false.

onColumnSort Function. If allowClientSideSorting is true, this function will be called immediately after a user sorts a column. Returned in arguments are an array of the new row indexes order with their prior index order values, the sorted column index and the prior sorted column index if it exists. To sort a column programmatically, call the myGridInstanceObject.sortColumn((int) columnIndex, (boolean) sortAscending) method.
Default is a function that does nothing.

onResizeGrid
Function. If allowGridResize is true, this function will be called as the user resizes the grid. Returned in arguments are the current width and height of the grid in pixels.
Default is a function that does nothing.

onResizeGridEnd
Function. If allowGridResize is true, this function will be called after the user finishes resizing the grid. Returned in arguments are the new width and height of the grid in pixels.
Default is a function that does nothing.

onResizeColumn
Function. If allowColumnResize is true, this function will be called as the user resizes a column. Returned in arguments are the column index and current width of the column in pixels.
Default is a function that does nothing.

onResizeColumnEnd
Function. If allowColumnResize is true, this function will be called after the user finishes resizing a column. Returned in arguments are the column index and new width of the column in pixels.
Default is a function that does nothing.

onRowSelect
Function. If allowSelections is true, this function will be called after the user selects one or more (shift key) rows. Returned in arguments are an array of newly selected row indexes, an array of newly unselected row indexes and the row index which was clicked on. To access all of the currently selected row indexes, look at the myGridInstanceObject.selectedIndexes array. To select or unselect all rows, call the myGridInstanceObject.toggleSelectAll(boolean) method. To select one or more rows in particular, call the myGridInstanceObject.selectIndexes(arrayOfRowIndexes) method.
Default is a function that does nothing.

onLoad
Function. This function will be called after the grid finishes loading. No arguments are returned.
Default is a function that does nothing.

supportMultipleGridsInView
Boolean. Whether or not the grid code should support multiple grids in the same window. By default this is set to false for performance gain in CSS selectors.
Default is false.

fixedCols
Integer. The number of columns to fix starting from the leftmost column. If showSelectionColumn is true this option will automatically increment by 1. Disabled automatically in touch devices.
Default is 0.

selectedBgColor
String. The color value to set as the background color for selected rows. allowSelections must be true for this option to be used.
Default is "#eaf1f7".

fixedSelectedBgColor
String. The color value to set as the background color for selected, fixed rows. allowSelections must be true and fixedCols > 0 for this option to be used.
Default is "#dce7f0".

colAlign
Array. An array of strings specifying column text alignment. colAlign[0] specifies the 1st column's text alignment, and so forth. Accepted array values are "left", "center" and "right".
Default is "left" for every column.

colBGColors
Array. An array of strings specifying column background color. colBGColors[0] specifies the 1st column's background color, and so forth.
Default is "#ffffff" for every column.

colSortTypes
Array. An array of string specifying column sort types. colSortTypes[0] specifies the 1st column's sort type, and so forth. allowClientSideSorting must be true for this option to be used. Accepted array values are "string", "number", "date", "custom" and "none". Use "none" to disable sorting for a particular column.
Default is "string" for every column except the column created by setting showSelectionColumn to true.

customSortCleaner
Function. If a column sort type specified in colSortTypes is set to "custom", this function will be called on every cell value within the column being sorted. ie, it is used within Javascript's native sort() function. Returned in arguments are the cell value and the column index being sorted on. You may want to use this option, for example, to sanitize formatted numbers for sorting comparisons.
Default is null.

*/

(function(window, document, undefined) {
"use strict";

var GridProto;
var Grid = function(element, options) {
    if ((this.element = (typeof(element) === "string") ? $(element) : element)) {
        this.css = { idRulePrefix : "#" + this.element.id + " ", sheet : null, rules : {} };
        this.columns = 0;
        this.columnWidths = [];
        this.cellData = { head : [], body : [], foot : [] };
        this.alignTimer = null;
        this.rawData = [];
        this.sortCache = {};
        this.lastSortedColumn = [-1, null];
        this.selectedIndexes = [];
        this.filtered = false;
        this.filterMap = [];
        this.usesTouch = (window.ontouchstart !== undefined);
        this.startEvt = (this.usesTouch) ? "touchstart" : "mousedown";
        this.moveEvt = (this.usesTouch) ? "touchmove" : "mousemove";
        this.endEvt = (this.usesTouch) ? "touchend" : "mouseup";
        this.setOptions(options);
        this.init();
    }
};
//////////
(GridProto = Grid.prototype).nothing = function(){};
//////////
GridProto.setOptions = function(options) {
    var hasOwnProp = Object.prototype.hasOwnProperty,
        option;
    this.options = {
        srcType : "", // "dom", "json", "xml"
        srcData : "",
        allowGridResize : false,
        allowColumnResize : false,
        allowClientSideSorting : false,
        allowSelections : false,
        allowMultipleSelections : false,
        showSelectionColumn : false,
        onColumnSort : this.nothing,
        onResizeGrid : this.nothing,
        onResizeGridEnd : this.nothing,
        onResizeColumn : this.nothing,
        onResizeColumnEnd : this.nothing,
        onRowSelect : this.nothing,
        onBodyLoaded : this.nothing,
        onBodyClear : this.nothing,
        onLoad : this.nothing,
        onBeforeUpdateRow : this.nothing,
        onAfterAppendRow : this.nothing,
        onBeforeDeleteRow : this.nothing,
        onDataChanged : this.nothing,
        onCalcFields : this.nothing, // expects function(rowdata) {..}
        onFilter : function(rowdata) {return true;},
        supportMultipleGridsInView : false,
        fixedCols : 0,
        selectedBgColor : "#eaf1f7",
        fixedSelectedBgColor : "#dce7f0",
        colAlign : [], // "left", "center", "right"
        colWidth: [], // incl. the selection autocolumn, LE 0 = hide
        colTag: [], // column names
        colBGColors : [],
        colSortTypes : [], // "string", "number", "date", "custom", "none"
        customSortCleaner : null,
        silent : false,
        noBodyMessage : "The grid is empty (because no data or no filter passed)."
    };
    if (options) {
        for (option in this.options) {
            if (hasOwnProp.call(this.options, option) && options[option] !== undefined) {
                this.options[option] = options[option];
            }
        }
    }
    this.options.allowColumnResize = this.options.allowColumnResize && !this.usesTouch;
    this.options.allowMultipleSelections = this.options.allowMultipleSelections && this.options.allowSelections;
    this.options.showSelectionColumn = this.options.showSelectionColumn && this.options.allowSelections;
    this.options.fixedCols = (!this.usesTouch) ? this.options.fixedCols : 0;
};
/////////
GridProto.init = function() {
    var srcType = this.options.srcType,
        srcData = this.options.srcData,
        data;
    this.generateSkeleton();
    this.addEvents();
    // DOM:
    if (srcType === "dom" && (srcData = (typeof(srcData) === "string") ? $(srcData) : srcData)) {
        this.convertData(this.convertDomDataToJsonData(srcData));
    // JSON:
    } else if (srcType === "json" && (data = parseJSON(srcData))) {
        this.convertData(data);
    // XML:
    } else if (srcType === "xml" && (data = parseXML(srcData))) {
        this.convertData(this.convertXmlDataToJsonData(data));
    }
    this.generateGrid();
    this.displayGrid();
};
/////////
GridProto.generateSkeleton = function() {
    var doc = document,
        elems = [["base", "g_Base", "docFrag"],
                 ["head", "g_Head", "base"],
                 ["headFixed", "g_HeadFixed", "head"],
                 ["headStatic", "g_HeadStatic", "head"],
                 ["foot", "g_Foot", "base"],
                 ["footFixed", "g_FootFixed", "foot"],
                 ["footStatic", "g_FootStatic", "foot"],
                 ["body", "g_Body", "base"],
                 ["bodyFixed", "g_BodyFixed", "body"],
                 ["bodyFixed2", "g_BodyFixed2", "bodyFixed"],
                 ["bodyStatic", "g_BodyStatic", "body"]];
    this.parentDimensions = { x : this.element.offsetWidth, y : this.element.offsetHeight };
    this.docFrag = doc.createDocumentFragment();
    for (var i=0, elem; elem=elems[i]; i++) {
        (this[elem[0]] = doc.createElement("DIV")).className = elem[1];
        this[elem[2]].appendChild(this[elem[0]]);
    }
    if (this.options.allowGridResize) {
        (this.baseResize = doc.createElement("DIV")).className = "g_BaseResize";
        this.base.appendChild(this.baseResize);
    }
};
/////////
GridProto.addEvents = function() {
    var wheelEvent;
    // Simulate mouse scrolling over non-scrollable content:
    if (this.options.fixedCols > 0 && !this.usesTouch && !msie) {
        try {
            wheelEvent = (WheelEvent("wheel")) ? "wheel" : undefined;
        } catch (e) {
            wheelEvent = (document.onmousewheel !== undefined) ? "mousewheel" : "DOMMouseScroll";
        }
        if (wheelEvent) {
            addEvent(this.bodyFixed, wheelEvent, bind(this.simulateMouseScroll, this));
        }
    }
    // Grid resizing:
    if (this.options.allowGridResize) {
        addEvent(this.baseResize, this.startEvt, bind(this.initResizeGrid, this));
    }
    // Column resizing and client side sorting:
    if (this.options.allowColumnResize || this.options.allowClientSideSorting) {
        addEvent(this.head, this.startEvt, bind(this.delegateHeaderEvent, this));
    }
    // Row selection:
    if (this.options.allowSelections) {
        addEvent(this.body, this.startEvt, bind(this.selectRange, this));
        if (this.options.showSelectionColumn) {
            addEvent(this.body, "click", bind(this.preventSelectionInputStateChange, this));
        }
    }
};
////////
GridProto.convertDomDataToJsonData = function(data) {
    var sections = { "thead" : "Head", "tbody" : "Body", "tfoot" : "Foot" },
        section, rows, row, cells, arr, arr2, i, j, k,
        json = {};
    // Cycle through all table rows, change sections when needed:
    if (((data || {}).tagName || "").toLowerCase() === "table") {
        for (i=0, j=0, rows=data.rows; row=rows[i]; i++) {
            if (row.sectionRowIndex === 0 && (section = sections[row.parentNode.tagName.toLowerCase()])) {
                json[section] = arr = (json[section] || []);
                j = arr.length;
            }
            arr[j++] = arr2 = [];
            k = (cells = row.cells).length;
            while (k) { arr2[--k] = cells[k].innerHTML; }
        }
    }
    return json;
};
/////////
GridProto.convertXmlDataToJsonData = function(data) {
    var sections = { "thead" : "Head", "tbody" : "Body", "tfoot" : "Foot" },
        cellText = (msie < 9) ? "text" : "textContent",
        nodes, node, section, rows, row, cells, cell, tag, n, i, j,
        arr, arr2, a, a2,
        json = {};
    // By section:
    if ((nodes = (data.getElementsByTagName("table")[0] || {}).childNodes)) {
        for (n=0; node=nodes[n]; n++) {
            if ((section = sections[node.nodeName]) && (rows = node.childNodes)) {
                json[section] = arr = (json[section] || []);
                a = arr.length;
                // By row:
                for (i=0; row=rows[i]; i++) {
                    if (row.nodeName === "tr" && (cells = row.childNodes)) {
                        arr[a++] = arr2 = [];
                        a2 = 0;
                        // By cell:
                        for (j=0; cell=cells[j]; j++) {
                            if ((tag = cell.nodeName) === "td" || tag === "th") {
                                arr2[a2++] = cell[cellText] || "";
                            }
                        }
                    }
                }
            }
        }
    }
    return json;
};
/////////
GridProto.convertData = function(data) {
    var base, cols, h, b, f;
    this.addSelectionColumn(data);
    this.rawData = data.Body || [];
    if ((base = data.Head || data.Body || data.Foot || null)) {
        cols = this.columns = base[0].length;
        h = this.cellData.head;
        b = this.cellData.body;
        f = this.cellData.foot;
        while (cols) { h[--cols] = []; b[cols] = []; f[cols] = []; }
        cols = this.columns;
        if (data.Head) {
            this.convertDataItem(h, data.Head, "<DIV class='g_C g_HR g_R", cols, this.options.allowColumnResize);
        } else {
            this.css.rules[".g_Head"] = { display : "none" };
        }
        if (data.Body) {
            this.convertDataItem(b, data.Body, "<DIV class='g_C g_BR g_R", cols, false);
        } else {
            this.css.rules[".g_BodyFixed"] = { display : "none" };
        }
        if (data.Foot) {
            this.convertDataItem(f, data.Foot, "<DIV class='g_C g_FR g_R", cols, false);
        } else {
            this.css.rules[".g_Foot"] = { display : "none" };
        }
    }
};
/////////
GridProto.toRawIndex = function(viewableIndex) {
    try {
        return (this.filtered ? this.filterMap[viewableIndex] : viewableIndex); 
    } catch(e){
        return -1;
    }
};
/////////
GridProto.toViewableIndex = function(rawIndex) {
    try {
        return (this.filtered ? this.rawData[rawIndex].mapIdx : rawIndex); 
    } catch(e) {
        return -1;
    }
};

/////////

// GridProto.viewableData = function() {
// if (this.filtered) {
// var out = [];
// for (var i = 0; i < this.filterMap.length; i++) {
// out[i] = this.rawData[this.filterMap[i]];
// }
// return out;
// } else {
// return this.rawData;
// }
// };
/////////
GridProto.viewableLength = function() {
return this.filtered ? this.filterMap.length : this.rawData.length;
};
/////////
// GridProto.appendDataItem = function(arr/* cellBody */, row, cols) {
// var filtOk = false, i, j;
// this.rawData = this.rawData.concat([row]);
// i = this.rawData.length - 1;
// j = i;
// if (this.filtered) {
// if (this.options.onFilter.call(this,row)) { // фильтрация данных
// filtOk = true;
// j = this.filterMap.length; // next element
// this.filterMap[j] = i;
// }
// }
// if (!this.filtered || filtOk) {
// this.options.onCalcFields.call(this,row); // passing by reference (no need in RETURN)
// rowDiv = "

";
// while (cols) { arr[--cols][j] = rowDiv + row[cols] || " "; }
// }
// };
//////////
// GridProto.updateDataItem = function(arr, row, cols, rowIdx) {
// var shift = this.options.showSelectionColumn ? 1 : 0;
// var j = rowIdx, filtOk = false, j;
// rowIdx = this.toRawIndex(rowIdx); // unmapping the filter
// for (var i = shift; i < this.columns; i++) { if (row[i-shift]) { this.rawData[rowIdx][i] = row[i - shift]; } }
// if (!this.filtered || (this.filtered && this.options.onFilter.call(this,row))) {
// this.options.onCalcFields.call(this,row); // passing by reference (no need in RETURN)
// rowDiv = "
";
// while (cols) { arr[--cols][j] = rowDiv + row[cols] || " "; }
// } else {
// while (cols) { arr[--cols].splice(j,1); }
// }
// };
//////////
// GridProto.deleteDataItem = function(arr, cols, rowIdx) {
// this.rawData.splice(this.toRawIndex(rowIdx),1);
// while (cols) { arr[--cols].splice(rowIdx,1); }
// };
//////////
GridProto.clearDataItem = function(arr,cols) {
var i, j = (arr[0] || []).length;
if (j > 0) {
while (cols) {
--cols;
i = j;
while (i) arr[cols].splice(--i,1);
}
}
};
////////////
GridProto.convertDataItem = function(arr, rows, rowClass, cols, allowColResize, prevFiltered, sortCall) {
var rowIdx = rows.length, rowDiv, row = [], colIdx, j = 0, k = 0, toSelectRaw = [], ni;
var isBody = (rowClass == "<DIV class='g_C g_BR g_R");
if (typeof(prevFiltered) == 'undefined') prevFiltered = false;
if (typeof(sortCall) == 'undefined') sortCall = false;
this.clearDataItem(arr,cols); // clearing the BODY arr
if (isBody && !sortCall) { // saving selection on filter mode switch
for (var i=0; i < this.selectedIndexes.length; i++) {
j = prevFiltered ? this.filterMap[this.selectedIndexes[i]] : this.selectedIndexes[i];
if (j < rows.length) { toSelectRaw[k++] = j; }
}
}
if (isBody) this.filterMap = [];
j = 0; k = 0;
for (var i=0; i < rows.length; i++) {
k = i;
row = rows[i];
if (isBody) {
if (this.filtered) {
if (sortCall) {
if (rows[i].mapIdx < 0) continue; // skipping rows filtered out by the non-sort call
} else {
if (!this.options.onFilter.call(this,row)) {
rows[i].mapIdx = -1;
continue; // фильтрация данных
}
this.options.onCalcFields.call(this,rows); // passing by reference (no need in RETURN)
}
this.filterMap[j] = i; // mapping "gridRow => rawData"
rows[i].mapIdx = j; // reverse mapping for the filter
k = j;
j++;
} else {
try { delete rows[i].mapIdx; } catch(ex){;} // freeing memory only needed in the filtered mode
}
}
rowDiv = rowClass + k + "'>";
colIdx = cols;
while (colIdx) { arr[--colIdx][k] = rowDiv + row[colIdx] || " "; }
}
if (isBody && !sortCall) { // restoring selection on filter mode switch
this.selectedIndexes = [];
for (var i=0; i < toSelectRaw.length; i++ ) {
j = this.filtered ? rows[toSelectRaw[i]].mapIdx : toSelectRaw[i];
if (j != -1) {
this.selectedIndexes = this.selectedIndexes.concat([j]);
}
}
}
if (allowColResize && (rowIdx = rows.length)) {
colIdx = cols;
while (colIdx) {
arr[--colIdx][0] = (" ") + arr[colIdx][0];
}
}
};
////////////
GridProto.addSelectionColumn = function(data) {
var html, rows, i;
if (this.options.showSelectionColumn) {
this.options.colBGColors.unshift(this.options.colBGColors[0] || "");
this.options.colSortTypes.unshift("none");
this.options.colAlign.unshift("left");
if (!this.usesTouch) {
this.options.fixedCols++;
}
if ((rows = data.Head) && (i = rows.length)) {
while (i) { rows[--i].unshift(""); }
}
if ((rows = data.Body) && (i = rows.length)) {
html = "<INPUT tabIndex='-1' type=";
html += ((this.options.allowMultipleSelections) ? "checkbox class=g_Cb" : "radio class=g_Rd");
html += "> ";
while (i) { rows[--i].unshift(html); }
}
if ((rows = data.Foot) && (i = rows.length)) {
while (i) { rows[--i].unshift(""); }
}
}
};
///////////
GridProto.generateGrid = function() {
this.hasHead = false;
this.hasBody = false;
this.hasFoot = false;
this.hasHeadOrFoot = false;
this.hasFixedCols = (this.options.fixedCols > 0);
this.generateGridHead();
this.generateGridBody();
this.generateGridFoot();
};
///////////
GridProto.generateGridHead = function() {
var hHTML;
this.hasHead = ((this.cellData.head[0] || []).length > 0);
this.hasHeadOrFoot = (this.hasHead || this.hasFoot);
if (this.hasHead) {
hHTML = this.generateGridSection(this.cellData.head);
this.headStatic.innerHTML = hHTML.fullHTML;
if (this.hasFixedCols) {
this.headFixed.innerHTML = hHTML.fixedHTML;
}
}
};
//////////
GridProto.generateGridBody = function() {
var bHTML;
this.hasBody = ((this.cellData.body[0] || []).length > 0);
if (this.hasBody || this.options.silent) {
bHTML = this.generateGridSection(this.cellData.body);
this.bodyStatic.innerHTML = bHTML.fullHTML;
if (this.hasFixedCols) {
this.bodyFixed2.innerHTML = bHTML.fixedHTML;
}
} else {
this.bodyStatic.innerHTML = "
"+ this.options.noBodyMessage + "
";
}
};
///////////
GridProto.generateGridFoot = function() {
var fHTML;
this.hasFoot = ((this.cellData.foot[0] || []).length > 0);
this.hasHeadOrFoot = (this.hasHead || this.hasFoot);
if (this.hasFoot) {
fHTML = this.generateGridSection(this.cellData.foot);
this.footStatic.innerHTML = fHTML.fullHTML;
if (this.hasFixedCols) {
this.footFixed.innerHTML = fHTML.fixedHTML;
}
}
};
///////////
GridProto.generateGridSection = function(cols) {
var replaceFunc = function($1, $2) { return cols[parseInt($2, 10)].join("
"); },
replaceRgx = /@(\d+)@/g,
fixedCols = this.options.fixedCols,
fHtml = [], sHtml = [],
colIdx = cols.length;
while (colIdx) {
if ((--colIdx) < fixedCols) {
fHtml[colIdx] = "
@" + colIdx + "@
";
sHtml[colIdx] = "
";
} else {
sHtml[colIdx] = "
@" + colIdx + "@
";
}
}
return { fixedHTML : (fixedCols) ? fHtml.join("").replace(replaceRgx, replaceFunc) : "",
fullHTML : sHtml.join("").replace(replaceRgx, replaceFunc) };
};
////////////
GridProto.displayGrid = function() {
var srcType = this.options.srcType,
srcData = this.options.srcData,
replace = false;
// Setup scrolling:
this.lastScrollLeft = 0;
this.lastScrollTop = 0;
this.body.onscroll = bind(this.syncScrolls, this);
// Prep style element:
try {
this.css.sheet.parentNode.removeChild(this.css.sheet);
} catch (e) {
(this.css.sheet = document.createElement("STYLE")).id = this.element.id + "SS";
this.css.sheet.type = "text/css";
}
// Insert grid into DOM:
if (srcType === "dom" && (srcData = (typeof(srcData) === "string") ? $(srcData) : srcData)) {
if ((replace = (this.element === srcData.parentNode))) {
this.element.replaceChild(this.docFrag, srcData);
}
}
if (!replace) {
this.element.appendChild(this.docFrag);
}
// Align columns:
this.alignTimer = window.setTimeout(bind(this.alignColumns, this, false, true), 16);
};
/////////////
GridProto.alignColumns = function(reAlign, fromInit) {
var sNodes = [this.headStatic.children || [], this.bodyStatic.children || [], this.footStatic.children || []],
fNodes = [this.headFixed.children || [], this.bodyFixed2.children || [], this.footFixed.children || []],
allowColumnResize = this.options.allowColumnResize,
colBGColors = this.options.colBGColors,
colAlign = this.options.colAlign,
fixedCols = this.options.fixedCols,
rules = this.css.rules,
colWidth, nodes;
// Compute base styles first, or remove old column width styling if realigning the columns:
if (reAlign !== true) {
this.computeBaseStyles();
} else {
for (var i=0, len=this.columns; i<len; i++) {
rules[".g_Cl" + i].width = "auto";
}
this.setRules();
}
// Compute column width, alignment and background styles:
this.columnWidths = [];
var vis = 'visible';
for (var i=0, len=this.columns; i<len; i++) {
if (typeof(this.options.colWidth[i]) != 'undefined') {
if (this.options.colWidth[i] > 0) {
colWidth = this.options.colWidth[i];
} else {
colWidth = 0;
vis = 'hide';
}
} else {
nodes = (i < fixedCols) ? fNodes : sNodes;
colWidth = Math.max((nodes[0][i] || {}).offsetWidth || 0,
(nodes[1][i] || {}).offsetWidth || 0,
(nodes[2][i] || {}).offsetWidth || 0);
}
this.columnWidths[i] = colWidth;
rules[".g_Cl" + i] = { "width" : colWidth + "px", "text-align" : (colAlign[i] || "left"), "visibility" : vis };
if ((colBGColors[i] || "#ffffff") !== "#ffffff") {
rules[".g_Cl" + i]["background-color"] = colBGColors[i];
}
if (allowColumnResize) {
rules[".g_RS" + i] = { "margin-left" : (colWidth - 2) + "px" };
}
}
this.setRules();
if (fromInit === true) {
this.options.onLoad.call(this);
this.options.onDataChanged.call(this);
}
};
/////////////
GridProto.computeBaseStyles = function() {
var rules = this.css.rules,
headHeight = (this.hasHead) ? this.head.offsetHeight : 0,
footHeight = (this.hasFoot) ? this.foot.offsetHeight : 0,
sBarSize = { "x" : this.body.offsetWidth - this.body.clientWidth,
"y" : this.body.offsetHeight - this.body.clientHeight };
rules[".g_C"] = { "visibility" : "visible" };
rules[".g_Cl"] = { "background-color" : "#fff" };
rules[".g_BodyStatic"] = { "padding" : headHeight + "px 0px " + footHeight + "px 0px" };
if (this.hasHead) {
rules[".g_Head"] = { "right" : sBarSize.x + "px" };
}
if (this.hasFoot) {
rules[".g_Foot"] = { "bottom" : sBarSize.y + "px", "right" : sBarSize.x + "px" };
}
if (this.hasFixedCols) {
rules[".g_BodyFixed" + ((msie < 8) ? "2" : "")] = { "top" : headHeight + "px", "bottom" : sBarSize.y + "px" };
}
if (this.options.allowGridResize) {
rules[".g_BaseResize"] = { "width" : sBarSize.x + "px", "height" : sBarSize.y + "px" };
}
if (this.options.allowColumnResize) {
rules[".g_ResizeDragger"] = { "bottom" : sBarSize.y + "px" };
rules[".g_RS"] = { "display" : "block",
"position" : "relative",
"margin-bottom" : (headHeight * -1) + "px",
"height" : headHeight + "px" };
}
};
/////////////
GridProto.syncScrolls = function(event) {
var sL = (this.hasHeadOrFoot) ? this.body.scrollLeft : 0,
sT = (this.hasFixedCols) ? this.body.scrollTop : 0;
if (sL !== this.lastScrollLeft) {
this.lastScrollLeft = sL;
if (this.hasHead) {
this.headStatic.style.marginLeft = (-1 * sL) + "px";
}
if (this.hasFoot) {
this.footStatic.style.marginLeft = (-1 * sL) + "px";
}
}
if (sT !== this.lastScrollTop) {
this.lastScrollTop = sT;
this.bodyFixed2.style.marginTop = (-1 * sT) + "px";
}
};
//////////////
GridProto.simulateMouseScroll = function(event) {
var event = event || window.event,
deltaY = 0;
if (event.deltaY !== undefined) {
deltaY = event.deltaY;
} else if (event.wheelDelta !== undefined) {
deltaY = event.wheelDelta * (-1/40);
} else if (event.detail !== undefined) {
deltaY = event.detail;
}
this.body.scrollTop += (deltaY * 33);
this.syncScrolls();
};
/////////////
GridProto.setRules = function() {
var idRulePrefix = (this.options.supportMultipleGridsInView) ? this.css.idRulePrefix : "",
hasOwnProp = Object.prototype.hasOwnProperty,
rules = this.css.rules,
sheet = this.css.sheet,
cssText = [], c = 0,
rule, props, prop,
doc = document;
for (rule in rules) {
if (hasOwnProp.call(rules, rule) && (props = rules[rule])) {
cssText[c++] = idRulePrefix + rule + "{";
for (prop in props) {
if (hasOwnProp.call(props, prop)) {
cssText[c++] = prop + ":" + props[prop] + ";";
}
}
cssText[c++] = "} ";
}
}
if (!sheet.styleSheet) {
sheet.appendChild(doc.createTextNode(cssText.join("")));
}
if (!$(sheet.id)) {
(doc.head || doc.getElementsByTagName("head")[0]).appendChild(sheet);
}
if (sheet.styleSheet) {
sheet.styleSheet.cssText = cssText.join("");
}
};
//////////////
GridProto.initResizeGrid = function(event) {
var event = event || window.event,
pagePos;
if (event.button !== 2 && this.options.allowGridResize) {
pagePos = getEventPositions(event, "page");
this.tmp = {
throttle : -1,
origX : pagePos.x,
origY : pagePos.y,
origWidth : this.parentDimensions.x,
origHeight : this.parentDimensions.y,
boundMoveEvt : bind(this.resizeGrid, this),
boundEndEvt : bind(this.endResizeGrid, this)
};
addEvent(document, this.moveEvt, this.tmp.boundMoveEvt);
addEvent(document, this.endEvt, this.tmp.boundEndEvt);
return stopEvent(event);
}
};
////////////
GridProto.resizeGrid = function(event) {
var pagePos, xDif, yDif, newWidth, newHeight, elemStyle;
if ((this.tmp.throttle++) & 1) {
pagePos = getEventPositions(event || window.event, "page");
xDif = pagePos.x - this.tmp.origX;
yDif = pagePos.y - this.tmp.origY;
newWidth = Math.max(60, (xDif > 0) ? this.tmp.origWidth + xDif : this.tmp.origWidth - Math.abs(xDif));
newHeight = Math.max(30, (yDif > 0) ? this.tmp.origHeight + yDif : this.tmp.origHeight - Math.abs(yDif));

        elemStyle = this.element.style;
        elemStyle.width = newWidth + "px";
        elemStyle.height = newHeight + "px";

        this.parentDimensions = { x : newWidth, y : newHeight };
        this.syncScrolls();
        clearTextSelections();
        this.options.onResizeGrid.apply(this, [newWidth, newHeight]);
    }
};
/////////////
GridProto.endResizeGrid = function(event) {
    removeEvent(document, this.moveEvt, this.tmp.boundMoveEvt);
    removeEvent(document, this.endEvt, this.tmp.boundEndEvt);
    this.options.onResizeGridEnd.apply(this, [this.parentDimensions.x, this.parentDimensions.y]);
    this.tmp = undefined;
};
////////////
GridProto.delegateHeaderEvent = function(event) {
    var event = event || window.event,
        target = event.target || event.srcElement,
        targetClass = target.className || "";
    if (event.button !== 2) {
        if (this.options.allowColumnResize && targetClass.indexOf("g_RS") > -1) {
            return this.initResizeColumn(event, target, targetClass);
        } else if (this.hasBody && this.options.allowClientSideSorting) {
            while (targetClass.indexOf("g_Cl") === -1 && targetClass !== "g_Head") {
                targetClass = (target = target.parentNode).className || "";
            }
            if (targetClass.indexOf("g_Cl") > -1) {
                this.sortColumn(parseInt(/g_Cl(\d+)/.exec(targetClass)[1], 10));
            }
        }
    }
};
/////////////
GridProto.initResizeColumn = function(event, target, targetClass) {
    var colIdx = parseInt(targetClass.replace(/g_RS/g, ""), 10),
        doc = document;
    this.tmp = {
        lastLeft : -1,
        colIdx : colIdx,
        origX : getEventPositions(event, "client").x,
        origWidth : this.columnWidths[colIdx],
        origLeft : target.offsetLeft,
        boundMoveEvt : bind(this.resizeColumn, this),
        boundEndEvt : bind(this.endResizeColumn, this),
        dragger : doc.createElement("DIV")
    };
    this.tmp.dragger.className = "g_ResizeDragger";
    this.tmp.dragger.style.left = this.tmp.origLeft + "px";
    this.base.insertBefore(this.tmp.dragger, this.base.firstChild);
    addEvent(doc, this.moveEvt, this.tmp.boundMoveEvt);
    addEvent(doc, this.endEvt, this.tmp.boundEndEvt);
    return stopEvent(event);
};
/////////////
GridProto.resizeColumn = function(event) {
    var clientX = getEventPositions(event || window.event, "client").x,
        xDif = clientX - this.tmp.origX,
        newWidth = Math.max(15, (xDif > 0) ? this.tmp.origWidth + xDif : this.tmp.origWidth - Math.abs(xDif)),
        newLeft = (xDif > 0) ? this.tmp.origLeft + xDif : this.tmp.origLeft - Math.abs(xDif);
    this.tmp.newWidth = newWidth;
    if (this.tmp.lastLeft !== newLeft && newWidth > 15) {
        this.tmp.dragger.style.left = newLeft + "px";
        this.tmp.lastLeft = newLeft;
    }
    clearTextSelections();
    this.options.onResizeColumn.apply(this, [this.tmp.colIdx, newWidth]);
};
/////////////
GridProto.endResizeColumn = function(event) {
    var newWidth = this.tmp.newWidth || this.tmp.origWidth,
        colIdx = this.tmp.colIdx;
    removeEvent(document, this.moveEvt, this.tmp.boundMoveEvt);
    removeEvent(document, this.endEvt, this.tmp.boundEndEvt);

    this.tmp.dragger.parentNode.removeChild(this.tmp.dragger);
    this.css.rules[".g_Cl" + colIdx]["width"] = newWidth + "px";
    this.css.rules[".g_RS" + colIdx]["margin-left"] = (newWidth - 2) + "px";
    this.columnWidths[colIdx] = newWidth;
    this.setRules();
    this.syncScrolls();
    this.options.onResizeColumnEnd.apply(this, [colIdx, newWidth]);
    this.tmp = undefined;
};
/////////////
GridProto.sortColumn = function(colIdx, sortAsc) {
    var colIdx = parseInt(colIdx, 10) || ((colIdx === 0) ? 0 : -1),
        colSortAs = (colIdx > -1) ? this.options.colSortTypes[colIdx] || "string" : "none",
        lastCol = this.lastSortedColumn;
    if (colSortAs !== "none") {
        sortAsc = (sortAsc === undefined) ? ((colIdx === lastCol[0]) ? !lastCol[1] : true) : !!sortAsc;
        this.sortRawData(colIdx, colSortAs, sortAsc);
    }
};
////////////
GridProto.sortRawData = function(colIdx, colSortAs, sortAsc) {
    var selIndexes, ltVal, gtVal, i, si,
        rawData = this.rawData,
        handleSelection = (this.options.allowSelections && (this.selectedIndexes.length > 0)),
        newIdxOrder = [],
        that = this;
    i = rawData.length;
    while (i) { rawData[--i].pIdx = i; } // Store prior index order:
    if (handleSelection) {  // saving the selection
        for (var i = 0; i < this.selectedIndexes.length; i++) {
            this.selectedIndexes[i] = this.toRawIndex(this.selectedIndexes[i]); 
        }
    }
    // Sort the body data by type:
    ltVal = (sortAsc) ? -1 : 1;
    gtVal = (sortAsc) ? 1 : -1;
    rawData.sort(function(a, b) { return that.getSortResult(colSortAs, colIdx, ltVal, gtVal, a[colIdx], b[colIdx]); });
    i = rawData.length;
    while (i) { // Generate new sort order (mapping old order => new order) array
        newIdxOrder[rawData[--i].pIdx] = i; 
    } 
    // Update the grid body HTML:
    this.convertDataItem(this.cellData.body, rawData, "<DIV class='g_C g_BR g_R", this.columns, false, this.filtered, true);
    this.generateGridBody();
    if (handleSelection) { // restoring the selection
        for (var i = 0; i < this.selectedIndexes.length; i++) {
            si = newIdxOrder[this.selectedIndexes[i]];
            this.selectedIndexes[i] = this.filtered ? rawData[si].mapIdx : si;
        }
        this.highlightRows(this.selectedIndexes, []);
    }
    // Fire sort event:
    this.options.onColumnSort.apply(this, [newIdxOrder.concat(), colIdx, this.lastSortedColumn[0]]);
    this.lastSortedColumn = [colIdx, sortAsc];
};
///////////
GridProto.getSortResult = function(type, colIdx, ltVal, gtVal, a, b, keyA, keyB) {
    if (a === b) { return 0; }
    if (this.sortCache[(keyA = type + "_" + a)] === undefined) {
        this.sortCache[keyA] = (type === "string") ? a :
                               (type === "number") ? parseFloat(a) || -Infinity :
                               (type === "date") ? new Date(a).getTime() || -Infinity :
                               (type === "custom") ? this.options.customSortCleaner(a, colIdx) : a;
    }
    if (this.sortCache[(keyB = type + "_" + b)] === undefined) {
        this.sortCache[keyB] = (type === "string") ? b :
                               (type === "number") ? parseFloat(b) || -Infinity :
                               (type === "date") ? new Date(b).getTime() || -Infinity :
                               (type === "custom") ? this.options.customSortCleaner(b, colIdx) : b;
    }
    return (this.sortCache[keyA] < this.sortCache[keyB]) ? ltVal : gtVal;
};
////////////
GridProto.toggleSelectAll = function(toggle) {
    var selIndexes = this.selectedIndexes, toSelect = [], toRemove = [],  i;
    if (this.hasBody && this.options.allowSelections) {
        if (toggle) {
            toSelect = [0];  // 1-st row for single selection fallback
            if (this.options.allowMultipleSelections) {
                i = this.viewableLength();
                while (i) { toSelect[--i] = i; }
            }
            this.selectIndexes(toSelect);
        } else if (selIndexes.length) {
            toRemove = selIndexes.concat(); // копирование всего массива в новый (не ссылка) массив
            this.selectedIndexes = [];
            this.highlightRows(toSelect, toRemove);
            this.options.onRowSelect.apply(this, [toSelect, toRemove, -1]);
        }
    }
};
////////////
GridProto.selectIndexes = function(rowIndexes) {
    var selIndexes = this.selectedIndexes,
        toSelect = [], toRemove = [], j = 0, i = rowIndexes.length,
        rows = this.viewableLength() - 1;
    while (i) {
        i = i - 1;
        if ((rowIndexes[i] > rows) || (rowIndexes[i] < 0)) {
            rowIndexes.splice(i,1); 
        }
    }
    i = rowIndexes.length;
    if (i && this.hasBody && this.options.allowSelections) {
        if (this.options.allowMultipleSelections) {
            while (i) {
                if (indexOf(selIndexes, rowIndexes[--i]) === -1) { toSelect[j++] = rowIndexes[i]; }
            }
        } else {
            toRemove = selIndexes.concat();
            toSelect[0] = rowIndexes[0];
            selIndexes = [];
        }
        this.selectedIndexes = selIndexes.concat(toSelect);
        this.highlightRows(toSelect, toRemove);
        this.options.onRowSelect.apply(this, [toSelect, toRemove, -1]);
    }
};
///////////
GridProto.selectRange = function(event) {
    var event = event || window.event,
        target = event.target || event.srcElement,
        targetClass, isSelCol, isCtrlKeyLike, update, rowIdx;
    if (event.button !== 2 && this.options.allowSelections) {
        targetClass = target.className || "";
        while (targetClass.indexOf("g_BR") === -1 && targetClass !== "g_Body") {
            targetClass = (target = target.parentNode).className || "";
        }
        if (targetClass.indexOf("g_BR") > -1) {
            update = true;
            rowIdx = parseInt(/g_R(\d+)/.exec(targetClass)[1], 10);
            targetClass = (target = target.parentNode).className || "";
            isSelCol = (this.options.showSelectionColumn && (targetClass.indexOf("g_Cl0") > -1));
            isCtrlKeyLike = this.usesTouch || isSelCol;
            if (this.usesTouch && this.options.showSelectionColumn && (update = isSelCol)) {
                stopEvent(event);
            }
            if (update) {
                this.updateSelectedIndexes(rowIdx, event.ctrlKey || isCtrlKeyLike, event.shiftKey);
            }
        }
    }
};
////////////
GridProto.updateSelectedIndexes = function(rowIdx, ctrlPressed, shiftPressed) {
    var selIndexes = this.selectedIndexes.concat(),
        rowIdxSelected = (indexOf(selIndexes, rowIdx) > -1),
        toSelect = [], toRemove = [],
        startIdx, i, j, len;
    if (!this.options.allowMultipleSelections || !selIndexes.length || (!ctrlPressed && !shiftPressed)) {
        toSelect = (rowIdxSelected && selIndexes.length === 1) ? [] : [rowIdx];
        toRemove = selIndexes.concat();
    } else if (ctrlPressed) {
        toSelect = rowIdxSelected ? [] : [rowIdx];
        toRemove = rowIdxSelected ? [rowIdx] : [];
    } else if (shiftPressed) {
        if ((startIdx = selIndexes[0]) <= rowIdx) {
            for (i=startIdx + 1, j=0; i<=rowIdx; i++) {
                if (indexOf(selIndexes, i) === -1) { toSelect[j++] = i; }
            }
        } else {
            for (i=startIdx - 1, j=0; i>=rowIdx; i--) {
                if (indexOf(selIndexes, i) === -1) { toSelect[j++] = i; }
            }
        }
    }
    for (i=0, len=toRemove.length; i<len; i++) {
        if ((j = indexOf(selIndexes, toRemove[i])) > -1) { selIndexes.splice(j, 1); }
    }
    this.selectedIndexes = selIndexes.concat(toSelect);
    this.highlightRows(toSelect, toRemove);
    if (ctrlPressed || shiftPressed) {
        (!msie) ? clearTextSelections() : window.setTimeout(clearTextSelections, 25);
    }
    this.options.onRowSelect.apply(this, [toSelect, toRemove, rowIdx]);
};
////////////
GridProto.highlightRows = function(toSelect, toRemove) {
    var nodes = [this.bodyFixed2.children, this.bodyStatic.children],
        fixedSelBgColor = this.options.fixedSelectedBgColor,
        selBgColor = this.options.selectedBgColor,
        fixedCols = this.options.fixedCols,
        colIdx = this.columns,
        bgColor, rows, inputs, i;
    while (colIdx) {
        rows = (((--colIdx) < fixedCols) ? nodes[0] : nodes[1])[colIdx].children;
        bgColor = (colIdx < fixedCols) ? fixedSelBgColor : selBgColor;
        i = toRemove.length;
        while (i) { rows[toRemove[--i]].style.backgroundColor = ""; }
        i = toSelect.length;
        while (i) { rows[toSelect[--i]].style.backgroundColor = bgColor; }
    }
    if (this.options.showSelectionColumn) {
        inputs = nodes[(!this.usesTouch) ? 0 : 1][0].getElementsByTagName("INPUT");
        i = toRemove.length;
        while (i) { inputs[toRemove[--i]].checked = false; }
        i = toSelect.length;
        while (i) { inputs[toSelect[--i]].checked = true; }
    }
};
///////////
GridProto.selectedRows = function() { /* return row[rowN >=0 ][colN >= 0] */
    var out = [], curr = [];
    for (var i=0; i < this.selectedIndexes.length; i++) {
        curr = curr.concat(this.rawData[this.toRawIndex(this.selectedIndexes[i])]);
        if (this.options.showSelectionColumn) {
            curr.shift(); // deleting the INPUT's column from the result
        }
        out.push(curr);
    }
    return out;
};
////////////
GridProto.rowData = function(rowIndex, useColTags) { /* return row[rowN >=0 ][colN >= 0] */
    var out = null, tag;
    var shift = this.options.showSelectionColumn ? 1 : 0;
    rowIndex = this.toRawIndex(rowIndex);
    if (useColTags) {
        out = {};
        for (var i=shift; i < this.rawData[rowIndex].length; i++) {
            if ( (this.options.colTag.length > 0) && (this.options.colTag[i-shift] != 'undefined'))  {
                tag = this.options.colTag[i-shift];
                out[tag] =  this.rawData[rowIndex][i];
            } else {
                out[i-shift] =  this.rawData[rowIndex][i];
            }
        }
    } else {
        out = [].concat(this.rawData[rowIndex]);
          if (shift == 1) {
            out.shift(); // deleting theINPUT's column from the result
        }
    }
    return out;
};
/////////////
GridProto.rowsData = function(onlySelected, useColTags) {  /* return row[rowN >=0 ][colN >= 0] */
    var i,out = [], tmp = [];
    if (typeof(useColTags) == 'undefined') useColTags = false;
    if (onlySelected) {
        for (i=0; i < this.selectedIndexes.length; i++) {
            out[i] = this.rowData(this.selectedIndexes[i],useColTags);
        }
    } else {
        var len = this.viewableLength();
        for (i=0; i < len; i++) {
            out[i] = [indexOf(this.selectedIndexes,i) == -1 ? false : true].concat(this.rowData(i,useColTags));
        }
    }
    return out;
};
////////////
GridProto.appendRow = function(data) {
    var html;
    if (this.options.showSelectionColumn) {
        html = "<LABEL class=g_SH><INPUT tabIndex='-1' type=";
        html += ((this.options.allowMultipleSelections) ? "checkbox class=g_Cb" : "radio  class=g_Rd");
        html += ">&nbsp;</LABEL>";
        data = [html].concat(data);
    }
    this.rawData = this.rawData.concat([data]);
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false);
    this.generateGridBody();
    if (this.selectedIndexes.length > 0) {
        this.highlightRows(this.selectedIndexes, []);
    }
    this.options.onAfterAppendRow(this, this.viewableLength() - 1);
    this.options.onDataChanged.call(this);
};
///////////
GridProto.deleteRows = function(rowIndexes) {
    if (!(rowIndexes.length > 0)) return;
    var to_do = false, rows, selects = this.selectedIndexes, toSelect = [], toRemove = [], j = 0, k = 0;
    rowIndexes.sort(function(a,b){return a - b});
    rows = rowIndexes.length;   
    while (rows) {
        to_do = true;
        this.options.onBeforeDeleteRow.call(this,rows);
        this.rawData.splice(this.toRawIndex(rowIndexes[--rows]),1)
        var sels = selects.length;
        for (var i=0; i < sels; i++) {
            if (typeof selects[i] != 'undefined') {
                if (selects[i] == rowIndexes[rows])  {
                    toRemove[j++] = selects[i];
                    selects.splice(i,1);
                }
                else if (selects[i] > rowIndexes[rows]) {
                    toRemove[j++] = selects[i];
                    selects[i] = selects[i] - 1;
                    toSelect[k++] = selects[i];
                } else {
                    toSelect[k++] = selects[i];
                }
           } else {
                toRemove[j++] = selects[i];
           }
        }
    }
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false);
    this.generateGridBody();
    if (selects.length > 0) {
        this.highlightRows(toSelect, toRemove);
    }
    if (to_do) { this.options.onDataChanged.call(this); }
};
////////////
GridProto.updateRow = function(rowIdx,newData) {
    var shift = this.options.showSelectionColumn ? 1 : 0;
    this.options.onBeforeUpdateRow.call(this,rowIdx);
    for (var i = shift; i < this.columns; i++) { if (newData[i-shift]) { this.rawData[this.toRawIndex(rowIdx)][i] = newData[i - shift]; } }
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false);
    this.generateGridBody();
    if (this.selectedIndexes.length > 0) {
        this.highlightRows(this.selectedIndexes, []);
    }
    this.options.onDataChanged.call(this);
};
////////////
GridProto.updateCell = function(rowIdx,colIdx/* >= 0, the selection column doesn't count here */, newData) {
    this.options.onBeforeUpdateRow.call(this,rowIdx);
    this.rawData[rowIdx][colIdx + (this.options.showSelectionColumn ? 1 : 0)] = newData;
    // full redraw since filtering & calculating fields may be applied
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false); 
    this.generateGridBody();
    if (this.selectedIndexes.length > 0) {
        this.highlightRows(this.selectedIndexes, []);
    }
    this.options.onDataChanged.call(this);
};
////////////
GridProto.updateFoot = function(footData) {
    var cols = this.columns;
    if (footData) {
        this.convertDataItem(this.cellData.foot, footData, "<DIV class='g_C g_FR g_R", cols, false);
    } else {
        this.css.rules[".g_Foot"] = { display : "none" };
    }
    this.hasFoot = ((this.cellData.foot[0] || []).length > 0);
    this.generateGridFoot();
};
/////////////
GridProto.clear = function(onlyViewable) {
    var rows, rowsF;
    if (onlyViewable) {
        rows = this.viewableLength();
        while (rows) this.rawData.splice(this.toRawIndex(--rows), 1); 
    } else {
        this.rawData = []; 
    }
    this.selectedIndexes = [];
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false);
    this.generateGridBody();
    this.options.onBodyClear.call(this);
    this.options.onDataChanged.call(this);
};
///////////
GridProto.clearViewable = function() { // очистка таблицы с учетом фильтрации (очищать только то,что проходит фильтр)
    this.clear(true);
};
///////////
GridProto.isEmpty = function(onlyViewable) { 
    return !(onlyViewable ? this.viewableLength() : this.rawData.length) > 0;
};
///////////
GridProto.isEmptyViewable = function() { 
    return this.isEmpty(true); 
};
///////////
GridProto.hasSelections = function() { return (this.selectedIndexes.length > 0); };
///////////
GridProto.loadData = function(data) {
    var htmla = [];
    if (this.rawData.length > 0) { this.clear(true); }
    if (this.options.showSelectionColumn) {
        htmla = [
            "<LABEL class=g_SH><INPUT tabIndex='-1' type=" +
            ((this.options.allowMultipleSelections) ? "checkbox class=g_Cb" : "radio  class=g_Rd") +
            ">&nbsp;</LABEL>"
        ];
    }
    for (var i=0; i < data.length; i++) {
        data[i] = htmla.concat(data[i]);
        this.rawData = this.rawData.concat([data[i]]);
    }
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false);
    this.generateGridBody();
    this.options.onBodyLoaded.call(this);
    this.options.onDataChanged.call(this);
};
////////////
GridProto.ResyncData2View = function(prevFiltered) { // mainly for updating on changing the onFilter function
    this.convertDataItem(this.cellData.body, this.rawData, "<DIV class='g_C g_BR g_R", this.columns, false, prevFiltered); // filling the body
    this.generateGridBody();
    this.options.onDataChanged.call(this);
    if (this.options.allowSelections && (this.selectedIndexes.length > 0)) { // to reflect filtering
        this.highlightRows(this.selectedIndexes, []);
    }
};
/////////////
GridProto.setFilter = function(filter_func,autoapply) {
    this.options.onFilter = filter_func;
    var prevFiltered = this.filtered;
    this.filtered = true;
    if (autoapply) this.ResyncData2View(prevFiltered); // will refill this.filterMap
};
/////////////
GridProto.clearFilter = function(autoapply) {
    this.options.onFilter = function(row){return true;};
    var prevFiltered = this.filtered;
    this.filtered = false;
    if (autoapply) this.ResyncData2View(prevFiltered); // will refill this.filterMap
};
/////////////
GridProto.preventSelectionInputStateChange = function(event) {
    var event = event || window.event,
        target = event.target || event.srcElement,
        targetClass = target.className || "",
        rowIdx;
    if (event.button !== 2) {
        if (targetClass.indexOf("g_Cb") > -1 || targetClass.indexOf("g_Rd") > -1) {
            do {
                targetClass = (target = target.parentNode).className || "";
            } while (targetClass.indexOf("g_BR") === -1 && targetClass !== "g_Body");

            if (targetClass.indexOf("g_BR") > -1) {
                rowIdx = parseInt(/g_R(\d+)/.exec(targetClass)[1], 10);
                (event.target || event.srcElement).checked = (indexOf(this.selectedIndexes, rowIdx) > -1);
            }
        }
    }
};
////////////
GridProto.cleanUp = function() {
    this.alignTimer = (this.alignTimer) ? window.clearTimeout(this.alignTimer) : null;
    this.element.innerHTML = "";
    try { this.css.sheet.parentNode.removeChild(this.css.sheet); } catch (e) {}
    this.options.onDataChanged.call(this);
    return null;
};
// Expose:
window.Grid = Grid;

})(this, this.document);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant