PunCha

导航

jQueryUI Plugin TableSorter的2个widget扩展

因为项目需要对Table进行汇总以及定义列的显示隐藏,所以需要对jQueryUI的TableSorter进行扩展,下面2个插件是我写的,第一个是用显示/隐藏列(右键点击列头会出现右键菜单)。第二个是对列宽度的修正(可以忽略)。第三个是对数据进行汇总,再table footer处会显示汇总列)


Widget的代码:

; (function ($) {
    "use strict";

    function getColumnsByIndex(table, iColIndex) {
        var expression = ':eq(' + iColIndex + ')';
        var $table = $(table);
        var $items = $table.find("tr").find('td' + expression);
        $.merge($items, $table.find('th' + expression));
        return $items;
    }

    function getColumnIndexById(table, colId) {
        return $(table).find("th").filter('[data-column-id="' + colId + '"]').index();
    }

    function getColumnsById(table, colId) {
        var index = getColumnIndexById(table, colId);
        return getColumnsByIndex(table, index);
    }

    function ColumnVisibilitySettings() {
        this.serialize = function (objSettings) {
            return {
                version: "1",
                visibilities: objSettings
            };
        };
        this.deserialize = function (jsonSettings) {
            if (jsonSettings) {
                var version = jsonSettings.version;
                var objSettings = {};
                switch (version) {
                    case "1":
                        return jsonSettings.visibilities;
                }
            }

            return undefined;
        };
    }

    $.tablesorter.addWidget({
        id: 'column-selector',
        priority: 51,   // Should be called later than filter
        options: {
            selector_contextmenu: '.tablesorter-context-menu'
        },

        m_config: null,
        m_widgetOptions: null,
        m_settingsLoaded: false,

        init: function (table, thisWidget, config, widgetOptions) {
            // widget initialization code - this is only *RUN ONCE*
            this.m_config = config;
            this.m_widgetOptions = widgetOptions;

            var self = this;
            var $table = config.$table;

            $(widgetOptions.selector_contextmenu).hide();

            $table.children('thead').bind("contextmenu", $.proxy(this.onShowContextMenu, this));

            // setup our event handler
            $table.on("save_column_visibility", $.proxy(this.saveColumnVisibility, this));
            $table.on("show_context_menu", $.proxy(this.onShowContextMenu, this));
        },

        format: function (table, config, widgetOptions) {
            // widget code to apply to the table *AFTER EACH SORT*
            if (!this.m_settingsLoaded) {
                this.loadColumnVisibility();
                this.m_settingsLoaded = true;
            }
        },

        remove: function (table, config, widgetOptions) {
            // do what ever needs to be done to remove stuff added by your widget
            // unbind events, restore hidden content, etc.
            $table.children('thead').unbind("contextmenu", $.proxy(this.onShowContextMenu, this));

            $table.off("save_column_visibility", $.proxy(this.saveColumnVisibility, this));
            $table.off("show_context_menu", $.proxy(this.onShowContextMenu, this));
        },

        onShowContextMenu: function (event, extraEvent) {

            // We allow to fack the event
            if (extraEvent) event = extraEvent;

            event.preventDefault();

            var self = this;
            var $table = this.m_config.$table;
            var $columnsUl = $('<ul></ul>');
            var $columns = $table.find("thead>tr>th");
            for (var i = 0; i < $columns.length; i++) {

                var $column = $($columns[i]);
                var columnDisplayName = $column.text();
                var columnId = $column.data('column-id');
                var columnIsFixed = $column.data('column-fixed');
                if (!columnId) {
                    console.log("No column id avaliable. Cannot config columns!");
                    return;
                }
                
                // Create li element
                var $columnLi = $('<li></li>').appendTo($columnsUl);

                // Create label for the checkbox
                var $label = $('<label for="' + columnDisplayName + '"></label>')
                    .append($('<span>' + columnDisplayName + '</span>'))
                    .appendTo($columnLi);

                // Create checkbox
                var $checkbox = $('<input type="checkbox" />')
                    .attr('name', columnDisplayName)
                    .data('column-id', columnId)
                    .prependTo($label)
                    .click(function () {
                        var $clickedCheckbox = $(this);
                        var columnId = $clickedCheckbox.data('column-id');
                        var $columns = getColumnsById($table, columnId);
                        $columns.fadeToggle("fast");
                        self.saveColumnVisibility();
                    });

                if (columnIsFixed)
                    $checkbox.attr('disabled', "disabled");

                // Update checkbox state
                if ($(":visible", $($columns[i])).length > 0) {
                    $checkbox.attr('checked', 'checked');
                }
            }

            // Show context menu
            this.showContextMenu($columnsUl, event);
        },

        showContextMenu: function (contextMenuContent, event) {
            var contextMenu = $(this.m_widgetOptions.selector_contextmenu).hide().empty();
            contextMenu.append(contextMenuContent);

            // Marker
            $('<div />')
                .css({
                    position: "fixed",
                    left: "0px",
                    top: "0px",
                    width: "100%",
                    height: "100%",
                    "z-index": "100"
                })
                .click(function () {
                    $(this).remove();
                    contextMenu.hide();
                })
                .bind('contextmenu', function () { return false; })
                .appendTo(document.body);

            contextMenu.css({
                left: event.pageX,
                top: event.pageY,
                "z-index": 101
            }).show();
        },

        loadColumnVisibility: function () {
            var $table = this.m_config.$table;
            var ts = $.tablesorter.storage;
            var jsonSettings = ts && ts(this.m_config.$table[0], "tablesorter-column-selector");
            var settings = new ColumnVisibilitySettings().deserialize(jsonSettings);
            if (settings) {
                for (var col in settings) {
                    if (settings[col] === true) getColumnsById($table, col).show();
                    else getColumnsById($table, col).hide();
                }
            }
        },

        saveColumnVisibility: function () {
            var $table = this.m_config.$table;
            var ts = $.tablesorter.storage;
            var settings = ts && ts(this.m_config.$table[0], "tablesorter-column-selector");

            if (ts) {
                setTimeout(function () {
                    var visibilities = {};
                    $table.find("thead>tr>th").each(function (index) {
                        var $col = $(this);
                        visibilities[$col.data("column-id")] = ($col.css('display') !== "none");
                    });
                    ts($table[0], "tablesorter-column-selector", new ColumnVisibilitySettings().serialize(visibilities));
                }, 1000);
            }
        },
    });

    $.tablesorter.addWidget({
        id: 'column-width-setter',
        priority: 81,   // Should be called later than filter
        options: {
        },

        m_config: null,

        init: function (table, thisWidget, config, widgetOptions) {
            this.m_config = config;
            this.resetColumnWidth();
        },

        resetColumnWidth: function () {
            var $columns = this.m_config.$headers;
            for (var i = 0; i < $columns.length; i++) {
                var width = parseInt($($columns[i]).attr("data-width"));
                if (width) {
                    getColumnsByIndex(this.m_config.$table, i).width(width);
                }
            }
        }
    });

    $.tablesorter.addWidget({
        id: 'summery-row',
        options: {
            selector_summery_row: '.tablesorter-summery-row',
            selector_column_enable_summery: '.tablesorter-enable-summery',
            show_NaN: true
        },

        init: function (table, thisWidget, config, widgetOptions) {
            // widget initialization code - this is only *RUN ONCE*
            this.m_config = config;
            this.m_widgetOptions = widgetOptions;
            var $table = config.$table;
            $table.bind("filterEnd", $.proxy(this.updateSummeryRow, this));
            this.updateSummeryRow();
        },

        updateSummeryRow: function () {
            var self = this;
            var $table = this.m_config.$table;
            var summery = $table.find(this.m_widgetOptions.selector_summery_row)
            var $headers = $table.find("thead>tr>th");
            $headers.each(function (index) {
                var $column = $(this);
                if ($column.filter(self.m_widgetOptions.selector_column_enable_summery).length <= 0)
                    return;
                var sum = 0;
                var isPercentage = false;
                var td = summery.find('td:eq(' + index + ')');
                $table.find("tbody:not(" + self.m_widgetOptions.selector_summery_row + ")>tr:visible").find("td:eq(" + index + ")").each(function (index) {
                    var sVal = $(this).text();
                    if (sVal.indexOf("%") !== -1) {
                        isPercentage = true;
                    }
                    var iVal = parseFloat(sVal);
                    sum += iVal;
                });

                if (!isNaN(sum) || self.m_widgetOptions.show_NaN) {
                    if (isPercentage) {
                        td.text(sum.toFixed(2) + "%");
                    }
                    else {
                        td.text(sum);
                    }
                }
            });
        }
    });

})(jQuery);

下面是在nodeJs的EJS模板里面的应用,支持右键点击列头以及左键点击Custom View按钮对显示的列进行定制:


js_make_table_sortable代码:

<!-- Require jQuery, jQueryUI -->
<!-- Table should has id "data-table" -->

<% include header_table %>
<script class="code" type="text/javascript">
    $(document).ready(function () {

        // Make table sort-able
        {
            // Extend the themes to change any of the default class names ** NEW **
            $.extend($.tablesorter.themes.jui, {
                // change default jQuery uitheme icons - find the full list of icons here: http://jqueryui.com/themeroller/ (hover over them for their name)
                table: 'ui-widget ui-widget-content ui-corner-all', // table classes
                header: 'ui-widget-header ui-corner-all ui-state-default', // header classes
                footerRow: '',
                footerCells: '',
                icons: 'ui-icon', // icon class added to the <i> in the header
                sortNone: 'ui-icon-carat-2-n-s',
                sortAsc: 'ui-icon-carat-1-n',
                sortDesc: 'ui-icon-carat-1-s',
                active: 'ui-state-active', // applied when column is sorted
                hover: 'ui-state-hover',  // hover class
                filterRow: '',
                even: 'ui-widget-content', // odd row zebra striping
                odd: 'ui-state-default'   // even row zebra striping
            });

            var dataTable = $("#data-table");

            // call the tablesorter plugin and apply the ui theme widget
            dataTable.tablesorter({
                theme: 'jui',
                headerTemplate: '{content} {icon}',
                widgets: ['uitheme', 'zebra', 'filter', 'column-selector', 'summery-row'],
                widgetOptions: {
                    zebra: ["even", "odd"],
                }
            });
        }
    });
</script>


EJS代码:

<% include js_make_table_sortable %>
<script class="code" type="text/javascript">
    $(document).ready(function () {

        var $table = $("#data-table");

        // Different View switch buttons
        $(".view-button[data-filter]").button().click(function () {
            var filter = $(this).data('filter');

            $table.find("th").each(function (index, header) {
                var $header = $(header);
                var index = $header.index();

                if ($header.filter(filter).length > 0) {
                    $table.find("tr").find("td:eq(" + index + ")").show();
                    $header.show();
                }
                else {
                    $table.find("tr").find("td:eq(" + index + ")").hide();
                    $header.hide();
                }
            });

            // Persist our settings
            $table.trigger("save_column_visibility");
        });

        $(".custom-view-button").button().click(function (event) {
            $table.trigger("show_context_menu", event);
        });
    });
</script>

<div id="table-container">
    <div class="tablesorter-context-menu" style="position: absolute;"></div>
    <div class="switch-view-area">
        <button class="view-button" data-filter=":not(false)">Full View</button>
        <button class="view-button" data-filter=".swd_view">SWD View</button>
        <button class="view-button" data-filter=".td_view">TD View</button>
        <button class="view-button" data-filter=".report_view">Report View</button>
        <button class="custom-view-button">Custom View</button>
    </div>
    <table id="data-table" class="tablesorter" style="max-width:1750px;">
        <caption><%= title %></caption>
        <thead>
            <tr>
                <th class="report_view swd_view td_view" data-width="10px" data-column-id="Ranking">Ranking</th>
                <th class="report_view swd_view td_view" data-width="10px" data-column-id="Bucket" data-column-fixed="true">Bucket</th>
                <th class="report_view swd_view td_view" data-width="10px" data-column-id="Resolved">Resolved</th>
                <th class="td_view" data-width="50px" data-column-id="PurportedlyFixedin">Purportedly Fixed in</th>
                <th class="swd_view" data-width="100px" data-column-id="Notes">Notes</th>
                <th class="tablesorter-enable-summery report_view swd_view" data-width="10px" data-column-id="CurrentReleaseCount">Current Release Count</th>
                <th class="tablesorter-enable-summery report_view swd_view" data-width="10px" data-column-id="ofTotal">% of Total</th>
                <th class="tablesorter-enable-summery swd_view" data-width="10px" data-column-id="UniqueUsers">Unique Users</th>
                <th class="tablesorter-enable-summery swd_view" data-width="10px" data-column-id="NewReportsinThisWeek">New Reports in This Week</th>
                <th class="tablesorter-enable-summery swd_view" data-width="10px" data-column-id="TotalCount">Total Count</th>
                <th class="report_view swd_view td_view" data-width="10px" data-column-id="DefectId">Defect Id</th>
                <th class="report_view td_view" data-width="400px" data-column-id="DefectStatus">Defect Status</th>
                <th class="swd_view" data-width="20px" data-column-id="Module">Module</th>
                <th class="swd_view" data-width="20px" data-column-id="Object">Object</th>
                <th class="swd_view" data-width="20px" data-column-id="Function">Function</th>
                <th class="swd_view" data-width="20px" data-column-id="Offset">Offset</th>
            </tr>
        </thead>
        <tbody>
            <% for (var i = 0; i < items.length; ++ i) { %>
                <tr>
                    <td><%= i+1 %></td>
                    <td><%- link('http://xxxx.xxxx.com/cer/ViewBucket.aspx?bucket=' + items[i].bucketId, items[i].bucketId, "class='bucket-link' target='_blank'") %></td>
                    <td><%= items[i].isResolved ? "Yes" : "No" %></td>
                    <td><%= items[i].purportedlyFixedIn %></td>
                    <td><%= items[i].note %></td>
                    <td><%= items[i].currentReleaseCount %></td>
                    <td><%= (items[i].totalPercent*100).toFixed(2)+"%"  %></td>
                    <td><%= items[i].uniqueUsers %></td>
                    <td><%= items[i].newReportsInThisWeek %></td>
                    <td><%= items[i].totalCount %></td>
                    <td><%- splitDefectIds(items[i].defectId) %></td>
                    <td><%- formatDefectStatus(items[i].defectStatus) %></td>
                    <td><%= items[i].module %></td>
                    <td><%= items[i].object %></td>
                    <td><%= items[i].function %></td>
                    <td><%= items[i].offset %></td>
                </tr>
            <% } %>
        </tbody>
        <tfoot>
            <tr class="tablesorter-summery-row">
                <td>Sum</td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
        </tfoot>
    </table>
</div>




posted on 2013-08-22 11:24  PunCha  阅读(222)  评论(0编辑  收藏  举报