Giter Site home page Giter Site logo

mmurph211 / grid Goto Github PK

View Code? Open in Web Editor NEW
259.0 26.0 78.0 274 KB

HTML-based table with fixed headers, fixed footers, fixed left columns, row selection, sorting and more.

Home Page: http://www.matts411.com/post/grid/

License: MIT License

JavaScript 66.04% HTML 27.34% CSS 6.62%

grid's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grid's Issues

How to format float type

My data is float type. I need format to string firstly and display them.

I also need sort them by float type. I can't find format parameter. May you add it?

CSS Styles not updated on subsequent calls

I'm using the grid in an HTML snippet that's returned by an AJAX call. On the first call, all is correct. On the second and subsequent calls, the CSS does not appear to be rewritten correctly.

Here's how I noticed it:

On the first call, the grid had 6 columns corresponding to months. The widths of the columns were set correctly.

On the second call, the grid had more than 6 columns. The widths of all columns after the 6th were not set.

A little digging revealed that the CSS styles for div.mgCl6, div.mgCl7, etc. were not set in style#store-history-gridSS.

My solution: add some JS code to explicitly remove the previous style sheet. ( Note that I'm using jQuery here ).

    // remove any styles that may already exist
    $('style#store-history-gridSS').remove();
    // Render the grid
    new Grid('store-history-grid', {
        srcType: 'dom',
        srcData: 'store-history-table',
        allowGridResize : true,
        allowColumnResize : true,
        allowClientSideSorting : true,
        allowSelections : true,
        allowMultipleSelections : true,
        fixedCols : 1,
        supportMultipleGridsInView : false
    });

Seems like a pretty easy patch, but I'm not sure where to make the change.

colspan rowspan

Hey man, don't leave this plugins unkempt...it's great
some tricks for make rospan or colspan please?

Multi-select issue with check boxes

First off, great work - I really like this grid.

I have a couple of issues that's stopping me from using it though.

From your demo, if I tick a bunch of check boxes and then accidentally click outside of the check box on another row it clears all my selected checked boxes - this could be quite frustrating if the grid is being used to edit selections.

The first issue is that clicking on another row should not clear the check boxes if "allowMultipleSelections" is true.

The second issue is more a feature request, it would be nice to be able to set the check box state by clicking anywhere on the row - this would make selecting items and them submitting the changes to the server really smooth and nice as selecting the check boxes at the moment is a little fiddly.

fixed right column

Great library. I'm trying to fix/freeze the right-most column. Is there an elegant way to do this using the API or should I start hacking at it? Thanks!

Freeze first and last column?

Hi, is it possible to freeze the first and last column? I see the library supports just freezing columns in order starting from the leftmost. Ideas?

Thanks.

Alternate color row conditional

Hello, first excuse my English, I have a table in which I want to alternate the colors depending on a php variable (a conditional) for example:
In a row, specifically, in a cell, I have a variable called state, if state is run, the entire row must have a green color, however if state has any other value, the row must have normal color table .
How I can do this?. Thank you.

Grid not working on Chrome

Plz help, grid cannot click on chrome, but work great on firefox.
Don't know what happen either. Your Demo not work either.

thx

resize columns and rows or table

Hi first of all thank you for the code and sorry for my english. My problem its that i want to block the resize to the whole table or just to limit it to 800px only, because if you keep trying to resize more bigger, it doesnt stop, also the columns resize. Thanks in advance

DOM and JSON Different Inputs.

Dear mmurph211 ,first would like to thank you on your efforts.
such a great plugin,i loved using it.
although i am facing some difficulties here.
i have a dynamic table and data is filled using an array for each row,so when i update the table and posted,some issue happened on table cells which is,i got the vertical instead of horizontal,like for Cell [3,2] it's being posted to Cell [2,3]
my srcType : "DOM"
when i tried JSON instead; i couldn't use the fixedCols option.

Any ideas how can i fix this??

Redraw / recalculate after hiding some rows

Is it possible to manually trigger a redraw / recalculation of the grid sizes?

I have a scenario where my grid has some extra header rows that can be shown or hidden via JavaScript (triggered by a button click elsewhere in the page). When I render the grid initially with these rows displayed, then later hide them (programmatically setting display: none), the grid does not redraw; instead, there's just blank white space where the now-missing cells were.

Touchscreen and FixedCols. usesTouch override would be nice

A group of users raised an issue today on a page enabled with Grid.js stating that the first column was not freezing on horizontal scroll. This group was all using Dell laptops with touch screens, which caused the 'fixedCols' attribute to fail.

L76: this.options.fixedCols = (!this.usesTouch) ? this.options.fixedCols : 0;

I solved it by adding delete window.ontouchstart; before the Grid() call.

If it were possible to set usesTouch=false in the Grid options it would be a more elegant solution.

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

////////////////////////////////////
//
// 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);

Can't sort on first column if selections are disabled.

I'm not sure if this is an "issue" or a "feature." If I disable the selections by setting the following attributes to false,


                        allowSelections : false, 
                        allowMultipleSelections : false, 
                        showSelectionColumn : false,

then I can no longer sort the first column of data by clicking on it's column label. Is it possible to allow sorting by the first column while selections are disabled, or is that disabled by design?

Hard to check boxes

Is it just me, or is the target size for the checkboxes on the right too small? If one is selecting multiple rows, its too easy to miss the actual box and end up losing the rest of your selection. I would make the whole cell area containing the checkbox the target, rather than just the box itself.

Re-rendering of Grid doesn't clear previously embedded styles

I'm re-rendering a Grid with new data and the embedded styles are not be re-generated. This is causing a bug where the column header widths are getting lost if the new grid has more columns (the additional column headers that is).

My workaround is to remove the generated style element before each re-render, but if this is something that could be done internally, that would be better.

Excelent plugin

Excelent for me, the use of table friendly is great, i remake all my project from slick grid to your grid plugin really is to easy.
Only a few questions:
how can i cookie the resize table columns width?
how can i make table columns reorder?
can i fix footers to?
can it support colspan, rowspan?
thanks a lot

Can't use it with ajax data loading

Hi,
I'm trying to use your great tool by loading the gridData by an ajax call with a json response.
Returned by ajax is a json string containing an object that parsed contains the headers and the rows.
It doesn't seems to work, I can't understand which type of data could be the best and what's the right data to set for gridData, I'm doing something like:
gridData = {
Head : response.headers,
Body : response.rows
}
the js error returned is:
rows[(intermediate value)].unshift is not a function

Thank you for any suggestion

About license

Hi
The license is "MIT-style license", but is it different from MIT license?
I'm in trouble because I don't know the license contents of "MIT-style license".
I am very happy if you can reply.

Thank you.

Input fields in cells

Support for <input> field seems limited or excluded within cells.
Is there a trick to get it working or is this simply not supported?

Otherwise, looks great!
Cheers,
Dean.

Initial Column Order

Hi Matt,

Love the MooGrid... so fast and browser friendly.

Wondering how you could set the initial column order? I've played around a bit but can't seem to figure it out.

Something else that would be great would be an easy way to export to excel, copy and paste does not work :(

Thanks,
Robin

No results returned.

Hi all. I tryed this plugin on my table. With chrome it's working perfect, but with IE9 it returns the error "no results returned", how can I fix that?
Of course I'm using: srcType : "dom"

how to display all in one page?

my problem is that i have so many columns in a table,i want have a view in one page rather than scroll right to see them.I wish the td could display the value in wrap mode ,so every columns could show in one view.Sorry for my poor English,i think you know my mean.
Thanks for your plugin!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.