Sunday 11 December 2011

table.css

table.example {
    border:1px solid black;
    border-collapse:collapse;
}
table.example th, table.example td {
    border:1px solid #aaaaaa;
    padding: 17px 15px 2px 15px;
}
table.example thead th {
    background-color:#ccccff;
}
table.example tfoot td {
    background-color:#ffccff;
}

table.example tr.tbody_header {
    font-weight:bold;
    text-align:center;
    background-color:#dddddd;
}

table.example a.pagelink {
    padding-left:5px;
    padding-right:5px;
    border:1px solid #666666;
    margin:0px 5px 0px 5px;
}
table.example a.currentpage {
    background-color:yellow;
}
/* Striping */
tr.alternate {
    background-color:#ffffcc;
}

/* Sorting */
th.table-sortable {
    cursor:pointer;
    background-image:url("../images/sortable.gif");
    background-position:center left;
    background-repeat:no-repeat;
    padding-left:12px;
}
th.table-sorted-asc {
    background-image:url("../images/sorted_up.gif");
    background-position:center left;
    background-repeat:no-repeat;
}
th.table-sorted-desc {
    background-image:url("../images/sorted_down.gif");
    background-position:center left;
    background-repeat:no-repeat;
}
th.table-filtered {
    background-image:url("filter.gif");
    background-position:center left;
    background-repeat:no-repeat;
}
select.table-autofilter {
    font-size:smaller;
}

/* Examples which stray from the default */
table.altstripe tr.alternate2 {
    background-color:#ccffff;
}

/* Sort Icon Styles */
table.sort01 th.table-sortable { background-image:url("../images/01_unsorted.gif"); }
table.sort01 th.table-sorted-asc { background-image:url("../images/01_ascending.gif"); }
table.sort01 th.table-sorted-desc { background-image:url("../images/01_descending.gif"); }

table.sort02 th.table-sortable { background-image:none; padding-left:16px; }
table.sort02 th.table-sorted-asc { background-image:url("../images/icons/02_ascending.gif"); }
table.sort02 th.table-sorted-desc { background-image:url("../images/icons/02_descending.gif"); }

table.sort03 th.table-sortable { background-image:none; }
table.sort03 th.table-sorted-asc { background-image:url("../images/icons/03_ascending.gif"); }
table.sort03 th.table-sorted-desc { background-image:url("../images/icons/03_descending.gif"); }

table.sort04 th.table-sortable { background-image:none; }
table.sort04 th.table-sorted-asc { background-image:url("../images/icons/04_ascending.gif"); }
table.sort04 th.table-sorted-desc { background-image:url("../images/icons/04_descending.gif"); }

table.sort05 th.table-sortable { background-image:url("05_unsorted.gif"); padding-left:16px;}
table.sort05 th.table-sorted-asc { background-image:url("05_ascending.gif"); }
table.sort05 th.table-sorted-desc { background-image:url("05_descending.gif"); }

table.sort06 th.table-sortable { background-image:none; padding-left:16px;}
table.sort06 th.table-sorted-asc { background-image:url("icons/06_ascending.gif"); }
table.sort06 th.table-sorted-desc { background-image:url("icons/06_descending.gif"); }

table.sort07 th.table-sortable { background-image:none; }
table.sort07 th.table-sorted-asc { background-image:url("07_ascending.gif"); }
table.sort07 th.table-sorted-desc { background-image:url("07_descending.gif"); }

table.sort08 th.table-sortable { background-image:none; }
table.sort08 th.table-sorted-asc { background-image:url("icons/08_ascending.gif"); }
table.sort08 th.table-sorted-desc { background-image:url("icons/08_descending.gif"); }

table.sort09 th.table-sortable { background-image:none; padding-left:30px;}
table.sort09 th.table-sorted-asc { background-image:url("icons/09_ascending.gif"); }
table.sort09 th.table-sorted-desc { background-image:url("icons/09_descending.gif"); }

table.sort10 th.table-sortable { background-image:url("icons/10_unsorted.gif"); }
table.sort10 th.table-sorted-asc { background-image:url("icons/10_ascending.gif"); }
table.sort10 th.table-sorted-desc { background-image:url("icons/10_descending.gif"); }

table.sort11 th.table-sortable { background-image:url("icons/11_unsorted.gif");padding-left:24px; }
table.sort11 th.table-sorted-asc { background-image:url("icons/11_ascending.gif"); }
table.sort11 th.table-sorted-desc { background-image:url("icons/11_descending.gif"); }

table.sort12 th.table-sortable { background-image:none; }
table.sort12 th.table-sorted-asc { background-image:url("icons/12_ascending.gif"); }
table.sort12 th.table-sorted-desc { background-image:url("icons/12_descending.gif"); }

table.sort13 th.table-sortable { background-image:none; }
table.sort13 th.table-sorted-asc { background-image:url("icons/13_ascending.gif"); }
table.sort13 th.table-sorted-desc { background-image:url("icons/13_descending.gif"); }

table.sort14 th.table-sortable { background-image:none; }
table.sort14 th.table-sorted-asc { background-image:url("icons/14_ascending.gif"); }
table.sort14 th.table-sorted-desc { background-image:url("icons/14_descending.gif"); }

table.sort15 th.table-sortable { background-image:none; }
table.sort15 th.table-sorted-asc { background-image:url("icons/15_ascending.gif"); }
table.sort15 th.table-sorted-desc { background-image:url("icons/15_descending.gif"); }

table.sort16 th.table-sortable { background-image:none; }
table.sort16 th.table-sorted-asc { background-image:url("icons/16_ascending.gif"); }
table.sort16 th.table-sorted-desc { background-image:url("icons/16_descending.gif"); }

table.sort17 th.table-sortable { background-image:none; }
table.sort17 th.table-sorted-asc { background-image:url("icons/17_ascending.gif"); }
table.sort17 th.table-sorted-desc { background-image:url("icons/17_descending.gif"); }

table.sort18 th.table-sortable { background-image:url("icons/18_unsorted.gif"); }
table.sort18 th.table-sorted-asc { background-image:url("icons/18_ascending.gif"); }
table.sort18 th.table-sorted-desc { background-image:url("icons/18_descending.gif"); }

table.sort19 th.table-sortable { background-image:url("icons/19_unsorted.gif");padding-left:24px; }
table.sort19 th.table-sorted-asc { background-image:url("icons/19_ascending.gif"); }
table.sort19 th.table-sorted-desc { background-image:url("icons/19_descending.gif"); }

/* Icons box */
.iconset {
    margin:5px;
    border:1px solid #cccccc;
    border-color:#cccccc #666666 #666666 #cccccc;
    text-align:center;
    cursor:pointer;
    width:100px;
}
.iconset img {
    margin:3px;
}

/* Documentation */
tr.doc_section {
    font-weight:bold;
    text-align:center;
    background-color:#dddddd;
}

table.js

var Sort = (function(){
    var sort = {};
    // Default alpha-numeric sort
    // --------------------------
    sort.alphanumeric = function(a,b) {
        return (a==b)?0:(a<b)?-1:1;
    };
    sort['default'] = sort.alphanumeric; // IE chokes on sort.default

    // This conversion is generalized to work for either a decimal separator of , or .
    sort.numeric_converter = function(separator) {
        return function(val) {
            if (typeof(val)=="string") {
                val = parseFloat(val.replace(/^[^\d\.]*([\d., ]+).*/g,"$1").replace(new RegExp("[^\\\d"+separator+"]","g"),'').replace(/,/,'.')) || 0;
            }
            return val || 0;
        };
    };

    // Numeric Sort   
    // ------------
    sort.numeric = function(a,b) {
        return sort.numeric.convert(a)-sort.numeric.convert(b);
    };
    sort.numeric.convert = sort.numeric_converter(".");

    // Numeric Sort    - comma decimal separator
    // --------------------------------------
    sort.numeric_comma = function(a,b) {
        return sort.numeric_comma.convert(a)-sort.numeric_comma.convert(b);
    };
    sort.numeric_comma.convert = sort.numeric_converter(",");

    // Case-insensitive Sort
    // ---------------------
    sort.ignorecase = function(a,b) {
        return sort.alphanumeric(sort.ignorecase.convert(a),sort.ignorecase.convert(b));
    };
    sort.ignorecase.convert = function(val) {
        if (val==null) { return ""; }
        return (""+val).toLowerCase();
    };

    // Currency Sort
    // -------------
    sort.currency = sort.numeric; // Just treat it as numeric!
    sort.currency_comma = sort.numeric_comma;

    // Date sort
    // ---------
    sort.date = function(a,b) {
        return sort.numeric(sort.date.convert(a),sort.date.convert(b));
    };
    // Convert 2-digit years to 4
    sort.date.fixYear=function(yr) {
        yr = +yr;
        if (yr<50) { yr += 2000; }
        else if (yr<100) { yr += 1900; }
        return yr;
    };
    sort.date.formats = [
        // YY[YY]-MM-DD
        { re:/(\d{2,4})-(\d{1,2})-(\d{1,2})/ , f:function(x){ return (new Date(sort.date.fixYear(x[1]),+x[2],+x[3])).getTime(); } }
        // MM/DD/YY[YY] or MM-DD-YY[YY]
        ,{ re:/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})/ , f:function(x){ return (new Date(sort.date.fixYear(x[3]),+x[1],+x[2])).getTime(); } }
        // Any catch-all format that new Date() can handle. This is not reliable except for long formats, for example: 31 Jan 2000 01:23:45 GMT
        ,{ re:/(.*\d{4}.*\d+:\d+\d+.*)/, f:function(x){ var d=new Date(x[1]); if(d){return d.getTime();} } }
    ];
    sort.date.convert = function(val) {
        var m,v, f = sort.date.formats;
        for (var i=0,L=f.length; i<L; i++) {
            if (m=val.match(f[i].re)) {
                v=f[i].f(m);
                if (typeof(v)!="undefined") { return v; }
            }
        }
        return 9999999999999; // So non-parsed dates will be last, not first
    };

    return sort;
})();

/**
 * The main Table namespace
 */
var Table = (function(){

    /**
     * Determine if a reference is defined
     */
    function def(o) {return (typeof o!="undefined");};

    /**
     * Determine if an object or class string contains a given class.
     */
    function hasClass(o,name) {
        return new RegExp("(^|\\s)"+name+"(\\s|$)").test(o.className);
    };

    /**
     * Add a class to an object
     */
    function addClass(o,name) {
        var c = o.className || "";
        if (def(c) && !hasClass(o,name)) {
            o.className += (c?" ":"") + name;
        }
    };

    /**
     * Remove a class from an object
     */
    function removeClass(o,name) {
        var c = o.className || "";
        o.className = c.replace(new RegExp("(^|\\s)"+name+"(\\s|$)"),"$1");
    };

    /**
     * For classes that match a given substring, return the rest
     */
    function classValue(o,prefix) {
        var c = o.className;
        if (c.match(new RegExp("(^|\\s)"+prefix+"([^ ]+)"))) {
            return RegExp.$2;
        }
        return null;
    };

    /**
     * Return true if an object is hidden.
     * This uses the "russian doll" technique to unwrap itself to the most efficient
     * function after the first pass. This avoids repeated feature detection that
     * would always fall into the same block of code.
     */
     function isHidden(o) {
        if (window.getComputedStyle) {
            var cs = window.getComputedStyle;
            return (isHidden = function(o) {
                return 'none'==cs(o,null).getPropertyValue('display');
            })(o);
        }
        else if (window.currentStyle) {
            return(isHidden = function(o) {
                return 'none'==o.currentStyle['display'];
            })(o);
        }
        return (isHidden = function(o) {
            return 'none'==o.style['display'];
        })(o);
    };

    /**
     * Get a parent element by tag name, or the original element if it is of the tag type
     */
    function getParent(o,a,b) {
        if (o!=null && o.nodeName) {
            if (o.nodeName==a || (b && o.nodeName==b)) {
                return o;
            }
            while (o=o.parentNode) {
                if (o.nodeName && (o.nodeName==a || (b && o.nodeName==b))) {
                    return o;
                }
            }
        }
        return null;
    };

    /**
     * Utility function to copy properties from one object to another
     */
    function copy(o1,o2) {
        for (var i=2;i<arguments.length; i++) {
            var a = arguments[i];
            if (def(o1[a])) {
                o2[a] = o1[a];
            }
        }
    }

    // The table object itself
    var table = {
        //Class names used in the code
        AutoStripeClassName:"table-autostripe",
        StripeClassNamePrefix:"table-stripeclass:",

        AutoSortClassName:"table-autosort",
        AutoSortColumnPrefix:"table-autosort:",
        AutoSortTitle:"Click to sort",
        SortedAscendingClassName:"table-sorted-asc",
        SortedDescendingClassName:"table-sorted-desc",
        SortableClassName:"table-sortable",
        SortableColumnPrefix:"table-sortable:",
        NoSortClassName:"table-nosort",

        AutoFilterClassName:"table-autofilter",
        FilteredClassName:"table-filtered",
        FilterableClassName:"table-filterable",
        FilteredRowcountPrefix:"table-filtered-rowcount:",
        RowcountPrefix:"table-rowcount:",
        FilterAllLabel:"Filter: All",

        AutoPageSizePrefix:"table-autopage:",
        AutoPageJumpPrefix:"table-page:",
        PageNumberPrefix:"table-page-number:",
        PageCountPrefix:"table-page-count:"
    };

    /**
     * A place to store misc table information, rather than in the table objects themselves
     */
    table.tabledata = {};

    /**
     * Resolve a table given an element reference, and make sure it has a unique ID
     */
    table.uniqueId=1;
    table.resolve = function(o,args) {
        if (o!=null && o.nodeName && o.nodeName!="TABLE") {
            o = getParent(o,"TABLE");
        }
        if (o==null) { return null; }
        if (!o.id) {
            var id = null;
            do { var id = "TABLE_"+(table.uniqueId++); }
                while (document.getElementById(id)!=null);
            o.id = id;
        }
        this.tabledata[o.id] = this.tabledata[o.id] || {};
        if (args) {
            copy(args,this.tabledata[o.id],"stripeclass","ignorehiddenrows","useinnertext","sorttype","col","desc","page","pagesize");
        }
        return o;
    };


    /**
     * Run a function against each cell in a table header or footer, usually
     * to add or remove css classes based on sorting, filtering, etc.
     */
    table.processTableCells = function(t, type, func, arg) {
        t = this.resolve(t);
        if (t==null) { return; }
        if (type!="TFOOT") {
            this.processCells(t.tHead, func, arg);
        }
        if (type!="THEAD") {
            this.processCells(t.tFoot, func, arg);
        }
    };

    /**
     * Internal method used to process an arbitrary collection of cells.
     * Referenced by processTableCells.
     * It's done this way to avoid getElementsByTagName() which would also return nested table cells.
     */
    table.processCells = function(section,func,arg) {
        if (section!=null) {
            if (section.rows && section.rows.length && section.rows.length>0) {
                var rows = section.rows;
                for (var j=0,L2=rows.length; j<L2; j++) {
                    var row = rows[j];
                    if (row.cells && row.cells.length && row.cells.length>0) {
                        var cells = row.cells;
                        for (var k=0,L3=cells.length; k<L3; k++) {
                            var cellsK = cells[k];
                            func.call(this,cellsK,arg);
                        }
                    }
                }
            }
        }
    };

    /**
     * Get the cellIndex value for a cell. This is only needed because of a Safari
     * bug that causes cellIndex to exist but always be 0.
     * Rather than feature-detecting each time it is called, the function will
     * re-write itself the first time it is called.
     */
    table.getCellIndex = function(td) {
        var tr = td.parentNode;
        var cells = tr.cells;
        if (cells && cells.length) {
            if (cells.length>1 && cells[cells.length-1].cellIndex>0) {
                // Define the new function, overwrite the one we're running now, and then run the new one
                (this.getCellIndex = function(td) {
                    return td.cellIndex;
                })(td);
            }
            // Safari will always go through this slower block every time. Oh well.
            for (var i=0,L=cells.length; i<L; i++) {
                if (tr.cells[i]==td) {
                    return i;
                }
            }
        }
        return 0;
    };

    /**
     * A map of node names and how to convert them into their "value" for sorting, filtering, etc.
     * These are put here so it is extensible.
     */
    table.nodeValue = {
        'INPUT':function(node) {
            if (def(node.value) && node.type && ((node.type!="checkbox" && node.type!="radio") || node.checked)) {
                return node.value;
            }
            return "";
        },
        'SELECT':function(node) {
            if (node.selectedIndex>=0 && node.options) {
                // Sort select elements by the visible text
                return node.options[node.selectedIndex].text;
            }
            return "";
        },
        'IMG':function(node) {
            return node.name || "";
        }
    };

    /**
     * Get the text value of a cell. Only use innerText if explicitly told to, because
     * otherwise we want to be able to handle sorting on inputs and other types
     */
    table.getCellValue = function(td,useInnerText) {
        if (useInnerText && def(td.innerText)) {
            return td.innerText;
        }
        if (!td.childNodes) {
            return "";
        }
        var childNodes=td.childNodes;
        var ret = "";
        for (var i=0,L=childNodes.length; i<L; i++) {
            var node = childNodes[i];
            var type = node.nodeType;
            // In order to get realistic sort results, we need to treat some elements in a special way.
            // These behaviors are defined in the nodeValue() object, keyed by node name
            if (type==1) {
                var nname = node.nodeName;
                if (this.nodeValue[nname]) {
                    ret += this.nodeValue[nname](node);
                }
                else {
                    ret += this.getCellValue(node);
                }
            }
            else if (type==3) {
                if (def(node.innerText)) {
                    ret += node.innerText;
                }
                else if (def(node.nodeValue)) {
                    ret += node.nodeValue;
                }
            }
        }
        return ret;
    };

    /**
     * Consider colspan and rowspan values in table header cells to calculate the actual cellIndex
     * of a given cell. This is necessary because if the first cell in row 0 has a rowspan of 2,
     * then the first cell in row 1 will have a cellIndex of 0 rather than 1, even though it really
     * starts in the second column rather than the first.
     * See: http://www.javascripttoolbox.com/temp/table_cellindex.html
     */
    table.tableHeaderIndexes = {};
    table.getActualCellIndex = function(tableCellObj) {
        if (!def(tableCellObj.cellIndex)) { return null; }
        var tableObj = getParent(tableCellObj,"TABLE");
        var cellCoordinates = tableCellObj.parentNode.rowIndex+"-"+this.getCellIndex(tableCellObj);

        // If it has already been computed, return the answer from the lookup table
        if (def(this.tableHeaderIndexes[tableObj.id])) {
            return this.tableHeaderIndexes[tableObj.id][cellCoordinates];     
        }

        var matrix = [];
        this.tableHeaderIndexes[tableObj.id] = {};
        var thead = getParent(tableCellObj,"THEAD");
        var trs = thead.getElementsByTagName('TR');

        // Loop thru every tr and every cell in the tr, building up a 2-d array "grid" that gets
        // populated with an "x" for each space that a cell takes up. If the first cell is colspan
        // 2, it will fill in values [0] and [1] in the first array, so that the second cell will
        // find the first empty cell in the first row (which will be [2]) and know that this is
        // where it sits, rather than its internal .cellIndex value of [1].
        for (var i=0; i<trs.length; i++) {
            var cells = trs[i].cells;
            for (var j=0; j<cells.length; j++) {
                var c = cells[j];
                var rowIndex = c.parentNode.rowIndex;
                var cellId = rowIndex+"-"+this.getCellIndex(c);
                var rowSpan = c.rowSpan || 1;
                var colSpan = c.colSpan || 1;
                var firstAvailCol;
                if(!def(matrix[rowIndex])) {
                    matrix[rowIndex] = [];
                }
                var m = matrix[rowIndex];
                // Find first available column in the first row
                for (var k=0; k<m.length+1; k++) {
                    if (!def(m[k])) {
                        firstAvailCol = k;
                        break;
                    }
                }
                this.tableHeaderIndexes[tableObj.id][cellId] = firstAvailCol;
                for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
                    if(!def(matrix[k])) {
                        matrix[k] = [];
                    }
                    var matrixrow = matrix[k];
                    for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
                        matrixrow[l] = "x";
                    }
                }
            }
        }
        // Store the map so future lookups are fast.
        return this.tableHeaderIndexes[tableObj.id][cellCoordinates];
    };

    /**
     * Sort all rows in each TBODY (tbodies are sorted independent of each other)
     */
    table.sort = function(o,args) {
        var t, tdata, sortconvert=null;
        // Allow for a simple passing of sort type as second parameter
        if (typeof(args)=="function") {
            args={sorttype:args};
        }
        args = args || {};

        // If no col is specified, deduce it from the object sent in
        if (!def(args.col)) {
            args.col = this.getActualCellIndex(o) || 0;
        }
        // If no sort type is specified, default to the default sort
        args.sorttype = args.sorttype || Sort['default'];

        // Resolve the table
        t = this.resolve(o,args);
        tdata = this.tabledata[t.id];

        // If we are sorting on the same column as last time, flip the sort direction
        if (def(tdata.lastcol) && tdata.lastcol==tdata.col && def(tdata.lastdesc)) {
            tdata.desc = !tdata.lastdesc;
        }
        else {
            tdata.desc = !!args.desc;
        }

        // Store the last sorted column so clicking again will reverse the sort order
        tdata.lastcol=tdata.col;
        tdata.lastdesc=!!tdata.desc;

        // If a sort conversion function exists, pre-convert cell values and then use a plain alphanumeric sort
        var sorttype = tdata.sorttype;
        if (typeof(sorttype.convert)=="function") {
            sortconvert=tdata.sorttype.convert;
            sorttype=Sort.alphanumeric;
        }

        // Loop through all THEADs and remove sorted class names, then re-add them for the col
        // that is being sorted
        this.processTableCells(t,"THEAD",
            function(cell) {
                if (hasClass(cell,this.SortableClassName)) {
                    removeClass(cell,this.SortedAscendingClassName);
                    removeClass(cell,this.SortedDescendingClassName);
                    // If the computed colIndex of the cell equals the sorted colIndex, flag it as sorted
                    if (tdata.col==table.getActualCellIndex(cell) && (classValue(cell,table.SortableClassName))) {
                        addClass(cell,tdata.desc?this.SortedAscendingClassName:this.SortedDescendingClassName);
                    }
                }
            }
        );

        // Sort each tbody independently
        var bodies = t.tBodies;
        if (bodies==null || bodies.length==0) { return; }

        // Define a new sort function to be called to consider descending or not
        var newSortFunc = (tdata.desc)?
            function(a,b){return sorttype(b[0],a[0]);}
            :function(a,b){return sorttype(a[0],b[0]);};

        var useinnertext=!!tdata.useinnertext;
        var col = tdata.col;

        for (var i=0,L=bodies.length; i<L; i++) {
            var tb = bodies[i], tbrows = tb.rows, rows = [];

            // Allow tbodies to request that they not be sorted
            if(!hasClass(tb,table.NoSortClassName)) {
                // Create a separate array which will store the converted values and refs to the
                // actual rows. This is the array that will be sorted.
                var cRow, cRowIndex=0;
                if (cRow=tbrows[cRowIndex]){
                    // Funky loop style because it's considerably faster in IE
                    do {
                        if (rowCells = cRow.cells) {
                            var cellValue = (col<rowCells.length)?this.getCellValue(rowCells[col],useinnertext):null;
                            if (sortconvert) cellValue = sortconvert(cellValue);
                            rows[cRowIndex] = [cellValue,tbrows[cRowIndex]];
                        }
                    } while (cRow=tbrows[++cRowIndex])
                }

                // Do the actual sorting
                rows.sort(newSortFunc);

                // Move the rows to the correctly sorted order. Appending an existing DOM object just moves it!
                cRowIndex=0;
                var displayedCount=0;
                var f=[removeClass,addClass];
                if (cRow=rows[cRowIndex]){
                    do {
                        tb.appendChild(cRow[1]);
                    } while (cRow=rows[++cRowIndex])
                }
            }
        }

        // If paging is enabled on the table, then we need to re-page because the order of rows has changed!
        if (tdata.pagesize) {
            this.page(t); // This will internally do the striping
        }
        else {
            // Re-stripe if a class name was supplied
            if (tdata.stripeclass) {
                this.stripe(t,tdata.stripeclass,!!tdata.ignorehiddenrows);
            }
        }
    };

    /**
    * Apply a filter to rows in a table and hide those that do not match.
    */
    table.filter = function(o,filters,args) {
        var cell;
        args = args || {};

        var t = this.resolve(o,args);
        var tdata = this.tabledata[t.id];

        // If new filters were passed in, apply them to the table's list of filters
        if (!filters) {
            // If a null or blank value was sent in for 'filters' then that means reset the table to no filters
            tdata.filters = null;
        }
        else {
            // Allow for passing a select list in as the filter, since this is common design
            if (filters.nodeName=="SELECT" && filters.type=="select-one" && filters.selectedIndex>-1) {
                filters={ 'filter':filters.options[filters.selectedIndex].value };
            }
            // Also allow for a regular input
            if (filters.nodeName=="INPUT" && filters.type=="text") {
                filters={ 'filter':"/^"+filters.value+"/" };
            }
            // Force filters to be an array
            if (typeof(filters)=="object" && !filters.length) {
                filters = [filters];
            }

            // Convert regular expression strings to RegExp objects and function strings to function objects
            for (var i=0,L=filters.length; i<L; i++) {
                var filter = filters[i];
                if (typeof(filter.filter)=="string") {
                    // If a filter string is like "/expr/" then turn it into a Regex
                    if (filter.filter.match(/^\/(.*)\/$/)) {
                        filter.filter = new RegExp(RegExp.$1);
                        filter.filter.regex=true;
                    }
                    // If filter string is like "function (x) { ... }" then turn it into a function
                    else if (filter.filter.match(/^function\s*\(([^\)]*)\)\s*\{(.*)}\s*$/)) {
                        filter.filter = Function(RegExp.$1,RegExp.$2);
                    }
                }
                // If some non-table object was passed in rather than a 'col' value, resolve it
                // and assign it's column index to the filter if it doesn't have one. This way,
                // passing in a cell reference or a select object etc instead of a table object
                // will automatically set the correct column to filter.
                if (filter && !def(filter.col) && (cell=getParent(o,"TD","TH"))) {
                    filter.col = this.getCellIndex(cell);
                }

                // Apply the passed-in filters to the existing list of filters for the table, removing those that have a filter of null or ""
                if ((!filter || !filter.filter) && tdata.filters) {
                    delete tdata.filters[filter.col];
                }
                else {
                    tdata.filters = tdata.filters || {};
                    tdata.filters[filter.col] = filter.filter;
                }
            }
            // If no more filters are left, then make sure to empty out the filters object
            for (var j in tdata.filters) { var keep = true; }
            if (!keep) {
                tdata.filters = null;
            }
        }       
        // Everything's been setup, so now scrape the table rows
        return table.scrape(o);
    };

    /**
     * "Page" a table by showing only a subset of the rows
     */
    table.page = function(t,page,args) {
        args = args || {};
        if (def(page)) { args.page = page; }
        return table.scrape(t,args);
    };

    /**
     * Jump forward or back any number of pages
     */
    table.pageJump = function(t,count,args) {
        t = this.resolve(t,args);
        return this.page(t,(table.tabledata[t.id].page||0)+count,args);
    };

    /**
     * Go to the next page of a paged table
     */   
    table.pageNext = function(t,args) {
        return this.pageJump(t,1,args);
    };

    /**
     * Go to the previous page of a paged table
     */   
    table.pagePrevious = function(t,args) {
        return this.pageJump(t,-1,args);
    };

    /**
    * Scrape a table to either hide or show each row based on filters and paging
    */
    table.scrape = function(o,args) {
        var col,cell,filterList,filterReset=false,filter;
        var page,pagesize,pagestart,pageend;
        var unfilteredrows=[],unfilteredrowcount=0,totalrows=0;
        var t,tdata,row,hideRow;
        args = args || {};

        // Resolve the table object
        t = this.resolve(o,args);
        tdata = this.tabledata[t.id];

        // Setup for Paging
        var page = tdata.page;
        if (def(page)) {
            // Don't let the page go before the beginning
            if (page<0) { tdata.page=page=0; }
            pagesize = tdata.pagesize || 25; // 25=arbitrary default
            pagestart = page*pagesize+1;
            pageend = pagestart + pagesize - 1;
        }

        // Scrape each row of each tbody
        var bodies = t.tBodies;
        if (bodies==null || bodies.length==0) { return; }
        for (var i=0,L=bodies.length; i<L; i++) {
            var tb = bodies[i];
            for (var j=0,L2=tb.rows.length; j<L2; j++) {
                row = tb.rows[j];
                hideRow = false;

                // Test if filters will hide the row
                if (tdata.filters && row.cells) {
                    var cells = row.cells;
                    var cellsLength = cells.length;
                    // Test each filter
                    for (col in tdata.filters) {
                        if (!hideRow) {
                            filter = tdata.filters[col];
                            if (filter && col<cellsLength) {
                                var val = this.getCellValue(cells[col]);
                                if (filter.regex && val.search) {
                                    hideRow=(val.search(filter)<0);
                                }
                                else if (typeof(filter)=="function") {
                                    hideRow=!filter(val,cells[col]);
                                }
                                else {
                                    hideRow = (val!=filter);
                                }
                            }
                        }
                    }
                }

                // Keep track of the total rows scanned and the total runs _not_ filtered out
                totalrows++;
                if (!hideRow) {
                    unfilteredrowcount++;
                    if (def(page)) {
                        // Temporarily keep an array of unfiltered rows in case the page we're on goes past
                        // the last page and we need to back up. Don't want to filter again!
                        unfilteredrows.push(row);
                        if (unfilteredrowcount<pagestart || unfilteredrowcount>pageend) {
                            hideRow = true;
                        }
                    }
                }

                row.style.display = hideRow?"none":"";
            }
        }

        if (def(page)) {
            // Check to see if filtering has put us past the requested page index. If it has,
            // then go back to the last page and show it.
            if (pagestart>=unfilteredrowcount) {
                pagestart = unfilteredrowcount-(unfilteredrowcount%pagesize);
                tdata.page = page = pagestart/pagesize;
                for (var i=pagestart,L=unfilteredrows.length; i<L; i++) {
                    unfilteredrows[i].style.display="";
                }
            }
        }

        // Loop through all THEADs and add/remove filtered class names
        this.processTableCells(t,"THEAD",
            function(c) {
                ((tdata.filters && def(tdata.filters[table.getCellIndex(c)]) && hasClass(c,table.FilterableClassName))?addClass:removeClass)(c,table.FilteredClassName);
            }
        );

        // Stripe the table if necessary
        if (tdata.stripeclass) {
            this.stripe(t);
        }

        // Calculate some values to be returned for info and updating purposes
        var pagecount = Math.floor(unfilteredrowcount/pagesize)+1;
        if (def(page)) {
            // Update the page number/total containers if they exist
            if (tdata.container_number) {
                tdata.container_number.innerHTML = page+1;
            }
            if (tdata.container_count) {
                tdata.container_count.innerHTML = pagecount;
            }
        }

        // Update the row count containers if they exist
        if (tdata.container_filtered_count) {
            tdata.container_filtered_count.innerHTML = unfilteredrowcount;
        }
        if (tdata.container_all_count) {
            tdata.container_all_count.innerHTML = totalrows;
        }
        return { 'data':tdata, 'unfilteredcount':unfilteredrowcount, 'total':totalrows, 'pagecount':pagecount, 'page':page, 'pagesize':pagesize };
    };

    /**
     * Shade alternate rows, aka Stripe the table.
     */
    table.stripe = function(t,className,args) {
        args = args || {};
        args.stripeclass = className;

        t = this.resolve(t,args);
        var tdata = this.tabledata[t.id];

        var bodies = t.tBodies;
        if (bodies==null || bodies.length==0) {
            return;
        }

        className = tdata.stripeclass;
        // Cache a shorter, quicker reference to either the remove or add class methods
        var f=[removeClass,addClass];
        for (var i=0,L=bodies.length; i<L; i++) {
            var tb = bodies[i], tbrows = tb.rows, cRowIndex=0, cRow, displayedCount=0;
            if (cRow=tbrows[cRowIndex]){
                // The ignorehiddenrows test is pulled out of the loop for a slight speed increase.
                // Makes a bigger difference in FF than in IE.
                // In this case, speed always wins over brevity!
                if (tdata.ignoreHiddenRows) {
                    do {
                        f[displayedCount++%2](cRow,className);
                    } while (cRow=tbrows[++cRowIndex])
                }
                else {
                    do {
                        if (!isHidden(cRow)) {
                            f[displayedCount++%2](cRow,className);
                        }
                    } while (cRow=tbrows[++cRowIndex])
                }
            }
        }
    };

    /**
     * Build up a list of unique values in a table column
     */
    table.getUniqueColValues = function(t,col) {
        var values={}, bodies = this.resolve(t).tBodies;
        for (var i=0,L=bodies.length; i<L; i++) {
            var tbody = bodies[i];
            for (var r=0,L2=tbody.rows.length; r<L2; r++) {
                values[this.getCellValue(tbody.rows[r].cells[col])] = true;
            }
        }
        var valArray = [];
        for (var val in values) {
            valArray.push(val);
        }
        return valArray.sort();
    };

    /**
     * Scan the document on load and add sorting, filtering, paging etc ability automatically
     * based on existence of class names on the table and cells.
     */
    table.auto = function(args) {
        var cells = [], tables = document.getElementsByTagName("TABLE");
        var val,tdata;
        if (tables!=null) {
            for (var i=0,L=tables.length; i<L; i++) {
                var t = table.resolve(tables[i]);
                tdata = table.tabledata[t.id];
                if (val=classValue(t,table.StripeClassNamePrefix)) {
                    tdata.stripeclass=val;
                }
                // Do auto-filter if necessary
                if (hasClass(t,table.AutoFilterClassName)) {
                    table.autofilter(t);
                }
                // Do auto-page if necessary
                if (val = classValue(t,table.AutoPageSizePrefix)) {
                    table.autopage(t,{'pagesize':+val});
                }
                // Do auto-sort if necessary
                if ((val = classValue(t,table.AutoSortColumnPrefix)) || (hasClass(t,table.AutoSortClassName))) {
                    table.autosort(t,{'col':(val==null)?null:+val});
                }
                // Do auto-stripe if necessary
                if (tdata.stripeclass && hasClass(t,table.AutoStripeClassName)) {
                    table.stripe(t);
                }
            }
        }
    };

    /**
     * Add sorting functionality to a table header cell
     */
    table.autosort = function(t,args) {
        t = this.resolve(t,args);
        var tdata = this.tabledata[t.id];
        this.processTableCells(t, "THEAD", function(c) {
            var type = classValue(c,table.SortableColumnPrefix);
            if (type!=null) {
                type = type || "default";
                c.title =c.title || table.AutoSortTitle;
                addClass(c,table.SortableClassName);
                c.onclick = Function("","Table.sort(this,{'sorttype':Sort['"+type+"']})");
                // If we are going to auto sort on a column, we need to keep track of what kind of sort it will be
                if (args.col!=null) {
                    if (args.col==table.getActualCellIndex(c)) {
                        tdata.sorttype=Sort['"+type+"'];
                    }
                }
            }
        } );
        if (args.col!=null) {
            table.sort(t,args);
        }
    };

    /**
     * Add paging functionality to a table
     */
    table.autopage = function(t,args) {
        t = this.resolve(t,args);
        var tdata = this.tabledata[t.id];
        if (tdata.pagesize) {
            this.processTableCells(t, "THEAD,TFOOT", function(c) {
                var type = classValue(c,table.AutoPageJumpPrefix);
                if (type=="next") { type = 1; }
                else if (type=="previous") { type = -1; }
                if (type!=null) {
                    c.onclick = Function("","Table.pageJump(this,"+type+")");
                }
            } );
            if (val = classValue(t,table.PageNumberPrefix)) {
                tdata.container_number = document.getElementById(val);
            }
            if (val = classValue(t,table.PageCountPrefix)) {
                tdata.container_count = document.getElementById(val);
            }
            return table.page(t,0,args);
        }
    };

    /**
     * A util function to cancel bubbling of clicks on filter dropdowns
     */
    table.cancelBubble = function(e) {
        e = e || window.event;
        if (typeof(e.stopPropagation)=="function") { e.stopPropagation(); }
        if (def(e.cancelBubble)) { e.cancelBubble = true; }
    };

    /**
     * Auto-filter a table
     */
    table.autofilter = function(t,args) {
        args = args || {};
        t = this.resolve(t,args);
        var tdata = this.tabledata[t.id],val;
        table.processTableCells(t, "THEAD", function(cell) {
            if (hasClass(cell,table.FilterableClassName)) {
                var cellIndex = table.getCellIndex(cell);
                var colValues = table.getUniqueColValues(t,cellIndex);
                if (colValues.length>0) {
                    if (typeof(args.insert)=="function") {
                        func.insert(cell,colValues);
                    }
                    else {
                        var sel = '<select onchange="Table.filter(this,this)" onclick="Table.cancelBubble(event)" class="'+table.AutoFilterClassName+'"><option value="">'+table.FilterAllLabel+'</option>';
                        for (var i=0; i<colValues.length; i++) {
                            sel += '<option value="'+colValues[i]+'">'+colValues[i]+'</option>';
                        }
                        sel += '</select>';
                        cell.innerHTML += "<br>"+sel;
                    }
                }
            }
        });
        if (val = classValue(t,table.FilteredRowcountPrefix)) {
            tdata.container_filtered_count = document.getElementById(val);
        }
        if (val = classValue(t,table.RowcountPrefix)) {
            tdata.container_all_count = document.getElementById(val);
        }
    };

    /**
     * Attach the auto event so it happens on load.
     * use jQuery's ready() function if available
     */
    if (typeof(jQuery)!="undefined") {
        jQuery(table.auto);
    }
    else if (window.addEventListener) {
        window.addEventListener( "load", table.auto, false );
    }
    else if (window.attachEvent) {
        window.attachEvent( "onload", table.auto );
    }

    return table;
})();

Monday 5 December 2011

Connectors in Mathematical logic

 We can make compound statements by connecting 2 or more simple statements .  The main connector that we use to make a compound statement are

  • Negation (NOT) ~
  • Conjunction (AND) ^
  • Disjunction (OR)
  • Conditional (if..then) ->
  • Bi conditional (if and only if (iff)) <->

What is Proposition in Mathematical logic?

A Proposition or Statement is a sentence which has truth value(true/false)
eg: sun rises in the east(T)
      earth is round in shape(F)

Sentence which are not propostion are
eg: what is your name?
      please give me 5 rupees
      shut your mouth
      what a beautiful flower it is!
    
commanding statements, exclamation, questions, request statements are not propostion.



Registers of 80386

There are sixteen registers in 80386 which are used by general purpose programmers. These registers are grouped into these basic categories:
General registers
Segment registers
Status and instruction registers

General Registers
They are used primarily to contain operands for arithmetic and logical operations. There are 8 general purpose, 32-bit registers.
32-bit registers : EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI

The 32 bit regsiters are broken down into 16 bit or 8 bit.

The lower word of each of 32 bit register can be addressed as a separate unit. This is useful for handling 16-bit data items.
16-bit registers : AX, BX, CX, DX, BP, SP, SI, DI

Each byte of the 16-bit registers AX, BX, CX, and DX has a separate name and can be treated as a unit. This feature is useful for handling characters and other 8-bit data items.
8 bits registers :  AH, BH, CH, DH , AL, BL, CL, DL

All of the general-purpose registers are available for addressing calculations and storing the results of most arithmetic and logical calculations; however, a few functions are dedicated to certain registers.

Data Types of 80386

The fundamental datatypes of 80386 are
bytes
words
doublewords

1 Byte = 8 bits
1 Word = 2 contiguous bytes
1 doubleword = 2 contiguous words

A byte is eight contiguous bits starting at any logical address. The bits are numbered 0 through 7.Bit zero is the least significant bit.

A word is two contiguous bytes starting at any byte address. A word thus contains 16 bits. The bits of a word are numbered from 0 through 15. bit 0 is the least significant bit.
The byte containing bit 0 of the word is called the low byte and the byte containing bit 15 is called the high byte.
Each byte within a word has its own address, and the smaller of the addresses is the address of the word. The byte at the lower address contains the eight least significant bits of the word   and the byte at the higher address contains the eight most significant bits.

A doubleword is two contiguous words starting at any byte address. A doubleword thus contains 32 bits. The bits of a doubleword are numbered from 0 through 31; bit 0 is the least significant bit.
The word containing bit 0 of the doubleword is called the low word.The word containing bit 31 is called the high word.
Each byte within a doubleword has its own address, and the smallest of the addresses is the address of the doubleword.
The byte at the lowest address contains the eight least significant bits of the doubleword and the byte at the highest address contains the eight most significant bits.

Additional data types

Integer
    A signed binary numeric value contained in a 32-bit doubleword, 16-bit word, or 8-bit byte. All operations assume a 2's complement representation.
high-order bit is used for a sign. bit 7 in a byte, bit 15 in a word, and bit 31 in a doubleword are used as sign bit. The sign bit has the value zero for positive integers and one for negative.
The range :
8-bit integer : -128 through +127
16-bit integers : -2^(15) through +2^(15) -1
32-bit integers : -2^(31) through +2^(31) -1

Ordinal
    An unsigned binary numeric value contained in a 32-bit doubleword, 16-bit word, or 8-bit byte.
The range :
8-bit integer : 0 through 255   (2^(8) -1)
16-bit integers : 0 through 65,535   (2^(16) -1)
32-bit integers : 0 through 2^(32) -1

Near Pointer 
    A 32-bit logical address. A near pointer is an offset within a segment.Used in either a flat or a segmented model of memory organization.

Far Pointer
    A 48-bit logical address of two components: a 16-bit segment selector component and a 32-bit offset component. Used in Segmented model.

String
    A contiguous sequence of bytes, words, or doublewords. A string may contain from zero bytes to 2^(32) -1 bytes (4 gigabytes).

Bit field
    A contiguous sequence of bits. A bit field may begin at any bit position of any byte and may contain up to 32 bits.

Bit string
    A contiguous sequence of bits. A bit string may begin at any bit position of any byte and may contain up to 2^(32) -1 bits.

BCD
    A byte (unpacked) representation of a decimal digit in the range 0 through 9.

Packed BCD
    A byte (packed) representation of two decimal digits, each in the range 0 through 9

Memory Organization in 80386

The Physical memory of 80386 is organized as a sequence of bytes. Each byte is assigned a unique address that ranges from zero to a maximum of 2^(32) -1 (4 gigabytes). The logical memory of 80386 can address up to 2^(46) bytes (64 terabytes).
The two types of memory models in 80386 are


  • A "flat" address space consisting of a single array of up to 4 gigabytes.
  • A segmented address space consisting of a collection of segmants.
Both  model provides memory protection.

Flat Model
In a flat model , a programmer sees memory as a single array up to 4GB. The processor maps the 4 GB flat space onto the physical address space by the address translation mechanisms.
A pointer into this flat address space is a 32-bit ordinal number that may range from 0 to 2^(32) -1. Relocation of separately-compiled modules in this space must be performed by systems software (e.g., linkers, locators, binders, loaders)

Segmented Model
In Segmented model, address space is viewed as collection of segments. The segmented model can consist of 16,383 segments.
Applications programmers view the logical address space of the 80386 as a collection of up to 16,383 one-dimensional subspaces, each with a specified length. Each of these linear subspaces is called a segment. A segment is a unit of contiguous address space. Segment sizes may range from one byte up to a maximum of 2^(32) bytes (4 gigabytes).

A pointer in this address space consists of two parts:
  •  A segment selector, which is a 16-bit field that identifies a segment.
  •  An offset, which is a 32-bit ordinal that addresses to the byte level within a  segment. 

80386 processor: Introduction

The 80386 is an advanced 32-bit microprocessor optimised for multitasking operating systems.It is designed for applications needing very high performance.
The processor can address up to four gigabytes of physical memory and 64 terabytes (2^(46) bytes) of virtual memory.

Tuesday 1 November 2011

Fixed : window.location not working in IE6

windows.location works fine in FF. But It fails though in IE6. I tried many variations window.location, window.location.href ,window.location.href.assign ,document.location,document.location.href but nothing worked out.

After few hours of googling I found the solution. we need to add the following line to get it worked in IE 6 .


      if(window.event ) {//IE 6
            window.event.returnValue = false;
            window.location = "http://www.google.com";
            return false;
      }else {//firefox
             window.location = "http://www.google.com";
      }



Wednesday 19 October 2011

How to calculate time difference in php

We can use time() function to calculate the difference between 2 dates in PHP. The time() function returns the current time as a Unix timestamp (the number of seconds since January 1 1970 00:00:00 GMT).

The below function  returns the number of minutes, hours, days and months between 2 timestamp.


    /* 
     * Minutes  =  60       seconds
     * Hour     =  3600     seconds
     * Day      =  86400    seconds 

     * Month    =  2592000  seconds
     */


function timeAgo($time) {
   //get the time difference between current time and login time
   $timeDif=time()-$time;

   // month difference

   if (($timeDif/2592000)>=1) {
       if(floor(($timeDif/2592000))==1){
           return floor(($timeDif/2592000))." month";
       }
       else {
          return floor(($timeDif/2592000))." months";
       }
    }
    //day difference
    elseif (($timeDif/86400)>=1) {
      if(floor(($timeDif/86400)) == 1){
         return floor(($timeDif/86400))." day";
      }
      else{
         return floor(($timeDif/86400))." days";
       }
    }
    //hour difference
    elseif(($timeDif/3600)>=1) {
      if(floor(($timeDif/3600)) ==1){
         return floor(($timeDif/3600))." hour";
      }
      else{
         return floor(($timeDif/3600))." hours";
      }
    }
    // minute differnece
    elseif(($timeDif/60)>=1) {
      if(floor(($timeDif/60)) ==1){
         return floor(($timeDif/60))." minutes";
      }
      else{
         return floor(($timeDif/60))." minutes";
      }
    }else {
    return "less than 1 minute";
    }
}