diff options
46 files changed, 9268 insertions, 9074 deletions
diff --git a/controls/slick.columnpicker.css b/controls/slick.columnpicker.css index aaf5fbe..a2845cc 100644 --- a/controls/slick.columnpicker.css +++ b/controls/slick.columnpicker.css @@ -1,30 +1,30 @@ .slick-columnpicker { - border: 1px solid #718BB7; - background: #f0f0f0; - padding: 6px; - -moz-box-shadow: 2px 2px 2px silver; - -webkit-box-shadow: 2px 2px 2px silver; - min-width: 100px; - cursor: default; + border: 1px solid #718BB7; + background: #f0f0f0; + padding: 6px; + -moz-box-shadow: 2px 2px 2px silver; + -webkit-box-shadow: 2px 2px 2px silver; + min-width: 100px; + cursor: default; } .slick-columnpicker li { - list-style: none; - margin: 0; - padding: 0; - background: none; + list-style: none; + margin: 0; + padding: 0; + background: none; } .slick-columnpicker input { - margin: 4px; + margin: 4px; } .slick-columnpicker li a { - display: block; - padding: 4px; - font-weight: bold; + display: block; + padding: 4px; + font-weight: bold; } .slick-columnpicker li a:hover { - background: white; + background: white; }
\ No newline at end of file diff --git a/controls/slick.columnpicker.js b/controls/slick.columnpicker.js index 4b84e0d..ca95211 100644 --- a/controls/slick.columnpicker.js +++ b/controls/slick.columnpicker.js @@ -1,105 +1,106 @@ -(function($) { - function SlickColumnPicker(columns,grid,options) - { - var $menu; - - var defaults = { - fadeSpeed: 250 - }; - - function init() { - grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu); - options = $.extend({}, defaults, options); - - $menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body); - - $menu.bind("mouseleave", function(e) { $(this).fadeOut(options.fadeSpeed) }); - $menu.bind("click", updateColumn); - - } - - function handleHeaderContextMenu(e, args) - { - e.preventDefault(); - $menu.empty(); - - var $li, $input; - for (var i=0; i<columns.length; i++) { - $li = $("<li />").appendTo($menu); - - $input = $("<input type='checkbox' />") - .attr("id", "columnpicker_" + i) - .data("id", columns[i].id) - .appendTo($li); - - if (grid.getColumnIndex(columns[i].id) != null) - $input.attr("checked","checked"); - - $("<label for='columnpicker_" + i + "' />") - .text(columns[i].name) - .appendTo($li); - } - - $("<hr/>").appendTo($menu); - $li = $("<li />").appendTo($menu); - $input = $("<input type='checkbox' id='autoresize' />").appendTo($li); - $("<label for='autoresize'>Force Fit Columns</label>").appendTo($li); - if (grid.getOptions().forceFitColumns) - $input.attr("checked", "checked"); - - $li = $("<li />").appendTo($menu); - $input = $("<input type='checkbox' id='syncresize' />").appendTo($li); - $("<label for='syncresize'>Synchronous Resizing</label>").appendTo($li); - if (grid.getOptions().syncColumnCellResize) - $input.attr("checked", "checked"); - - $menu - .css("top", e.pageY - 10) - .css("left", e.pageX - 10) - .fadeIn(options.fadeSpeed); - } - - function updateColumn(e) - { - if (e.target.id == 'autoresize') { - if (e.target.checked) { - grid.setOptions({forceFitColumns: true}); - grid.autosizeColumns(); - } else { - grid.setOptions({forceFitColumns: false}); - } - return; - } - - if (e.target.id == 'syncresize') { - if (e.target.checked) { - grid.setOptions({syncColumnCellResize: true}); - } else { - grid.setOptions({syncColumnCellResize: false}); - } - return; - } - - if ($(e.target).is(":checkbox")) { - if ($menu.find(":checkbox:checked").length == 0) { - $(e.target).attr("checked","checked"); - return; - } - - var visibleColumns = []; - $menu.find(":checkbox[id^=columnpicker]").each(function(i,e) { - if ($(this).is(":checked")) { - visibleColumns.push(columns[i]); - } - }); - grid.setColumns(visibleColumns); - } - } - - - init(); - } - - // Slick.Controls.ColumnPicker - $.extend(true, window, { Slick: { Controls: { ColumnPicker: SlickColumnPicker }}}); +(function ($) { + function SlickColumnPicker(columns, grid, options) { + var $menu; + + var defaults = { + fadeSpeed:250 + }; + + function init() { + grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu); + options = $.extend({}, defaults, options); + + $menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body); + + $menu.bind("mouseleave", function (e) { + $(this).fadeOut(options.fadeSpeed) + }); + $menu.bind("click", updateColumn); + + } + + function handleHeaderContextMenu(e, args) { + e.preventDefault(); + $menu.empty(); + + var $li, $input; + for (var i = 0; i < columns.length; i++) { + $li = $("<li />").appendTo($menu); + + $input = $("<input type='checkbox' />") + .attr("id", "columnpicker_" + i) + .data("id", columns[i].id) + .appendTo($li); + + if (grid.getColumnIndex(columns[i].id) != null) { + $input.attr("checked", "checked"); + } + + $("<label for='columnpicker_" + i + "' />") + .text(columns[i].name) + .appendTo($li); + } + + $("<hr/>").appendTo($menu); + $li = $("<li />").appendTo($menu); + $input = $("<input type='checkbox' id='autoresize' />").appendTo($li); + $("<label for='autoresize'>Force Fit Columns</label>").appendTo($li); + if (grid.getOptions().forceFitColumns) { + $input.attr("checked", "checked"); + } + + $li = $("<li />").appendTo($menu); + $input = $("<input type='checkbox' id='syncresize' />").appendTo($li); + $("<label for='syncresize'>Synchronous Resizing</label>").appendTo($li); + if (grid.getOptions().syncColumnCellResize) { + $input.attr("checked", "checked"); + } + + $menu + .css("top", e.pageY - 10) + .css("left", e.pageX - 10) + .fadeIn(options.fadeSpeed); + } + + function updateColumn(e) { + if (e.target.id == 'autoresize') { + if (e.target.checked) { + grid.setOptions({forceFitColumns:true}); + grid.autosizeColumns(); + } else { + grid.setOptions({forceFitColumns:false}); + } + return; + } + + if (e.target.id == 'syncresize') { + if (e.target.checked) { + grid.setOptions({syncColumnCellResize:true}); + } else { + grid.setOptions({syncColumnCellResize:false}); + } + return; + } + + if ($(e.target).is(":checkbox")) { + if ($menu.find(":checkbox:checked").length == 0) { + $(e.target).attr("checked", "checked"); + return; + } + + var visibleColumns = []; + $menu.find(":checkbox[id^=columnpicker]").each(function (i, e) { + if ($(this).is(":checked")) { + visibleColumns.push(columns[i]); + } + }); + grid.setColumns(visibleColumns); + } + } + + init(); + } + + // Slick.Controls.ColumnPicker + $.extend(true, window, { Slick:{ Controls:{ ColumnPicker:SlickColumnPicker }}}); })(jQuery); diff --git a/controls/slick.pager.css b/controls/slick.pager.css index ec40122..e360ec6 100644 --- a/controls/slick.pager.css +++ b/controls/slick.pager.css @@ -1,48 +1,41 @@ -.slick-pager -{ - width: 100%; - height: 26px; - border: 1px solid gray; - border-top: 0; - background: url('../images/header-columns-bg.gif') repeat-x center bottom; - vertical-align: middle; +.slick-pager { + width: 100%; + height: 26px; + border: 1px solid gray; + border-top: 0; + background: url('../images/header-columns-bg.gif') repeat-x center bottom; + vertical-align: middle; } -.slick-pager .slick-pager-status -{ - display: inline-block; - padding: 6px; +.slick-pager .slick-pager-status { + display: inline-block; + padding: 6px; } -.slick-pager .ui-icon-container -{ - display: inline-block; - margin: 2px; - border-color: gray; +.slick-pager .ui-icon-container { + display: inline-block; + margin: 2px; + border-color: gray; } -.slick-pager .slick-pager-nav -{ - display: inline-block; - float: left; - padding: 2px; +.slick-pager .slick-pager-nav { + display: inline-block; + float: left; + padding: 2px; } -.slick-pager .slick-pager-settings -{ - display: block; - float: right; - padding: 2px; +.slick-pager .slick-pager-settings { + display: block; + float: right; + padding: 2px; } -.slick-pager .slick-pager-settings * -{ - vertical-align: middle; +.slick-pager .slick-pager-settings * { + vertical-align: middle; } -.slick-pager .slick-pager-settings a -{ - padding: 2px; - text-decoration: underline; - cursor: pointer; +.slick-pager .slick-pager-settings a { + padding: 2px; + text-decoration: underline; + cursor: pointer; } diff --git a/controls/slick.pager.js b/controls/slick.pager.js index ce1921d..5da5c25 100644 --- a/controls/slick.pager.js +++ b/controls/slick.pager.js @@ -1,148 +1,151 @@ -(function($) { - function SlickGridPager(dataView, grid, $container) - { - var $status; - - function init() - { - dataView.onPagingInfoChanged.subscribe(function(e,pagingInfo) { - updatePager(pagingInfo); - }); - - constructPagerUI(); - updatePager(dataView.getPagingInfo()); - } +(function ($) { + function SlickGridPager(dataView, grid, $container) { + var $status; - function getNavState() - { - var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit(); - var pagingInfo = dataView.getPagingInfo(); - var lastPage = Math.floor(pagingInfo.totalRows/pagingInfo.pageSize); - - return { - canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, - canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage, - canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, - canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage, - pagingInfo: pagingInfo, - lastPage: lastPage - } - } + function init() { + dataView.onPagingInfoChanged.subscribe(function (e, pagingInfo) { + updatePager(pagingInfo); + }); - function setPageSize(n) - { - dataView.setRefreshHints({ - isFilterUnchanged: true - }); - dataView.setPagingOptions({pageSize:n}); - } + constructPagerUI(); + updatePager(dataView.getPagingInfo()); + } - function gotoFirst() - { - if (getNavState().canGotoFirst) - dataView.setPagingOptions({pageNum: 0}); - } + function getNavState() { + var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit(); + var pagingInfo = dataView.getPagingInfo(); + var lastPage = Math.floor(pagingInfo.totalRows / pagingInfo.pageSize); + + return { + canGotoFirst:!cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, + canGotoLast:!cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage, + canGotoPrev:!cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, + canGotoNext:!cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage, + pagingInfo:pagingInfo, + lastPage:lastPage + } + } - function gotoLast() - { - var state = getNavState(); - if (state.canGotoLast) - dataView.setPagingOptions({pageNum: state.lastPage}); - } + function setPageSize(n) { + dataView.setRefreshHints({ + isFilterUnchanged:true + }); + dataView.setPagingOptions({pageSize:n}); + } - function gotoPrev() - { - var state = getNavState(); - if (state.canGotoPrev) - dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum-1}); - } + function gotoFirst() { + if (getNavState().canGotoFirst) { + dataView.setPagingOptions({pageNum:0}); + } + } - function gotoNext() - { - var state = getNavState(); - if (state.canGotoNext) - dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum+1}); - } + function gotoLast() { + var state = getNavState(); + if (state.canGotoLast) { + dataView.setPagingOptions({pageNum:state.lastPage}); + } + } + + function gotoPrev() { + var state = getNavState(); + if (state.canGotoPrev) { + dataView.setPagingOptions({pageNum:state.pagingInfo.pageNum - 1}); + } + } + + function gotoNext() { + var state = getNavState(); + if (state.canGotoNext) { + dataView.setPagingOptions({pageNum:state.pagingInfo.pageNum + 1}); + } + } - function constructPagerUI() - { - $container.empty(); - - var $nav = $("<span class='slick-pager-nav' />").appendTo($container); - var $settings = $("<span class='slick-pager-settings' />").appendTo($container); - $status = $("<span class='slick-pager-status' />").appendTo($container); - - $settings - .append("<span class='slick-pager-settings-expanded' style='display:none'>Show: <a data=0>All</a><a data='-1'>Auto</a><a data=25>25</a><a data=50>50</a><a data=100>100</a></span>"); - - $settings.find("a[data]").click(function(e) { - var pagesize = $(e.target).attr("data"); - if (pagesize != undefined) - { - if (pagesize == -1) - { - var vp = grid.getViewport(); - setPageSize(vp.bottom-vp.top); - } - else - setPageSize(parseInt(pagesize)); - } - }); - - var icon_prefix = "<span class='ui-state-default ui-corner-all ui-icon-container'><span class='ui-icon "; - var icon_suffix = "' /></span>"; - - $(icon_prefix + "ui-icon-lightbulb" + icon_suffix) - .click(function() { $(".slick-pager-settings-expanded").toggle() }) - .appendTo($settings); - - $(icon_prefix + "ui-icon-seek-first" + icon_suffix) - .click(gotoFirst) - .appendTo($nav); - - $(icon_prefix + "ui-icon-seek-prev" + icon_suffix) - .click(gotoPrev) - .appendTo($nav); - - $(icon_prefix + "ui-icon-seek-next" + icon_suffix) - .click(gotoNext) - .appendTo($nav); - - $(icon_prefix + "ui-icon-seek-end" + icon_suffix) - .click(gotoLast) - .appendTo($nav); - - $container.find(".ui-icon-container") - .hover(function() { - $(this).toggleClass("ui-state-hover"); - }); - - $container.children().wrapAll("<div class='slick-pager' />"); + function constructPagerUI() { + $container.empty(); + + var $nav = $("<span class='slick-pager-nav' />").appendTo($container); + var $settings = $("<span class='slick-pager-settings' />").appendTo($container); + $status = $("<span class='slick-pager-status' />").appendTo($container); + + $settings + .append("<span class='slick-pager-settings-expanded' style='display:none'>Show: <a data=0>All</a><a data='-1'>Auto</a><a data=25>25</a><a data=50>50</a><a data=100>100</a></span>"); + + $settings.find("a[data]").click(function (e) { + var pagesize = $(e.target).attr("data"); + if (pagesize != undefined) { + if (pagesize == -1) { + var vp = grid.getViewport(); + setPageSize(vp.bottom - vp.top); + } + else { + setPageSize(parseInt(pagesize)); + } } + }); + var icon_prefix = "<span class='ui-state-default ui-corner-all ui-icon-container'><span class='ui-icon "; + var icon_suffix = "' /></span>"; - function updatePager(pagingInfo) - { - var state = getNavState(); + $(icon_prefix + "ui-icon-lightbulb" + icon_suffix) + .click(function () { + $(".slick-pager-settings-expanded").toggle() + }) + .appendTo($settings); - $container.find(".slick-pager-nav span").removeClass("ui-state-disabled"); - if (!state.canGotoFirst) $container.find(".ui-icon-seek-first").addClass("ui-state-disabled"); - if (!state.canGotoLast) $container.find(".ui-icon-seek-end").addClass("ui-state-disabled"); - if (!state.canGotoNext) $container.find(".ui-icon-seek-next").addClass("ui-state-disabled"); - if (!state.canGotoPrev) $container.find(".ui-icon-seek-prev").addClass("ui-state-disabled"); + $(icon_prefix + "ui-icon-seek-first" + icon_suffix) + .click(gotoFirst) + .appendTo($nav); + $(icon_prefix + "ui-icon-seek-prev" + icon_suffix) + .click(gotoPrev) + .appendTo($nav); - if (pagingInfo.pageSize == 0) - $status.text("Showing all " + pagingInfo.totalRows + " rows"); - else - $status.text("Showing page " + (pagingInfo.pageNum+1) + " of " + (Math.floor(pagingInfo.totalRows/pagingInfo.pageSize)+1)); - } + $(icon_prefix + "ui-icon-seek-next" + icon_suffix) + .click(gotoNext) + .appendTo($nav); + $(icon_prefix + "ui-icon-seek-end" + icon_suffix) + .click(gotoLast) + .appendTo($nav); + $container.find(".ui-icon-container") + .hover(function () { + $(this).toggleClass("ui-state-hover"); + }); - init(); + $container.children().wrapAll("<div class='slick-pager' />"); } - // Slick.Controls.Pager - $.extend(true, window, { Slick: { Controls: { Pager: SlickGridPager }}}); + + function updatePager(pagingInfo) { + var state = getNavState(); + + $container.find(".slick-pager-nav span").removeClass("ui-state-disabled"); + if (!state.canGotoFirst) { + $container.find(".ui-icon-seek-first").addClass("ui-state-disabled"); + } + if (!state.canGotoLast) { + $container.find(".ui-icon-seek-end").addClass("ui-state-disabled"); + } + if (!state.canGotoNext) { + $container.find(".ui-icon-seek-next").addClass("ui-state-disabled"); + } + if (!state.canGotoPrev) { + $container.find(".ui-icon-seek-prev").addClass("ui-state-disabled"); + } + + + if (pagingInfo.pageSize == 0) { + $status.text("Showing all " + pagingInfo.totalRows + " rows"); + } + else { + $status.text("Showing page " + (pagingInfo.pageNum + 1) + " of " + (Math.floor(pagingInfo.totalRows / pagingInfo.pageSize) + 1)); + } + } + + init(); + } + + // Slick.Controls.Pager + $.extend(true, window, { Slick:{ Controls:{ Pager:SlickGridPager }}}); })(jQuery); diff --git a/examples/example-checkbox-row-select.html b/examples/example-checkbox-row-select.html index 99b78d1..e1d0124 100644 --- a/examples/example-checkbox-row-select.html +++ b/examples/example-checkbox-row-select.html @@ -1,31 +1,31 @@ <!DOCTYPE HTML> <html> <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .slick-cell-checkboxsel { - background: #f0f0f0; - border-right-color: silver; - border-right-style: solid; - } - </style> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .slick-cell-checkboxsel { + background: #f0f0f0; + border-right-color: silver; + border-right-style: solid; + } + </style> </head> <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>Checkbox row select column</li> - </ul> - </div> - </div> + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>Checkbox row select column</li> + </ul> + </div> +</div> <script src="../lib/firebugx.js"></script> @@ -45,46 +45,42 @@ <script src="../slick.grid.js"></script> <script> - var grid; - var data = []; - var options = { - editable: true, - enableCellNavigation: true, - asyncEditorLoading: false, - autoEdit: false - }; - var columns = []; + var grid; + var data = []; + var options = { + editable:true, + enableCellNavigation:true, + asyncEditorLoading:false, + autoEdit:false + }; + var columns = []; + $(function () { + for (var i = 0; i < 100; i++) { + var d = (data[i] = {}); + d[0] = "Row " + i; + } - $(function() - { - for (var i=0; i<100; i++) { - var d = (data[i] = {}); - d[0] = "Row " + i; - } + var checkboxSelector = new Slick.CheckboxSelectColumn({ + cssClass:"slick-cell-checkboxsel" + }); - var checkboxSelector = new Slick.CheckboxSelectColumn({ - cssClass: "slick-cell-checkboxsel" - }); + columns.push(checkboxSelector.getColumnDefinition()); - columns.push(checkboxSelector.getColumnDefinition()); + for (var i = 0; i < 5; i++) { + columns.push({ + id:i, + name:String.fromCharCode("A".charCodeAt(0) + i), + field:i, + width:100, + editor:TextCellEditor + }); + } - for (var i = 0; i < 5; i++) { - columns.push({ - id: i, - name: String.fromCharCode("A".charCodeAt(0) + i), - field: i, - width: 100, - editor: TextCellEditor - }); - } - - - grid = new Slick.Grid("#myGrid", data, columns, options); - grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow:false})); - grid.registerPlugin(checkboxSelector); - - }) + grid = new Slick.Grid("#myGrid", data, columns, options); + grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow:false})); + grid.registerPlugin(checkboxSelector); + }) </script> </body> </html> diff --git a/examples/example-colspan.html b/examples/example-colspan.html index c6a2c81..4f17f47 100644 --- a/examples/example-colspan.html +++ b/examples/example-colspan.html @@ -1,89 +1,89 @@ <!DOCTYPE HTML> <html> - <head> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div id="myGrid" style="width:600px;height:500px;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - <ul> - <li>column span</li> - </ul> - </td> - </tr> - </table> +<head> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div id="myGrid" style="width:600px;height:500px;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + <ul> + <li>column span</li> + </ul> + </td> + </tr> +</table> - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../plugins/slick.cellrangedecorator.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../slick.grid.js"></script> +<script src="../slick.core.js"></script> +<script src="../plugins/slick.cellrangedecorator.js"></script> +<script src="../plugins/slick.cellrangeselector.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../slick.grid.js"></script> - <script> - var grid; +<script> + var grid; - var columns = [ - {id:"title", name:"Title", field:"title"}, - {id:"duration", name:"Duration", field:"duration"}, - {id:"%", name:"% Complete", field:"percentComplete", selectable:false}, - {id:"start", name:"Start", field:"start"}, - {id:"finish", name:"Finish", field:"finish"}, - {id:"effort-driven", name:"Effort Driven", field:"effortDriven"} - ]; + var columns = [ + {id:"title", name:"Title", field:"title"}, + {id:"duration", name:"Duration", field:"duration"}, + {id:"%", name:"% Complete", field:"percentComplete", selectable:false}, + {id:"start", name:"Start", field:"start"}, + {id:"finish", name:"Finish", field:"finish"}, + {id:"effort-driven", name:"Effort Driven", field:"effortDriven"} + ]; - var options = { - enableCellNavigation: true, - enableColumnReorder: false - }; + var options = { + enableCellNavigation:true, + enableColumnReorder:false + }; - $(function() { - var data = []; - for (var i = 0; i < 10; i++) { - data[i] = { - title: "Task " + i, - duration: "5 days", - percentComplete: Math.round(Math.random() * 100), - start: "01/01/2009", - finish: "01/05/2009", - effortDriven: (i % 5 == 0) - }; - } + $(function () { + var data = []; + for (var i = 0; i < 10; i++) { + data[i] = { + title:"Task " + i, + duration:"5 days", + percentComplete:Math.round(Math.random() * 100), + start:"01/01/2009", + finish:"01/05/2009", + effortDriven:(i % 5 == 0) + }; + } - data.getItemMetadata = function (row) { - if (row % 2 === 1) { - return { - "columns": { - "duration": { - "colspan": 3 - } - } - }; - } - else { - return { - "columns": { - 0: { - "colspan": "*" - } - } - }; - } - }; + data.getItemMetadata = function (row) { + if (row % 2 === 1) { + return { + "columns":{ + "duration":{ + "colspan":3 + } + } + }; + } + else { + return { + "columns":{ + 0:{ + "colspan":"*" + } + } + }; + } + }; - grid = new Slick.Grid("#myGrid", data, columns, options); + grid = new Slick.Grid("#myGrid", data, columns, options); - grid.setSelectionModel(new Slick.CellSelectionModel()); - }) - </script> - </body> + grid.setSelectionModel(new Slick.CellSelectionModel()); + }) +</script> +</body> </html> diff --git a/examples/example-composite-editor-item-details.html b/examples/example-composite-editor-item-details.html index 4b07f8d..b27399c 100644 --- a/examples/example-composite-editor-item-details.html +++ b/examples/example-composite-editor-item-details.html @@ -1,233 +1,236 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example: CompositeEditor</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - - .item-details-form { - z-index: 10000; - display:inline-block; - border:1px solid black; - margin:8px; - padding:10px; - background: #efefef; - -moz-box-shadow: 0px 0px 15px black; - -webkit-box-shadow: 0px 0px 15px black; - box-shadow: 0px 0px 15px black; - - position:absolute; - top: 10px; - left: 150px; - } - - .item-details-form-buttons { - float: right; - } - - .item-details-row { - - } - - .item-details-label { - margin-left:10px; - margin-top:20px; - display:block; - font-weight:bold; - } - - .item-details-editor-container { - width:200px; - height:20px; - border:1px solid silver; - background:white; - display:block; - margin:10px; - margin-top:4px; - padding:0; - padding-left:4px; - padding-right:4px; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>using a CompositeEditor to implement detached item edit form</li> - </ul> - - <h2>Options:</h2> - <button onclick="openDetails()">Open Item Edit for active row</button> - </div> - </div> - - <script id="itemDetailsTemplate" type="text/x-jquery-tmpl"> - <div class='item-details-form'> - {{each columns}} - <div class='item-details-label'> - ${name} - </div> - <div class='item-details-editor-container' data-editorid='${id}'></div> - {{/each}} - - <hr/> - <div class='item-details-form-buttons'> - <button data-action='save'>Save</button> - <button data-action='cancel'>Cancel</button> - </div> - </div> - </script> - - - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - <script src="slick.compositeeditor.js"></script> - - <script> - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - var grid; - var data = []; - var columns = [ - {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, - {id:"desc", name:"Description", field:"description", width:100, editor:TextCellEditor}, - {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, - {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} - ]; - var options = { - editable: true, - enableAddRow: true, - enableCellNavigation: true, - asyncEditorLoading: false, - autoEdit: false - }; - - - - function openDetails() { - if (grid.getEditorLock().isActive() && !grid.getEditorLock().commitCurrentEdit()) { - return; +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example: CompositeEditor</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + + .item-details-form { + z-index: 10000; + display: inline-block; + border: 1px solid black; + margin: 8px; + padding: 10px; + background: #efefef; + -moz-box-shadow: 0px 0px 15px black; + -webkit-box-shadow: 0px 0px 15px black; + box-shadow: 0px 0px 15px black; + + position: absolute; + top: 10px; + left: 150px; + } + + .item-details-form-buttons { + float: right; + } + + .item-details-row { + + } + + .item-details-label { + margin-left: 10px; + margin-top: 20px; + display: block; + font-weight: bold; + } + + .item-details-editor-container { + width: 200px; + height: 20px; + border: 1px solid silver; + background: white; + display: block; + margin: 10px; + margin-top: 4px; + padding: 0; + padding-left: 4px; + padding-right: 4px; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>using a CompositeEditor to implement detached item edit form</li> + </ul> + + <h2>Options:</h2> + <button onclick="openDetails()">Open Item Edit for active row</button> + </div> +</div> + +<script id="itemDetailsTemplate" type="text/x-jquery-tmpl"> + <div class='item-details-form'> + {{each columns}} + <div class='item-details-label'> + ${name} + </div> + <div class='item-details-editor-container' data-editorid='${id}'></div> + {{/each}} + + <hr/> + <div class='item-details-form-buttons'> + <button data-action='save'>Save</button> + <button data-action='cancel'>Cancel</button> + </div> + </div> +</script> + + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../plugins/slick.cellrangeselector.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> +<script src="slick.compositeeditor.js"></script> + +<script> + function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } + } + + var grid; + var data = []; + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, + {id:"desc", name:"Description", field:"description", width:100, editor:TextCellEditor}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} + ]; + var options = { + editable:true, + enableAddRow:true, + enableCellNavigation:true, + asyncEditorLoading:false, + autoEdit:false + }; + + + function openDetails() { + if (grid.getEditorLock().isActive() && !grid.getEditorLock().commitCurrentEdit()) { + return; + } + + var $modal = $("<div class='item-details-form'></div>"); + + $modal = $("#itemDetailsTemplate") + .tmpl({ + context:grid.getDataItem(grid.getActiveCell().row), + columns:columns + }) + .appendTo("body"); + + $modal.keydown(function (e) { + if (e.which == $.ui.keyCode.ENTER) { + grid.getEditController().commitCurrentEdit(); + e.stopPropagation(); + e.preventDefault(); + } + else if (e.which == $.ui.keyCode.ESCAPE) { + grid.getEditController().cancelCurrentEdit(); + e.stopPropagation(); + e.preventDefault(); + } + }); + + $modal.find("[data-action=save]").click(function () { + grid.getEditController().commitCurrentEdit(); + }); + + $modal.find("[data-action=cancel]").click(function () { + grid.getEditController().cancelCurrentEdit(); + }); + + + var containers = $.map(columns, function (c) { + return $modal.find("[data-editorid=" + c.id + "]"); + }); + + var compositeEditor = new Slick.CompositeEditor( + columns, + containers, + { + destroy:function () { + $modal.remove(); + } } - - var $modal = $("<div class='item-details-form'></div>"); - - $modal = $("#itemDetailsTemplate") - .tmpl({ - context: grid.getDataItem(grid.getActiveCell().row), - columns: columns - }) - .appendTo("body"); - - $modal.keydown(function(e) { - if (e.which == $.ui.keyCode.ENTER) { - grid.getEditController().commitCurrentEdit(); - e.stopPropagation(); - e.preventDefault(); - } - else if (e.which == $.ui.keyCode.ESCAPE) { - grid.getEditController().cancelCurrentEdit(); - e.stopPropagation(); - e.preventDefault(); - } - }); - - $modal.find("[data-action=save]").click(function() { - grid.getEditController().commitCurrentEdit(); - }); - - $modal.find("[data-action=cancel]").click(function() { - grid.getEditController().cancelCurrentEdit(); - }); - - - var containers = $.map(columns, function(c) { return $modal.find("[data-editorid=" + c.id + "]"); }); - - var compositeEditor = new Slick.CompositeEditor( - columns, - containers, - { - destroy: function () { $modal.remove(); } - } - ); - - grid.editActiveCell(compositeEditor); + ); + + grid.editActiveCell(compositeEditor); + } + + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); + + d["title"] = "Task " + i; + d["description"] = "This is a sample task description.\n It can be multiline"; + d["duration"] = "5 days"; + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } + + grid = new Slick.Grid("#myGrid", data, columns, options); + + grid.onAddNewRow.subscribe(function (e, args) { + var item = args.item; + var column = args.column; + grid.invalidateRow(data.length); + data.push(item); + grid.updateRowCount(); + grid.render(); + }); + + + grid.onValidationError.subscribe(function (e, args) { + // handle validation errors originating from the CompositeEditor + if (args.editor && (args.editor instanceof Slick.CompositeEditor)) { + var err; + var idx = args.validationResults.errors.length; + while (idx--) { + err = args.validationResults.errors[idx]; + $(err.container).stop(true, true).effect("highlight", {color:"red"}); } + } + }); - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - - d["title"] = "Task " + i; - d["description"] = "This is a sample task description.\n It can be multiline"; - d["duration"] = "5 days"; - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - - grid.onAddNewRow.subscribe(function(e, args) { - var item = args.item; - var column = args.column; - grid.invalidateRow(data.length); - data.push(item); - grid.updateRowCount(); - grid.render(); - }); - - - grid.onValidationError.subscribe(function(e, args) { - // handle validation errors originating from the CompositeEditor - if (args.editor && (args.editor instanceof Slick.CompositeEditor)) { - var err; - var idx = args.validationResults.errors.length; - while (idx--) { - err = args.validationResults.errors[idx]; - $(err.container).stop(true,true).effect("highlight", {color:"red"}); - } - } - }); - - grid.setActiveCell(0, 0); - }) - </script> - </body> + grid.setActiveCell(0, 0); + }) +</script> +</body> </html> diff --git a/examples/example-custom-column-value-extractor.html b/examples/example-custom-column-value-extractor.html index 934a822..53f64bd 100644 --- a/examples/example-custom-column-value-extractor.html +++ b/examples/example-custom-column-value-extractor.html @@ -1,76 +1,76 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example: Custom column value extractor</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div id="myGrid" style="width:600px;height:500px;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - <ul> - <li>using <u>dataItemColumnValueExtractor</u> option to specify a custom column value extractor</li> - </ul> - </td> - </tr> - </table> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example: Custom column value extractor</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div id="myGrid" style="width:600px;height:500px;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + <ul> + <li>using <u>dataItemColumnValueExtractor</u> option to specify a custom column value extractor</li> + </ul> + </td> + </tr> +</table> - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> - <script> +<script> - var grid; + var grid; - var columns = [ - {id:"title", name:"Name", field:"name"}, - {id:"field1", name:"Field1", field:"values", fieldIdx: 0}, - {id:"field2", name:"Field2", field:"values", fieldIdx: 1}, - {id:"field3", name:"Field3", field:"values", fieldIdx: 2} - ]; + var columns = [ + {id:"title", name:"Name", field:"name"}, + {id:"field1", name:"Field1", field:"values", fieldIdx:0}, + {id:"field2", name:"Field2", field:"values", fieldIdx:1}, + {id:"field3", name:"Field3", field:"values", fieldIdx:2} + ]; - var options = { - enableCellNavigation: true, - enableColumnReorder: false, - dataItemColumnValueExtractor: getItemColumnValue - }; + var options = { + enableCellNavigation:true, + enableColumnReorder:false, + dataItemColumnValueExtractor:getItemColumnValue + }; - // Get the item column value using a custom 'fieldIdx' column param - function getItemColumnValue(item, column) { - var values = item[column.field]; - if (column.fieldIdx !== undefined) { - return values && values[column.fieldIdx]; - } else { - return values; - } - } + // Get the item column value using a custom 'fieldIdx' column param + function getItemColumnValue(item, column) { + var values = item[column.field]; + if (column.fieldIdx !== undefined) { + return values && values[column.fieldIdx]; + } else { + return values; + } + } - $(function() { - var data = []; - for (var i = 0; i < 500; i++) { - data[i] = { - name: "Item " + i, - values: [ - Math.round(Math.random() * 100), - Math.round(Math.random() * 100), - Math.round(Math.random() * 100) - ] - }; - } + $(function () { + var data = []; + for (var i = 0; i < 500; i++) { + data[i] = { + name:"Item " + i, + values:[ + Math.round(Math.random() * 100), + Math.round(Math.random() * 100), + Math.round(Math.random() * 100) + ] + }; + } - grid = new Slick.Grid("#myGrid", data, columns, options); - }) + grid = new Slick.Grid("#myGrid", data, columns, options); + }) - </script> - </body> +</script> +</body> </html> diff --git a/examples/example-grouping.html b/examples/example-grouping.html index 3e3c883..132f6a8 100644 --- a/examples/example-grouping.html +++ b/examples/example-grouping.html @@ -1,350 +1,361 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example: Grouping</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../controls/slick.columnpicker.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div class="grid-header" style="width:100%"> - <label>SlickGrid</label> - </div> - <div id="myGrid" style="width:100%;height:500px;"></div> - <div id="pager" style="width:100%;height:20px;"></div> - </div> - - <div class="options-panel"> - <b>Options:</b> - <hr/> - <div style="padding:6px;"> - <label style="width:200px;float:left">Show tasks with % at least: </label> - <div style="padding:2px;"> - <div style="width:100px;display:inline-block;" id="pcSlider"></div> - </div> - <br/> - <label style="width:200px;float:left">And title including:</label> - <input type=text id="txtSearch" style="width:100px;"> - <br/><br/> - <hr/> - <button onclick="clearGrouping()">Clear grouping</button><br/> - <button onclick="groupByDuration()">Group by duration & sort groups by value</button><br/> - <button onclick="groupByDurationOrderByCount()">Group by duration & sort groups by count</button><br/> - <button onclick="groupByDurationOrderByCountGroupCollapsed()">Group by duration & sort groups by count, group collapsed</button><br/> - <br/> - <button onclick="collapseAllGroups()">Collapse all groups</button><br/> - <button onclick="expandAllGroups()">Expand all groups</button><br/> - </div> - <hr/> - <h2>Demonstrates:</h2> - <ul> - <li> - Fully dynamic and interactive grouping with filtering and aggregates over <b>50'000</b> items<br> - Personally, this is just the coolest slickest thing I've ever seen done with DHTML grids! - </li> - </ul> - </div> - </div> - - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../plugins/slick.cellrangedecorator.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../slick.grid.js"></script> - <script src="../slick.groupitemmetadataprovider.js"></script> - <script src="../slick.dataview.js"></script> - <script src="../controls/slick.pager.js"></script> - <script src="../controls/slick.columnpicker.js"></script> - - <script> - var dataView; - var grid; - var data = []; - - var columns = [ - {id:"sel", name:"#", field:"num", cssClass:"cell-selection", width:40, resizable:false, selectable:false, focusable:false }, - {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", sortable:true, editor:TextCellEditor}, - {id:"duration", name:"Duration", field:"duration", sortable:true}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, formatter:GraphicalPercentCompleteCellFormatter, sortable:true, groupTotalsFormatter:avgTotalsFormatter}, - {id:"start", name:"Start", field:"start", minWidth:60, sortable:true}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, sortable:true}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, sortable:true} - ]; - - var options = { - enableCellNavigation: true, - editable: true - }; - - var sortcol = "title"; - var sortdir = 1; - var percentCompleteThreshold = 0; - var prevPercentCompleteThreshold = 0; - var searchString = ""; - - function avgTotalsFormatter(totals, columnDef) { - return "avg: " + Math.round(totals.avg[columnDef.field]) + "%"; +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example: Grouping</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <link rel="stylesheet" href="../controls/slick.columnpicker.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div class="grid-header" style="width:100%"> + <label>SlickGrid</label> + </div> + <div id="myGrid" style="width:100%;height:500px;"></div> + <div id="pager" style="width:100%;height:20px;"></div> + </div> + + <div class="options-panel"> + <b>Options:</b> + <hr/> + <div style="padding:6px;"> + <label style="width:200px;float:left">Show tasks with % at least: </label> + + <div style="padding:2px;"> + <div style="width:100px;display:inline-block;" id="pcSlider"></div> + </div> + <br/> + <label style="width:200px;float:left">And title including:</label> + <input type=text id="txtSearch" style="width:100px;"> + <br/><br/> + <hr/> + <button onclick="clearGrouping()">Clear grouping</button> + <br/> + <button onclick="groupByDuration()">Group by duration & sort groups by value</button> + <br/> + <button onclick="groupByDurationOrderByCount()">Group by duration & sort groups by count</button> + <br/> + <button onclick="groupByDurationOrderByCountGroupCollapsed()">Group by duration & sort groups by count, group + collapsed + </button> + <br/> + <br/> + <button onclick="collapseAllGroups()">Collapse all groups</button> + <br/> + <button onclick="expandAllGroups()">Expand all groups</button> + <br/> + </div> + <hr/> + <h2>Demonstrates:</h2> + <ul> + <li> + Fully dynamic and interactive grouping with filtering and aggregates over <b>50'000</b> items<br> + Personally, this is just the coolest slickest thing I've ever seen done with DHTML grids! + </li> + </ul> + </div> +</div> + + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../plugins/slick.cellrangedecorator.js"></script> +<script src="../plugins/slick.cellrangeselector.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../slick.grid.js"></script> +<script src="../slick.groupitemmetadataprovider.js"></script> +<script src="../slick.dataview.js"></script> +<script src="../controls/slick.pager.js"></script> +<script src="../controls/slick.columnpicker.js"></script> + +<script> +var dataView; +var grid; +var data = []; + +var columns = [ + {id:"sel", name:"#", field:"num", cssClass:"cell-selection", width:40, resizable:false, selectable:false, focusable:false }, + {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", sortable:true, editor:TextCellEditor}, + {id:"duration", name:"Duration", field:"duration", sortable:true}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, formatter:GraphicalPercentCompleteCellFormatter, sortable:true, groupTotalsFormatter:avgTotalsFormatter}, + {id:"start", name:"Start", field:"start", minWidth:60, sortable:true}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, sortable:true}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, sortable:true} +]; + +var options = { + enableCellNavigation:true, + editable:true +}; + +var sortcol = "title"; +var sortdir = 1; +var percentCompleteThreshold = 0; +var prevPercentCompleteThreshold = 0; +var searchString = ""; + +function avgTotalsFormatter(totals, columnDef) { + return "avg: " + Math.round(totals.avg[columnDef.field]) + "%"; +} + +function myFilter(item, args) { + return item["percentComplete"] >= args.percentComplete && + (args.searchString == "" || + item["title"].indexOf(args.searchString) == -1); +} + +function percentCompleteSort(a, b) { + return a["percentComplete"] - b["percentComplete"]; +} + +function comparer(a, b) { + var x = a[sortcol], y = b[sortcol]; + return (x == y ? 0 : (x > y ? 1 : -1)); +} + +function collapseAllGroups() { + dataView.beginUpdate(); + for (var i = 0; i < dataView.getGroups().length; i++) { + dataView.collapseGroup(dataView.getGroups()[i].value); + } + dataView.endUpdate(); +} + +function expandAllGroups() { + dataView.beginUpdate(); + for (var i = 0; i < dataView.getGroups().length; i++) { + dataView.expandGroup(dataView.getGroups()[i].value); + } + dataView.endUpdate(); +} + +function clearGrouping() { + dataView.groupBy(null); +} + +function groupByDuration() { + dataView.groupBy( + "duration", + function (g) { + return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; + }, + function (a, b) { + return a.value - b.value; + } + ); + dataView.setAggregators([ + new Slick.Data.Aggregators.Avg("percentComplete") + ], false); +} + +function groupByDurationOrderByCount() { + dataView.groupBy( + "duration", + function (g) { + return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; + }, + function (a, b) { + return a.count - b.count; + } + ); + dataView.setAggregators([ + new Slick.Data.Aggregators.Avg("percentComplete") + ], false); +} + +function groupByDurationOrderByCountGroupCollapsed() { + dataView.groupBy( + "duration", + function (g) { + return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; + }, + function (a, b) { + return a.count - b.count; + } + ); + dataView.setAggregators([ + new Slick.Data.Aggregators.Avg("percentComplete") + ], true); +} + +$(".grid-header .ui-icon") + .addClass("ui-state-default ui-corner-all") + .mouseover(function (e) { + $(e.target).addClass("ui-state-hover") + }) + .mouseout(function (e) { + $(e.target).removeClass("ui-state-hover") + }); + +$(function () { + // prepare the data + for (var i = 0; i < 50000; i++) { + var d = (data[i] = {}); + + d["id"] = "id_" + i; + d["num"] = i; + d["title"] = "Task " + i; + d["duration"] = Math.round(Math.random() * 14); + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } + + var groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); + dataView = new Slick.Data.DataView({ + groupItemMetadataProvider:groupItemMetadataProvider + }); + grid = new Slick.Grid("#myGrid", dataView, columns, options); + + // register the group item metadata provider to add expand/collapse group handlers + grid.registerPlugin(groupItemMetadataProvider); + grid.setSelectionModel(new Slick.CellSelectionModel()); + + var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); + var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options); + + + grid.onSort.subscribe(function (e, args) { + sortdir = args.sortAsc ? 1 : -1; + sortcol = args.sortCol.field; + + if ($.browser.msie && $.browser.version <= 8) { + // using temporary Object.prototype.toString override + // more limited and does lexicographic sort only by default, but can be much faster + + var percentCompleteValueFn = function () { + var val = this["percentComplete"]; + if (val < 10) { + return "00" + val; } - - function myFilter(item, args) { - return item["percentComplete"] >= args.percentComplete && - (args.searchString == "" || - item["title"].indexOf(args.searchString) == -1); - } - - function percentCompleteSort(a,b) { - return a["percentComplete"] - b["percentComplete"]; - } - - function comparer(a,b) { - var x = a[sortcol], y = b[sortcol]; - return (x == y ? 0 : (x > y ? 1 : -1)); - } - - function collapseAllGroups() { - dataView.beginUpdate(); - for (var i = 0; i < dataView.getGroups().length; i++) { - dataView.collapseGroup(dataView.getGroups()[i].value); - } - dataView.endUpdate(); + else if (val < 100) { + return "0" + val; } - - function expandAllGroups() { - dataView.beginUpdate(); - for (var i = 0; i < dataView.getGroups().length; i++) { - dataView.expandGroup(dataView.getGroups()[i].value); - } - dataView.endUpdate(); + else { + return val; } - - function clearGrouping() { - dataView.groupBy(null); - } - - function groupByDuration() { - dataView.groupBy( - "duration", - function (g) { - return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; - }, - function (a, b) { - return a.value - b.value; - } - ); - dataView.setAggregators([ - new Slick.Data.Aggregators.Avg("percentComplete") - ], false); - } - - function groupByDurationOrderByCount() { - dataView.groupBy( - "duration", - function (g) { - return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; - }, - function (a, b) { - return a.count - b.count; - } - ); - dataView.setAggregators([ - new Slick.Data.Aggregators.Avg("percentComplete") - ], false); - } - - function groupByDurationOrderByCountGroupCollapsed() { - dataView.groupBy( - "duration", - function (g) { - return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; - }, - function (a, b) { - return a.count - b.count; - } - ); - dataView.setAggregators([ - new Slick.Data.Aggregators.Avg("percentComplete") - ], true); - } - - $(".grid-header .ui-icon") - .addClass("ui-state-default ui-corner-all") - .mouseover(function(e) { - $(e.target).addClass("ui-state-hover") - }) - .mouseout(function(e) { - $(e.target).removeClass("ui-state-hover") - }); - - $(function() - { - // prepare the data - for (var i=0; i<50000; i++) { - var d = (data[i] = {}); - - d["id"] = "id_" + i; - d["num"] = i; - d["title"] = "Task " + i; - d["duration"] = Math.round(Math.random() * 14); - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - var groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); - dataView = new Slick.Data.DataView({ - groupItemMetadataProvider: groupItemMetadataProvider - }); - grid = new Slick.Grid("#myGrid", dataView, columns, options); - - // register the group item metadata provider to add expand/collapse group handlers - grid.registerPlugin(groupItemMetadataProvider); - grid.setSelectionModel(new Slick.CellSelectionModel()); - - var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); - var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options); - - - grid.onSort.subscribe(function(e, args) { - sortdir = args.sortAsc ? 1 : -1; - sortcol = args.sortCol.field; - - if ($.browser.msie && $.browser.version <= 8) { - // using temporary Object.prototype.toString override - // more limited and does lexicographic sort only by default, but can be much faster - - var percentCompleteValueFn = function() { - var val = this["percentComplete"]; - if (val < 10) - return "00" + val; - else if (val < 100) - return "0" + val; - else - return val; - }; - - // use numeric sort of % and lexicographic for everything else - dataView.fastSort((sortcol=="percentComplete")?percentCompleteValueFn:sortcol,args.sortAsc); - } - else { - // using native sort with comparer - // preferred method but can be very slow in IE with huge datasets - dataView.sort(comparer, args.sortAsc); - } - }); - - // wire up model events to drive the grid - dataView.onRowCountChanged.subscribe(function(e,args) { - grid.updateRowCount(); - grid.render(); - }); - - dataView.onRowsChanged.subscribe(function(e,args) { - grid.invalidateRows(args.rows); - grid.render(); - }); - - - var h_runfilters = null; - - // wire up the slider to apply the filter to the model - $("#pcSlider,#pcSlider2").slider({ - "range": "min", - "slide": function(event,ui) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - if (percentCompleteThreshold != ui.value) - { - window.clearTimeout(h_runfilters); - h_runfilters = window.setTimeout(filterAndUpdate, 10); - percentCompleteThreshold = ui.value; - } - } - }); - - - // wire up the search textbox to apply the filter to the model - $("#txtSearch,#txtSearch2").keyup(function(e) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - // clear on Esc - if (e.which == 27) - this.value = ""; - - searchString = this.value; - dataView.refresh(); - }); - - - function filterAndUpdate() { - var isNarrowing = percentCompleteThreshold > prevPercentCompleteThreshold; - var isExpanding = percentCompleteThreshold < prevPercentCompleteThreshold; - var renderedRange = grid.getRenderedRange(); - - dataView.setFilterArgs({ - percentComplete: percentCompleteThreshold, - searchString: searchString - }); - dataView.setRefreshHints({ - ignoreDiffsBefore:renderedRange.top, - ignoreDiffsAfter:renderedRange.bottom + 1, - isFilterNarrowing:isNarrowing, - isFilterExpanding:isExpanding - }); - dataView.refresh(); - - prevPercentCompleteThreshold = percentCompleteThreshold; - } - - // initialize the model after all the events have been hooked up - dataView.beginUpdate(); - dataView.setItems(data); - dataView.setFilter(myFilter); - dataView.setFilterArgs({ - percentComplete: percentCompleteThreshold, - searchString: searchString - }); - dataView.groupBy( - "duration", - function (g) { - return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; - }, - function (a, b) { - return a.value - b.value; - } - ); - dataView.setAggregators([ - new Slick.Data.Aggregators.Avg("percentComplete") - ], false); - dataView.collapseGroup(0); - dataView.endUpdate(); - - $("#gridContainer").resizable(); - }) - - </script> - - </body> + }; + + // use numeric sort of % and lexicographic for everything else + dataView.fastSort((sortcol == "percentComplete") ? percentCompleteValueFn : sortcol, args.sortAsc); + } + else { + // using native sort with comparer + // preferred method but can be very slow in IE with huge datasets + dataView.sort(comparer, args.sortAsc); + } + }); + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function (e, args) { + grid.updateRowCount(); + grid.render(); + }); + + dataView.onRowsChanged.subscribe(function (e, args) { + grid.invalidateRows(args.rows); + grid.render(); + }); + + + var h_runfilters = null; + + // wire up the slider to apply the filter to the model + $("#pcSlider,#pcSlider2").slider({ + "range":"min", + "slide":function (event, ui) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + if (percentCompleteThreshold != ui.value) { + window.clearTimeout(h_runfilters); + h_runfilters = window.setTimeout(filterAndUpdate, 10); + percentCompleteThreshold = ui.value; + } + } + }); + + + // wire up the search textbox to apply the filter to the model + $("#txtSearch,#txtSearch2").keyup(function (e) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + // clear on Esc + if (e.which == 27) { + this.value = ""; + } + + searchString = this.value; + dataView.refresh(); + }); + + + function filterAndUpdate() { + var isNarrowing = percentCompleteThreshold > prevPercentCompleteThreshold; + var isExpanding = percentCompleteThreshold < prevPercentCompleteThreshold; + var renderedRange = grid.getRenderedRange(); + + dataView.setFilterArgs({ + percentComplete:percentCompleteThreshold, + searchString:searchString + }); + dataView.setRefreshHints({ + ignoreDiffsBefore:renderedRange.top, + ignoreDiffsAfter:renderedRange.bottom + 1, + isFilterNarrowing:isNarrowing, + isFilterExpanding:isExpanding + }); + dataView.refresh(); + + prevPercentCompleteThreshold = percentCompleteThreshold; + } + + // initialize the model after all the events have been hooked up + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + dataView.setFilterArgs({ + percentComplete:percentCompleteThreshold, + searchString:searchString + }); + dataView.groupBy( + "duration", + function (g) { + return "Duration: " + g.value + " <span style='color:green'>(" + g.count + " items)</span>"; + }, + function (a, b) { + return a.value - b.value; + } + ); + dataView.setAggregators([ + new Slick.Data.Aggregators.Avg("percentComplete") + ], false); + dataView.collapseGroup(0); + dataView.endUpdate(); + + $("#gridContainer").resizable(); +}) + +</script> + +</body> </html> diff --git a/examples/example-header-row.html b/examples/example-header-row.html index 9f03a4f..ba3532a 100644 --- a/examples/example-header-row.html +++ b/examples/example-header-row.html @@ -1,139 +1,138 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .slick-headerrow-column { - background: #87ceeb; - } - - .slick-headerrow-column input { - margin: 0; - padding: 0; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>Using a fixed header row to implement column-level filters</li> - <li>Type numbers in textboxes to filter grid data</li> - </ul> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.dataview.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var dataView; - var grid; - var data = []; - var options = { - enableCellNavigation: true, - showHeaderRow: true - }; - var columns = []; - var columnFilters = {}; - - for (var i = 0; i < 10; i++) { - columns.push({ - id: i, - name: String.fromCharCode("A".charCodeAt(0) + i), - field: i, - width: 60 - }); +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .slick-headerrow-column { + background: #87ceeb; + } + + .slick-headerrow-column input { + margin: 0; + padding: 0; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>Using a fixed header row to implement column-level filters</li> + <li>Type numbers in textboxes to filter grid data</li> + </ul> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.dataview.js"></script> +<script src="../slick.grid.js"></script> + +<script> + var dataView; + var grid; + var data = []; + var options = { + enableCellNavigation:true, + showHeaderRow:true + }; + var columns = []; + var columnFilters = {}; + + for (var i = 0; i < 10; i++) { + columns.push({ + id:i, + name:String.fromCharCode("A".charCodeAt(0) + i), + field:i, + width:60 + }); + } + + + function updateHeaderRow() { + for (var i = 0; i < columns.length; i++) { + if (columns[i].id !== "selector") { + var header = grid.getHeaderRowColumn(columns[i].id); + $(header).empty(); + $("<input type='text'>") + .data("columnId", columns[i].id) + .width($(header).width() - 4) + .val(columnFilters[columns[i].id]) + .appendTo(header); + } + } + } + + function filter(item) { + for (var columnId in columnFilters) { + if (columnId !== undefined && columnFilters[columnId] !== "") { + var c = grid.getColumns()[grid.getColumnIndex(columnId)]; + if (item[c.field] != columnFilters[columnId]) { + return false; } - - - function updateHeaderRow() { - for (var i = 0; i < columns.length; i++) { - if (columns[i].id !== "selector") { - var header = grid.getHeaderRowColumn(columns[i].id); - $(header).empty(); - $("<input type='text'>") - .data("columnId", columns[i].id) - .width($(header).width() - 4) - .val(columnFilters[columns[i].id]) - .appendTo(header); - } - } - } - - function filter(item) { - for (var columnId in columnFilters) { - if (columnId !== undefined && columnFilters[columnId] !== "") { - var c = grid.getColumns()[grid.getColumnIndex(columnId)]; - if (item[c.field] != columnFilters[columnId]) { - return false; - } - } - } - return true; - } - - $(function() - { - for (var i=0; i<100; i++) { - var d = (data[i] = {}); - d["id"] = i; - for (var j=0; j<columns.length; j++) { - d[j] = Math.round(Math.random() * 10); - } - } - - dataView = new Slick.Data.DataView(); - grid = new Slick.Grid("#myGrid", dataView, columns, options); - - - dataView.onRowCountChanged.subscribe(function(e,args) { - grid.updateRowCount(); - grid.render(); - }); - - dataView.onRowsChanged.subscribe(function(e,args) { - grid.invalidateRows(args.rows); - grid.render(); - }); - - $(grid.getHeaderRow()).delegate(":input", "change keyup", function(e) { - columnFilters[$(this).data("columnId")] = $.trim($(this).val()); - dataView.refresh(); - }); - - - grid.onColumnsReordered.subscribe(function(e, args) { - updateHeaderRow(); - }); - - grid.onColumnsResized.subscribe(function(e, args) { - updateHeaderRow(); - }); - - dataView.beginUpdate(); - dataView.setItems(data); - dataView.setFilter(filter); - dataView.endUpdate(); - - updateHeaderRow(); - - }) - </script> - </body> + } + } + return true; + } + + $(function () { + for (var i = 0; i < 100; i++) { + var d = (data[i] = {}); + d["id"] = i; + for (var j = 0; j < columns.length; j++) { + d[j] = Math.round(Math.random() * 10); + } + } + + dataView = new Slick.Data.DataView(); + grid = new Slick.Grid("#myGrid", dataView, columns, options); + + + dataView.onRowCountChanged.subscribe(function (e, args) { + grid.updateRowCount(); + grid.render(); + }); + + dataView.onRowsChanged.subscribe(function (e, args) { + grid.invalidateRows(args.rows); + grid.render(); + }); + + $(grid.getHeaderRow()).delegate(":input", "change keyup", function (e) { + columnFilters[$(this).data("columnId")] = $.trim($(this).val()); + dataView.refresh(); + }); + + + grid.onColumnsReordered.subscribe(function (e, args) { + updateHeaderRow(); + }); + + grid.onColumnsResized.subscribe(function (e, args) { + updateHeaderRow(); + }); + + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(filter); + dataView.endUpdate(); + + updateHeaderRow(); + + }) +</script> +</body> </html> diff --git a/examples/example-optimizing-dataview.html b/examples/example-optimizing-dataview.html index 864c1d2..740781b 100644 --- a/examples/example-optimizing-dataview.html +++ b/examples/example-optimizing-dataview.html @@ -1,66 +1,67 @@ <!DOCTYPE HTML> <html> <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example: Optimizing DataView</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8"/> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" - charset="utf-8"/> - <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8"/> - <style> - .cell-title { - font-weight: bold; - } + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example: Optimizing DataView</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } - .cell-effort-driven { - text-align: center; - } + .cell-effort-driven { + text-align: center; + } - .cell-selection { - border-right-color: silver; - border-right-style: solid; - background: #f5f5f5; - color: gray; - text-align: right; - font-size: 10px; - } - </style> + .cell-selection { + border-right-color: silver; + border-right-style: solid; + background: #f5f5f5; + color: gray; + text-align: right; + font-size: 10px; + } + </style> </head> <body> <div style="position:relative"> - <div style="width:600px;"> - <div class="grid-header" style="width:100%"> - <label>SlickGrid</label> - </div> - <div id="myGrid" style="width:100%;height:500px;"></div> - <div id="pager" style="width:100%;height:20px;"></div> + <div style="width:600px;"> + <div class="grid-header" style="width:100%"> + <label>SlickGrid</label> </div> - - <div class="options-panel"> - <b>Search:</b> - <hr/> - <div style="padding:6px;"> - <label style="width:200px;float:left">Show tasks with % at least: </label> - <div style="padding:2px;"> - <div style="width:100px;display:inline-block;" id="pcSlider"></div> - </div> - </div> - <br/> - - <p> - This page demonstrates various techniques for optimizing DataView performance - for large client-side datasets. This page displays an interactive grid with - 500'000 rows with real-time filtering.<br/> - This is achieved by: - <ul> - <li>Using a batch filtering function to cut down on the cost of function calls.</li> - <li>Providing hints to indicate whether a filtering operation will result in - narrowing or expanding scope or whether the scope is unchanged.</li> - <li>Providing a range of rows for which onRowsChanged even should be fired.</li> - </ul> - </p> + <div id="myGrid" style="width:100%;height:500px;"></div> + <div id="pager" style="width:100%;height:20px;"></div> + </div> + + <div class="options-panel"> + <b>Search:</b> + <hr/> + <div style="padding:6px;"> + <label style="width:200px;float:left">Show tasks with % at least: </label> + + <div style="padding:2px;"> + <div style="width:100px;display:inline-block;" id="pcSlider"></div> + </div> </div> + <br/> + + <p> + This page demonstrates various techniques for optimizing DataView performance + for large client-side datasets. This page displays an interactive grid with + 500'000 rows with real-time filtering.<br/> + This is achieved by: + <ul> + <li>Using a batch filtering function to cut down on the cost of function calls.</li> + <li>Providing hints to indicate whether a filtering operation will result in + narrowing or expanding scope or whether the scope is unchanged. + </li> + <li>Providing a range of rows for which onRowsChanged even should be fired.</li> + </ul> + </p> + </div> </div> @@ -75,105 +76,105 @@ <script src="../controls/slick.pager.js"></script> <script> - var dataView; - var grid; - var data = []; - - var columns = [ - {id:"sel", name:"#", field:"num", behavior:"select", cssClass:"cell-selection", width:40, resizable:false, selectable:false }, - {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title"}, - {id:"duration", name:"Duration", field:"duration"}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter}, - {id:"start", name:"Start", field:"start", minWidth:60}, - {id:"finish", name:"Finish", field:"finish", minWidth:60}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter} - ]; - - var options = { - editable:false, - enableAddRow:false, - enableCellNavigation:true - }; - - var percentCompleteThreshold = 0; - var prevPercentCompleteThreshold = 0; - var searchString = ""; - var h_runfilters = null; - - function myFilter(item, args) { - return item["percentComplete"] >= args; + var dataView; + var grid; + var data = []; + + var columns = [ + {id:"sel", name:"#", field:"num", behavior:"select", cssClass:"cell-selection", width:40, resizable:false, selectable:false }, + {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title"}, + {id:"duration", name:"Duration", field:"duration"}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter}, + {id:"start", name:"Start", field:"start", minWidth:60}, + {id:"finish", name:"Finish", field:"finish", minWidth:60}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter} + ]; + + var options = { + editable:false, + enableAddRow:false, + enableCellNavigation:true + }; + + var percentCompleteThreshold = 0; + var prevPercentCompleteThreshold = 0; + var searchString = ""; + var h_runfilters = null; + + function myFilter(item, args) { + return item["percentComplete"] >= args; + } + + function DataItem(i) { + this.num = i; + this.id = "id_" + i; + this.percentComplete = Math.round(Math.random() * 100); + this.effortDriven = (i % 5 == 0); + this.start = "01/01/2009"; + this.finish = "01/05/2009"; + this.title = "Task " + i; + this.duration = "5 days"; + } + + + $(function () { + // prepare the data + for (var i = 0; i < 500000; i++) { + data[i] = new DataItem(i); } - function DataItem(i) { - this.num = i; - this.id = "id_" + i; - this.percentComplete = Math.round(Math.random() * 100); - this.effortDriven = (i % 5 == 0); - this.start = "01/01/2009"; - this.finish = "01/05/2009"; - this.title = "Task " + i; - this.duration = "5 days"; - } - - - $(function() { - // prepare the data - for (var i = 0; i < 500000; i++) { - data[i] = new DataItem(i); - } - - dataView = new Slick.Data.DataView(); - grid = new Slick.Grid("#myGrid", dataView, columns, options); - var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); - - // wire up model events to drive the grid - dataView.onRowCountChanged.subscribe(function(e, args) { - grid.updateRowCount(); - grid.render(); - }); - - dataView.onRowsChanged.subscribe(function(e, args) { - grid.invalidateRows(args.rows); - grid.render(); - }); - - - // wire up the slider to apply the filter to the model - $("#pcSlider").slider({ - "range":"min", - "slide":function(event, ui) { - if (percentCompleteThreshold != ui.value) { - window.clearTimeout(h_runfilters); - h_runfilters = window.setTimeout(filterAndUpdate, 10); - percentCompleteThreshold = ui.value; - } - } - }); - - function filterAndUpdate() { - var isNarrowing = percentCompleteThreshold > prevPercentCompleteThreshold; - var isExpanding = percentCompleteThreshold < prevPercentCompleteThreshold; - var renderedRange = grid.getRenderedRange(); - - dataView.setFilterArgs(percentCompleteThreshold); - dataView.setRefreshHints({ - ignoreDiffsBefore:renderedRange.top, - ignoreDiffsAfter:renderedRange.bottom + 1, - isFilterNarrowing:isNarrowing, - isFilterExpanding:isExpanding - }); - dataView.refresh(); - - prevPercentCompleteThreshold = percentCompleteThreshold; + dataView = new Slick.Data.DataView(); + grid = new Slick.Grid("#myGrid", dataView, columns, options); + var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function (e, args) { + grid.updateRowCount(); + grid.render(); + }); + + dataView.onRowsChanged.subscribe(function (e, args) { + grid.invalidateRows(args.rows); + grid.render(); + }); + + + // wire up the slider to apply the filter to the model + $("#pcSlider").slider({ + "range": "min", + "slide": function (event, ui) { + if (percentCompleteThreshold != ui.value) { + window.clearTimeout(h_runfilters); + h_runfilters = window.setTimeout(filterAndUpdate, 10); + percentCompleteThreshold = ui.value; } + } + }); + + function filterAndUpdate() { + var isNarrowing = percentCompleteThreshold > prevPercentCompleteThreshold; + var isExpanding = percentCompleteThreshold < prevPercentCompleteThreshold; + var renderedRange = grid.getRenderedRange(); + + dataView.setFilterArgs(percentCompleteThreshold); + dataView.setRefreshHints({ + ignoreDiffsBefore:renderedRange.top, + ignoreDiffsAfter:renderedRange.bottom + 1, + isFilterNarrowing:isNarrowing, + isFilterExpanding:isExpanding + }); + dataView.refresh(); + + prevPercentCompleteThreshold = percentCompleteThreshold; + } - // initialize the model after all the events have been hooked up - dataView.beginUpdate(); - dataView.setItems(data); - dataView.setFilter(myFilter); - dataView.setFilterArgs(0); - dataView.endUpdate(); - }) + // initialize the model after all the events have been hooked up + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + dataView.setFilterArgs(0); + dataView.endUpdate(); + }) </script> </body> </html> diff --git a/examples/example-spreadsheet.html b/examples/example-spreadsheet.html index 01f51a0..d1054b2 100644 --- a/examples/example-spreadsheet.html +++ b/examples/example-spreadsheet.html @@ -1,169 +1,169 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 3: Editing</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .slick-cell.copied { - background: blue; - background: rgba(0,0,255,0.2); - -webkit-transition: 0.5s background; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>Select a range of cells with a mouse</li> - <li>Use Ctrl-C and Ctrl-V keyboard shortcuts to cut and paste cells</li> - <li>Use Esc to cancel a copy and paste operation</li> - <li>Edit the cell and select a cell range to paste the range</li> - </ul> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../plugins/slick.autotooltips.js"></script> - <script src="../plugins/slick.cellrangedecorator.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellcopymanager.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var grid; - var data = []; - var options = { - editable: true, - enableAddRow: true, - enableCellNavigation: true, - asyncEditorLoading: false, - autoEdit: false - }; - - var columns = [{ - id: "selector", - name: "", - field: "num", - width: 30 - }]; - - for (var i = 0; i < 26; i++) { - columns.push({ - id: i, - name: String.fromCharCode("A".charCodeAt(0) + i), - field: i, - width: 60, - editor: FormulaEditor - }); +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 3: Editing</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .slick-cell.copied { + background: blue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: 0.5s background; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>Select a range of cells with a mouse</li> + <li>Use Ctrl-C and Ctrl-V keyboard shortcuts to cut and paste cells</li> + <li>Use Esc to cancel a copy and paste operation</li> + <li>Edit the cell and select a cell range to paste the range</li> + </ul> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../plugins/slick.autotooltips.js"></script> +<script src="../plugins/slick.cellrangedecorator.js"></script> +<script src="../plugins/slick.cellrangeselector.js"></script> +<script src="../plugins/slick.cellcopymanager.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> + var grid; + var data = []; + var options = { + editable:true, + enableAddRow:true, + enableCellNavigation:true, + asyncEditorLoading:false, + autoEdit:false + }; + + var columns = [ + { + id:"selector", + name:"", + field:"num", + width:30 + } + ]; + + for (var i = 0; i < 26; i++) { + columns.push({ + id:i, + name:String.fromCharCode("A".charCodeAt(0) + i), + field:i, + width:60, + editor:FormulaEditor + }); + } + + /*** + * A proof-of-concept cell editor with Excel-like range selection and insertion. + */ + function FormulaEditor(args) { + var _self = this; + var _editor = new TextCellEditor(args); + var _selector; + + $.extend(this, _editor); + + function init() { + // register a plugin to select a range and append it to the textbox + // since events are fired in reverse order (most recently added are executed first), + // this will override other plugins like moverows or selection model and will + // not require the grid to not be in the edit mode + _selector = new Slick.CellRangeSelector(); + _selector.onCellRangeSelected.subscribe(_self.handleCellRangeSelected); + args.grid.registerPlugin(_selector); + } + + this.destroy = function () { + _selector.onCellRangeSelected.unsubscribe(_self.handleCellRangeSelected); + grid.unregisterPlugin(_selector); + _editor.destroy(); + }; + + this.handleCellRangeSelected = function (e, args) { + _editor.setValue( + _editor.getValue() + + grid.getColumns()[args.range.fromCell].name + + args.range.fromRow + + ":" + + grid.getColumns()[args.range.toCell].name + + args.range.toRow + ); + }; + + + init(); + } + + + $(function () { + for (var i = 0; i < 100; i++) { + var d = (data[i] = {}); + d["num"] = i; + } + + grid = new Slick.Grid("#myGrid", data, columns, options); + + grid.setSelectionModel(new Slick.CellSelectionModel()); + grid.registerPlugin(new Slick.AutoTooltips()); + + // set keyboard focus on the grid + grid.getCanvasNode().focus(); + + var copyManager = new Slick.CellCopyManager(); + grid.registerPlugin(copyManager); + + copyManager.onPasteCells.subscribe(function (e, args) { + if (args.from.length !== 1 || args.to.length !== 1) { + throw "This implementation only supports single range copy and paste operations"; + } + + var from = args.from[0]; + var to = args.to[0]; + var val; + for (var i = 0; i <= from.toRow - from.fromRow; i++) { + for (var j = 0; j <= from.toCell - from.fromCell; j++) { + if (i <= to.toRow - to.fromRow && j <= to.toCell - to.fromCell) { + val = data[from.fromRow + i][columns[from.fromCell + j].field]; + data[to.fromRow + i][columns[to.fromCell + j].field] = val; + grid.invalidateRow(to.fromRow + i); + } } - - /*** - * A proof-of-concept cell editor with Excel-like range selection and insertion. - */ - function FormulaEditor(args) { - var _self = this; - var _editor = new TextCellEditor(args); - var _selector; - - $.extend(this, _editor); - - function init() { - // register a plugin to select a range and append it to the textbox - // since events are fired in reverse order (most recently added are executed first), - // this will override other plugins like moverows or selection model and will - // not require the grid to not be in the edit mode - _selector = new Slick.CellRangeSelector(); - _selector.onCellRangeSelected.subscribe(_self.handleCellRangeSelected); - args.grid.registerPlugin(_selector); - } - - this.destroy = function() { - _selector.onCellRangeSelected.unsubscribe(_self.handleCellRangeSelected); - grid.unregisterPlugin(_selector); - _editor.destroy(); - }; - - this.handleCellRangeSelected = function(e, args) { - _editor.setValue( - _editor.getValue() + - grid.getColumns()[args.range.fromCell].name + - args.range.fromRow + - ":" + - grid.getColumns()[args.range.toCell].name + - args.range.toRow - ); - }; - - - init(); - } - - - - $(function() - { - for (var i=0; i<100; i++) { - var d = (data[i] = {}); - d["num"] = i; - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - - grid.setSelectionModel(new Slick.CellSelectionModel()); - grid.registerPlugin(new Slick.AutoTooltips()); - - // set keyboard focus on the grid - grid.getCanvasNode().focus(); - - var copyManager = new Slick.CellCopyManager(); - grid.registerPlugin(copyManager); - - copyManager.onPasteCells.subscribe(function(e,args) { - if (args.from.length !== 1 || args.to.length !== 1) { - throw "This implementation only supports single range copy and paste operations"; - } - - var from = args.from[0]; - var to = args.to[0]; - var val; - for (var i = 0; i <= from.toRow - from.fromRow; i++) { - for (var j = 0; j <= from.toCell - from.fromCell; j++) { - if (i <= to.toRow - to.fromRow && j <= to.toCell - to.fromCell) { - val = data[from.fromRow + i][columns[from.fromCell + j].field]; - data[to.fromRow + i][columns[to.fromCell + j].field] = val; - grid.invalidateRow(to.fromRow + i); - } - } - } - grid.render(); - }); - - grid.onAddNewRow.subscribe(function(e, args) { - var item = args.item; - var column = args.column; - grid.invalidateRow(data.length); - data.push(item); - grid.updateRowCount(); - grid.render(); - }); - }) - </script> - </body> + } + grid.render(); + }); + + grid.onAddNewRow.subscribe(function (e, args) { + var item = args.item; + var column = args.column; + grid.invalidateRow(data.length); + data.push(item); + grid.updateRowCount(); + grid.render(); + }); + }) +</script> +</body> </html> diff --git a/examples/example1-simple.html b/examples/example1-simple.html index 768f4f0..dfa7697 100644 --- a/examples/example1-simple.html +++ b/examples/example1-simple.html @@ -1,69 +1,68 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 1: Basic grid</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div id="myGrid" style="width:600px;height:500px;display:none;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - <ul> - <li>basic grid with minimal configuration</li> - </ul> - </td> - </tr> - </table> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 1: Basic grid</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div id="myGrid" style="width:600px;height:500px;display:none;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + <ul> + <li>basic grid with minimal configuration</li> + </ul> + </td> + </tr> +</table> - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> - <script> +<script> - var grid; + var grid; - var columns = [ - {id:"title", name:"Title", field:"title"}, - {id:"duration", name:"Duration", field:"duration"}, - {id:"%", name:"% Complete", field:"percentComplete"}, - {id:"start", name:"Start", field:"start"}, - {id:"finish", name:"Finish", field:"finish"}, - {id:"effort-driven", name:"Effort Driven", field:"effortDriven"} - ]; + var columns = [ + {id:"title", name:"Title", field:"title"}, + {id:"duration", name:"Duration", field:"duration"}, + {id:"%", name:"% Complete", field:"percentComplete"}, + {id:"start", name:"Start", field:"start"}, + {id:"finish", name:"Finish", field:"finish"}, + {id:"effort-driven", name:"Effort Driven", field:"effortDriven"} + ]; - var options = { - enableCellNavigation: true, - enableColumnReorder: false - }; + var options = { + enableCellNavigation:true, + enableColumnReorder:false + }; - $(function() { - var data = []; - for (var i = 0; i < 500; i++) { - data[i] = { - title: "Task " + i, - duration: "5 days", - percentComplete: Math.round(Math.random() * 100), - start: "01/01/2009", - finish: "01/05/2009", - effortDriven: (i % 5 == 0) - }; - } + $(function () { + var data = []; + for (var i = 0; i < 500; i++) { + data[i] = { + title: "Task " + i, + duration: "5 days", + percentComplete: Math.round(Math.random() * 100), + start: "01/01/2009", + finish: "01/05/2009", + effortDriven: (i % 5 == 0) + }; + } - grid = new Slick.Grid("#myGrid", data, columns, options); + grid = new Slick.Grid("#myGrid", data, columns, options); - $("#myGrid").show(); - }) - - </script> - </body> + $("#myGrid").show(); + }) +</script> +</body> </html> diff --git a/examples/example10-async-post-render.html b/examples/example10-async-post-render.html index 74b4a05..0b275e0 100644 --- a/examples/example10-async-post-render.html +++ b/examples/example10-async-post-render.html @@ -1,126 +1,134 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 10: Async post render</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - - .description * { - font-size: 11pt; - } - </style> - </head> - <body> - <div style="width:600px;float:left;"> - <div class="grid-header" style="width:100%"> - <label>Scores:</label> - </div> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div style="margin-left:650px;margin-top:40px;" class="description"> - <h2>Demonstrates:</h2> - <p> - With SlickGrid, you can still have rich, complex cells rendered against the actual DOM nodes while still preserving the speed and responsiveness. - This is achieved through async background post-rendering. - SlickGrid exposes a <u>asyncPostRender</u> property on a column which you can use to set your own function that will manipulate the cell DOM node directly. - The event is fired one by one for all visible rows in the viewport on a timer so it doesn't impact the UI responsiveness. - You should still make sure that post-processing one row doesn't take too long though. - SlickGrid will figure out what and when needs to be updated for you. - </p> - <p> - The example below is a list of 500 rows with a title and 5 integer cells followed by graphical representation of these integers. - The graph is drawn using a CANVAS element in the background. - The grid is editable, so you can edit the numbers and see the changes reflected (almost) immediately in the graph. - The graph cell behaves just like an ordinary cell and can be resized/reordered. - The graphs themselves are created using the excellent <a href="http://www.omnipotent.net/jquery.sparkline/" target="_blank">jQuery Sparklines</a> library. - </p> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../lib/jquery.sparkline.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - function waitingFormatter(value) { - return "wait..."; - } - - function renderSparkline(cellNode, row, dataContext, colDef) { - var vals = [ - dataContext["n1"], - dataContext["n2"], - dataContext["n3"], - dataContext["n4"], - dataContext["n5"] - ]; - - $(cellNode).empty().sparkline(vals, {width:"100%"}); - } - - var grid; - - var data = []; - - var columns = [ - {id:"title", name:"Title", field:"title", sortable:false, width:120, cssClass:"cell-title"}, - {id:"n1", name:"1", field:"n1", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, - {id:"n2", name:"2", field:"n2", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, - {id:"n3", name:"3", field:"n3", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, - {id:"n4", name:"4", field:"n4", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, - {id:"n5", name:"5", field:"n5", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, - {id:"chart", name:"Chart", sortable:false, width:60, formatter:waitingFormatter, rerenderOnResize:true, asyncPostRender:renderSparkline} - ]; - - var options = { - editable: true, - enableAddRow: false, - enableCellNavigation: true, - asyncEditorLoading: false, - enableAsyncPostRender: true - }; - - - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - - d["title"] = "Record " + i; - d["n1"] = Math.round(Math.random() * 10); - d["n2"] = Math.round(Math.random() * 10); - d["n3"] = Math.round(Math.random() * 10); - d["n4"] = Math.round(Math.random() * 10); - d["n5"] = Math.round(Math.random() * 10); - } - - - grid = new Slick.Grid("#myGrid", data, columns, options); - }) - </script> - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 10: Async post render</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + + .description * { + font-size: 11pt; + } + </style> +</head> +<body> +<div style="width:600px;float:left;"> + <div class="grid-header" style="width:100%"> + <label>Scores:</label> + </div> + <div id="myGrid" style="width:100%;height:500px;"></div> +</div> + +<div style="margin-left:650px;margin-top:40px;" class="description"> + <h2>Demonstrates:</h2> + + <p> + With SlickGrid, you can still have rich, complex cells rendered against the actual DOM nodes while still preserving + the speed and responsiveness. + This is achieved through async background post-rendering. + SlickGrid exposes a <u>asyncPostRender</u> property on a column which you can use to set your own function that will + manipulate the cell DOM node directly. + The event is fired one by one for all visible rows in the viewport on a timer so it doesn't impact the UI + responsiveness. + You should still make sure that post-processing one row doesn't take too long though. + SlickGrid will figure out what and when needs to be updated for you. + </p> + + <p> + The example below is a list of 500 rows with a title and 5 integer cells followed by graphical representation of + these integers. + The graph is drawn using a CANVAS element in the background. + The grid is editable, so you can edit the numbers and see the changes reflected (almost) immediately in the graph. + The graph cell behaves just like an ordinary cell and can be resized/reordered. + The graphs themselves are created using the excellent <a href="http://www.omnipotent.net/jquery.sparkline/" + target="_blank">jQuery Sparklines</a> library. + </p> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery.sparkline.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> + function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } + } + + function waitingFormatter(value) { + return "wait..."; + } + + function renderSparkline(cellNode, row, dataContext, colDef) { + var vals = [ + dataContext["n1"], + dataContext["n2"], + dataContext["n3"], + dataContext["n4"], + dataContext["n5"] + ]; + + $(cellNode).empty().sparkline(vals, {width:"100%"}); + } + + var grid; + + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", sortable:false, width:120, cssClass:"cell-title"}, + {id:"n1", name:"1", field:"n1", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, + {id:"n2", name:"2", field:"n2", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, + {id:"n3", name:"3", field:"n3", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, + {id:"n4", name:"4", field:"n4", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, + {id:"n5", name:"5", field:"n5", sortable:false, editor:IntegerCellEditor, width:40, validator:requiredFieldValidator}, + {id:"chart", name:"Chart", sortable:false, width:60, formatter:waitingFormatter, rerenderOnResize:true, asyncPostRender:renderSparkline} + ]; + + var options = { + editable:true, + enableAddRow:false, + enableCellNavigation:true, + asyncEditorLoading:false, + enableAsyncPostRender:true + }; + + + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); + + d["title"] = "Record " + i; + d["n1"] = Math.round(Math.random() * 10); + d["n2"] = Math.round(Math.random() * 10); + d["n3"] = Math.round(Math.random() * 10); + d["n4"] = Math.round(Math.random() * 10); + d["n5"] = Math.round(Math.random() * 10); + } + + + grid = new Slick.Grid("#myGrid", data, columns, options); + }) +</script> +</body> </html> diff --git a/examples/example11-autoheight.html b/examples/example11-autoheight.html index 885d60e..072bd60 100644 --- a/examples/example11-autoheight.html +++ b/examples/example11-autoheight.html @@ -1,65 +1,84 @@ <!DOCTYPE html> <html lang="en"> - <head> - <meta http-equiv="X-UA-Compatible" content="IE=edge" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>SlickGrid example 11: No vertical scrolling (autoHeight)</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - html,body { margin:0; padding:0; overflow:auto; } - body { font:11px Helvetica,Arial,sans-serif; } - #container { margin:10px; border:solid 1px gray; width:480px; background:white;} - #description { position:fixed; top:30px; right:30px; width:25em; } - </style> - </head> - <body> - <div id="container"></div> - <div id="description"> - <h2>Demonstrates:</h2> - <ul> - <li>autoHeight:true grid option</li> - </ul> - </div> +<head> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <title>SlickGrid example 11: No vertical scrolling (autoHeight)</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + html, body { + margin: 0; + padding: 0; + overflow: auto; + } - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> + body { + font: 11px Helvetica, Arial, sans-serif; + } - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> + #container { + margin: 10px; + border: solid 1px gray; + width: 480px; + background: white; + } - <script> - var grid, - data = [], - columns = [ - { id:"title", name:"Title", field:"title" }, - { id:"duration", name:"Duration", field:"duration" }, - { id:"%", name:"% Complete", field:"percentComplete" }, - { id:"start", name:"Start", field:"start" }, - { id:"finish", name:"Finish", field:"finish" }, - { id:"effort-driven", name:"Effort Driven", field:"effortDriven" } - ], - options = { - editable: false, - enableAddRow: false, - enableCellNavigation: false, - autoHeight: true - }; + #description { + position: fixed; + top: 30px; + right: 30px; + width: 25em; + } + </style> +</head> +<body> +<div id="container"></div> +<div id="description"> + <h2>Demonstrates:</h2> + <ul> + <li>autoHeight:true grid option</li> + </ul> +</div> - for (var i = 100; i-- > 0;) { - data[i] = { - title: "Task " + i, - duration: "5 days", - percentComplete: Math.round(Math.random() * 100), - start: "01/01/2009", - finish: "01/05/2009", - effortDriven: (i % 5 == 0) - }; - } +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - grid = new Slick.Grid("#container", data, columns, options); - </script> +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> - </body> +<script> + var grid, + data = [], + columns = [ + { id:"title", name:"Title", field:"title" }, + { id:"duration", name:"Duration", field:"duration" }, + { id:"%", name:"% Complete", field:"percentComplete" }, + { id:"start", name:"Start", field:"start" }, + { id:"finish", name:"Finish", field:"finish" }, + { id:"effort-driven", name:"Effort Driven", field:"effortDriven" } + ], + options = { + editable:false, + enableAddRow:false, + enableCellNavigation:false, + autoHeight:true + }; + + for (var i = 100; i-- > 0;) { + data[i] = { + title:"Task " + i, + duration:"5 days", + percentComplete:Math.round(Math.random() * 100), + start:"01/01/2009", + finish:"01/05/2009", + effortDriven:(i % 5 == 0) + }; + } + + grid = new Slick.Grid("#container", data, columns, options); +</script> + +</body> </html>
\ No newline at end of file diff --git a/examples/example12-fillbrowser.html b/examples/example12-fillbrowser.html index 5c728eb..464cda6 100644 --- a/examples/example12-fillbrowser.html +++ b/examples/example12-fillbrowser.html @@ -1,86 +1,113 @@ <!DOCTYPE html> <html lang="en"> - <head> - <meta http-equiv="X-UA-Compatible" content="IE=edge" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>SlickGrid example 12: Fill browser</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - html,body { margin:0; padding:0; background-color:White; overflow:auto; } - body { font:11px Helvetica,Arial,sans-serif; } - #container { position:absolute; top:0; left:0; right:0; bottom:0; } - #description { position:fixed; top:30px; right:30px; width:25em; background:beige; border:solid 1px gray; z-index:1000; } - #description h2 { padding-left:0.5em; } - </style> - </head> - <body> - <div id="container"></div> - <div id="description"> - <h2>Demonstrates:</h2> - <ul> - <li>Grid filling browser window completely (using absolute positioning)</li> - <li>Grid resizing when browser window changes size</li> - <li>Overall performance of the grid when displaying large tabular data (17 columns x 10,000 rows)</li> - </ul> - </div> +<head> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <title>SlickGrid example 12: Fill browser</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + html, body { + margin: 0; + padding: 0; + background-color: White; + overflow: auto; + } - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> + body { + font: 11px Helvetica, Arial, sans-serif; + } - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> - <script> - var grid, - data = [], - columns = [ - { id:"title", name:"Title", field:"title", width:120 }, - { id:"duration", name:"Duration", field:"duration", width:120 }, - { id:"%", name:"% Complete", field:"percentComplete", width:120 }, - { id:"start", name:"Start", field:"start", width:120 }, - { id:"finish", name:"Finish", field:"finish", width:120 }, - { id:"effort-driven", name:"Effort Driven", field:"effortDriven", width:120 }, - { id:"c7", name:"C7", field:"c7", width:120 }, - { id:"c8", name:"C8", field:"c8", width:120 }, - { id:"c9", name:"C9", field:"c9", width:120 }, - { id:"c10", name:"C10", field:"c10", width:120 }, - { id:"c11", name:"C11", field:"c11", width:120 }, - { id:"c12", name:"C12", field:"c12", width:120 }, - { id:"c13", name:"C13", field:"c13", width:120 }, - { id:"c14", name:"C14", field:"c14", width:120 }, - { id:"c15", name:"C15", field:"c15", width:120 }, - { id:"c16", name:"C16", field:"c16", width:120 }, - { id:"c17", name:"C17", field:"c17", width:120 } - ], - options = { - enableCellNavigation: false, - enableColumnReorder: false - }; + #container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } - for (var i = 10000; i-- > 0;) { - data[i] = { - title: "Task " + i, - duration: "5 days", - percentComplete: Math.round(Math.random() * 100), - start: "01/01/2009", - finish: "01/05/2009", - effortDriven: (i % 5 == 0), - c7: "C7-" + i, - c8: "C8-" + i, - c9: "C9-" + i, - c10: "C10-" + i, - c11: "C11-" + i, - c12: "C12-" + i, - c13: "C13-" + i, - c14: "C14-" + i, - c15: "C15-" + i, - c16: "C16-" + i, - c17: "C17-" + i - }; - } + #description { + position: fixed; + top: 30px; + right: 30px; + width: 25em; + background: beige; + border: solid 1px gray; + z-index: 1000; + } - grid = new Slick.Grid("#container", data, columns, options); - </script> + #description h2 { + padding-left: 0.5em; + } + </style> +</head> +<body> +<div id="container"></div> +<div id="description"> + <h2>Demonstrates:</h2> + <ul> + <li>Grid filling browser window completely (using absolute positioning)</li> + <li>Grid resizing when browser window changes size</li> + <li>Overall performance of the grid when displaying large tabular data (17 columns x 10,000 rows)</li> + </ul> +</div> - </body> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> +<script> + var grid, + data = [], + columns = [ + { id:"title", name:"Title", field:"title", width:120 }, + { id:"duration", name:"Duration", field:"duration", width:120 }, + { id:"%", name:"% Complete", field:"percentComplete", width:120 }, + { id:"start", name:"Start", field:"start", width:120 }, + { id:"finish", name:"Finish", field:"finish", width:120 }, + { id:"effort-driven", name:"Effort Driven", field:"effortDriven", width:120 }, + { id:"c7", name:"C7", field:"c7", width:120 }, + { id:"c8", name:"C8", field:"c8", width:120 }, + { id:"c9", name:"C9", field:"c9", width:120 }, + { id:"c10", name:"C10", field:"c10", width:120 }, + { id:"c11", name:"C11", field:"c11", width:120 }, + { id:"c12", name:"C12", field:"c12", width:120 }, + { id:"c13", name:"C13", field:"c13", width:120 }, + { id:"c14", name:"C14", field:"c14", width:120 }, + { id:"c15", name:"C15", field:"c15", width:120 }, + { id:"c16", name:"C16", field:"c16", width:120 }, + { id:"c17", name:"C17", field:"c17", width:120 } + ], + options = { + enableCellNavigation:false, + enableColumnReorder:false + }; + + for (var i = 10000; i-- > 0;) { + data[i] = { + title:"Task " + i, + duration:"5 days", + percentComplete:Math.round(Math.random() * 100), + start:"01/01/2009", + finish:"01/05/2009", + effortDriven:(i % 5 == 0), + c7:"C7-" + i, + c8:"C8-" + i, + c9:"C9-" + i, + c10:"C10-" + i, + c11:"C11-" + i, + c12:"C12-" + i, + c13:"C13-" + i, + c14:"C14-" + i, + c15:"C15-" + i, + c16:"C16-" + i, + c17:"C17-" + i + }; + } + + grid = new Slick.Grid("#container", data, columns, options); +</script> + +</body> </html>
\ No newline at end of file diff --git a/examples/example13-getItem-sorting.html b/examples/example13-getItem-sorting.html index 1fb2656..bf693f8 100644 --- a/examples/example13-getItem-sorting.html +++ b/examples/example13-getItem-sorting.html @@ -1,97 +1,124 @@ <!DOCTYPE html> <html lang="en"> - <head> - <meta http-equiv="X-UA-Compatible" content="IE=edge" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>SlickGrid example 13: Indexed Sorting using Functional Data Provider</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - html,body { margin:0; padding:0; background-color:White; overflow:auto; } - body { font:11px Helvetica,Arial,sans-serif; } - #container { position:absolute; top:0; left:0; right:0; bottom:0; } - #description { position:fixed; top:30px; right:30px; width:25em; background-color:beige; border:solid 1px gray; z-index:1000; } - #description h2 { padding-left:0.5em; } - </style> - </head> - <body> - <div id="container"></div> - <div id="description"> - <h2>Demonstrates:</h2> - <ul> - <li>Sorting grid items by an index</li> - <li>Using the getItem method to provide data</li> - </ul> - </div> +<head> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <title>SlickGrid example 13: Indexed Sorting using Functional Data Provider</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + html, body { + margin: 0; + padding: 0; + background-color: White; + overflow: auto; + } - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> + body { + font: 11px Helvetica, Arial, sans-serif; + } - <script> - var grid, - data = [], - columns = [ - { id: "title", name: "Title", field: "title", width: 240, sortable: true }, - { id: "c1", name: "Sort 1", field: "c1", width: 240, sortable: true }, - { id: "c2", name: "Sort 2", field: "c2", width: 240, sortable: true }, - { id: "c3", name: "Sort 3", field: "c3", width: 240, sortable: true } - ], - options = { - enableCellNavigation: false, - enableColumnReorder: false - }, numberOfItems = 25000, items = [], indices, isAsc = true, currentSortCol = { id: "title" }, i; + #container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + #description { + position: fixed; + top: 30px; + right: 30px; + width: 25em; + background-color: beige; + border: solid 1px gray; + z-index: 1000; + } - function randomize(items) { - /// <summary> - /// Copies and shuffles the specified array and returns a new shuffled array. - /// </summary> - var randomItems = $.extend(true, null, items), randomIndex, temp, index; - for (index = items.length; index-- > 0;) { - randomIndex = Math.round(Math.random() * items.length - 1); - if (randomIndex > -1) { - temp = randomItems[randomIndex]; - randomItems[randomIndex] = randomItems[index]; - randomItems[index] = temp; - } - } - return randomItems; - } + #description h2 { + padding-left: 0.5em; + } + </style> +</head> +<body> +<div id="container"></div> +<div id="description"> + <h2>Demonstrates:</h2> + <ul> + <li>Sorting grid items by an index</li> + <li>Using the getItem method to provide data</li> + </ul> +</div> - /// Build the items and indices. - for (i = numberOfItems; i-- > 0;) { - items[i] = i; - data[i] = { - title: "Task ".concat(i + 1) - }; - } - indices = { title: items, c1: randomize(items), c2: randomize(items), c3: randomize(items) }; +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> - // Assign values to the data. - for (i = numberOfItems; i-- > 0;) { - data[indices.c1[i]].c1 = "Value ".concat(i + 1); - data[indices.c2[i]].c2 = "Value ".concat(i + 1); - data[indices.c3[i]].c3 = "Value ".concat(i + 1); - } +<script> + var grid, + data = [], + columns = [ + { id:"title", name:"Title", field:"title", width:240, sortable:true }, + { id:"c1", name:"Sort 1", field:"c1", width:240, sortable:true }, + { id:"c2", name:"Sort 2", field:"c2", width:240, sortable:true }, + { id:"c3", name:"Sort 3", field:"c3", width:240, sortable:true } + ], + options = { + enableCellNavigation:false, + enableColumnReorder:false + }, numberOfItems = 25000, items = [], indices, isAsc = true, currentSortCol = { id:"title" }, i; - // Define function used to get the data and sort it. - function getItem(index) { - return isAsc ? data[indices[currentSortCol.id][index]] : data[indices[currentSortCol.id][(data.length - 1) - index]]; - } - function getLength() { - return data.length; - } - grid = new Slick.Grid("#container", {getLength: getLength, getItem: getItem}, columns, options); - grid.onSort.subscribe(function(e,args) { - currentSortCol = args.sortCol; - isAsc = args.sortAsc; - grid.invalidateAllRows(); - grid.render(); - }); - </script> + function randomize(items) { + /// <summary> + /// Copies and shuffles the specified array and returns a new shuffled array. + /// </summary> + var randomItems = $.extend(true, null, items), randomIndex, temp, index; + for (index = items.length; index-- > 0;) { + randomIndex = Math.round(Math.random() * items.length - 1); + if (randomIndex > -1) { + temp = randomItems[randomIndex]; + randomItems[randomIndex] = randomItems[index]; + randomItems[index] = temp; + } + } + return randomItems; + } - </body> + /// Build the items and indices. + for (i = numberOfItems; i-- > 0;) { + items[i] = i; + data[i] = { + title:"Task ".concat(i + 1) + }; + } + indices = { title:items, c1:randomize(items), c2:randomize(items), c3:randomize(items) }; + + // Assign values to the data. + for (i = numberOfItems; i-- > 0;) { + data[indices.c1[i]].c1 = "Value ".concat(i + 1); + data[indices.c2[i]].c2 = "Value ".concat(i + 1); + data[indices.c3[i]].c3 = "Value ".concat(i + 1); + } + + // Define function used to get the data and sort it. + function getItem(index) { + return isAsc ? data[indices[currentSortCol.id][index]] : data[indices[currentSortCol.id][(data.length - 1) - index]]; + } + function getLength() { + return data.length; + } + + grid = new Slick.Grid("#container", {getLength:getLength, getItem:getItem}, columns, options); + grid.onSort.subscribe(function (e, args) { + currentSortCol = args.sortCol; + isAsc = args.sortAsc; + grid.invalidateAllRows(); + grid.render(); + }); +</script> + +</body> </html>
\ No newline at end of file diff --git a/examples/example14-highlighting.html b/examples/example14-highlighting.html index 3ac061f..928feae 100644 --- a/examples/example14-highlighting.html +++ b/examples/example14-highlighting.html @@ -1,149 +1,156 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 14: Highlighting and Flashing cells</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .load-medium { - color: orange; - font-weight: bold; - } - .load-hi { - color: red; - font-weight: bold; - } - .changed { - background: pink; - } - .current-server { - border: 1px solid black; - background: orange; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:520px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>About</h2> - This example simulates a real-time display of CPU utilization in a web farm. - Data is updated in real-time, and cells with changed data are highlighted. - You can also click "Find current server" to scroll the row displaying data for the current - server into view and flash it. - <h2>Demonstrates</h2> - <ul> - <li>setHighlightedCells()</li> - <li>flashCell()</li> - </ul> - <h2>Controls</h2> - <button onclick="simulateRealTimeUpdates()">Start simulation</button> - <button onclick="findCurrentServer()">Find current server</button> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../plugins/slick.rowmovemanager.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var grid; - var data = []; - var columns = [{id:"server", name:"Server", field:"server", width:180}]; - var currentServer; - - function cpuUtilizationFormatter(row, cell, value, columnDef, dataContext) { - if (value > 90) - return "<span class='load-hi'>" + value + "%</span>"; - else if (value > 70) - return "<span class='load-medium'>" + value + "%</span>"; - else - return value + "%"; - } - - for (var i=0; i<4; i++) { - columns.push({ - id: "cpu" + i, - name: "CPU" + i, - field: i, - width: 80, - formatter: cpuUtilizationFormatter - }); - } - - var options = { - editable: false, - enableAddRow: false, - enableCellNavigation: true, - cellHighlightCssClass: "changed", - cellFlashingCssClass: "current-server" - }; - - - - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - d.server = "Server " + i; - for (var j=0; j<columns.length; j++) { - d[j] = Math.round(Math.random()*100); - } - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - - currentServer = Math.round(Math.random()*(data.length-1)); - }); - - - function simulateRealTimeUpdates() { - var changes = {}; - var numberOfUpdates = Math.round(Math.random()*(data.length/10)); - for (var i=0; i<numberOfUpdates; i++) { - var server = Math.round(Math.random()*(data.length-1)); - var cpu = Math.round(Math.random()*(columns.length-1)); - var delta = Math.round(Math.random()*50)-25; - var col = grid.getColumnIndex("cpu" + cpu); - var val = data[server][col] + delta; - val = Math.max(0,val); - val = Math.min(100,val); - - data[server][col] = val; - - if (!changes[server]) - changes[server] = {}; - - changes[server]["cpu" + cpu] = "changed"; - - grid.invalidateRow(server); - } - - grid.setCellCssStyles("highlight", changes); - grid.render(); - - setTimeout(simulateRealTimeUpdates, 500); - } - - function findCurrentServer() { - grid.scrollRowIntoView(currentServer); - grid.flashCell(currentServer,grid.getColumnIndex("server"),100); - } - - </script> - - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 14: Highlighting and Flashing cells</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .load-medium { + color: orange; + font-weight: bold; + } + + .load-hi { + color: red; + font-weight: bold; + } + + .changed { + background: pink; + } + + .current-server { + border: 1px solid black; + background: orange; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:520px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>About</h2> + This example simulates a real-time display of CPU utilization in a web farm. + Data is updated in real-time, and cells with changed data are highlighted. + You can also click "Find current server" to scroll the row displaying data for the current + server into view and flash it. + <h2>Demonstrates</h2> + <ul> + <li>setHighlightedCells()</li> + <li>flashCell()</li> + </ul> + <h2>Controls</h2> + <button onclick="simulateRealTimeUpdates()">Start simulation</button> + <button onclick="findCurrentServer()">Find current server</button> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../plugins/slick.rowmovemanager.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> + var grid; + var data = []; + var columns = [ + {id:"server", name:"Server", field:"server", width:180} + ]; + var currentServer; + + function cpuUtilizationFormatter(row, cell, value, columnDef, dataContext) { + if (value > 90) { + return "<span class='load-hi'>" + value + "%</span>"; + } + else if (value > 70) { + return "<span class='load-medium'>" + value + "%</span>"; + } + else { + return value + "%"; + } + } + + for (var i = 0; i < 4; i++) { + columns.push({ + id:"cpu" + i, + name:"CPU" + i, + field:i, + width:80, + formatter:cpuUtilizationFormatter + }); + } + + var options = { + editable:false, + enableAddRow:false, + enableCellNavigation:true, + cellHighlightCssClass:"changed", + cellFlashingCssClass:"current-server" + }; + + + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); + d.server = "Server " + i; + for (var j = 0; j < columns.length; j++) { + d[j] = Math.round(Math.random() * 100); + } + } + + grid = new Slick.Grid("#myGrid", data, columns, options); + + currentServer = Math.round(Math.random() * (data.length - 1)); + }); + + + function simulateRealTimeUpdates() { + var changes = {}; + var numberOfUpdates = Math.round(Math.random() * (data.length / 10)); + for (var i = 0; i < numberOfUpdates; i++) { + var server = Math.round(Math.random() * (data.length - 1)); + var cpu = Math.round(Math.random() * (columns.length - 1)); + var delta = Math.round(Math.random() * 50) - 25; + var col = grid.getColumnIndex("cpu" + cpu); + var val = data[server][col] + delta; + val = Math.max(0, val); + val = Math.min(100, val); + + data[server][col] = val; + + if (!changes[server]) { + changes[server] = {}; + } + + changes[server]["cpu" + cpu] = "changed"; + + grid.invalidateRow(server); + } + + grid.setCellCssStyles("highlight", changes); + grid.render(); + + setTimeout(simulateRealTimeUpdates, 500); + } + + function findCurrentServer() { + grid.scrollRowIntoView(currentServer); + grid.flashCell(currentServer, grid.getColumnIndex("server"), 100); + } + +</script> + +</body> </html> diff --git a/examples/example2-formatters.html b/examples/example2-formatters.html index d7f77e3..e260535 100644 --- a/examples/example2-formatters.html +++ b/examples/example2-formatters.html @@ -1,85 +1,79 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 2: Formatters</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 2: Formatters</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } - .cell-effort-driven { - text-align: center; - } - </style> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div id="myGrid" style="width:600px;height:500px;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - <ul> - <li>width, minWidth, maxWidth, resizable, cssClass column attributes</li> - <li>custom column formatters</li> - </ul> - </td> - </tr> - </table> + .cell-effort-driven { + text-align: center; + } + </style> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div id="myGrid" style="width:600px;height:500px;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + <ul> + <li>width, minWidth, maxWidth, resizable, cssClass column attributes</li> + <li>custom column formatters</li> + </ul> + </td> + </tr> +</table> - <script src="../lib/firebugx.js"></script> +<script src="../lib/firebugx.js"></script> - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> - <script> - var grid; +<script> + var grid; + var data = []; + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title"}, + {id:"duration", name:"Duration", field:"duration"}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter}, + {id:"start", name:"Start", field:"start", minWidth:60}, + {id:"finish", name:"Finish", field:"finish", minWidth:60}, + {id:"effort-driven", name:"Effort Driven", sortable:false, width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter} + ]; - var data = []; + var options = { + editable:false, + enableAddRow:false, + enableCellNavigation:true + }; - var columns = [ - {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title"}, - {id:"duration", name:"Duration", field:"duration"}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter}, - {id:"start", name:"Start", field:"start", minWidth:60}, - {id:"finish", name:"Finish", field:"finish", minWidth:60}, - {id:"effort-driven", name:"Effort Driven", sortable:false, width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter} - ]; + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); - var options = { - editable: false, - enableAddRow: false, - enableCellNavigation: true - }; + d["title"] = "Task " + i; + d["duration"] = "5 days"; + d["percentComplete"] = Math.min(100, Math.round(Math.random() * 110)); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } - - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - - d["title"] = "Task " + i; - d["duration"] = "5 days"; - d["percentComplete"] = Math.min(100, Math.round(Math.random() * 110)); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - }) - - </script> - - </body> + grid = new Slick.Grid("#myGrid", data, columns, options); + }) +</script> +</body> </html> diff --git a/examples/example3-editing.html b/examples/example3-editing.html index 154a6e5..d0c79f6 100644 --- a/examples/example3-editing.html +++ b/examples/example3-editing.html @@ -1,111 +1,106 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 3: Editing</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 3: Editing</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } - .cell-effort-driven { - text-align: center; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> + .cell-effort-driven { + text-align: center; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>adding basic keyboard navigation and editing</li> - <li>custom editors and validators</li> - <li>auto-edit settings</li> - </ul> + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>adding basic keyboard navigation and editing</li> + <li>custom editors and validators</li> + <li>auto-edit settings</li> + </ul> - <h2>Options:</h2> - <button onclick="grid.setOptions({autoEdit:true})">Auto-edit ON</button> - - <button onclick="grid.setOptions({autoEdit:false})">Auto-edit OFF</button> - </div> - </div> + <h2>Options:</h2> + <button onclick="grid.setOptions({autoEdit:true})">Auto-edit ON</button> + + <button onclick="grid.setOptions({autoEdit:false})">Auto-edit OFF</button> + </div> +</div> - <script src="../lib/firebugx.js"></script> +<script src="../lib/firebugx.js"></script> - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../slick.core.js"></script> - <script src="../plugins/slick.cellrangedecorator.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> +<script src="../slick.core.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> - <script> - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } +<script> + function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } else { + return {valid:true, msg:null}; + } + } - var grid; - var data = []; - var columns = [ - {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, - {id:"desc", name:"Description", field:"description", width:100, editor:LongTextCellEditor}, - {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, - {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} - ]; - var options = { - editable: true, - enableAddRow: true, - enableCellNavigation: true, - asyncEditorLoading: false, - autoEdit: false - }; + var grid; + var data = []; + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, + {id:"desc", name:"Description", field:"description", width:100, editor:LongTextCellEditor}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} + ]; + var options = { + editable:true, + enableAddRow:true, + enableCellNavigation:true, + asyncEditorLoading:false, + autoEdit:false + }; - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); - d["title"] = "Task " + i; - d["description"] = "This is a sample task description.\n It can be multiline"; - d["duration"] = "5 days"; - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } + d["title"] = "Task " + i; + d["description"] = "This is a sample task description.\n It can be multiline"; + d["duration"] = "5 days"; + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } - grid = new Slick.Grid("#myGrid", data, columns, options); + grid = new Slick.Grid("#myGrid", data, columns, options); - //grid.registerPlugin(new Slick.CellRangeSelector()); + grid.setSelectionModel(new Slick.CellSelectionModel()); - grid.setSelectionModel(new Slick.CellSelectionModel()); - - grid.onAddNewRow.subscribe(function(e, args) { - var item = args.item; - var column = args.column; - grid.invalidateRow(data.length); - data.push(item); - grid.updateRowCount(); - grid.render(); - }); - }) - </script> - </body> + grid.onAddNewRow.subscribe(function (e, args) { + var item = args.item; + grid.invalidateRow(data.length); + data.push(item); + grid.updateRowCount(); + grid.render(); + }); + }) +</script> +</body> </html> diff --git a/examples/example3a-compound-editors.html b/examples/example3a-compound-editors.html index b80b306..c5bcfbe 100644 --- a/examples/example3a-compound-editors.html +++ b/examples/example3a-compound-editors.html @@ -1,143 +1,144 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 3a: Advanced Editing</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - </style> - </head> - <body> - - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>compound cell editors driving multiple fields from one cell</li> - <li>providing validation from the editor</li> - <li>hooking into validation events</li> - </ul> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var grid; - var data = []; - - var columns = [ - {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor}, - {id:"range", name:"Range", width:120, formatter:NumericRangeFormatter, editor:NumericRangeEditor} - ]; - - var options = { - editable: true, - enableAddRow: false, - enableCellNavigation: true, - asyncEditorLoading: false - }; - - function NumericRangeFormatter(row, cell, value, columnDef, dataContext) { - return dataContext.from + " - " + dataContext.to; - } - - function NumericRangeEditor(args) { - var $from, $to; - var scope = this; - - this.init = function() { - $from = $("<INPUT type=text style='width:40px' />") - .appendTo(args.container) - .bind("keydown", scope.handleKeyDown); - - $(args.container).append(" to "); - - $to = $("<INPUT type=text style='width:40px' />") - .appendTo(args.container) - .bind("keydown", scope.handleKeyDown); - - scope.focus(); - }; - - this.handleKeyDown = function(e) { - if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.TAB) { - e.stopImmediatePropagation(); - } - }; - - this.destroy = function() { - $(args.container).empty(); - }; - - this.focus = function() { - $from.focus(); - }; - - this.serializeValue = function() { - return {from:parseInt($from.val(),10), to:parseInt($to.val(),10)}; - }; - - this.applyValue = function(item,state) { - item.from = state.from; - item.to = state.to; - }; - - this.loadValue = function(item) { - $from.val(item.from); - $to.val(item.to); - }; - - this.isValueChanged = function() { - return args.item.from != parseInt($from.val(),10) || args.item.to != parseInt($from.val(),10); - }; - - this.validate = function() { - if (isNaN(parseInt($from.val(),10)) || isNaN(parseInt($to.val(),10))) - return {valid: false, msg: "Please type in valid numbers."}; - - if (parseInt($from.val(),10) > parseInt($to.val(),10)) - return {valid: false, msg: "'from' cannot be greater than 'to'"}; - - return {valid: true, msg: null}; - }; - - this.init(); - } - - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - - d["title"] = "Task " + i; - d["from"] = Math.round(Math.random() * 100); - d["to"] = d["from"] + Math.round(Math.random() * 100); - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - - grid.onValidationError.subscribe(function(e,args) { - alert(args.validationResults.msg); - }); - }) - </script> - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 3a: Advanced Editing</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + </style> +</head> +<body> + +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>compound cell editors driving multiple fields from one cell</li> + <li>providing validation from the editor</li> + <li>hooking into validation events</li> + </ul> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> + var grid; + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor}, + {id:"range", name:"Range", width:120, formatter:NumericRangeFormatter, editor:NumericRangeEditor} + ]; + + var options = { + editable:true, + enableAddRow:false, + enableCellNavigation:true, + asyncEditorLoading:false + }; + + function NumericRangeFormatter(row, cell, value, columnDef, dataContext) { + return dataContext.from + " - " + dataContext.to; + } + + function NumericRangeEditor(args) { + var $from, $to; + var scope = this; + + this.init = function () { + $from = $("<INPUT type=text style='width:40px' />") + .appendTo(args.container) + .bind("keydown", scope.handleKeyDown); + + $(args.container).append(" to "); + + $to = $("<INPUT type=text style='width:40px' />") + .appendTo(args.container) + .bind("keydown", scope.handleKeyDown); + + scope.focus(); + }; + + this.handleKeyDown = function (e) { + if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.TAB) { + e.stopImmediatePropagation(); + } + }; + + this.destroy = function () { + $(args.container).empty(); + }; + + this.focus = function () { + $from.focus(); + }; + + this.serializeValue = function () { + return {from:parseInt($from.val(), 10), to:parseInt($to.val(), 10)}; + }; + + this.applyValue = function (item, state) { + item.from = state.from; + item.to = state.to; + }; + + this.loadValue = function (item) { + $from.val(item.from); + $to.val(item.to); + }; + + this.isValueChanged = function () { + return args.item.from != parseInt($from.val(), 10) || args.item.to != parseInt($from.val(), 10); + }; + + this.validate = function () { + if (isNaN(parseInt($from.val(), 10)) || isNaN(parseInt($to.val(), 10))) { + return {valid:false, msg:"Please type in valid numbers."}; + } + + if (parseInt($from.val(), 10) > parseInt($to.val(), 10)) { + return {valid:false, msg:"'from' cannot be greater than 'to'"}; + } + + return {valid:true, msg:null}; + }; + + this.init(); + } + + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); + + d["title"] = "Task " + i; + d["from"] = Math.round(Math.random() * 100); + d["to"] = d["from"] + Math.round(Math.random() * 100); + } + + grid = new Slick.Grid("#myGrid", data, columns, options); + + grid.onValidationError.subscribe(function (e, args) { + alert(args.validationResults.msg); + }); + }) +</script> +</body> </html> diff --git a/examples/example3b-editing-with-undo.html b/examples/example3b-editing-with-undo.html index b1a318e..1301145 100644 --- a/examples/example3b-editing-with-undo.html +++ b/examples/example3b-editing-with-undo.html @@ -1,109 +1,110 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 3: Editing</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - - <div class="options-panel"> - <h2>Demonstrates:</h2> - <ul> - <li>Using "editCommandHandler" option to intercept edit commands and implement undo support</li> - </ul> - - <h2>Controls:</h2> - <button onclick="undo()"><img src="../images/arrow_undo.png" align="absmiddle"> Undo</button> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - var grid; - var data = []; - - var columns = [ - {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, - {id:"desc", name:"Description", field:"description", width:100, editor:LongTextCellEditor}, - {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, - {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} - ]; - - var options = { - editable: true, - enableAddRow: false, - enableCellNavigation: true, - asyncEditorLoading: false, - autoEdit: false, - editCommandHandler: queueAndExecuteCommand - }; - - var commandQueue = []; - - function queueAndExecuteCommand(item,column,editCommand) { - commandQueue.push(editCommand); - editCommand.execute(); - } - - function undo() { - var command = commandQueue.pop(); - if (command && Slick.GlobalEditorLock.cancelCurrentEdit()) { - command.undo(); - grid.gotoCell(command.row,command.cell,false); - } - } - - $(function() - { - for (var i=0; i<500; i++) { - var d = (data[i] = {}); - - d["title"] = "Task " + i; - d["description"] = "This is a sample task description.\n It can be multiline"; - d["duration"] = "5 days"; - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - grid = new Slick.Grid("#myGrid", data, columns, options); - }) - </script> - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 3: Editing</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + + <div class="options-panel"> + <h2>Demonstrates:</h2> + <ul> + <li>Using "editCommandHandler" option to intercept edit commands and implement undo support</li> + </ul> + + <h2>Controls:</h2> + <button onclick="undo()"><img src="../images/arrow_undo.png" align="absmiddle"> Undo</button> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> + function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } + } + + var grid; + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator}, + {id:"desc", name:"Description", field:"description", width:100, editor:LongTextCellEditor}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor} + ]; + + var options = { + editable:true, + enableAddRow:false, + enableCellNavigation:true, + asyncEditorLoading:false, + autoEdit:false, + editCommandHandler:queueAndExecuteCommand + }; + + var commandQueue = []; + + function queueAndExecuteCommand(item, column, editCommand) { + commandQueue.push(editCommand); + editCommand.execute(); + } + + function undo() { + var command = commandQueue.pop(); + if (command && Slick.GlobalEditorLock.cancelCurrentEdit()) { + command.undo(); + grid.gotoCell(command.row, command.cell, false); + } + } + + $(function () { + for (var i = 0; i < 500; i++) { + var d = (data[i] = {}); + + d["title"] = "Task " + i; + d["description"] = "This is a sample task description.\n It can be multiline"; + d["duration"] = "5 days"; + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } + + grid = new Slick.Grid("#myGrid", data, columns, options); + }) +</script> +</body> </html> diff --git a/examples/example4-model.html b/examples/example4-model.html index 0ff1fa3..d717fad 100644 --- a/examples/example4-model.html +++ b/examples/example4-model.html @@ -1,372 +1,383 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 4: Model</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../controls/slick.columnpicker.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - - .cell-selection { - border-right-color: silver; - border-right-style: solid; - background: #f5f5f5; - color: gray; - text-align: right; - font-size: 10px; +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 4: Model</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <link rel="stylesheet" href="../controls/slick.columnpicker.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + + .cell-selection { + border-right-color: silver; + border-right-style: solid; + background: #f5f5f5; + color: gray; + text-align: right; + font-size: 10px; + } + + .slick-row.selected .cell-selection { + background-color: transparent; /* show default selected row background */ + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div class="grid-header" style="width:100%"> + <label>SlickGrid</label> + <span style="float:right" class="ui-icon ui-icon-search" title="Toggle search panel" + onclick="toggleFilterRow()"></span> + </div> + <div id="myGrid" style="width:100%;height:500px;"></div> + <div id="pager" style="width:100%;height:20px;"></div> + </div> + + <div class="options-panel"> + <b>Search:</b> + <hr/> + <div style="padding:6px;"> + <label style="width:200px;float:left">Show tasks with % at least: </label> + + <div style="padding:2px;"> + <div style="width:100px;display:inline-block;" id="pcSlider"></div> + </div> + <br/> + <label style="width:200px;float:left">And title including:</label> + <input type=text id="txtSearch" style="width:100px;"> + <br/><br/> + <button id="btnSelectRows">Select first 10 rows</button> + + <br/> + + <h2>Demonstrates:</h2> + <ul> + <li>a filtered Model (DataView) as a data source instead of a simple array</li> + <li>grid reacting to model events (onRowCountChanged, onRowsChanged)</li> + <li> + <b>FAST</b> DataView recalculation and <b>real-time</b> grid updating in response to data changes.<br/> + The grid holds <b>50'000</b> rows, yet you are able to sort, filter, scroll, navigate and edit as if it had 50 + rows. + </li> + <li>adding new rows, bidirectional sorting</li> + <li>column options: cannotTriggerInsert</li> + <li>events: onCellChange, onAddNewRow, onKeyDown, onSelectedRowsChanged, onSort</li> + <li><font color=red>NOTE:</font> all filters are immediately applied to new/edited rows</li> + <li>Handling row selection against model changes.</li> + <li>Paging.</li> + <li>inline filter panel</li> + </ul> + </div> + </div> +</div> + +<div id="inlineFilterPanel" style="display:none;background:#dddddd;padding:3px;color:black;"> + Show tasks with title including <input type="text" id="txtSearch2"> + and % at least + <div style="width:100px;display:inline-block;" id="pcSlider2"></div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../plugins/slick.rowselectionmodel.js"></script> +<script src="../slick.grid.js"></script> +<script src="../slick.dataview.js"></script> +<script src="../controls/slick.pager.js"></script> +<script src="../controls/slick.columnpicker.js"></script> + +<script> +var dataView; +var grid; +var data = []; +var selectedRowIds = []; + +var columns = [ + {id:"sel", name:"#", field:"num", behavior:"select", cssClass:"cell-selection", width:40, cannotTriggerInsert:true, resizable:false, selectable:false }, + {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator, sortable:true}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, sortable:true}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, sortable:true}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, sortable:true}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, sortable:true}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true, sortable:true} +]; + +var options = { + editable:true, + enableAddRow:true, + enableCellNavigation:true, + asyncEditorLoading:true, + forceFitColumns:false, + topPanelHeight:25 +}; + +var sortcol = "title"; +var sortdir = 1; +var percentCompleteThreshold = 0; +var searchString = ""; + +function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } +} + +function myFilter(item, args) { + if (item["percentComplete"] < args.percentCompleteThreshold) { + return false; + } + + if (args.searchString != "" && item["title"].indexOf(args.searchString) == -1) { + return false; + } + + return true; +} + +function percentCompleteSort(a, b) { + return a["percentComplete"] - b["percentComplete"]; +} + +function comparer(a, b) { + var x = a[sortcol], y = b[sortcol]; + return (x == y ? 0 : (x > y ? 1 : -1)); +} + +function toggleFilterRow() { + if ($(grid.getTopPanel()).is(":visible")) { + grid.hideTopPanel(); + } else { + grid.showTopPanel(); + } +} + + +$(".grid-header .ui-icon") + .addClass("ui-state-default ui-corner-all") + .mouseover(function (e) { + $(e.target).addClass("ui-state-hover") + }) + .mouseout(function (e) { + $(e.target).removeClass("ui-state-hover") + }); + +$(function () { + // prepare the data + for (var i = 0; i < 50000; i++) { + var d = (data[i] = {}); + + d["id"] = "id_" + i; + d["num"] = i; + d["title"] = "Task " + i; + d["duration"] = "5 days"; + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } + + + dataView = new Slick.Data.DataView(); + grid = new Slick.Grid("#myGrid", dataView, columns, options); + grid.setSelectionModel(new Slick.RowSelectionModel()); + + var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); + var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options); + + + // move the filter panel defined in a hidden div into grid top panel + $("#inlineFilterPanel") + .appendTo(grid.getTopPanel()) + .show(); + + grid.onCellChange.subscribe(function (e, args) { + dataView.updateItem(args.item.id, args.item); + }); + + grid.onAddNewRow.subscribe(function (e, args) { + var item = {"num":data.length, "id":"new_" + (Math.round(Math.random() * 10000)), "title":"New task", "duration":"1 day", "percentComplete":0, "start":"01/01/2009", "finish":"01/01/2009", "effortDriven":false}; + $.extend(item, args.item); + dataView.addItem(item); + }); + + grid.onKeyDown.subscribe(function (e) { + // select all rows on ctrl-a + if (e.which != 65 || !e.ctrlKey) { + return false; + } + + var rows = []; + selectedRowIds = []; + + for (var i = 0; i < dataView.getLength(); i++) { + rows.push(i); + selectedRowIds.push(dataView.getItem(i).id); + } + + grid.setSelectedRows(rows); + e.preventDefault(); + }); + + grid.onSelectedRowsChanged.subscribe(function (e) { + selectedRowIds = []; + var rows = grid.getSelectedRows(); + for (var i = 0, l = rows.length; i < l; i++) { + var item = dataView.getItem(rows[i]); + if (item) { + selectedRowIds.push(item.id); + } + } + }); + + grid.onSort.subscribe(function (e, args) { + sortdir = args.sortAsc ? 1 : -1; + sortcol = args.sortCol.field; + + if ($.browser.msie && $.browser.version <= 8) { + // using temporary Object.prototype.toString override + // more limited and does lexicographic sort only by default, but can be much faster + + var percentCompleteValueFn = function () { + var val = this["percentComplete"]; + if (val < 10) { + return "00" + val; + } else if (val < 100) { + return "0" + val; + } else { + return val; } - - .slick-row.selected .cell-selection { - background-color: transparent; /* show default selected row background */ - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div class="grid-header" style="width:100%"> - <label>SlickGrid</label> - <span style="float:right" class="ui-icon ui-icon-search" title="Toggle search panel" onclick="toggleFilterRow()"></span> - </div> - <div id="myGrid" style="width:100%;height:500px;"></div> - <div id="pager" style="width:100%;height:20px;"></div> - </div> - - <div class="options-panel"> - <b>Search:</b> - <hr/> - <div style="padding:6px;"> - <label style="width:200px;float:left">Show tasks with % at least: </label> - <div style="padding:2px;"> - <div style="width:100px;display:inline-block;" id="pcSlider"></div> - </div> - <br/> - <label style="width:200px;float:left">And title including:</label> - <input type=text id="txtSearch" style="width:100px;"> - <br/><br/> - <button id="btnSelectRows">Select first 10 rows</button> - - <br/> - <h2>Demonstrates:</h2> - <ul> - <li>a filtered Model (DataView) as a data source instead of a simple array</li> - <li>grid reacting to model events (onRowCountChanged, onRowsChanged)</li> - <li> - <b>FAST</b> DataView recalculation and <b>real-time</b> grid updating in response to data changes.<br/> - The grid holds <b>50'000</b> rows, yet you are able to sort, filter, scroll, navigate and edit as if it had 50 rows. - </li> - <li>adding new rows, bidirectional sorting</li> - <li>column options: cannotTriggerInsert</li> - <li>events: onCellChange, onAddNewRow, onKeyDown, onSelectedRowsChanged, onSort</li> - <li><font color=red>NOTE:</font> all filters are immediately applied to new/edited rows</li> - <li>Handling row selection against model changes.</li> - <li>Paging.</li> - <li>inline filter panel</li> - </ul> - </div> - </div> - </div> - - <div id="inlineFilterPanel" style="display:none;background:#dddddd;padding:3px;color:black;"> - Show tasks with title including <input type="text" id="txtSearch2"> - and % at least <div style="width:100px;display:inline-block;" id="pcSlider2"></div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../plugins/slick.rowselectionmodel.js"></script> - <script src="../slick.grid.js"></script> - <script src="../slick.dataview.js"></script> - <script src="../controls/slick.pager.js"></script> - <script src="../controls/slick.columnpicker.js"></script> - - <script> - var dataView; - var grid; - var data = []; - var selectedRowIds = []; - - var columns = [ - {id:"sel", name:"#", field:"num", behavior:"select", cssClass:"cell-selection", width:40, cannotTriggerInsert:true, resizable:false, selectable:false }, - {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator, sortable:true}, - {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, sortable:true}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, sortable:true}, - {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, sortable:true}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, sortable:true}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true, sortable:true} - ]; - - var options = { - editable: true, - enableAddRow: true, - enableCellNavigation: true, - asyncEditorLoading: true, - forceFitColumns: false, - topPanelHeight: 25 - }; - - var sortcol = "title"; - var sortdir = 1; - var percentCompleteThreshold = 0; - var searchString = ""; - - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - function myFilter(item, args) { - if (item["percentComplete"] < args.percentCompleteThreshold) - return false; - - if (args.searchString != "" && item["title"].indexOf(args.searchString) == -1) - return false; - - return true; - } - - function percentCompleteSort(a,b) { - return a["percentComplete"] - b["percentComplete"]; - } - - function comparer(a,b) { - var x = a[sortcol], y = b[sortcol]; - return (x == y ? 0 : (x > y ? 1 : -1)); - } - - function toggleFilterRow() { - if ($(grid.getTopPanel()).is(":visible")) - grid.hideTopPanel(); - else - grid.showTopPanel(); + }; + + // use numeric sort of % and lexicographic for everything else + dataView.fastSort((sortcol == "percentComplete") ? percentCompleteValueFn : sortcol, args.sortAsc); + } else { + // using native sort with comparer + // preferred method but can be very slow in IE with huge datasets + dataView.sort(comparer, args.sortAsc); + } + }); + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function (e, args) { + grid.updateRowCount(); + grid.render(); + }); + + dataView.onRowsChanged.subscribe(function (e, args) { + grid.invalidateRows(args.rows); + grid.render(); + + if (selectedRowIds.length > 0) { + // since how the original data maps onto rows has changed, + // the selected rows in the grid need to be updated + var selRows = []; + for (var i = 0; i < selectedRowIds.length; i++) { + var idx = dataView.getRowById(selectedRowIds[i]); + if (idx != undefined) { + selRows.push(idx); } - - - $(".grid-header .ui-icon") - .addClass("ui-state-default ui-corner-all") - .mouseover(function(e) { - $(e.target).addClass("ui-state-hover") - }) - .mouseout(function(e) { - $(e.target).removeClass("ui-state-hover") - }); - - $(function() - { - // prepare the data - for (var i=0; i<50000; i++) { - var d = (data[i] = {}); - - d["id"] = "id_" + i; - d["num"] = i; - d["title"] = "Task " + i; - d["duration"] = "5 days"; - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - - dataView = new Slick.Data.DataView(); - grid = new Slick.Grid("#myGrid", dataView, columns, options); - grid.setSelectionModel(new Slick.RowSelectionModel()); - - var pager = new Slick.Controls.Pager(dataView, grid, $("#pager")); - var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options); - - - // move the filter panel defined in a hidden div into grid top panel - $("#inlineFilterPanel") - .appendTo(grid.getTopPanel()) - .show(); - - grid.onCellChange.subscribe(function(e,args) { - dataView.updateItem(args.item.id,args.item); - }); - - grid.onAddNewRow.subscribe(function(e,args) { - var item = {"num": data.length, "id": "new_" + (Math.round(Math.random()*10000)), "title":"New task", "duration":"1 day", "percentComplete":0, "start":"01/01/2009", "finish":"01/01/2009", "effortDriven":false}; - $.extend(item,args.item); - dataView.addItem(item); - }); - - grid.onKeyDown.subscribe(function(e) { - // select all rows on ctrl-a - if (e.which != 65 || !e.ctrlKey) - return false; - - var rows = []; - selectedRowIds = []; - - for (var i = 0; i < dataView.getLength(); i++) { - rows.push(i); - selectedRowIds.push(dataView.getItem(i).id); - } - - grid.setSelectedRows(rows); - e.preventDefault(); - }); - - grid.onSelectedRowsChanged.subscribe(function(e) { - selectedRowIds = []; - var rows = grid.getSelectedRows(); - for (var i = 0, l = rows.length; i < l; i++) { - var item = dataView.getItem(rows[i]); - if (item) selectedRowIds.push(item.id); - } - }); - - grid.onSort.subscribe(function(e, args) { - sortdir = args.sortAsc ? 1 : -1; - sortcol = args.sortCol.field; - - if ($.browser.msie && $.browser.version <= 8) { - // using temporary Object.prototype.toString override - // more limited and does lexicographic sort only by default, but can be much faster - - var percentCompleteValueFn = function() { - var val = this["percentComplete"]; - if (val < 10) - return "00" + val; - else if (val < 100) - return "0" + val; - else - return val; - }; - - // use numeric sort of % and lexicographic for everything else - dataView.fastSort((sortcol=="percentComplete")?percentCompleteValueFn:sortcol,args.sortAsc); - } - else { - // using native sort with comparer - // preferred method but can be very slow in IE with huge datasets - dataView.sort(comparer, args.sortAsc); - } - }); - - // wire up model events to drive the grid - dataView.onRowCountChanged.subscribe(function(e,args) { - grid.updateRowCount(); - grid.render(); - }); - - dataView.onRowsChanged.subscribe(function(e,args) { - grid.invalidateRows(args.rows); - grid.render(); - - if (selectedRowIds.length > 0) - { - // since how the original data maps onto rows has changed, - // the selected rows in the grid need to be updated - var selRows = []; - for (var i = 0; i < selectedRowIds.length; i++) - { - var idx = dataView.getRowById(selectedRowIds[i]); - if (idx != undefined) - selRows.push(idx); - } - - grid.setSelectedRows(selRows); - } - }); - - dataView.onPagingInfoChanged.subscribe(function(e,pagingInfo) { - var isLastPage = pagingInfo.pageSize*(pagingInfo.pageNum+1)-1 >= pagingInfo.totalRows; - var enableAddRow = isLastPage || pagingInfo.pageSize==0; - var options = grid.getOptions(); - - if (options.enableAddRow != enableAddRow) - grid.setOptions({enableAddRow:enableAddRow}); - }); - - - - var h_runfilters = null; - - // wire up the slider to apply the filter to the model - $("#pcSlider,#pcSlider2").slider({ - "range": "min", - "slide": function(event,ui) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - if (percentCompleteThreshold != ui.value) - { - window.clearTimeout(h_runfilters); - h_runfilters = window.setTimeout(updateFilter, 10); - percentCompleteThreshold = ui.value; - } - } - }); - - - // wire up the search textbox to apply the filter to the model - $("#txtSearch,#txtSearch2").keyup(function(e) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - // clear on Esc - if (e.which == 27) - this.value = ""; - - searchString = this.value; - updateFilter(); - }); - - function updateFilter() { - dataView.setFilterArgs({ - percentCompleteThreshold: percentCompleteThreshold, - searchString: searchString - }); - dataView.refresh(); - } - - $("#btnSelectRows").click(function() { - if (!Slick.GlobalEditorLock.commitCurrentEdit()) { return; } - - var rows = []; - selectedRowIds = []; - - for (var i=0; i<10 && i<dataView.getLength(); i++) { - rows.push(i); - selectedRowIds.push(dataView.getItem(i).id); - } - - grid.setSelectedRows(rows); - }); - - - // initialize the model after all the events have been hooked up - dataView.beginUpdate(); - dataView.setItems(data); - dataView.setFilterArgs({ - percentCompleteThreshold: percentCompleteThreshold, - searchString: searchString - }); - dataView.setFilter(myFilter); - dataView.endUpdate(); - - $("#gridContainer").resizable(); - }) - - </script> - - </body> + } + + grid.setSelectedRows(selRows); + } + }); + + dataView.onPagingInfoChanged.subscribe(function (e, pagingInfo) { + var isLastPage = pagingInfo.pageSize * (pagingInfo.pageNum + 1) - 1 >= pagingInfo.totalRows; + var enableAddRow = isLastPage || pagingInfo.pageSize == 0; + var options = grid.getOptions(); + + if (options.enableAddRow != enableAddRow) { + grid.setOptions({enableAddRow:enableAddRow}); + } + }); + + + var h_runfilters = null; + + // wire up the slider to apply the filter to the model + $("#pcSlider,#pcSlider2").slider({ + "range": "min", + "slide": function (event, ui) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + if (percentCompleteThreshold != ui.value) { + window.clearTimeout(h_runfilters); + h_runfilters = window.setTimeout(updateFilter, 10); + percentCompleteThreshold = ui.value; + } + } + }); + + + // wire up the search textbox to apply the filter to the model + $("#txtSearch,#txtSearch2").keyup(function (e) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + // clear on Esc + if (e.which == 27) { + this.value = ""; + } + + searchString = this.value; + updateFilter(); + }); + + function updateFilter() { + dataView.setFilterArgs({ + percentCompleteThreshold:percentCompleteThreshold, + searchString:searchString + }); + dataView.refresh(); + } + + $("#btnSelectRows").click(function () { + if (!Slick.GlobalEditorLock.commitCurrentEdit()) { + return; + } + + var rows = []; + selectedRowIds = []; + + for (var i = 0; i < 10 && i < dataView.getLength(); i++) { + rows.push(i); + selectedRowIds.push(dataView.getItem(i).id); + } + + grid.setSelectedRows(rows); + }); + + + // initialize the model after all the events have been hooked up + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilterArgs({ + percentCompleteThreshold:percentCompleteThreshold, + searchString:searchString + }); + dataView.setFilter(myFilter); + dataView.endUpdate(); + + $("#gridContainer").resizable(); +}) +</script> +</body> </html> diff --git a/examples/example5-collapsing.html b/examples/example5-collapsing.html index a874dd8..40a7223 100644 --- a/examples/example5-collapsing.html +++ b/examples/example5-collapsing.html @@ -1,262 +1,275 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 5: Collapsing</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - font-weight: bold; - } - - .cell-effort-driven { - text-align: center; - } - - .toggle { - height: 9px; - width: 9px; - display: inline-block; - } - - .toggle.expand { - background: url(../images/expand.gif) no-repeat center center; - } - - .toggle.collapse { - background: url(../images/collapse.gif) no-repeat center center; - } - - </style> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div style="border:1px solid gray;background:wheat;padding:6px;"> - <label>Show tasks with % at least: </label> - <div style="padding:4px;"> - <div style="width:100px;" id="pcSlider"></div> - </div> - <br/> - <label>And title including:</label> - <input type=text id="txtSearch"> - </div> - <br/> - <div id="myGrid" style="width:600px;height:500px;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - <ul> - <li>implementing expand/collapse as a filter for DataView</li> - </ul> - </td> - </tr> - </table> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - <script src="../slick.dataview.js"></script> - - <script> - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - - var TaskNameFormatter = function(row, cell, value, columnDef, dataContext) { - var spacer = "<span style='display:inline-block;height:1px;width:" + (15 * dataContext["indent"]) + "px'></span>"; - var idx = dataView.getIdxById(dataContext.id); - if (data[idx+1] && data[idx+1].indent > data[idx].indent) { - if (dataContext._collapsed) - return spacer + " <span class='toggle expand'></span> " + value; - else - return spacer + " <span class='toggle collapse'></span> " + value; - } - else - return spacer + " <span class='toggle'></span> " + value; - }; - - var dataView; - var grid; - - var data = []; - - var columns = [ - {id:"title", name:"Title", field:"title", width:220, cssClass:"cell-title", formatter:TaskNameFormatter, editor:TextCellEditor, validator:requiredFieldValidator}, - {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, - {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, - {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, - {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, - {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true} - ]; - - var options = { - editable: true, - enableAddRow: true, - enableCellNavigation: true, - asyncEditorLoading: false - }; - - - var percentCompleteThreshold = 0; - var searchString = ""; - - function myFilter(item) { - if (item["percentComplete"] < percentCompleteThreshold) - return false; - - if (searchString != "" && item["title"].indexOf(searchString) == -1) - return false; - - var idx = dataView.getIdxById(item.id); - - if (item.parent != null) { - var parent = data[item.parent]; - - while (parent) { - if (parent._collapsed || (parent["percentComplete"] < percentCompleteThreshold) || (searchString != "" && parent["title"].indexOf(searchString) == -1) ) - return false; - - parent = data[parent.parent]; - } - } - - return true; - } - - function percentCompleteSort(a,b) { - return a["percentComplete"] - b["percentComplete"]; - } - - $(function() - { - var indent = 0; - var parents = []; - - // prepare the data - for (var i=0; i<1000; i++) { - var d = (data[i] = {}); - var parent = null; - - if (Math.random() > 0.8) { - indent++; - parent = i - 1; - parents.push(parent); - } - else if (Math.random() < 0.3 && indent > 0) { - indent--; - parent = parents.pop(); - } - else if (parents.length > 0) - parent = parents[parents.length-1]; - - if (indent == 0) - parent = null; - - d["id"] = "id_" + i; - d["indent"] = indent; - d["parent"] = parent; - d["title"] = "Task " + i; - d["duration"] = "5 days"; - d["percentComplete"] = Math.round(Math.random() * 100); - d["start"] = "01/01/2009"; - d["finish"] = "01/05/2009"; - d["effortDriven"] = (i % 5 == 0); - } - - - // initialize the model - dataView = new Slick.Data.DataView(); - dataView.beginUpdate(); - dataView.setItems(data); - dataView.setFilter(myFilter); - dataView.endUpdate(); - - - // initialize the grid - grid = new Slick.Grid("#myGrid", dataView, columns, options); - - grid.onCellChange.subscribe(function(e,args) { - dataView.updateItem(args.item.id,args.item); - }); - - grid.onAddNewRow.subscribe(function(e,args) { - var item = {"id": "new_" + (Math.round(Math.random()*10000)), "indent":0, "title":"New task", "duration":"1 day", "percentComplete":0, "start":"01/01/2009", "finish":"01/01/2009", "effortDriven":false}; - $.extend(item,args.item); - dataView.addItem(item); - }); - - grid.onClick.subscribe(function(e,args) { - if ($(e.target).hasClass("toggle")) { - var item = dataView.getItem(args.row); - if (item) { - if (!item._collapsed) - item._collapsed = true; - else - item._collapsed = false; - - dataView.updateItem(item.id, item); - } - e.stopImmediatePropagation(); - } - }); - - - // wire up model events to drive the grid - dataView.onRowCountChanged.subscribe(function(e,args) { - grid.updateRowCount(); - grid.render(); - }); - - dataView.onRowsChanged.subscribe(function(e,args) { - grid.invalidateRows(args.rows); - grid.render(); - }); - - - var h_runfilters = null; - - // wire up the slider to apply the filter to the model - $("#pcSlider").slider({ - "range": "min", - "slide": function(event,ui) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - if (percentCompleteThreshold != ui.value) - { - window.clearTimeout(h_runfilters); - h_runfilters = window.setTimeout(dataView.refresh, 10); - percentCompleteThreshold = ui.value; - } - } - }); - - - // wire up the search textbox to apply the filter to the model - $("#txtSearch").keyup(function(e) { - Slick.GlobalEditorLock.cancelCurrentEdit(); - - // clear on Esc - if (e.which == 27) - this.value = ""; - - searchString = this.value; - dataView.refresh(); - }) - }) - </script> - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 5: Collapsing</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + + .toggle { + height: 9px; + width: 9px; + display: inline-block; + } + + .toggle.expand { + background: url(../images/expand.gif) no-repeat center center; + } + + .toggle.collapse { + background: url(../images/collapse.gif) no-repeat center center; + } + + </style> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div style="border:1px solid gray;background:wheat;padding:6px;"> + <label>Show tasks with % at least: </label> + + <div style="padding:4px;"> + <div style="width:100px;" id="pcSlider"></div> + </div> + <br/> + <label>And title including:</label> + <input type=text id="txtSearch"> + </div> + <br/> + + <div id="myGrid" style="width:600px;height:500px;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + <ul> + <li>implementing expand/collapse as a filter for DataView</li> + </ul> + </td> + </tr> +</table> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> +<script src="../slick.dataview.js"></script> + +<script> + function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } + } + + + var TaskNameFormatter = function (row, cell, value, columnDef, dataContext) { + var spacer = "<span style='display:inline-block;height:1px;width:" + (15 * dataContext["indent"]) + "px'></span>"; + var idx = dataView.getIdxById(dataContext.id); + if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) { + if (dataContext._collapsed) { + return spacer + " <span class='toggle expand'></span> " + value; + } + else { + return spacer + " <span class='toggle collapse'></span> " + value; + } + } + else { + return spacer + " <span class='toggle'></span> " + value; + } + }; + + var dataView; + var grid; + + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", width:220, cssClass:"cell-title", formatter:TaskNameFormatter, editor:TextCellEditor, validator:requiredFieldValidator}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true} + ]; + + var options = { + editable:true, + enableAddRow:true, + enableCellNavigation:true, + asyncEditorLoading:false + }; + + + var percentCompleteThreshold = 0; + var searchString = ""; + + function myFilter(item) { + if (item["percentComplete"] < percentCompleteThreshold) { + return false; + } + + if (searchString != "" && item["title"].indexOf(searchString) == -1) { + return false; + } + + var idx = dataView.getIdxById(item.id); + + if (item.parent != null) { + var parent = data[item.parent]; + + while (parent) { + if (parent._collapsed || (parent["percentComplete"] < percentCompleteThreshold) || (searchString != "" && parent["title"].indexOf(searchString) == -1)) { + return false; + } + + parent = data[parent.parent]; + } + } + + return true; + } + + function percentCompleteSort(a, b) { + return a["percentComplete"] - b["percentComplete"]; + } + + $(function () { + var indent = 0; + var parents = []; + + // prepare the data + for (var i = 0; i < 1000; i++) { + var d = (data[i] = {}); + var parent = null; + + if (Math.random() > 0.8) { + indent++; + parent = i - 1; + parents.push(parent); + } + else if (Math.random() < 0.3 && indent > 0) { + indent--; + parent = parents.pop(); + } + else if (parents.length > 0) { + parent = parents[parents.length - 1]; + } + + if (indent == 0) { + parent = null; + } + + d["id"] = "id_" + i; + d["indent"] = indent; + d["parent"] = parent; + d["title"] = "Task " + i; + d["duration"] = "5 days"; + d["percentComplete"] = Math.round(Math.random() * 100); + d["start"] = "01/01/2009"; + d["finish"] = "01/05/2009"; + d["effortDriven"] = (i % 5 == 0); + } + + + // initialize the model + dataView = new Slick.Data.DataView(); + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + dataView.endUpdate(); + + + // initialize the grid + grid = new Slick.Grid("#myGrid", dataView, columns, options); + + grid.onCellChange.subscribe(function (e, args) { + dataView.updateItem(args.item.id, args.item); + }); + + grid.onAddNewRow.subscribe(function (e, args) { + var item = {"id":"new_" + (Math.round(Math.random() * 10000)), "indent":0, "title":"New task", "duration":"1 day", "percentComplete":0, "start":"01/01/2009", "finish":"01/01/2009", "effortDriven":false}; + $.extend(item, args.item); + dataView.addItem(item); + }); + + grid.onClick.subscribe(function (e, args) { + if ($(e.target).hasClass("toggle")) { + var item = dataView.getItem(args.row); + if (item) { + if (!item._collapsed) { + item._collapsed = true; + } + else { + item._collapsed = false; + } + + dataView.updateItem(item.id, item); + } + e.stopImmediatePropagation(); + } + }); + + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function (e, args) { + grid.updateRowCount(); + grid.render(); + }); + + dataView.onRowsChanged.subscribe(function (e, args) { + grid.invalidateRows(args.rows); + grid.render(); + }); + + + var h_runfilters = null; + + // wire up the slider to apply the filter to the model + $("#pcSlider").slider({ + "range":"min", + "slide":function (event, ui) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + if (percentCompleteThreshold != ui.value) { + window.clearTimeout(h_runfilters); + h_runfilters = window.setTimeout(dataView.refresh, 10); + percentCompleteThreshold = ui.value; + } + } + }); + + + // wire up the search textbox to apply the filter to the model + $("#txtSearch").keyup(function (e) { + Slick.GlobalEditorLock.cancelCurrentEdit(); + + // clear on Esc + if (e.which == 27) { + this.value = ""; + } + + searchString = this.value; + dataView.refresh(); + }) + }) +</script> +</body> </html> diff --git a/examples/example6-ajax-loading.html b/examples/example6-ajax-loading.html index a2c9624..978ca7e 100644 --- a/examples/example6-ajax-loading.html +++ b/examples/example6-ajax-loading.html @@ -1,156 +1,154 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 6: AJAX Load</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-story { - white-space: normal!important; - line-height: 19px!important; - } - - .loading-indicator { - display: inline-block; - padding: 12px; - background: white; - -opacity: 0.5; - color: black; - font-weight: bold; - z-index: 9999; - border: 1px solid red; - -moz-border-radius: 10px; - -webkit-border-radius: 10px; - -moz-box-shadow: 0 0 5px red; - -webkit-box-shadow: 0px 0px 5px red; - -text-shadow: 1px 1px 1px white; - } - - .loading-indicator label { - padding-left: 20px; - background: url('../images/ajax-loader-small.gif') no-repeat center left; - } - </style> - </head> - <body> - <div style="width:700px;float:left;"> - <div class="grid-header" style="width:100%"> - <label>Digg stories</label> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 6: AJAX Load</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-story { + white-space: normal !important; + line-height: 19px !important; + } + + .loading-indicator { + display: inline-block; + padding: 12px; + background: white; + -opacity: 0.5; + color: black; + font-weight: bold; + z-index: 9999; + border: 1px solid red; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + -moz-box-shadow: 0 0 5px red; + -webkit-box-shadow: 0px 0px 5px red; + -text-shadow: 1px 1px 1px white; + } + + .loading-indicator label { + padding-left: 20px; + background: url('../images/ajax-loader-small.gif') no-repeat center left; + } + </style> +</head> +<body> +<div style="width:700px;float:left;"> + <div class="grid-header" style="width:100%"> + <label>Digg stories</label> <span style="float:right;display:inline-block;"> Search: <input type="text" id="txtSearch" value="apple"> </span> - </div> - <div id="myGrid" style="width:100%;height:600px;"></div> - <div id="pager" style="width:100%;height:20px;"></div> - </div> - <div style="margin-left:750px;margin-top:40px;;"> - <h2>Demonstrates:</h2> - <ul> - <li>loading data through AJAX</li> - <li>custom row height</li> - </ul> - - <h2>WARNING:</h2> - <ul> - <li>Digg API uses request rate limiting. You may occasionally get an error if you do a frequent scrolling/resorting/searching.</li> - </ul> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../lib/jquery.jsonp-1.1.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.remotemodel.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var grid; - var data = []; - var loader = new Slick.Data.RemoteModel(); - - var storyTitleFormatter = function(row, cell, value, columnDef, dataContext) { - return "<b><a href='" + dataContext["link"] + "' target=_blank>" + dataContext["title"] + "</a></b><br/>" + dataContext["description"]; - }; - - - var columns = [ - {id:"num", name:"#", field:"index", width:40}, - {id:"story", name:"Story", width:580, formatter:storyTitleFormatter, cssClass:"cell-story"}, - {id:"diggs", name:"Diggs", field:"diggs", width:60, sortable:true} - ]; - - var options = { - rowHeight: 64, - editable: false, - enableAddRow: false, - enableCellNavigation: false - }; - - var loadingIndicator = null; - - - - $(function() - { - grid = new Slick.Grid("#myGrid", loader.data, columns, options); - - grid.onViewportChanged.subscribe(function(e,args) { - var vp = grid.getViewport(); - loader.ensureData(vp.top, vp.bottom); - }); - - grid.onSort.subscribe(function(e,args) { - loader.setSort(args.sortCol.field, args.sortAsc ? 1 : -1); - var vp = grid.getViewport(); - loader.ensureData(vp.top, vp.bottom); - }); - - loader.onDataLoading.subscribe(function() { - if (!loadingIndicator) - { - loadingIndicator = $("<span class='loading-indicator'><label>Buffering...</label></span>").appendTo(document.body); - var $g = $("#myGrid"); - - loadingIndicator - .css("position", "absolute") - .css("top", $g.position().top + $g.height()/2 - loadingIndicator.height()/2) - .css("left", $g.position().left + $g.width()/2 - loadingIndicator.width()/2) - } - - loadingIndicator.show(); - }); - - loader.onDataLoaded.subscribe(function(e,args) { - for (var i = args.from; i <= args.to; i++) { - grid.invalidateRow(i); - } - - grid.updateRowCount(); - grid.render(); - - loadingIndicator.fadeOut(); - }); - - $("#txtSearch").keyup(function(e) { - if (e.which == 13) { - loader.setSearch($(this).val()); - var vp = grid.getViewport(); - loader.ensureData(vp.top, vp.bottom); - } - }); - - - - // load the first page - grid.onViewportChanged.notify(); - }) - </script> - </body> + </div> + <div id="myGrid" style="width:100%;height:600px;"></div> + <div id="pager" style="width:100%;height:20px;"></div> +</div> +<div style="margin-left:750px;margin-top:40px;;"> + <h2>Demonstrates:</h2> + <ul> + <li>loading data through AJAX</li> + <li>custom row height</li> + </ul> + + <h2>WARNING:</h2> + <ul> + <li>Digg API uses request rate limiting. You may occasionally get an error if you do a frequent + scrolling/resorting/searching. + </li> + </ul> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery.jsonp-1.1.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.remotemodel.js"></script> +<script src="../slick.grid.js"></script> + +<script> + var grid; + var data = []; + var loader = new Slick.Data.RemoteModel(); + + var storyTitleFormatter = function (row, cell, value, columnDef, dataContext) { + return "<b><a href='" + dataContext["link"] + "' target=_blank>" + dataContext["title"] + "</a></b><br/>" + dataContext["description"]; + }; + + + var columns = [ + {id:"num", name:"#", field:"index", width:40}, + {id:"story", name:"Story", width:580, formatter:storyTitleFormatter, cssClass:"cell-story"}, + {id:"diggs", name:"Diggs", field:"diggs", width:60, sortable:true} + ]; + + var options = { + rowHeight:64, + editable:false, + enableAddRow:false, + enableCellNavigation:false + }; + + var loadingIndicator = null; + + + $(function () { + grid = new Slick.Grid("#myGrid", loader.data, columns, options); + + grid.onViewportChanged.subscribe(function (e, args) { + var vp = grid.getViewport(); + loader.ensureData(vp.top, vp.bottom); + }); + + grid.onSort.subscribe(function (e, args) { + loader.setSort(args.sortCol.field, args.sortAsc ? 1 : -1); + var vp = grid.getViewport(); + loader.ensureData(vp.top, vp.bottom); + }); + + loader.onDataLoading.subscribe(function () { + if (!loadingIndicator) { + loadingIndicator = $("<span class='loading-indicator'><label>Buffering...</label></span>").appendTo(document.body); + var $g = $("#myGrid"); + + loadingIndicator + .css("position", "absolute") + .css("top", $g.position().top + $g.height() / 2 - loadingIndicator.height() / 2) + .css("left", $g.position().left + $g.width() / 2 - loadingIndicator.width() / 2) + } + + loadingIndicator.show(); + }); + + loader.onDataLoaded.subscribe(function (e, args) { + for (var i = args.from; i <= args.to; i++) { + grid.invalidateRow(i); + } + + grid.updateRowCount(); + grid.render(); + + loadingIndicator.fadeOut(); + }); + + $("#txtSearch").keyup(function (e) { + if (e.which == 13) { + loader.setSearch($(this).val()); + var vp = grid.getViewport(); + loader.ensureData(vp.top, vp.bottom); + } + }); + + + // load the first page + grid.onViewportChanged.notify(); + }) +</script> +</body> </html> diff --git a/examples/example7-events.html b/examples/example7-events.html index 4d0b44e..33ccc83 100644 --- a/examples/example7-events.html +++ b/examples/example7-events.html @@ -3,9 +3,9 @@ <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>SlickGrid example 7: Events</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> - <link rel="stylesheet" href="examples.css" type="text/css" /> + <link rel="stylesheet" href="../slick.grid.css" type="text/css"/> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css"/> + <link rel="stylesheet" href="examples.css" type="text/css"/> <style> .cell-title { font-weight: bold; @@ -97,10 +97,10 @@ e.preventDefault(); var cell = grid.getCellFromEvent(e); $("#contextMenu") - .data("row", cell.row) - .css("top", e.pageY) - .css("left", e.pageX) - .show(); + .data("row", cell.row) + .css("top", e.pageY) + .css("left", e.pageX) + .show(); $("body").one("click", function () { $("#contextMenu").hide(); @@ -119,8 +119,9 @@ }); $("#contextMenu").click(function (e) { - if (!$(e.target).is("li")) + if (!$(e.target).is("li")) { return; + } var row = $(this).data("row"); data[row].priority = $(e.target).attr("data"); diff --git a/examples/example8-alternative-display.html b/examples/example8-alternative-display.html index 8fe622e..acf0f6b 100644 --- a/examples/example8-alternative-display.html +++ b/examples/example8-alternative-display.html @@ -1,174 +1,176 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 8: Alternative display</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .slick-cell { - background: white!important; - border-color: transparent!important; - line-height: 19px!important; - } - - /* alternating offsets */ - .slick-row .cell-inner { - margin-right: 60px; - } - - .slick-row[row$="1"] .cell-inner, .slick-row[row$="3"] .cell-inner, .slick-row[row$="5"] .cell-inner, - .slick-row[row$="7"] .cell-inner, .slick-row[row$="9"] .cell-inner { - margin-left: 60px; - margin-right: 0; - } - - .contact-card-cell { - border-color: transparent!important; - } - - .cell-inner { - height: 80px; - margin: 10px; - padding: 10px; - background: #fafafa; - border: 1px solid gray; - -moz-border-radius: 10px; - -webkit-border-radius: 10px; - -moz-box-shadow: 1px 1px 5px silver; - -webkit-box-shadow: 1px 1px 5px silver; - -webkit-transition: all 0.5s; - } - - .cell-inner:hover { - background: #f0f0f0; - } - - .cell-left { - width: 40px; - height: 100%; - float: left; - border-right: 1px dotted gray; - background: url("../images/user_identity.gif") no-repeat top center; - } - - .cell-main { - margin-left: 50px; - } - </style> - </head> - <body> - <table width="100%"> - <tr> - <td valign="top" width="50%"> - <div id="myGrid" style="width:600px;height:500px;"></div> - </td> - <td valign="top"> - <h2>Demonstrates:</h2> - - <ul> - <li>Template-based rendering using John Resig's <a href="http://ejohn.org/blog/javascript-micro-templating/" target=_blank>micro-templates</a> while still using SlickGrid's virtual rendering technology.</li> - </ul> - </td> - </tr> - </table> - - <!-- cell template --> - <script type="text/html" id="cell_template"> - <div class="cell-inner"> - <div class="cell-left"></div> - <div class="cell-main"> - <b><%=name%></b><br/> - <%=title%><br/> - <%=email%><br/> - <%=phone%> - </div> - </div> - </script> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../slick.grid.js"></script> - - <script> - // Simple JavaScript Templating - // John Resig - http://ejohn.org/ - MIT Licensed - (function(){ - var cache = {}; - - this.tmpl = function tmpl(str, data){ - // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. - var fn = !/\W/.test(str) ? - cache[str] = cache[str] || - tmpl(document.getElementById(str).innerHTML) : - - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - new Function("obj", - "var p=[],print=function(){p.push.apply(p,arguments);};" + - - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + - - // Convert the template into pure JavaScript - str - .replace(/[\r\t\n]/g, " ") - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)'/g, "$1\r") - .replace(/\t=(.*?)%>/g, "',$1,'") - .split("\t").join("');") - .split("%>").join("p.push('") - .split("\r").join("\\'") - + "');}return p.join('');"); - - // Provide some basic currying to the user - return data ? fn( data ) : fn; - }; - })(); - - - var grid; - var data = []; - - var columns = [ - {id:"contact-card", name:"Contacts", formatter:renderCell, width:500, cssClass:"contact-card-cell"} - ]; - - var options = { - rowHeight: 140, - editable: false, - enableAddRow: false, - enableCellNavigation: false, - enableColumnReorder: false - }; - - var compiled_template = tmpl("cell_template"); - - function renderCell(row, cell, value, columnDef, dataContext) { - return compiled_template(dataContext); - } - - $(function() - { - for (var i=0; i<100; i++) { - var d = (data[i] = {}); - - d["name"] = "User " + i; - d["email"] = "test.user@nospam.org"; - d["title"] = "Regional sales manager"; - d["phone"] = "206-000-0000"; - } - - - grid = new Slick.Grid("#myGrid", data, columns, options); - }) - - </script> - </body> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 8: Alternative display</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .slick-cell { + background: white !important; + border-color: transparent !important; + line-height: 19px !important; + } + + /* alternating offsets */ + .slick-row .cell-inner { + margin-right: 60px; + } + + .slick-row[row$="1"] .cell-inner, .slick-row[row$="3"] .cell-inner, .slick-row[row$="5"] .cell-inner, + .slick-row[row$="7"] .cell-inner, .slick-row[row$="9"] .cell-inner { + margin-left: 60px; + margin-right: 0; + } + + .contact-card-cell { + border-color: transparent !important; + } + + .cell-inner { + height: 80px; + margin: 10px; + padding: 10px; + background: #fafafa; + border: 1px solid gray; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + -moz-box-shadow: 1px 1px 5px silver; + -webkit-box-shadow: 1px 1px 5px silver; + -webkit-transition: all 0.5s; + } + + .cell-inner:hover { + background: #f0f0f0; + } + + .cell-left { + width: 40px; + height: 100%; + float: left; + border-right: 1px dotted gray; + background: url("../images/user_identity.gif") no-repeat top center; + } + + .cell-main { + margin-left: 50px; + } + </style> +</head> +<body> +<table width="100%"> + <tr> + <td valign="top" width="50%"> + <div id="myGrid" style="width:600px;height:500px;"></div> + </td> + <td valign="top"> + <h2>Demonstrates:</h2> + + <ul> + <li>Template-based rendering using John Resig's <a href="http://ejohn.org/blog/javascript-micro-templating/" + target=_blank>micro-templates</a> while still using + SlickGrid's virtual rendering technology. + </li> + </ul> + </td> + </tr> +</table> + +<!-- cell template --> +<script type="text/html" id="cell_template"> + <div class="cell-inner"> + <div class="cell-left"></div> + <div class="cell-main"> + <b><%=name%></b><br/> + <%=title%><br/> + <%=email%><br/> + <%=phone%> + </div> + </div> +</script> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../slick.grid.js"></script> + +<script> + // Simple JavaScript Templating + // John Resig - http://ejohn.org/ - MIT Licensed + (function () { + var cache = {}; + + this.tmpl = function tmpl(str, data) { + // Figure out if we're getting a template, or if we need to + // load the template - and be sure to cache the result. + var fn = !/\W/.test(str) ? + cache[str] = cache[str] || + tmpl(document.getElementById(str).innerHTML) : + + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + + + // Introduce the data as local variables using with(){} + "with(obj){p.push('" + + + // Convert the template into pure JavaScript + str + .replace(/[\r\t\n]/g, " ") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "',$1,'") + .split("\t").join("');") + .split("%>").join("p.push('") + .split("\r").join("\\'") + + "');}return p.join('');"); + + // Provide some basic currying to the user + return data ? fn(data) : fn; + }; + })(); + + + var grid; + var data = []; + + var columns = [ + {id:"contact-card", name:"Contacts", formatter:renderCell, width:500, cssClass:"contact-card-cell"} + ]; + + var options = { + rowHeight:140, + editable:false, + enableAddRow:false, + enableCellNavigation:false, + enableColumnReorder:false + }; + + var compiled_template = tmpl("cell_template"); + + function renderCell(row, cell, value, columnDef, dataContext) { + return compiled_template(dataContext); + } + + $(function () { + for (var i = 0; i < 100; i++) { + var d = (data[i] = {}); + + d["name"] = "User " + i; + d["email"] = "test.user@nospam.org"; + d["title"] = "Regional sales manager"; + d["phone"] = "206-000-0000"; + } + + + grid = new Slick.Grid("#myGrid", data, columns, options); + }) + +</script> +</body> </html> diff --git a/examples/example9-row-reordering.html b/examples/example9-row-reordering.html index fa3920e..fbfd18a 100644 --- a/examples/example9-row-reordering.html +++ b/examples/example9-row-reordering.html @@ -1,305 +1,314 @@ <!DOCTYPE HTML> <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <title>SlickGrid example 9: Row reordering</title> - <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" /> - <style> - .cell-title { - zfont-weight: bold; - } - .cell-effort-driven { - text-align: center; - } - .cell-reorder { - cursor: move; - background: url("../images/drag-handle.png") no-repeat center center; - } - - .cell-selection { - border-right-color: silver; - border-right-style: solid; - background: #f5f5f5; - color: gray; - text-align: right; - font-size: 10px; - } - - .slick-row.selected .cell-selection { - background-color: transparent; /* show default selected row background */ - } - - .recycle-bin { - width: 120px; - border: 1px solid gray; - background: beige; - padding: 4px; - font-size: 12pt; - font-weight: bold; - color: black; - text-align: center; - -moz-border-radius: 10px; - } - - .red { - background: red; - } - - .bold { - font-weight: bold; - } - </style> - </head> - <body> - <div style="position:relative"> - <div style="width:600px;"> - <div class="grid-header" style="width:100%"> - <label>Santa's TODO list:</label> - </div> - <div id="myGrid" style="width:100%;height:500px;"></div> - </div> - <div class="options-panel"> - <b>Tips:</b> - <hr/> - <div style="padding:6px;"> - Click to select, Ctrl-click to toggle selection, Shift-click to select a range.<br/> - Drag one or more rows by the handle to reorder.<br/> - Drag one or more rows to the recycle bin to delete. - - <br/> - <br/> - <div id="dropzone" class="recycle-bin"> - Recycle Bin - </div> - </div> - </div> - </div> - - <script src="../lib/firebugx.js"></script> - - <script src="../lib/jquery-1.7.min.js"></script> - <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> - <script src="../lib/jquery.event.drag-2.0.min.js"></script> - <script src="../lib/jquery.event.drop-2.0.min.js"></script> - - <script src="../slick.core.js"></script> - <script src="../plugins/slick.cellrangeselector.js"></script> - <script src="../plugins/slick.cellselectionmodel.js"></script> - <script src="../plugins/slick.rowselectionmodel.js"></script> - <script src="../plugins/slick.rowmovemanager.js"></script> - <script src="../slick.editors.js"></script> - <script src="../slick.grid.js"></script> - - <script> - var grid; - var data = []; - - var columns = [ - { - id: "#", - name: "", - width: 40, - behavior: "selectAndMove", - selectable: false, - resizable: false, - cssClass: "cell-reorder dnd" - }, - { - id: "name", - name: "Name", - field: "name", - width: 500, - cssClass: "cell-title", - editor: TextCellEditor, - validator: requiredFieldValidator - }, - { - id: "complete", - name: "Complete", - width: 60, - cssClass: "cell-effort-driven", - field: "complete", - cannotTriggerInsert: true, - formatter: BoolCellFormatter, - editor: YesNoCheckboxCellEditor +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example 9: Row reordering</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" /> + <link rel="stylesheet" href="examples.css" type="text/css" /> + <style> + .cell-title { + zfont-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + + .cell-reorder { + cursor: move; + background: url("../images/drag-handle.png") no-repeat center center; + } + + .cell-selection { + border-right-color: silver; + border-right-style: solid; + background: #f5f5f5; + color: gray; + text-align: right; + font-size: 10px; + } + + .slick-row.selected .cell-selection { + background-color: transparent; /* show default selected row background */ + } + + .recycle-bin { + width: 120px; + border: 1px solid gray; + background: beige; + padding: 4px; + font-size: 12pt; + font-weight: bold; + color: black; + text-align: center; + -moz-border-radius: 10px; + } + + .red { + background: red; + } + + .bold { + font-weight: bold; + } + </style> +</head> +<body> +<div style="position:relative"> + <div style="width:600px;"> + <div class="grid-header" style="width:100%"> + <label>Santa's TODO list:</label> + </div> + <div id="myGrid" style="width:100%;height:500px;"></div> + </div> + <div class="options-panel"> + <b>Tips:</b> + <hr/> + <div style="padding:6px;"> + Click to select, Ctrl-click to toggle selection, Shift-click to select a range.<br/> + Drag one or more rows by the handle to reorder.<br/> + Drag one or more rows to the recycle bin to delete. + + <br/> + <br/> + + <div id="dropzone" class="recycle-bin"> + Recycle Bin + </div> + </div> + </div> +</div> + +<script src="../lib/firebugx.js"></script> + +<script src="../lib/jquery-1.7.min.js"></script> +<script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> +<script src="../lib/jquery.event.drag-2.0.min.js"></script> +<script src="../lib/jquery.event.drop-2.0.min.js"></script> + +<script src="../slick.core.js"></script> +<script src="../plugins/slick.cellrangeselector.js"></script> +<script src="../plugins/slick.cellselectionmodel.js"></script> +<script src="../plugins/slick.rowselectionmodel.js"></script> +<script src="../plugins/slick.rowmovemanager.js"></script> +<script src="../slick.editors.js"></script> +<script src="../slick.grid.js"></script> + +<script> +var grid; +var data = []; + +var columns = [ + { + id:"#", + name:"", + width:40, + behavior:"selectAndMove", + selectable:false, + resizable:false, + cssClass:"cell-reorder dnd" + }, + { + id:"name", + name:"Name", + field:"name", + width:500, + cssClass:"cell-title", + editor:TextCellEditor, + validator:requiredFieldValidator + }, + { + id:"complete", + name:"Complete", + width:60, + cssClass:"cell-effort-driven", + field:"complete", + cannotTriggerInsert:true, + formatter:BoolCellFormatter, + editor:YesNoCheckboxCellEditor + } +]; + +var options = { + editable:true, + enableAddRow:true, + enableRowReordering:true, + enableCellNavigation:true, + forceFitColumns:true, + autoEdit:false +}; + +function requiredFieldValidator(value) { + if (value == null || value == undefined || !value.length) { + return {valid:false, msg:"This is a required field"}; + } + else { + return {valid:true, msg:null}; + } +} + +$(function () { + data = [ + { name:"Make a list", complete:true}, + { name:"Check it twice", complete:false}, + { name:"Find out who's naughty", complete:false}, + { name:"Find out who's nice", complete:false} + ]; + + grid = new Slick.Grid("#myGrid", data, columns, options); + + grid.setSelectionModel(new Slick.RowSelectionModel()); + + var moveRowsPlugin = new Slick.RowMoveManager(); + + moveRowsPlugin.onBeforeMoveRows.subscribe(function (e, data) { + for (var i = 0; i < data.rows.length; i++) { + // no point in moving before or after itself + if (data.rows[i] == data.insertBefore || data.rows[i] == data.insertBefore - 1) { + e.stopPropagation(); + return false; + } + } + + return true; + }); + + moveRowsPlugin.onMoveRows.subscribe(function (e, args) { + var extractedRows = [], left, right; + var rows = args.rows; + var insertBefore = args.insertBefore; + left = data.slice(0, insertBefore); + right = data.slice(insertBefore, data.length); + + for (var i = 0; i < rows.length; i++) { + extractedRows.push(data[rows[i]]); + } + + rows.sort().reverse(); + + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + if (row < insertBefore) { + left.splice(row, 1); + } + else { + right.splice(row - insertBefore, 1); + } + } + + data = left.concat(extractedRows.concat(right)); + + var selectedRows = []; + for (var i = 0; i < rows.length; i++) + selectedRows.push(left.length + i); + + grid.resetActiveCell(); + grid.setData(data); + grid.setSelectedRows(selectedRows); + grid.render(); + }); + + grid.registerPlugin(moveRowsPlugin); + + grid.onDragInit.subscribe(function (e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + }); + + grid.onDragStart.subscribe(function (e, dd) { + var cell = grid.getCellFromEvent(e); + if (!cell) { + return; + } + + dd.row = cell.row; + if (!data[dd.row]) { + return; + } + + if (Slick.GlobalEditorLock.isActive()) { + return; + } + + e.stopImmediatePropagation(); + dd.mode = "recycle"; + + var selectedRows = grid.getSelectedRows(); + + if (!selectedRows.length || $.inArray(dd.row, selectedRows) == -1) { + selectedRows = [dd.row]; + grid.setSelectedRows(selectedRows); + } + + dd.rows = selectedRows; + dd.count = selectedRows.length; + + var proxy = $("<span></span>") + .css({ + position:"absolute", + display:"inline-block", + padding:"4px 10px", + background:"#e0e0e0", + border:"1px solid gray", + "z-index":99999, + "-moz-border-radius":"8px", + "-moz-box-shadow":"2px 2px 6px silver" + }) + .text("Drag to Recycle Bin to delete " + dd.count + " selected row(s)") + .appendTo("body"); + + dd.helper = proxy; + + $(dd.available).css("background", "pink"); + + return proxy; + }); + + grid.onDrag.subscribe(function (e, dd) { + if (dd.mode != "recycle") { + return; + } + e.stopImmediatePropagation(); + dd.helper.css({top:e.pageY + 5, left:e.pageX + 5}); + }); + + grid.onDragEnd.subscribe(function (e, dd) { + if (dd.mode != "recycle") { + return; + } + e.stopImmediatePropagation(); + dd.helper.remove(); + $(dd.available).css("background", "beige"); + }); + + + $("#dropzone") + .bind("dropstart", function (e, dd) { + $(this).css("background", "yellow"); + }) + .bind("dropend", function (e, dd) { + $(dd.available).css("background", "pink"); + }) + .bind("drop", function (e, dd) { + var rowsToDelete = dd.rows.sort().reverse(); + for (var i = 0; i < rowsToDelete.length; i++) { + data.splice(rowsToDelete[i], 1); } - ]; - - var options = { - editable: true, - enableAddRow: true, - enableRowReordering: true, - enableCellNavigation: true, - forceFitColumns: true, - autoEdit: false - }; - - function requiredFieldValidator(value) { - if (value == null || value == undefined || !value.length) - return {valid:false, msg:"This is a required field"}; - else - return {valid:true, msg:null}; - } - - $(function() - { - data = [ - { name: "Make a list", complete: true}, - { name: "Check it twice", complete: false}, - { name: "Find out who's naughty", complete: false}, - { name: "Find out who's nice", complete: false} - ]; - - grid = new Slick.Grid("#myGrid", data, columns, options); - - grid.setSelectionModel(new Slick.RowSelectionModel()); - - var moveRowsPlugin = new Slick.RowMoveManager(); - - moveRowsPlugin.onBeforeMoveRows.subscribe(function(e,data) { - for (var i = 0; i < data.rows.length; i++) { - // no point in moving before or after itself - if (data.rows[i] == data.insertBefore || data.rows[i] == data.insertBefore - 1) { - e.stopPropagation(); - return false; - } - } - - return true; - }); - - moveRowsPlugin.onMoveRows.subscribe(function(e,args) { - var extractedRows = [], left, right; - var rows = args.rows; - var insertBefore = args.insertBefore; - left = data.slice(0,insertBefore); - right = data.slice(insertBefore,data.length); - - for (var i=0; i<rows.length; i++) { - extractedRows.push(data[rows[i]]); - } - - rows.sort().reverse(); - - for (var i=0; i<rows.length; i++) { - var row = rows[i]; - if (row < insertBefore) - left.splice(row,1); - else - right.splice(row-insertBefore,1); - } - - data = left.concat(extractedRows.concat(right)); - - var selectedRows = []; - for (var i=0; i<rows.length; i++) - selectedRows.push(left.length+i); - - grid.resetActiveCell(); - grid.setData(data); - grid.setSelectedRows(selectedRows); - grid.render(); - }); - - grid.registerPlugin(moveRowsPlugin); - - grid.onDragInit.subscribe(function(e,dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - }); - - grid.onDragStart.subscribe(function(e,dd) { - var cell = grid.getCellFromEvent(e); - if (!cell) - return; - - dd.row = cell.row; - if (!data[dd.row]) - return; - - if (Slick.GlobalEditorLock.isActive()) - return; - - e.stopImmediatePropagation(); - dd.mode = "recycle"; - - var selectedRows = grid.getSelectedRows(); - - if (!selectedRows.length || $.inArray(dd.row,selectedRows) == -1) { - selectedRows = [dd.row]; - grid.setSelectedRows(selectedRows); - } - - dd.rows = selectedRows; - dd.count = selectedRows.length; - - var proxy = $("<span></span>") - .css({ - position: "absolute", - display: "inline-block", - padding: "4px 10px", - background: "#e0e0e0", - border: "1px solid gray", - "z-index": 99999, - "-moz-border-radius": "8px", - "-moz-box-shadow": "2px 2px 6px silver" - }) - .text("Drag to Recycle Bin to delete " + dd.count + " selected row(s)") - .appendTo("body"); - - dd.helper = proxy; - - $(dd.available).css("background","pink"); - - return proxy; - }); - - grid.onDrag.subscribe(function(e,dd) { - if (dd.mode != "recycle") { - return; - } - e.stopImmediatePropagation(); - dd.helper.css({top: e.pageY + 5, left: e.pageX + 5}); - }); - - grid.onDragEnd.subscribe(function(e,dd) { - if (dd.mode != "recycle") { - return; - } - e.stopImmediatePropagation(); - dd.helper.remove(); - $(dd.available).css("background","beige"); - }); - - - $("#dropzone") - .bind("dropstart", function(e,dd) { - $(this).css("background","yellow"); - }) - .bind("dropend", function(e,dd) { - $(dd.available).css("background","pink"); - }) - .bind("drop", function(e,dd) { - var rowsToDelete = dd.rows.sort().reverse(); - for (var i=0; i<rowsToDelete.length; i++) { - data.splice(rowsToDelete[i],1); - } - grid.invalidate(); - grid.setSelectedRows([]); - }); - - - grid.onAddNewRow.subscribe(function(e, args) { - var item = {name:"New task", complete: false}; - $.extend(item, args.item); - data.push(item); - grid.invalidateRows([data.length - 1]); - grid.updateRowCount(); - grid.render(); - }); - }) - </script> - </body> + grid.invalidate(); + grid.setSelectedRows([]); + }); + + + grid.onAddNewRow.subscribe(function (e, args) { + var item = {name:"New task", complete:false}; + $.extend(item, args.item); + data.push(item); + grid.invalidateRows([data.length - 1]); + grid.updateRowCount(); + grid.render(); + }); +}) +</script> +</body> </html> diff --git a/examples/examples.css b/examples/examples.css index 63b63e3..8e7d746 100644 --- a/examples/examples.css +++ b/examples/examples.css @@ -1,239 +1,230 @@ @import url('slick-default-theme.css'); * { - font-family: arial; - font-size: 8pt; + font-family: arial; + font-size: 8pt; } body { - background: beige; - padding: 0; - margin: 8; + background: beige; + padding: 0; + margin: 8; } h2 { - font-size: 10pt; - border-bottom: 1px dotted gray; + font-size: 10pt; + border-bottom: 1px dotted gray; } ul { - margin-left: 0; - padding: 0; - cursor: default; + margin-left: 0; + padding: 0; + cursor: default; } li { - background: url("../images/arrow_right_spearmint.png") no-repeat center left; - padding: 0 0 0 14px; + background: url("../images/arrow_right_spearmint.png") no-repeat center left; + padding: 0 0 0 14px; - list-style: none; - margin: 0; -} + list-style: none; + margin: 0; +} #myGrid { - background: white; - outline: 0; - border: 1px solid gray; + background: white; + outline: 0; + border: 1px solid gray; } .grid-header { - border: 1px solid gray; - border-bottom: 0; - border-top: 0; - background: url('../images/header-bg.gif') repeat-x center top; - color: black; - height: 24px; - line-height: 24px; -} + border: 1px solid gray; + border-bottom: 0; + border-top: 0; + background: url('../images/header-bg.gif') repeat-x center top; + color: black; + height: 24px; + line-height: 24px; +} .grid-header label { - display: inline-block; - font-weight: bold; - margin: auto auto auto 6px; + display: inline-block; + font-weight: bold; + margin: auto auto auto 6px; } .grid-header .ui-icon { - margin: 4px 4px auto 6px; - background-color: transparent; - border-color: transparent; + margin: 4px 4px auto 6px; + background-color: transparent; + border-color: transparent; } .grid-header .ui-icon.ui-state-hover { - background-color: white; + background-color: white; } .grid-header #txtSearch { - margin: 0 4px 0 4px; - padding: 2px 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border: 1px solid silver; + margin: 0 4px 0 4px; + padding: 2px 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border: 1px solid silver; } .options-panel { - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border: 1px solid silver; - background: #f0f0f0; - padding: 4px; - margin-bottom: 20px; - width:320px; - position:absolute; - top:0px; - left:650px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border: 1px solid silver; + background: #f0f0f0; + padding: 4px; + margin-bottom: 20px; + width: 320px; + position: absolute; + top: 0px; + left: 650px; } - - /* Individual cell styles */ .slick-cell.task-name { - font-weight: bold; - text-align: right; + font-weight: bold; + text-align: right; } - .slick-cell.task-percent { - text-align: right; + text-align: right; } - .slick-cell.cell-move-handle { - font-weight: bold; - text-align: right; - border-right: solid gray; + font-weight: bold; + text-align: right; + border-right: solid gray; - background: #efefef; - cursor: move; + background: #efefef; + cursor: move; } .cell-move-handle:hover { - background: #b6b9bd; + background: #b6b9bd; } .slick-row.selected .cell-move-handle { - background: #D5DC8D; + background: #D5DC8D; } .slick-row .cell-actions { - text-align: left; + text-align: left; } .slick-row.complete { - background-color: #DFD; - color: #555; + background-color: #DFD; + color: #555; } .percent-complete-bar { - display: inline-block; - height: 6px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; + display: inline-block; + height: 6px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; } - /* TextCellEditor, DateCellEditor */ input.editor-text { - width: 100%; - height: 100%; - border: 0; - margin: 0; - background: transparent; - outline: 0; - padding: 0; + width: 100%; + height: 100%; + border: 0; + margin: 0; + background: transparent; + outline: 0; + padding: 0; } .ui-datepicker-trigger { - margin-top: 2px; - padding: 0; - vertical-align: top; + margin-top: 2px; + padding: 0; + vertical-align: top; } /* PercentCompleteCellEditor */ input.editor-percentcomplete { - width: 100%; - height: 100%; - border: 0; - margin: 0; - background: transparent; - outline: 0; - padding: 0; + width: 100%; + height: 100%; + border: 0; + margin: 0; + background: transparent; + outline: 0; + padding: 0; - float: left; + float: left; } .editor-percentcomplete-picker { - position: relative; - display: inline-block; - width: 16px; - height: 100%; - background: url("../images/pencil.gif") no-repeat center center; - overflow: visible; - z-index: 1000; - float: right; -} - + position: relative; + display: inline-block; + width: 16px; + height: 100%; + background: url("../images/pencil.gif") no-repeat center center; + overflow: visible; + z-index: 1000; + float: right; +} + .editor-percentcomplete-helper { - border: 0 solid gray; - position: absolute; - top: -2px; - left: -9px; - background: url("../images/editor-helper-bg.gif") no-repeat top left; - padding-left: 9px; + border: 0 solid gray; + position: absolute; + top: -2px; + left: -9px; + background: url("../images/editor-helper-bg.gif") no-repeat top left; + padding-left: 9px; - width: 120px; - height: 140px; - display: none; - overflow: visible; + width: 120px; + height: 140px; + display: none; + overflow: visible; } - .editor-percentcomplete-wrapper { - background:beige; - padding: 20px 8px; + background: beige; + padding: 20px 8px; - width:100%; - height:98px; - border:1px solid gray; - border-left:0; + width: 100%; + height: 98px; + border: 1px solid gray; + border-left: 0; } .editor-percentcomplete-buttons { - float: right; + float: right; } .editor-percentcomplete-buttons button { - width: 80px; + width: 80px; } - .editor-percentcomplete-slider { - float: left; + float: left; } - .editor-percentcomplete-picker:hover .editor-percentcomplete-helper { - display: block; +.editor-percentcomplete-picker:hover .editor-percentcomplete-helper { + display: block; } .editor-percentcomplete-helper:hover { - display: block; + display: block; } - - /* YesNoSelectCellEditor */ select.editor-yesno { - width: 100%; - margin: 0; - vertical-align: middle; + width: 100%; + margin: 0; + vertical-align: middle; } /* YesNoCheckboxCellEditor */ input.editor-checkbox { - margin: 0; - height: 100%; - padding: 0; - border: 0; + margin: 0; + height: 100%; + padding: 0; + border: 0; } diff --git a/examples/slick-default-theme.css b/examples/slick-default-theme.css index 5c96b9f..c1f3f2d 100644 --- a/examples/slick-default-theme.css +++ b/examples/slick-default-theme.css @@ -6,100 +6,99 @@ classes should alter those! */ .slick-header-columns { - background: url('../images/header-columns-bg.gif') repeat-x center bottom; - border-bottom: 1px solid silver; + background: url('../images/header-columns-bg.gif') repeat-x center bottom; + border-bottom: 1px solid silver; } .slick-header-column { - background: url('../images/header-columns-bg.gif') repeat-x center bottom; - border-right: 1px solid silver; + background: url('../images/header-columns-bg.gif') repeat-x center bottom; + border-right: 1px solid silver; } .slick-header-column:hover, .slick-header-column-active { - background: white url('../images/header-columns-over-bg.gif') repeat-x center bottom; + background: white url('../images/header-columns-over-bg.gif') repeat-x center bottom; } .slick-headerrow { - background: #fafafa; + background: #fafafa; } .slick-headerrow-column { - background: #fafafa; - border-bottom: 0; - height: 100%; + background: #fafafa; + border-bottom: 0; + height: 100%; } .slick-row.ui-state-active { - background: #F5F7D7; + background: #F5F7D7; } .slick-row { - position: absolute; - background: white; - border: 0px; - line-height: 20px; + position: absolute; + background: white; + border: 0px; + line-height: 20px; } .slick-row.selected { - z-index: 10; - background: #DFE8F6; + z-index: 10; + background: #DFE8F6; } .slick-cell { - padding-left: 4px; - padding-right: 4px; + padding-left: 4px; + padding-right: 4px; } .slick-group { - border-bottom: 2px solid silver; + border-bottom: 2px solid silver; } .slick-group-toggle { - width: 9px; - height: 9px; - margin-right: 5px; + width: 9px; + height: 9px; + margin-right: 5px; } .slick-group-toggle.expanded { - background: url(../images/collapse.gif) no-repeat center center; + background: url(../images/collapse.gif) no-repeat center center; } .slick-group-toggle.collapsed { - background: url(../images/expand.gif) no-repeat center center; + background: url(../images/expand.gif) no-repeat center center; } .slick-group-totals { - color: gray; - background: white; + color: gray; + background: white; } .slick-cell.selected { - background-color: beige; + background-color: beige; } .slick-cell.active { - border-color: gray; - border-style: solid; + border-color: gray; + border-style: solid; } - .slick-sortable-placeholder { - background: silver!important; + background: silver !important; } -.slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] { - background: #fafafa; +.slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] { + background: #fafafa; } .slick-row.ui-state-active { - background: #F5F7D7; + background: #F5F7D7; } .slick-row.loading { - opacity: 0.5; - filter: alpha(opacity=50); + opacity: 0.5; + filter: alpha(opacity = 50); } .slick-cell.invalid { - border-color: red; + border-color: red; }
\ No newline at end of file diff --git a/examples/slick.compositeeditor.js b/examples/slick.compositeeditor.js index 9dbbc41..9854672 100644 --- a/examples/slick.compositeeditor.js +++ b/examples/slick.compositeeditor.js @@ -1,213 +1,211 @@ -;(function($) { +; +(function ($) { + $.extend(true, window, { + Slick:{ + CompositeEditor:CompositeEditor + } + }); + + + /*** + * A composite SlickGrid editor factory. + * Generates an editor that is composed of multiple editors for given columns. + * Individual editors are provided given containers instead of the original cell. + * Validation will be performed on all editors individually and the results will be aggregated into one + * validation result. + * + * + * The returned editor will have its prototype set to CompositeEditor, so you can use the "instanceof" check. + * + * NOTE: This doesn't work for detached editors since they will be created and positioned relative to the + * active cell and not the provided container. + * + * @namespace Slick + * @class CompositeEditor + * @constructor + * @param columns {Array} Column definitions from which editors will be pulled. + * @param containers {Array} Container HTMLElements in which editors will be placed. + * @param options {Object} Options hash: + * validationFailedMsg - A generic failed validation message set on the aggregated validation resuls. + * hide - A function to be called when the grid asks the editor to hide itself. + * show - A function to be called when the grid asks the editor to show itself. + * position - A function to be called when the grid asks the editor to reposition itself. + * destroy - A function to be called when the editor is destroyed. + */ + function CompositeEditor(columns, containers, options) { + var defaultOptions = { + validationFailedMsg:"Some of the fields have failed validation", + show:null, + hide:null, + position:null, + destroy:null + }; + + var noop = function () { + }; + + var firstInvalidEditor; + + options = $.extend({}, defaultOptions, options); + + + function getContainerBox(i) { + var c = containers[i]; + var offset = $(c).offset(); + var w = $(c).width(); + var h = $(c).height(); + + return { + top:offset.top, + left:offset.left, + bottom:offset.top + h, + right:offset.left + w, + width:w, + height:h, + visible:true + }; + } + + + function editor(args) { + var editors = []; + + + function init() { + var newArgs = {}; + var idx = columns.length; + while (idx--) { + if (columns[idx].editor) { + newArgs = $.extend({}, args); + newArgs.container = containers[idx]; + newArgs.column = columns[idx]; + newArgs.position = getContainerBox(idx); + newArgs.commitChanges = noop; + newArgs.cancelChanges = noop; + + editors[idx] = new (columns[idx].editor)(newArgs); + } + } + } + - $.extend(true, window, { - Slick: { - CompositeEditor: CompositeEditor + this.destroy = function () { + var idx = editors.length; + while (idx--) { + editors[idx].destroy(); } - }); - - - /*** - * A composite SlickGrid editor factory. - * Generates an editor that is composed of multiple editors for given columns. - * Individual editors are provided given containers instead of the original cell. - * Validation will be performed on all editors individually and the results will be aggregated into one - * validation result. - * - * - * The returned editor will have its prototype set to CompositeEditor, so you can use the "instanceof" check. - * - * NOTE: This doesn't work for detached editors since they will be created and positioned relative to the - * active cell and not the provided container. - * - * @namespace Slick - * @class CompositeEditor - * @constructor - * @param columns {Array} Column definitions from which editors will be pulled. - * @param containers {Array} Container HTMLElements in which editors will be placed. - * @param options {Object} Options hash: - * validationFailedMsg - A generic failed validation message set on the aggregated validation resuls. - * hide - A function to be called when the grid asks the editor to hide itself. - * show - A function to be called when the grid asks the editor to show itself. - * position - A function to be called when the grid asks the editor to reposition itself. - * destroy - A function to be called when the editor is destroyed. - */ - function CompositeEditor(columns, containers, options) { - var defaultOptions = { - validationFailedMsg: "Some of the fields have failed validation", - show: null, - hide: null, - position: null, - destroy: null - }; - - var noop = function () {}; - - var firstInvalidEditor; - - options = $.extend({}, defaultOptions, options); - - - function getContainerBox(i) { - var c = containers[i]; - var offset = $(c).offset(); - var w = $(c).width(); - var h = $(c).height(); - - return { - top: offset.top, - left: offset.left, - bottom: offset.top + h, - right: offset.left + w, - width: w, - height: h, - visible: true - }; + + options.destroy && options.destroy(); + }; + + + this.focus = function () { + // if validation has failed, set the focus to the first invalid editor + (firstInvalidEditor || editors[0]).focus(); + }; + + + this.isValueChanged = function () { + var idx = editors.length; + while (idx--) { + if (editors[idx].isValueChanged()) { + return true; + } + } + return false; + }; + + + this.serializeValue = function () { + var serializedValue = []; + var idx = editors.length; + while (idx--) { + serializedValue[idx] = editors[idx].serializeValue(); } + return serializedValue; + }; - function editor(args) { - var editors = []; - - - function init() { - var newArgs = {}; - var idx = columns.length; - while (idx--) { - if (columns[idx].editor) { - newArgs = $.extend({}, args); - newArgs.container = containers[idx]; - newArgs.column = columns[idx]; - newArgs.position = getContainerBox(idx); - newArgs.commitChanges = noop; - newArgs.cancelChanges = noop; - - editors[idx] = new (columns[idx].editor)(newArgs); - } - } - } - - - this.destroy = function () { - var idx = editors.length; - while (idx--) { - editors[idx].destroy(); - } - - options.destroy && options.destroy(); - }; - - - this.focus = function () { - // if validation has failed, set the focus to the first invalid editor - (firstInvalidEditor || editors[0]).focus(); - }; - - - this.isValueChanged = function () { - var idx = editors.length; - while (idx--) { - if (editors[idx].isValueChanged()) { - return true; - } - } - return false; - }; - - - this.serializeValue = function () { - var serializedValue = []; - var idx = editors.length; - while (idx--) { - serializedValue[idx] = editors[idx].serializeValue(); - } - return serializedValue; - }; - - - this.applyValue = function (item, state) { - var idx = editors.length; - while (idx--) { - editors[idx].applyValue(item, state[idx]); - } - }; - - - this.loadValue = function (item) { - var idx = editors.length; - while (idx--) { - editors[idx].loadValue(item); - } - }; - - - this.validate = function () { - var validationResults; - var errors = []; - - firstInvalidEditor = null; - - var idx = editors.length; - while (idx--) { - validationResults = editors[idx].validate(); - if (!validationResults.valid) { - firstInvalidEditor = editors[idx]; - errors.push({ - index: idx, - editor: editors[idx], - container: containers[idx], - msg: validationResults.msg - }); - } - } - - if (errors.length) { - return { - valid: false, - msg: options.validationFailedMsg, - errors: errors - }; - } - else { - return { - valid: true, - msg: "" - }; - } - }; - - - this.hide = function () { - var idx = editors.length; - while (idx--) { - editors[idx].hide && editors[idx].hide(); - } - - options.hide && options.hide(); - }; - - - this.show = function () { - var idx = editors.length; - while (idx--) { - editors[idx].show && editors[idx].show(); - } - - options.show && options.show(); - }; - - - this.position = function (box) { - options.position && options.position(box); - }; - - - init(); + this.applyValue = function (item, state) { + var idx = editors.length; + while (idx--) { + editors[idx].applyValue(item, state[idx]); } + }; - // so we can do "editor instanceof Slick.CompositeEditor - editor.prototype = this; - return editor; + this.loadValue = function (item) { + var idx = editors.length; + while (idx--) { + editors[idx].loadValue(item); + } + }; + + + this.validate = function () { + var validationResults; + var errors = []; + + firstInvalidEditor = null; + + var idx = editors.length; + while (idx--) { + validationResults = editors[idx].validate(); + if (!validationResults.valid) { + firstInvalidEditor = editors[idx]; + errors.push({ + index:idx, + editor:editors[idx], + container:containers[idx], + msg:validationResults.msg + }); + } + } + + if (errors.length) { + return { + valid:false, + msg:options.validationFailedMsg, + errors:errors + }; + } else { + return { + valid:true, + msg:"" + }; + } + }; + + + this.hide = function () { + var idx = editors.length; + while (idx--) { + editors[idx].hide && editors[idx].hide(); + } + options.hide && options.hide(); + }; + + + this.show = function () { + var idx = editors.length; + while (idx--) { + editors[idx].show && editors[idx].show(); + } + options.show && options.show(); + }; + + + this.position = function (box) { + options.position && options.position(box); + }; + + + init(); } + + // so we can do "editor instanceof Slick.CompositeEditor + editor.prototype = this; + + return editor; + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.autotooltips.js b/plugins/slick.autotooltips.js index c5c4897..4d2baea 100644 --- a/plugins/slick.autotooltips.js +++ b/plugins/slick.autotooltips.js @@ -1,49 +1,48 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "AutoTooltips": AutoTooltips - } - }); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "AutoTooltips":AutoTooltips + } + }); - function AutoTooltips(options) { - var _grid; - var _self = this; - var _defaults = { - maxToolTipLength: null - }; + function AutoTooltips(options) { + var _grid; + var _self = this; + var _defaults = { + maxToolTipLength:null + }; - function init(grid) { - options = $.extend(true,{},_defaults,options); - _grid = grid; - _grid.onMouseEnter.subscribe(handleMouseEnter); - } + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + _grid.onMouseEnter.subscribe(handleMouseEnter); + } - function destroy() { - _grid.onMouseEnter.unsubscribe(handleMouseEnter); - } + function destroy() { + _grid.onMouseEnter.unsubscribe(handleMouseEnter); + } - function handleMouseEnter(e,args) { - var cell = _grid.getCellFromEvent(e); - if (cell) { - var node = _grid.getCellNode(cell.row, cell.cell); - if ($(node).innerWidth() < node.scrollWidth) { - var text = $.trim($(node).text()); - if (options.maxToolTipLength && text.length > options.maxToolTipLength) { - text = text.substr(0, options.maxToolTipLength - 3) + "..."; - } - $(node).attr("title",text); - } - else { - $(node).attr("title",""); - } - } + function handleMouseEnter(e, args) { + var cell = _grid.getCellFromEvent(e); + if (cell) { + var node = _grid.getCellNode(cell.row, cell.cell); + if ($(node).innerWidth() < node.scrollWidth) { + var text = $.trim($(node).text()); + if (options.maxToolTipLength && text.length > options.maxToolTipLength) { + text = text.substr(0, options.maxToolTipLength - 3) + "..."; + } + $(node).attr("title", text); + } else { + $(node).attr("title", ""); } - - $.extend(this, { - "init": init, - "destroy": destroy - }); + } } + + $.extend(this, { + "init":init, + "destroy":destroy + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.cellcopymanager.js b/plugins/slick.cellcopymanager.js index 2b283f0..1d9c613 100644 --- a/plugins/slick.cellcopymanager.js +++ b/plugins/slick.cellcopymanager.js @@ -1,86 +1,86 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellCopyManager": CellCopyManager - } - }); - - - function CellCopyManager() { - var _grid; - var _self = this; - var _copiedRanges; +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "CellCopyManager":CellCopyManager + } + }); - function init(grid) { - _grid = grid; - _grid.onKeyDown.subscribe(handleKeyDown); - } - function destroy() { - _grid.onKeyDown.unsubscribe(handleKeyDown); - } + function CellCopyManager() { + var _grid; + var _self = this; + var _copiedRanges; - function handleKeyDown(e, args) { - var ranges; - if (!_grid.getEditorLock().isActive()) { - if (e.which == $.ui.keyCode.ESCAPE) { - if (_copiedRanges) { - e.preventDefault(); - clearCopySelection(); - _self.onCopyCancelled.notify({ranges:_copiedRanges}); - _copiedRanges = null; - } - } + function init(grid) { + _grid = grid; + _grid.onKeyDown.subscribe(handleKeyDown); + } - if (e.which == 67 && (e.ctrlKey || e.metaKey)) { - ranges = _grid.getSelectionModel().getSelectedRanges(); - if (ranges.length != 0) { - e.preventDefault(); - _copiedRanges = ranges; - markCopySelection(ranges); - _self.onCopyCells.notify({ranges:ranges}); - } - } + function destroy() { + _grid.onKeyDown.unsubscribe(handleKeyDown); + } - if (e.which == 86 && (e.ctrlKey || e.metaKey)) { - if (_copiedRanges) { - e.preventDefault(); - clearCopySelection(); - ranges = _grid.getSelectionModel().getSelectedRanges(); - _self.onPasteCells.notify({from:_copiedRanges, to:ranges}); - _copiedRanges = null; - } - } - } + function handleKeyDown(e, args) { + var ranges; + if (!_grid.getEditorLock().isActive()) { + if (e.which == $.ui.keyCode.ESCAPE) { + if (_copiedRanges) { + e.preventDefault(); + clearCopySelection(); + _self.onCopyCancelled.notify({ranges:_copiedRanges}); + _copiedRanges = null; + } } - function markCopySelection(ranges) { - var columns = _grid.getColumns(); - var hash = {}; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - hash[j] = {}; - for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { - hash[j][columns[k].id] = "copied"; - } - } - } - _grid.setCellCssStyles("copy-manager", hash); + if (e.which == 67 && (e.ctrlKey || e.metaKey)) { + ranges = _grid.getSelectionModel().getSelectedRanges(); + if (ranges.length != 0) { + e.preventDefault(); + _copiedRanges = ranges; + markCopySelection(ranges); + _self.onCopyCells.notify({ranges:ranges}); + } } - function clearCopySelection() { - _grid.removeCellCssStyles("copy-manager"); + if (e.which == 86 && (e.ctrlKey || e.metaKey)) { + if (_copiedRanges) { + e.preventDefault(); + clearCopySelection(); + ranges = _grid.getSelectionModel().getSelectedRanges(); + _self.onPasteCells.notify({from:_copiedRanges, to:ranges}); + _copiedRanges = null; + } } + } + } - $.extend(this, { - "init": init, - "destroy": destroy, - "clearCopySelection": clearCopySelection, + function markCopySelection(ranges) { + var columns = _grid.getColumns(); + var hash = {}; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + hash[j] = {}; + for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { + hash[j][columns[k].id] = "copied"; + } + } + } + _grid.setCellCssStyles("copy-manager", hash); + } - "onCopyCells": new Slick.Event(), - "onCopyCancelled": new Slick.Event(), - "onPasteCells": new Slick.Event() - }); + function clearCopySelection() { + _grid.removeCellCssStyles("copy-manager"); } + + $.extend(this, { + "init":init, + "destroy":destroy, + "clearCopySelection":clearCopySelection, + + "onCopyCells":new Slick.Event(), + "onCopyCancelled":new Slick.Event(), + "onPasteCells":new Slick.Event() + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.cellrangedecorator.js b/plugins/slick.cellrangedecorator.js index 38df0b6..342f705 100644 --- a/plugins/slick.cellrangedecorator.js +++ b/plugins/slick.cellrangedecorator.js @@ -1,65 +1,64 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellRangeDecorator": CellRangeDecorator - } - }); - - /*** - * Displays an overlay on top of a given cell range. - * - * TODO: - * Currently, it blocks mouse events to DOM nodes behind it. - * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding. - * Could also construct the borders separately using 4 individual DIVs. - * - * @param {Grid} grid - * @param {Object} options - */ - function CellRangeDecorator(grid, options) { - var _elem; - var _defaults = { - selectionCss: { - "zIndex": "9999", - "border": "2px dashed red" - } - }; - - options = $.extend(true, {}, _defaults, options); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "CellRangeDecorator":CellRangeDecorator + } + }); + /*** + * Displays an overlay on top of a given cell range. + * + * TODO: + * Currently, it blocks mouse events to DOM nodes behind it. + * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding. + * Could also construct the borders separately using 4 individual DIVs. + * + * @param {Grid} grid + * @param {Object} options + */ + function CellRangeDecorator(grid, options) { + var _elem; + var _defaults = { + selectionCss:{ + "zIndex":"9999", + "border":"2px dashed red" + } + }; - function show(range) { - if (!_elem) { - _elem = $("<div></div>", {css: options.selectionCss}) - .css("position", "absolute") - .appendTo(grid.getCanvasNode()); + options = $.extend(true, {}, _defaults, options); - } - var from = grid.getCellNodeBox(range.fromRow,range.fromCell); - var to = grid.getCellNodeBox(range.toRow,range.toCell); + function show(range) { + if (!_elem) { + _elem = $("<div></div>", {css:options.selectionCss}) + .css("position", "absolute") + .appendTo(grid.getCanvasNode()); + } - _elem.css({ - top: from.top - 1, - left: from.left - 1, - height: to.bottom - from.top - 2, - width: to.right - from.left - 2 - }); + var from = grid.getCellNodeBox(range.fromRow, range.fromCell); + var to = grid.getCellNodeBox(range.toRow, range.toCell); - return _elem; - } + _elem.css({ + top:from.top - 1, + left:from.left - 1, + height:to.bottom - from.top - 2, + width:to.right - from.left - 2 + }); - function hide() { - if (_elem) { - _elem.remove(); - _elem = null; - } - } + return _elem; + } - $.extend(this, { - "show": show, - "hide": hide - }); + function hide() { + if (_elem) { + _elem.remove(); + _elem = null; + } } + + $.extend(this, { + "show":show, + "hide":hide + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.cellrangeselector.js b/plugins/slick.cellrangeselector.js index 3928b2f..118239e 100644 --- a/plugins/slick.cellrangeselector.js +++ b/plugins/slick.cellrangeselector.js @@ -1,112 +1,112 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellRangeSelector": CellRangeSelector - } - }); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "CellRangeSelector":CellRangeSelector + } + }); + + + function CellRangeSelector(options) { + var _grid; + var _canvas; + var _dragging; + var _decorator; + var _self = this; + var _defaults = { + selectionCss:{ + "border":"2px dashed blue" + } + }; + + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _decorator = new Slick.CellRangeDecorator(grid, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _grid.onDragInit.subscribe(handleDragInit); + _grid.onDragStart.subscribe(handleDragStart); + _grid.onDrag.subscribe(handleDrag); + _grid.onDragEnd.subscribe(handleDragEnd); + } + function destroy() { + _grid.onDragInit.unsubscribe(handleDragInit); + _grid.onDragStart.unsubscribe(handleDragStart); + _grid.onDrag.unsubscribe(handleDrag); + _grid.onDragEnd.unsubscribe(handleDragEnd); + } - function CellRangeSelector(options) { - var _grid; - var _canvas; - var _dragging; - var _decorator; - var _self = this; - var _defaults = { - selectionCss: { - "border": "2px dashed blue" - } - }; - - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _decorator = new Slick.CellRangeDecorator(grid, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onDragInit.subscribe(handleDragInit); - _grid.onDragStart.subscribe(handleDragStart); - _grid.onDrag.subscribe(handleDrag); - _grid.onDragEnd.subscribe(handleDragEnd); - } + function handleDragInit(e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + } - function destroy() { - _grid.onDragInit.unsubscribe(handleDragInit); - _grid.onDragStart.unsubscribe(handleDragStart); - _grid.onDrag.unsubscribe(handleDrag); - _grid.onDragEnd.unsubscribe(handleDragEnd); + function handleDragStart(e, dd) { + var cell = _grid.getCellFromEvent(e); + if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { + if (_grid.canCellBeSelected(cell.row, cell.cell)) { + _dragging = true; + e.stopImmediatePropagation(); } + } + if (!_dragging) { + return; + } - function handleDragInit(e, dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - } + var start = _grid.getCellFromPoint( + dd.startX - $(_canvas).offset().left, + dd.startY - $(_canvas).offset().top); - function handleDragStart(e, dd) { - var cell = _grid.getCellFromEvent(e); - if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { - if (_grid.canCellBeSelected(cell.row,cell.cell)) { - _dragging = true; - e.stopImmediatePropagation(); - } - } - if (!_dragging) { - return; - } - - var start = _grid.getCellFromPoint( - dd.startX - $(_canvas).offset().left, - dd.startY - $(_canvas).offset().top); - - dd.range = {start:start,end:{}}; - - return _decorator.show(new Slick.Range(start.row,start.cell)); - } + dd.range = {start:start, end:{}}; - function handleDrag(e, dd) { - if (!_dragging) { - return; - } - e.stopImmediatePropagation(); - - var end = _grid.getCellFromPoint( - e.pageX - $(_canvas).offset().left, - e.pageY - $(_canvas).offset().top); + return _decorator.show(new Slick.Range(start.row, start.cell)); + } - if (!_grid.canCellBeSelected(end.row,end.cell)) { - return; - } + function handleDrag(e, dd) { + if (!_dragging) { + return; + } + e.stopImmediatePropagation(); - dd.range.end = end; - _decorator.show(new Slick.Range(dd.range.start.row,dd.range.start.cell,end.row,end.cell)); - } + var end = _grid.getCellFromPoint( + e.pageX - $(_canvas).offset().left, + e.pageY - $(_canvas).offset().top); - function handleDragEnd(e, dd) { - if (!_dragging) { - return; - } - - _dragging = false; - e.stopImmediatePropagation(); - - _decorator.hide(); - _self.onCellRangeSelected.notify({ - range: new Slick.Range( - dd.range.start.row, - dd.range.start.cell, - dd.range.end.row, - dd.range.end.cell - ) - }); - } + if (!_grid.canCellBeSelected(end.row, end.cell)) { + return; + } - $.extend(this, { - "init": init, - "destroy": destroy, + dd.range.end = end; + _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell)); + } - "onBeforeCellRangeSelected": new Slick.Event(), - "onCellRangeSelected": new Slick.Event() - }); + function handleDragEnd(e, dd) { + if (!_dragging) { + return; + } + + _dragging = false; + e.stopImmediatePropagation(); + + _decorator.hide(); + _self.onCellRangeSelected.notify({ + range:new Slick.Range( + dd.range.start.row, + dd.range.start.cell, + dd.range.end.row, + dd.range.end.cell + ) + }); } + + $.extend(this, { + "init":init, + "destroy":destroy, + + "onBeforeCellRangeSelected":new Slick.Event(), + "onCellRangeSelected":new Slick.Event() + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.cellselectionmodel.js b/plugins/slick.cellselectionmodel.js index e1dd565..c517117 100644 --- a/plugins/slick.cellselectionmodel.js +++ b/plugins/slick.cellselectionmodel.js @@ -1,92 +1,92 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellSelectionModel": CellSelectionModel - } +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "CellSelectionModel":CellSelectionModel + } + }); + + + function CellSelectionModel(options) { + var _grid; + var _canvas; + var _ranges = []; + var _self = this; + var _selector = new Slick.CellRangeSelector({ + "selectionCss":{ + "border":"2px solid black" + } }); + var _options; + var _defaults = { + selectActiveCell:true + }; + + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _grid.onActiveCellChanged.subscribe(handleActiveCellChange); + grid.registerPlugin(_selector); + _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); + } + function destroy() { + _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); + _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); + _grid.unregisterPlugin(_selector); + } - function CellSelectionModel(options) { - var _grid; - var _canvas; - var _ranges = []; - var _self = this; - var _selector = new Slick.CellRangeSelector({ - "selectionCss": { - "border": "2px solid black" - } - }); - var _options; - var _defaults = { - selectActiveCell: true - }; - - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onActiveCellChanged.subscribe(handleActiveCellChange); - grid.registerPlugin(_selector); - _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); - } + function removeInvalidRanges(ranges) { + var result = []; - function destroy() { - _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); - _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); - _grid.unregisterPlugin(_selector); + for (var i = 0; i < ranges.length; i++) { + var r = ranges[i]; + if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) { + result.push(r); } + } - function removeInvalidRanges(ranges) { - var result = []; - - for (var i = 0; i < ranges.length; i++) { - var r = ranges[i]; - if (_grid.canCellBeSelected(r.fromRow,r.fromCell) && _grid.canCellBeSelected(r.toRow,r.toCell)) { - result.push(r); - } - } - - return result; - } + return result; + } - function setSelectedRanges(ranges) { - _ranges = removeInvalidRanges(ranges); - _self.onSelectedRangesChanged.notify(_ranges); - } + function setSelectedRanges(ranges) { + _ranges = removeInvalidRanges(ranges); + _self.onSelectedRangesChanged.notify(_ranges); + } - function getSelectedRanges() { - return _ranges; - } + function getSelectedRanges() { + return _ranges; + } - function handleBeforeCellRangeSelected(e, args) { - if (_grid.getEditorLock().isActive()) { - e.stopPropagation(); - return false; - } - } + function handleBeforeCellRangeSelected(e, args) { + if (_grid.getEditorLock().isActive()) { + e.stopPropagation(); + return false; + } + } - function handleCellRangeSelected(e, args) { - setSelectedRanges([args.range]); - } + function handleCellRangeSelected(e, args) { + setSelectedRanges([args.range]); + } - function handleActiveCellChange(e, args) { - if (_options.selectActiveCell) { - setSelectedRanges([new Slick.Range(args.row,args.cell)]); - } - } + function handleActiveCellChange(e, args) { + if (_options.selectActiveCell) { + setSelectedRanges([new Slick.Range(args.row, args.cell)]); + } + } - $.extend(this, { - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, + $.extend(this, { + "getSelectedRanges":getSelectedRanges, + "setSelectedRanges":setSelectedRanges, - "init": init, - "destroy": destroy, + "init":init, + "destroy":destroy, - "onSelectedRangesChanged": new Slick.Event() - }); - } + "onSelectedRangesChanged":new Slick.Event() + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.checkboxselectcolumn.js b/plugins/slick.checkboxselectcolumn.js index 3a95255..c7078b9 100644 --- a/plugins/slick.checkboxselectcolumn.js +++ b/plugins/slick.checkboxselectcolumn.js @@ -1,136 +1,135 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CheckboxSelectColumn": CheckboxSelectColumn - } - }); - +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "CheckboxSelectColumn":CheckboxSelectColumn + } + }); - function CheckboxSelectColumn(options) { - var _grid; - var _self = this; - var _selectedRowsLookup = {}; - var _defaults = { - columnId: "_checkbox_selector", - cssClass: null, - toolTip: "Select/Deselect All", - width: 30 - }; - var _options = $.extend(true,{},_defaults,options); + function CheckboxSelectColumn(options) { + var _grid; + var _self = this; + var _selectedRowsLookup = {}; + var _defaults = { + columnId:"_checkbox_selector", + cssClass:null, + toolTip:"Select/Deselect All", + width:30 + }; - function init(grid) { - _grid = grid; - _grid.onSelectedRowsChanged.subscribe(handleSelectedRowsChanged); - _grid.onClick.subscribe(handleClick); - _grid.onHeaderClick.subscribe(handleHeaderClick); - } + var _options = $.extend(true, {}, _defaults, options); - function destroy() { - _grid.onSelectedRowsChanged.unsubscribe(handleSelectedRowsChanged); - _grid.onClick.unsubscribe(handleClick); - _grid.onHeaderClick.unsubscribe(handleHeaderClick); - } + function init(grid) { + _grid = grid; + _grid.onSelectedRowsChanged.subscribe(handleSelectedRowsChanged); + _grid.onClick.subscribe(handleClick); + _grid.onHeaderClick.subscribe(handleHeaderClick); + } - function handleSelectedRowsChanged(e, args) { - var selectedRows = _grid.getSelectedRows(); - var lookup = {}, row, i; - for (i = 0; i < selectedRows.length; i++) { - row = selectedRows[i]; - lookup[row] = true; - if (lookup[row] !== _selectedRowsLookup[row]) { - _grid.invalidateRow(row); - delete _selectedRowsLookup[row]; - } - } - for (i in _selectedRowsLookup) { - _grid.invalidateRow(i); - } - _selectedRowsLookup = lookup; - _grid.render(); + function destroy() { + _grid.onSelectedRowsChanged.unsubscribe(handleSelectedRowsChanged); + _grid.onClick.unsubscribe(handleClick); + _grid.onHeaderClick.unsubscribe(handleHeaderClick); + } - if (selectedRows.length == _grid.getDataLength()) { - _grid.updateColumnHeader(_options.columnId, "<input type='checkbox' checked='checked'>", _options.toolTip); - } - else { - _grid.updateColumnHeader(_options.columnId, "<input type='checkbox'>", _options.toolTip); - } + function handleSelectedRowsChanged(e, args) { + var selectedRows = _grid.getSelectedRows(); + var lookup = {}, row, i; + for (i = 0; i < selectedRows.length; i++) { + row = selectedRows[i]; + lookup[row] = true; + if (lookup[row] !== _selectedRowsLookup[row]) { + _grid.invalidateRow(row); + delete _selectedRowsLookup[row]; } + } + for (i in _selectedRowsLookup) { + _grid.invalidateRow(i); + } + _selectedRowsLookup = lookup; + _grid.render(); - function handleClick(e, args) { - // clicking on a row select checkbox - if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) { - // if editing, try to commit - if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { - e.preventDefault(); - e.stopImmediatePropagation(); - return; - } + if (selectedRows.length == _grid.getDataLength()) { + _grid.updateColumnHeader(_options.columnId, "<input type='checkbox' checked='checked'>", _options.toolTip); + } else { + _grid.updateColumnHeader(_options.columnId, "<input type='checkbox'>", _options.toolTip); + } + } - if (_selectedRowsLookup[args.row]) { - _grid.setSelectedRows($.grep(_grid.getSelectedRows(),function(n) { return n != args.row })); - } - else { - _grid.setSelectedRows(_grid.getSelectedRows().concat(args.row)); - } - e.stopPropagation(); - e.stopImmediatePropagation(); - } + function handleClick(e, args) { + // clicking on a row select checkbox + if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) { + // if editing, try to commit + if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; } - function handleHeaderClick(e, args) { - if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) { - // if editing, try to commit - if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { - e.preventDefault(); - e.stopImmediatePropagation(); - return; - } - - if ($(e.target).is(":checked")) { - var rows = []; - for (var i = 0; i < _grid.getDataLength(); i++) { - rows.push(i); - } - _grid.setSelectedRows(rows); - } - else { - _grid.setSelectedRows([]); - } - e.stopPropagation(); - e.stopImmediatePropagation(); - } + if (_selectedRowsLookup[args.row]) { + _grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) { + return n != args.row + })); + } else { + _grid.setSelectedRows(_grid.getSelectedRows().concat(args.row)); } + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } - function getColumnDefinition() { - return { - id: _options.columnId, - name: "<input type='checkbox'>", - toolTip: _options.toolTip, - field: "sel", - width: _options.width, - resizable: false, - sortable: false, - cssClass: _options.cssClass, - formatter: checkboxSelectionFormatter - }; + function handleHeaderClick(e, args) { + if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) { + // if editing, try to commit + if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; } - function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) { - if (dataContext) { - return _selectedRowsLookup[row] - ? "<input type='checkbox' checked='checked'>" - : "<input type='checkbox'>"; - } - return null; + if ($(e.target).is(":checked")) { + var rows = []; + for (var i = 0; i < _grid.getDataLength(); i++) { + rows.push(i); + } + _grid.setSelectedRows(rows); + } else { + _grid.setSelectedRows([]); } + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } - $.extend(this, { - "init": init, - "destroy": destroy, + function getColumnDefinition() { + return { + id:_options.columnId, + name:"<input type='checkbox'>", + toolTip:_options.toolTip, + field:"sel", + width:_options.width, + resizable:false, + sortable:false, + cssClass:_options.cssClass, + formatter:checkboxSelectionFormatter + }; + } - "getColumnDefinition": getColumnDefinition - }); + function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) { + if (dataContext) { + return _selectedRowsLookup[row] + ? "<input type='checkbox' checked='checked'>" + : "<input type='checkbox'>"; + } + return null; } + + $.extend(this, { + "init":init, + "destroy":destroy, + + "getColumnDefinition":getColumnDefinition + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.rowmovemanager.js b/plugins/slick.rowmovemanager.js index afb3519..3b26c47 100644 --- a/plugins/slick.rowmovemanager.js +++ b/plugins/slick.rowmovemanager.js @@ -1,133 +1,132 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "RowMoveManager": RowMoveManager - } - }); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "RowMoveManager":RowMoveManager + } + }); + + function RowMoveManager() { + var _grid; + var _canvas; + var _dragging; + var _self = this; + + function init(grid) { + _grid = grid; + _canvas = _grid.getCanvasNode(); + _grid.onDragInit.subscribe(handleDragInit); + _grid.onDragStart.subscribe(handleDragStart); + _grid.onDrag.subscribe(handleDrag); + _grid.onDragEnd.subscribe(handleDragEnd); + } - function RowMoveManager() { - var _grid; - var _canvas; - var _dragging; - var _self = this; - - function init(grid) { - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onDragInit.subscribe(handleDragInit); - _grid.onDragStart.subscribe(handleDragStart); - _grid.onDrag.subscribe(handleDrag); - _grid.onDragEnd.subscribe(handleDragEnd); - } + function destroy() { + _grid.onDragInit.unsubscribe(handleDragInit); + _grid.onDragStart.unsubscribe(handleDragStart); + _grid.onDrag.unsubscribe(handleDrag); + _grid.onDragEnd.unsubscribe(handleDragEnd); + } - function destroy() { - _grid.onDragInit.unsubscribe(handleDragInit); - _grid.onDragStart.unsubscribe(handleDragStart); - _grid.onDrag.unsubscribe(handleDrag); - _grid.onDragEnd.unsubscribe(handleDragEnd); - } + function handleDragInit(e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + } - function handleDragInit(e, dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - } + function handleDragStart(e, dd) { + var cell = _grid.getCellFromEvent(e); + if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) { + return false; + } - function handleDragStart(e,dd) { - var cell = _grid.getCellFromEvent(e); - if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) { - return false; - } + _dragging = true; + e.stopImmediatePropagation(); - _dragging = true; - e.stopImmediatePropagation(); + var selectedRows = _grid.getSelectedRows(); - var selectedRows = _grid.getSelectedRows(); + if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) { + selectedRows = [cell.row]; + _grid.setSelectedRows(selectedRows); + } - if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) { - selectedRows = [cell.row]; - _grid.setSelectedRows(selectedRows); - } + var rowHeight = _grid.getOptions().rowHeight; - var rowHeight = _grid.getOptions().rowHeight; + dd.selectedRows = selectedRows; - dd.selectedRows = selectedRows; + dd.selectionProxy = $("<div class='slick-reorder-proxy'/>") + .css("position", "absolute") + .css("zIndex", "99999") + .css("width", $(_canvas).innerWidth()) + .css("height", rowHeight * selectedRows.length) + .appendTo(_canvas); - dd.selectionProxy = $("<div class='slick-reorder-proxy'/>") - .css("position", "absolute") - .css("zIndex", "99999") - .css("width", $(_canvas).innerWidth()) - .css("height", rowHeight*selectedRows.length) - .appendTo(_canvas); + dd.guide = $("<div class='slick-reorder-guide'/>") + .css("position", "absolute") + .css("zIndex", "99998") + .css("width", $(_canvas).innerWidth()) + .css("top", -1000) + .appendTo(_canvas); - dd.guide = $("<div class='slick-reorder-guide'/>") - .css("position", "absolute") - .css("zIndex", "99998") - .css("width", $(_canvas).innerWidth()) - .css("top", -1000) - .appendTo(_canvas); + dd.insertBefore = -1; - dd.insertBefore = -1; + return $("<div></div>").appendTo(_canvas); + } - return $("<div></div>").appendTo(_canvas); + function handleDrag(e, dd) { + if (!_dragging) { + return; + } + + e.stopImmediatePropagation(); + + var top = e.pageY - $(_canvas).offset().top; + dd.selectionProxy.css("top", top - 5); + + var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength())); + if (insertBefore !== dd.insertBefore) { + var eventData = { + "rows":dd.selectedRows, + "insertBefore":insertBefore + }; + + if (_self.onBeforeMoveRows.notify(eventData) === false) { + dd.guide.css("top", -1000); + dd.canMove = false; + } else { + dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight); + dd.canMove = true; } - function handleDrag(e,dd) { - if (!_dragging) { - return; - } - - e.stopImmediatePropagation(); - - var top = e.pageY - $(_canvas).offset().top; - dd.selectionProxy.css("top",top-5); - - var insertBefore = Math.max(0,Math.min(Math.round(top/_grid.getOptions().rowHeight),_grid.getDataLength())); - if (insertBefore !== dd.insertBefore) { - var eventData = { - "rows": dd.selectedRows, - "insertBefore": insertBefore - }; - - if (_self.onBeforeMoveRows.notify(eventData) === false) { - dd.guide.css("top", -1000); - dd.canMove = false; - } - else { - dd.guide.css("top",insertBefore*_grid.getOptions().rowHeight); - dd.canMove = true; - } - - dd.insertBefore = insertBefore; - } - } + dd.insertBefore = insertBefore; + } + } - function handleDragEnd(e,dd) { - if (!_dragging) { - return; - } - _dragging = false; - e.stopImmediatePropagation(); - - dd.guide.remove(); - dd.selectionProxy.remove(); - - if (dd.canMove) { - var eventData = { - "rows": dd.selectedRows, - "insertBefore": dd.insertBefore - }; - // TODO: _grid.remapCellCssClasses ? - _self.onMoveRows.notify(eventData); - } - } + function handleDragEnd(e, dd) { + if (!_dragging) { + return; + } + _dragging = false; + e.stopImmediatePropagation(); + + dd.guide.remove(); + dd.selectionProxy.remove(); + + if (dd.canMove) { + var eventData = { + "rows":dd.selectedRows, + "insertBefore":dd.insertBefore + }; + // TODO: _grid.remapCellCssClasses ? + _self.onMoveRows.notify(eventData); + } + } - $.extend(this, { - "onBeforeMoveRows": new Slick.Event(), - "onMoveRows": new Slick.Event(), + $.extend(this, { + "onBeforeMoveRows":new Slick.Event(), + "onMoveRows":new Slick.Event(), - "init": init, - "destroy": destroy - }); - } + "init":init, + "destroy":destroy + }); + } })(jQuery);
\ No newline at end of file diff --git a/plugins/slick.rowselectionmodel.js b/plugins/slick.rowselectionmodel.js index a539939..e675c83 100644 --- a/plugins/slick.rowselectionmodel.js +++ b/plugins/slick.rowselectionmodel.js @@ -1,186 +1,187 @@ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "RowSelectionModel": RowSelectionModel - } - }); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "RowSelectionModel":RowSelectionModel + } + }); + + function RowSelectionModel(options) { + var _grid; + var _ranges = []; + var _self = this; + var _handler = new Slick.EventHandler(); + var _inHandler; + var _options; + var _defaults = { + selectActiveRow:true + }; + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _handler.subscribe(_grid.onActiveCellChanged, + wrapHandler(handleActiveCellChange)); + _handler.subscribe(_grid.onKeyDown, + wrapHandler(handleKeyDown)); + _handler.subscribe(_grid.onClick, + wrapHandler(handleClick)); + } - function RowSelectionModel(options) { - var _grid; - var _ranges = []; - var _self = this; - var _handler = new Slick.EventHandler(); - var _inHandler; - var _options; - var _defaults = { - selectActiveRow: true - }; - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _handler.subscribe(_grid.onActiveCellChanged, - wrapHandler(handleActiveCellChange)); - _handler.subscribe(_grid.onKeyDown, - wrapHandler(handleKeyDown)); - _handler.subscribe(_grid.onClick, - wrapHandler(handleClick)); - } + function destroy() { + _handler.unsubscribeAll(); + } - function destroy() { - _handler.unsubscribeAll(); + function wrapHandler(handler) { + return function () { + if (!_inHandler) { + _inHandler = true; + handler.apply(this, arguments); + _inHandler = false; } + }; + } - function wrapHandler(handler) { - return function() { - if (!_inHandler) { - _inHandler = true; - handler.apply(this, arguments); - _inHandler = false; - } - }; + function rangesToRows(ranges) { + var rows = []; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); } + } + return rows; + } - function rangesToRows(ranges) { - var rows = []; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - rows.push(j); - } - } - return rows; - } + function rowsToRanges(rows) { + var ranges = []; + var lastCell = _grid.getColumns().length - 1; + for (var i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } - function rowsToRanges(rows) { - var ranges = []; - var lastCell = _grid.getColumns().length - 1; - for (var i = 0; i < rows.length; i++) { - ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); - } - return ranges; - } + function getRowsRange(from, to) { + var i, rows = []; + for (i = from; i <= to; i++) { + rows.push(i); + } + for (i = to; i < from; i++) { + rows.push(i); + } + return rows; + } - function getRowsRange(from,to) { - var i, rows = []; - for (i = from; i <= to; i++) { - rows.push(i); - } - for (i = to; i < from; i++) { - rows.push(i); - } - return rows; - } + function getSelectedRows() { + return rangesToRows(_ranges); + } - function getSelectedRows() { - return rangesToRows(_ranges); - } + function setSelectedRows(rows) { + setSelectedRanges(rowsToRanges(rows)); + } - function setSelectedRows(rows) { - setSelectedRanges(rowsToRanges(rows)); - } + function setSelectedRanges(ranges) { + _ranges = ranges; + _self.onSelectedRangesChanged.notify(_ranges); + } - function setSelectedRanges(ranges) { - _ranges = ranges; - _self.onSelectedRangesChanged.notify(_ranges); - } + function getSelectedRanges() { + return _ranges; + } - function getSelectedRanges() { - return _ranges; - } + function handleActiveCellChange(e, data) { + if (_options.selectActiveRow) { + setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); + } + } - function handleActiveCellChange(e, data) { - if (_options.selectActiveRow) { - setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); - } + function handleKeyDown(e) { + var activeRow = _grid.getActiveCell(); + if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { + var selectedRows = getSelectedRows(); + selectedRows.sort(function (x, y) { + return x - y + }); + + if (!selectedRows.length) { + selectedRows = [activeRow.row]; } - function handleKeyDown(e) { - var activeRow = _grid.getActiveCell(); - if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { - var selectedRows = getSelectedRows(); - selectedRows.sort(function(x,y) { return x-y }); - - if (!selectedRows.length) { - selectedRows = [activeRow.row]; - } - - var top = selectedRows[0]; - var bottom = selectedRows[selectedRows.length - 1]; - var active; - - if (e.which == 40) { - active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; - } - else { - active = activeRow.row < bottom ? --bottom : --top; - } - - if (active >= 0 && active < _grid.getDataLength()) { - _grid.scrollRowIntoView(active); - _ranges = rowsToRanges(getRowsRange(top,bottom)); - setSelectedRanges(_ranges); - } - - e.preventDefault(); - e.stopPropagation(); - } + var top = selectedRows[0]; + var bottom = selectedRows[selectedRows.length - 1]; + var active; + + if (e.which == 40) { + active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; + } else { + active = activeRow.row < bottom ? --bottom : --top; } - function handleClick(e) { - var cell = _grid.getCellFromEvent(e); - if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { - return false; - } + if (active >= 0 && active < _grid.getDataLength()) { + _grid.scrollRowIntoView(active); + _ranges = rowsToRanges(getRowsRange(top, bottom)); + setSelectedRanges(_ranges); + } - var selection = rangesToRows(_ranges); - var idx = $.inArray(cell.row, selection); + e.preventDefault(); + e.stopPropagation(); + } + } - if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { - return false; - } - else if (_grid.getOptions().multiSelect) { - if (idx === -1 && (e.ctrlKey || e.metaKey)) { - selection.push(cell.row); - _grid.setActiveCell(cell.row, cell.cell); - } - else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { - selection = $.grep(selection, function(o, i) { return (o !== cell.row); }); - _grid.setActiveCell(cell.row, cell.cell); - } - else if (selection.length && e.shiftKey) { - var last = selection.pop(); - var from = Math.min(cell.row, last); - var to = Math.max(cell.row, last); - selection = []; - for (var i = from; i <= to; i++) { - if (i !== last) { - selection.push(i); - } - } - selection.push(last); - _grid.setActiveCell(cell.row, cell.cell); - } + function handleClick(e) { + var cell = _grid.getCellFromEvent(e); + if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + var selection = rangesToRows(_ranges); + var idx = $.inArray(cell.row, selection); + + if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { + return false; + } + else if (_grid.getOptions().multiSelect) { + if (idx === -1 && (e.ctrlKey || e.metaKey)) { + selection.push(cell.row); + _grid.setActiveCell(cell.row, cell.cell); + } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { + selection = $.grep(selection, function (o, i) { + return (o !== cell.row); + }); + _grid.setActiveCell(cell.row, cell.cell); + } else if (selection.length && e.shiftKey) { + var last = selection.pop(); + var from = Math.min(cell.row, last); + var to = Math.max(cell.row, last); + selection = []; + for (var i = from; i <= to; i++) { + if (i !== last) { + selection.push(i); } + } + selection.push(last); + _grid.setActiveCell(cell.row, cell.cell); + } + } - _ranges = rowsToRanges(selection); - setSelectedRanges(_ranges); - e.stopImmediatePropagation(); + _ranges = rowsToRanges(selection); + setSelectedRanges(_ranges); + e.stopImmediatePropagation(); - return true; - } + return true; + } - $.extend(this, { - "getSelectedRows": getSelectedRows, - "setSelectedRows": setSelectedRows, + $.extend(this, { + "getSelectedRows":getSelectedRows, + "setSelectedRows":setSelectedRows, - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, + "getSelectedRanges":getSelectedRanges, + "setSelectedRanges":setSelectedRanges, - "init": init, - "destroy": destroy, + "init":init, + "destroy":destroy, - "onSelectedRangesChanged": new Slick.Event() - }); - } + "onSelectedRangesChanged":new Slick.Event() + }); + } })(jQuery);
\ No newline at end of file diff --git a/slick.core.js b/slick.core.js index 8911560..b84079a 100644 --- a/slick.core.js +++ b/slick.core.js @@ -4,421 +4,421 @@ * @namespace Slick */ -(function($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Event": Event, - "EventData": EventData, - "EventHandler": EventHandler, - "Range": Range, - "NonDataRow": NonDataItem, - "Group": Group, - "GroupTotals": GroupTotals, - "EditorLock": EditorLock, - - /*** - * A global singleton editor lock. - * @class GlobalEditorLock - * @static - * @constructor - */ - "GlobalEditorLock": new EditorLock() - } - }); +(function ($) { + // register namespace + $.extend(true, window, { + "Slick":{ + "Event":Event, + "EventData":EventData, + "EventHandler":EventHandler, + "Range":Range, + "NonDataRow":NonDataItem, + "Group":Group, + "GroupTotals":GroupTotals, + "EditorLock":EditorLock, + + /*** + * A global singleton editor lock. + * @class GlobalEditorLock + * @static + * @constructor + */ + "GlobalEditorLock":new EditorLock() + } + }); + + /*** + * An event object for passing data to event handlers and letting them control propagation. + * <p>This is pretty much identical to how W3C and jQuery implement events.</p> + * @class EventData + * @constructor + */ + function EventData() { + var isPropagationStopped = false; + var isImmediatePropagationStopped = false; /*** - * An event object for passing data to event handlers and letting them control propagation. - * <p>This is pretty much identical to how W3C and jQuery implement events.</p> - * @class EventData - * @constructor + * Stops event from propagating up the DOM tree. + * @method stopPropagation */ - function EventData() { - var isPropagationStopped = false; - var isImmediatePropagationStopped = false; - - /*** - * Stops event from propagating up the DOM tree. - * @method stopPropagation - */ - this.stopPropagation = function() { - isPropagationStopped = true; - }; - - /*** - * Returns whether stopPropagation was called on this event object. - * @method isPropagationStopped - * @return {Boolean} - */ - this.isPropagationStopped = function() { - return isPropagationStopped; - }; - - /*** - * Prevents the rest of the handlers from being executed. - * @method stopImmediatePropagation - */ - this.stopImmediatePropagation = function() { - isImmediatePropagationStopped = true; - }; - - /*** - * Returns whether stopImmediatePropagation was called on this event object.\ - * @method isImmediatePropagationStopped - * @return {Boolean} - */ - this.isImmediatePropagationStopped = function() { - return isImmediatePropagationStopped; - } - } + this.stopPropagation = function () { + isPropagationStopped = true; + }; /*** - * A simple publisher-subscriber implementation. - * @class Event - * @constructor + * Returns whether stopPropagation was called on this event object. + * @method isPropagationStopped + * @return {Boolean} */ - function Event() { - var handlers = []; - - /*** - * Adds an event handler to be called when the event is fired. - * <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code> - * object the event was fired with.<p> - * @method subscribe - * @param fn {Function} Event handler. - */ - this.subscribe = function(fn) { - handlers.push(fn); - }; - - /*** - * Removes an event handler added with <code>subscribe(fn)</code>. - * @method unsubscribe - * @param fn {Function} Event handler to be removed. - */ - this.unsubscribe = function(fn) { - for (var i = handlers.length - 1; i >= 0; i--) { - if (handlers[i] === fn) { - handlers.splice(i, 1); - } - } - }; - - /*** - * Fires an event notifying all subscribers. - * @method notify - * @param args {Object} Additional data object to be passed to all handlers. - * @param e {EventData} - * Optional. - * An <code>EventData</code> object to be passed to all handlers. - * For DOM events, an existing W3C/jQuery event object can be passed in. - * @param scope {Object} - * Optional. - * The scope ("this") within which the handler will be executed. - * If not specified, the scope will be set to the <code>Event</code> instance. - */ - this.notify = function(args, e, scope) { - e = e || new EventData(); - scope = scope || this; - - var returnValue; - for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { - returnValue = handlers[i].call(scope, e, args); - } - - return returnValue; - }; - } + this.isPropagationStopped = function () { + return isPropagationStopped; + }; - function EventHandler() { - var handlers = []; - - this.subscribe = function(event, handler) { - handlers.push({ - event: event, - handler: handler - }); - event.subscribe(handler); - }; - - this.unsubscribe = function(event, handler) { - var i = handlers.length; - while (i--) { - if (handlers[i].event === event && - handlers[i].handler === handler) { - handlers.splice(i, 1); - event.unsubscribe(handler); - return; - } - } - }; - - this.unsubscribeAll = function() { - var i = handlers.length; - while (i--) { - handlers[i].event.unsubscribe(handlers[i].handler); - } - handlers = []; - } + /*** + * Prevents the rest of the handlers from being executed. + * @method stopImmediatePropagation + */ + this.stopImmediatePropagation = function () { + isImmediatePropagationStopped = true; + }; + + /*** + * Returns whether stopImmediatePropagation was called on this event object.\ + * @method isImmediatePropagationStopped + * @return {Boolean} + */ + this.isImmediatePropagationStopped = function () { + return isImmediatePropagationStopped; } + } + + /*** + * A simple publisher-subscriber implementation. + * @class Event + * @constructor + */ + function Event() { + var handlers = []; /*** - * A structure containing a range of cells. - * @class Range - * @constructor - * @param fromRow {Integer} Starting row. - * @param fromCell {Integer} Starting cell. - * @param toRow {Integer} Optional. Ending row. Defaults to <code>fromRow</code>. - * @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>. + * Adds an event handler to be called when the event is fired. + * <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code> + * object the event was fired with.<p> + * @method subscribe + * @param fn {Function} Event handler. */ - function Range(fromRow, fromCell, toRow, toCell) { - if (toRow === undefined && toCell === undefined) { - toRow = fromRow; - toCell = fromCell; + this.subscribe = function (fn) { + handlers.push(fn); + }; + + /*** + * Removes an event handler added with <code>subscribe(fn)</code>. + * @method unsubscribe + * @param fn {Function} Event handler to be removed. + */ + this.unsubscribe = function (fn) { + for (var i = handlers.length - 1; i >= 0; i--) { + if (handlers[i] === fn) { + handlers.splice(i, 1); } + } + }; + + /*** + * Fires an event notifying all subscribers. + * @method notify + * @param args {Object} Additional data object to be passed to all handlers. + * @param e {EventData} + * Optional. + * An <code>EventData</code> object to be passed to all handlers. + * For DOM events, an existing W3C/jQuery event object can be passed in. + * @param scope {Object} + * Optional. + * The scope ("this") within which the handler will be executed. + * If not specified, the scope will be set to the <code>Event</code> instance. + */ + this.notify = function (args, e, scope) { + e = e || new EventData(); + scope = scope || this; + + var returnValue; + for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { + returnValue = handlers[i].call(scope, e, args); + } + + return returnValue; + }; + } + + function EventHandler() { + var handlers = []; - /*** - * @property fromRow - * @type {Integer} - */ - this.fromRow = Math.min(fromRow, toRow); - - /*** - * @property fromCell - * @type {Integer} - */ - this.fromCell = Math.min(fromCell, toCell); - - /*** - * @property toRow - * @type {Integer} - */ - this.toRow = Math.max(fromRow, toRow); - - /*** - * @property toCell - * @type {Integer} - */ - this.toCell = Math.max(fromCell, toCell); - - /*** - * Returns whether a range represents a single row. - * @method isSingleRow - * @return {Boolean} - */ - this.isSingleRow = function() { - return this.fromRow == this.toRow; - }; - - /*** - * Returns whether a range represents a single cell. - * @method isSingleCell - * @return {Boolean} - */ - this.isSingleCell = function() { - return this.fromRow == this.toRow && this.fromCell == this.toCell; - }; - - /*** - * Returns whether a range contains a given cell. - * @method contains - * @param row {Integer} - * @param cell {Integer} - * @return {Boolean} - */ - this.contains = function(row, cell) { - return row >= this.fromRow && row <= this.toRow && - cell >= this.fromCell && cell <= this.toCell; - }; - - /*** - * Returns a readable representation of a range. - * @method toString - * @return {String} - */ - this.toString = function() { - if (this.isSingleCell()) { - return "(" + this.fromRow + ":" + this.fromCell + ")"; - } - else { - return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; - } + this.subscribe = function (event, handler) { + handlers.push({ + event:event, + handler:handler + }); + event.subscribe(handler); + }; + + this.unsubscribe = function (event, handler) { + var i = handlers.length; + while (i--) { + if (handlers[i].event === event && + handlers[i].handler === handler) { + handlers.splice(i, 1); + event.unsubscribe(handler); + return; } + } + }; + + this.unsubscribeAll = function () { + var i = handlers.length; + while (i--) { + handlers[i].event.unsubscribe(handlers[i].handler); + } + handlers = []; + } + } + + /*** + * A structure containing a range of cells. + * @class Range + * @constructor + * @param fromRow {Integer} Starting row. + * @param fromCell {Integer} Starting cell. + * @param toRow {Integer} Optional. Ending row. Defaults to <code>fromRow</code>. + * @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>. + */ + function Range(fromRow, fromCell, toRow, toCell) { + if (toRow === undefined && toCell === undefined) { + toRow = fromRow; + toCell = fromCell; } + /*** + * @property fromRow + * @type {Integer} + */ + this.fromRow = Math.min(fromRow, toRow); /*** - * A base class that all special / non-data rows (like Group and GroupTotals) derive from. - * @class NonDataItem - * @constructor + * @property fromCell + * @type {Integer} */ - function NonDataItem() { - this.__nonDataRow = true; - } + this.fromCell = Math.min(fromCell, toCell); + /*** + * @property toRow + * @type {Integer} + */ + this.toRow = Math.max(fromRow, toRow); /*** - * Information about a group of rows. - * @class Group - * @extends Slick.NonDataItem - * @constructor + * @property toCell + * @type {Integer} */ - function Group() { - this.__group = true; - this.__updated = false; - - /*** - * Number of rows in the group. - * @property count - * @type {Integer} - */ - this.count = 0; - - /*** - * Grouping value. - * @property value - * @type {Object} - */ - this.value = null; - - /*** - * Formatted display value of the group. - * @property title - * @type {String} - */ - this.title = null; - - /*** - * Whether a group is collapsed. - * @property collapsed - * @type {Boolean} - */ - this.collapsed = false; - - /*** - * GroupTotals, if any. - * @property totals - * @type {GroupTotals} - */ - this.totals = null; - } + this.toCell = Math.max(fromCell, toCell); - Group.prototype = new NonDataItem(); + /*** + * Returns whether a range represents a single row. + * @method isSingleRow + * @return {Boolean} + */ + this.isSingleRow = function () { + return this.fromRow == this.toRow; + }; /*** - * Compares two Group instances. - * @method equals + * Returns whether a range represents a single cell. + * @method isSingleCell * @return {Boolean} - * @param group {Group} Group instance to compare to. */ - Group.prototype.equals = function(group) { - return this.value === group.value && - this.count === group.count && - this.collapsed === group.collapsed; + this.isSingleCell = function () { + return this.fromRow == this.toRow && this.fromCell == this.toCell; }; /*** - * Information about group totals. - * An instance of GroupTotals will be created for each totals row and passed to the aggregators - * so that they can store arbitrary data in it. That data can later be accessed by group totals - * formatters during the display. - * @class GroupTotals - * @extends Slick.NonDataItem - * @constructor + * Returns whether a range contains a given cell. + * @method contains + * @param row {Integer} + * @param cell {Integer} + * @return {Boolean} */ - function GroupTotals() { - this.__groupTotals = true; - - /*** - * Parent Group. - * @param group - * @type {Group} - */ - this.group = null; + this.contains = function (row, cell) { + return row >= this.fromRow && row <= this.toRow && + cell >= this.fromCell && cell <= this.toCell; + }; + + /*** + * Returns a readable representation of a range. + * @method toString + * @return {String} + */ + this.toString = function () { + if (this.isSingleCell()) { + return "(" + this.fromRow + ":" + this.fromCell + ")"; + } + else { + return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; + } } + } + + + /*** + * A base class that all special / non-data rows (like Group and GroupTotals) derive from. + * @class NonDataItem + * @constructor + */ + function NonDataItem() { + this.__nonDataRow = true; + } + - GroupTotals.prototype = new NonDataItem(); + /*** + * Information about a group of rows. + * @class Group + * @extends Slick.NonDataItem + * @constructor + */ + function Group() { + this.__group = true; + this.__updated = false; /*** - * A locking helper to track the active edit controller and ensure that only a single controller - * can be active at a time. This prevents a whole class of state and validation synchronization - * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress - * and attempt a commit or cancel before proceeding. - * @class EditorLock - * @constructor + * Number of rows in the group. + * @property count + * @type {Integer} */ - function EditorLock() { - var activeEditController = null; - - /*** - * Returns true if a specified edit controller is active (has the edit lock). - * If the parameter is not specified, returns true if any edit controller is active. - * @method isActive - * @param editController {EditController} - * @return {Boolean} - */ - this.isActive = function(editController) { - return (editController ? activeEditController === editController : activeEditController !== null); - }; - - /*** - * Sets the specified edit controller as the active edit controller (acquire edit lock). - * If another edit controller is already active, and exception will be thrown. - * @method activate - * @param editController {EditController} edit controller acquiring the lock - */ - this.activate = function(editController) { - if (editController === activeEditController) { // already activated? - return; - } - if (activeEditController !== null) { - throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; - } - if (!editController.commitCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; - } - if (!editController.cancelCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; - } - activeEditController = editController; - }; - - /*** - * Unsets the specified edit controller as the active edit controller (release edit lock). - * If the specified edit controller is not the active one, an exception will be thrown. - * @method deactivate - * @param editController {EditController} edit controller releasing the lock - */ - this.deactivate = function(editController) { - if (activeEditController !== editController) { - throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; - } - activeEditController = null; - }; - - /*** - * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit - * controller and returns whether the commit attempt was successful (commit may fail due to validation - * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded - * and false otherwise. If no edit controller is active, returns true. - * @method commitCurrentEdit - * @return {Boolean} - */ - this.commitCurrentEdit = function() { - return (activeEditController ? activeEditController.commitCurrentEdit() : true); - }; - - /*** - * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit - * controller and returns whether the edit was successfully cancelled. If no edit controller is - * active, returns true. - * @method cancelCurrentEdit - * @return {Boolean} - */ - this.cancelCurrentEdit = function cancelCurrentEdit() { - return (activeEditController ? activeEditController.cancelCurrentEdit() : true); - }; - } + this.count = 0; + + /*** + * Grouping value. + * @property value + * @type {Object} + */ + this.value = null; + + /*** + * Formatted display value of the group. + * @property title + * @type {String} + */ + this.title = null; + + /*** + * Whether a group is collapsed. + * @property collapsed + * @type {Boolean} + */ + this.collapsed = false; + + /*** + * GroupTotals, if any. + * @property totals + * @type {GroupTotals} + */ + this.totals = null; + } + + Group.prototype = new NonDataItem(); + + /*** + * Compares two Group instances. + * @method equals + * @return {Boolean} + * @param group {Group} Group instance to compare to. + */ + Group.prototype.equals = function (group) { + return this.value === group.value && + this.count === group.count && + this.collapsed === group.collapsed; + }; + + /*** + * Information about group totals. + * An instance of GroupTotals will be created for each totals row and passed to the aggregators + * so that they can store arbitrary data in it. That data can later be accessed by group totals + * formatters during the display. + * @class GroupTotals + * @extends Slick.NonDataItem + * @constructor + */ + function GroupTotals() { + this.__groupTotals = true; + + /*** + * Parent Group. + * @param group + * @type {Group} + */ + this.group = null; + } + + GroupTotals.prototype = new NonDataItem(); + + /*** + * A locking helper to track the active edit controller and ensure that only a single controller + * can be active at a time. This prevents a whole class of state and validation synchronization + * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress + * and attempt a commit or cancel before proceeding. + * @class EditorLock + * @constructor + */ + function EditorLock() { + var activeEditController = null; + + /*** + * Returns true if a specified edit controller is active (has the edit lock). + * If the parameter is not specified, returns true if any edit controller is active. + * @method isActive + * @param editController {EditController} + * @return {Boolean} + */ + this.isActive = function (editController) { + return (editController ? activeEditController === editController : activeEditController !== null); + }; + + /*** + * Sets the specified edit controller as the active edit controller (acquire edit lock). + * If another edit controller is already active, and exception will be thrown. + * @method activate + * @param editController {EditController} edit controller acquiring the lock + */ + this.activate = function (editController) { + if (editController === activeEditController) { // already activated? + return; + } + if (activeEditController !== null) { + throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; + } + if (!editController.commitCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; + } + if (!editController.cancelCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; + } + activeEditController = editController; + }; + + /*** + * Unsets the specified edit controller as the active edit controller (release edit lock). + * If the specified edit controller is not the active one, an exception will be thrown. + * @method deactivate + * @param editController {EditController} edit controller releasing the lock + */ + this.deactivate = function (editController) { + if (activeEditController !== editController) { + throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; + } + activeEditController = null; + }; + + /*** + * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit + * controller and returns whether the commit attempt was successful (commit may fail due to validation + * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded + * and false otherwise. If no edit controller is active, returns true. + * @method commitCurrentEdit + * @return {Boolean} + */ + this.commitCurrentEdit = function () { + return (activeEditController ? activeEditController.commitCurrentEdit() : true); + }; + + /*** + * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit + * controller and returns whether the edit was successfully cancelled. If no edit controller is + * active, returns true. + * @method cancelCurrentEdit + * @return {Boolean} + */ + this.cancelCurrentEdit = function cancelCurrentEdit() { + return (activeEditController ? activeEditController.cancelCurrentEdit() : true); + }; + } })(jQuery); diff --git a/slick.dataview.js b/slick.dataview.js index 9a4605c..790ec94 100644 --- a/slick.dataview.js +++ b/slick.dataview.js @@ -1,738 +1,763 @@ -(function($) { - $.extend(true, window, { - Slick: { - Data: { - DataView: DataView, - Aggregators: { - Avg: AvgAggregator, - Min: MinAggregator, - Max: MaxAggregator - } - } - } - }); - - - /*** - * A sample Model implementation. - * Provides a filtered view of the underlying data. - * - * Relies on the data item having an "id" property uniquely identifying it. - */ - function DataView(options) { - var self = this; - - var defaults = { - groupItemMetadataProvider: null - }; - - - // private - var idProperty = "id"; // property holding a unique row id - var items = []; // data by index - var rows = []; // data by row - var idxById = {}; // indexes by id - var rowsById = null; // rows by id; lazy-calculated - var filter = null; // filter function - var updated = null; // updated item ids - var suspend = false; // suspends the recalculation - var sortAsc = true; - var fastSortField; - var sortComparer; - var refreshHints = {}; - var prevRefreshHints = {}; - var filterArgs; - var filteredItems = []; - var compiledFilter; - var compiledFilterWithCaching; - var filterCache = []; - - // grouping - var groupingGetter; - var groupingGetterIsAFn; - var groupingFormatter; - var groupingComparer; - var groups = []; - var collapsedGroups = {}; - var aggregators; - var aggregateCollapsed = false; - var compiledAccumulators; - - var pagesize = 0; - var pagenum = 0; - var totalRows = 0; - - // events - var onRowCountChanged = new Slick.Event(); - var onRowsChanged = new Slick.Event(); - var onPagingInfoChanged = new Slick.Event(); - - options = $.extend(true, {}, defaults, options); - - - function beginUpdate() { - suspend = true; - } +(function ($) { + $.extend(true, window, { + Slick:{ + Data:{ + DataView:DataView, + Aggregators:{ + Avg:AvgAggregator, + Min:MinAggregator, + Max:MaxAggregator + } + } + } + }); + + + /*** + * A sample Model implementation. + * Provides a filtered view of the underlying data. + * + * Relies on the data item having an "id" property uniquely identifying it. + */ + function DataView(options) { + var self = this; + + var defaults = { + groupItemMetadataProvider:null + }; + + + // private + var idProperty = "id"; // property holding a unique row id + var items = []; // data by index + var rows = []; // data by row + var idxById = {}; // indexes by id + var rowsById = null; // rows by id; lazy-calculated + var filter = null; // filter function + var updated = null; // updated item ids + var suspend = false; // suspends the recalculation + var sortAsc = true; + var fastSortField; + var sortComparer; + var refreshHints = {}; + var prevRefreshHints = {}; + var filterArgs; + var filteredItems = []; + var compiledFilter; + var compiledFilterWithCaching; + var filterCache = []; + + // grouping + var groupingGetter; + var groupingGetterIsAFn; + var groupingFormatter; + var groupingComparer; + var groups = []; + var collapsedGroups = {}; + var aggregators; + var aggregateCollapsed = false; + var compiledAccumulators; + + var pagesize = 0; + var pagenum = 0; + var totalRows = 0; + + // events + var onRowCountChanged = new Slick.Event(); + var onRowsChanged = new Slick.Event(); + var onPagingInfoChanged = new Slick.Event(); + + options = $.extend(true, {}, defaults, options); + + + function beginUpdate() { + suspend = true; + } - function endUpdate() { - suspend = false; - refresh(); - } + function endUpdate() { + suspend = false; + refresh(); + } - function setRefreshHints(hints){ - refreshHints = hints; - } + function setRefreshHints(hints) { + refreshHints = hints; + } - function setFilterArgs(args) { - filterArgs = args; - } + function setFilterArgs(args) { + filterArgs = args; + } - function updateIdxById(startingIndex) { - startingIndex = startingIndex || 0; - var id; - for (var i = startingIndex, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined) { - throw "Each data element must implement a unique 'id' property"; - } - idxById[id] = i; - } + function updateIdxById(startingIndex) { + startingIndex = startingIndex || 0; + var id; + for (var i = startingIndex, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined) { + throw "Each data element must implement a unique 'id' property"; } + idxById[id] = i; + } + } - function ensureIdUniqueness() { - var id; - for (var i = 0, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined || idxById[id] !== i) { - throw "Each data element must implement a unique 'id' property"; - } - } + function ensureIdUniqueness() { + var id; + for (var i = 0, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined || idxById[id] !== i) { + throw "Each data element must implement a unique 'id' property"; } + } + } - function getItems() { - return items; - } + function getItems() { + return items; + } - function setItems(data, objectIdProperty) { - if (objectIdProperty !== undefined) idProperty = objectIdProperty; - items = filteredItems = data; - idxById = {}; - updateIdxById(); - ensureIdUniqueness(); - refresh(); - } + function setItems(data, objectIdProperty) { + if (objectIdProperty !== undefined) { + idProperty = objectIdProperty; + } + items = filteredItems = data; + idxById = {}; + updateIdxById(); + ensureIdUniqueness(); + refresh(); + } - function setPagingOptions(args) { - if (args.pageSize != undefined) - pagesize = args.pageSize; + function setPagingOptions(args) { + if (args.pageSize != undefined) { + pagesize = args.pageSize; + } - if (args.pageNum != undefined) - pagenum = Math.min(args.pageNum, Math.ceil(totalRows / pagesize)); + if (args.pageNum != undefined) { + pagenum = Math.min(args.pageNum, Math.ceil(totalRows / pagesize)); + } - onPagingInfoChanged.notify(getPagingInfo(), null, self); + onPagingInfoChanged.notify(getPagingInfo(), null, self); - refresh(); - } + refresh(); + } - function getPagingInfo() { - return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows}; - } + function getPagingInfo() { + return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows}; + } - function sort(comparer, ascending) { - sortAsc = ascending; - sortComparer = comparer; - fastSortField = null; - if (ascending === false) items.reverse(); - items.sort(comparer); - if (ascending === false) items.reverse(); - idxById = {}; - updateIdxById(); - refresh(); - } + function sort(comparer, ascending) { + sortAsc = ascending; + sortComparer = comparer; + fastSortField = null; + if (ascending === false) { + items.reverse(); + } + items.sort(comparer); + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } - /*** - * Provides a workaround for the extremely slow sorting in IE. - * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString - * to return the value of that field and then doing a native Array.sort(). - */ - function fastSort(field, ascending) { - sortAsc = ascending; - fastSortField = field; - sortComparer = null; - var oldToString = Object.prototype.toString; - Object.prototype.toString = (typeof field == "function")?field:function() { return this[field] }; - // an extra reversal for descending sort keeps the sort stable - // (assuming a stable native sort implementation, which isn't true in some cases) - if (ascending === false) items.reverse(); - items.sort(); - Object.prototype.toString = oldToString; - if (ascending === false) items.reverse(); - idxById = {}; - updateIdxById(); - refresh(); - } + /*** + * Provides a workaround for the extremely slow sorting in IE. + * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString + * to return the value of that field and then doing a native Array.sort(). + */ + function fastSort(field, ascending) { + sortAsc = ascending; + fastSortField = field; + sortComparer = null; + var oldToString = Object.prototype.toString; + Object.prototype.toString = (typeof field == "function") ? field : function () { + return this[field] + }; + // an extra reversal for descending sort keeps the sort stable + // (assuming a stable native sort implementation, which isn't true in some cases) + if (ascending === false) { + items.reverse(); + } + items.sort(); + Object.prototype.toString = oldToString; + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } - function reSort() { - if (sortComparer) { - sort(sortComparer, sortAsc); - } - else if (fastSortField) { - fastSort(fastSortField, sortAsc); - } - } + function reSort() { + if (sortComparer) { + sort(sortComparer, sortAsc); + } + else if (fastSortField) { + fastSort(fastSortField, sortAsc); + } + } - function setFilter(filterFn) { - filter = filterFn; - compiledFilter = compileFilter(); - compiledFilterWithCaching = compileFilterWithCaching(); - refresh(); - } + function setFilter(filterFn) { + filter = filterFn; + compiledFilter = compileFilter(); + compiledFilterWithCaching = compileFilterWithCaching(); + refresh(); + } - function groupBy(valueGetter, valueFormatter, sortComparer) { - if (!options.groupItemMetadataProvider) { - options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); - } - - groupingGetter = valueGetter; - groupingGetterIsAFn = typeof groupingGetter === "function"; - groupingFormatter = valueFormatter; - groupingComparer = sortComparer; - collapsedGroups = {}; - groups = []; - refresh(); - } + function groupBy(valueGetter, valueFormatter, sortComparer) { + if (!options.groupItemMetadataProvider) { + options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); + } + + groupingGetter = valueGetter; + groupingGetterIsAFn = typeof groupingGetter === "function"; + groupingFormatter = valueFormatter; + groupingComparer = sortComparer; + collapsedGroups = {}; + groups = []; + refresh(); + } - function setAggregators(groupAggregators, includeCollapsed) { - aggregators = groupAggregators; - aggregateCollapsed = (includeCollapsed !== undefined) - ? includeCollapsed : aggregateCollapsed; + function setAggregators(groupAggregators, includeCollapsed) { + aggregators = groupAggregators; + aggregateCollapsed = (includeCollapsed !== undefined) + ? includeCollapsed : aggregateCollapsed; - // pre-compile accumulator loops - compiledAccumulators = []; - var idx = aggregators.length; - while (idx--) { - compiledAccumulators[idx] = compileAccumulatorLoop(aggregators[idx]); - } + // pre-compile accumulator loops + compiledAccumulators = []; + var idx = aggregators.length; + while (idx--) { + compiledAccumulators[idx] = compileAccumulatorLoop(aggregators[idx]); + } - refresh(); - } + refresh(); + } - function getItemByIdx(i) { - return items[i]; - } + function getItemByIdx(i) { + return items[i]; + } - function getIdxById(id) { - return idxById[id]; - } + function getIdxById(id) { + return idxById[id]; + } - function ensureRowsByIdCache() { - if (!rowsById) { - rowsById = {}; - for (var i = 0, l = rows.length; i < l; i++) { - rowsById[rows[i][idProperty]] = i; - } - } + function ensureRowsByIdCache() { + if (!rowsById) { + rowsById = {}; + for (var i = 0, l = rows.length; i < l; i++) { + rowsById[rows[i][idProperty]] = i; } + } + } - function getRowById(id) { - ensureRowsByIdCache(); - return rowsById[id]; - } + function getRowById(id) { + ensureRowsByIdCache(); + return rowsById[id]; + } - function getItemById(id) { - return items[idxById[id]]; - } + function getItemById(id) { + return items[idxById[id]]; + } - function updateItem(id, item) { - if (idxById[id] === undefined || id !== item[idProperty]) - throw "Invalid or non-matching id"; - items[idxById[id]] = item; - if (!updated) updated = {}; - updated[id] = true; - refresh(); - } + function updateItem(id, item) { + if (idxById[id] === undefined || id !== item[idProperty]) { + throw "Invalid or non-matching id"; + } + items[idxById[id]] = item; + if (!updated) { + updated = {}; + } + updated[id] = true; + refresh(); + } - function insertItem(insertBefore, item) { - items.splice(insertBefore, 0, item); - updateIdxById(insertBefore); - refresh(); - } + function insertItem(insertBefore, item) { + items.splice(insertBefore, 0, item); + updateIdxById(insertBefore); + refresh(); + } - function addItem(item) { - items.push(item); - updateIdxById(items.length - 1); - refresh(); - } + function addItem(item) { + items.push(item); + updateIdxById(items.length - 1); + refresh(); + } - function deleteItem(id) { - var idx = idxById[id]; - if (idx === undefined) { - throw "Invalid id"; - } - delete idxById[id]; - items.splice(idx, 1); - updateIdxById(idx); - refresh(); - } + function deleteItem(id) { + var idx = idxById[id]; + if (idx === undefined) { + throw "Invalid id"; + } + delete idxById[id]; + items.splice(idx, 1); + updateIdxById(idx); + refresh(); + } - function getLength() { - return rows.length; - } + function getLength() { + return rows.length; + } - function getItem(i) { - return rows[i]; - } + function getItem(i) { + return rows[i]; + } - function getItemMetadata(i) { - var item = rows[i]; - if (item === undefined) { - return null; - } + function getItemMetadata(i) { + var item = rows[i]; + if (item === undefined) { + return null; + } - // overrides for group rows - if (item.__group) { - return options.groupItemMetadataProvider.getGroupRowMetadata(item); - } + // overrides for group rows + if (item.__group) { + return options.groupItemMetadataProvider.getGroupRowMetadata(item); + } - // overrides for totals rows - if (item.__groupTotals) { - return options.groupItemMetadataProvider.getTotalsRowMetadata(item); - } + // overrides for totals rows + if (item.__groupTotals) { + return options.groupItemMetadataProvider.getTotalsRowMetadata(item); + } - return null; - } + return null; + } - function collapseGroup(groupingValue) { - collapsedGroups[groupingValue] = true; - refresh(); - } + function collapseGroup(groupingValue) { + collapsedGroups[groupingValue] = true; + refresh(); + } - function expandGroup(groupingValue) { - delete collapsedGroups[groupingValue]; - refresh(); - } + function expandGroup(groupingValue) { + delete collapsedGroups[groupingValue]; + refresh(); + } - function getGroups() { - return groups; - } + function getGroups() { + return groups; + } - function extractGroups(rows) { - var group; - var val; - var groups = []; - var groupsByVal = []; - var r; - - for (var i = 0, l = rows.length; i < l; i++) { - r = rows[i]; - val = (groupingGetterIsAFn) ? groupingGetter(r) : r[groupingGetter]; - val = val || 0; - group = groupsByVal[val]; - if (!group) { - group = new Slick.Group(); - group.count = 0; - group.value = val; - group.rows = []; - groups[groups.length] = group; - groupsByVal[val] = group; - } - - group.rows[group.count++] = r; - } - - return groups; - } + function extractGroups(rows) { + var group; + var val; + var groups = []; + var groupsByVal = []; + var r; + + for (var i = 0, l = rows.length; i < l; i++) { + r = rows[i]; + val = (groupingGetterIsAFn) ? groupingGetter(r) : r[groupingGetter]; + val = val || 0; + group = groupsByVal[val]; + if (!group) { + group = new Slick.Group(); + group.count = 0; + group.value = val; + group.rows = []; + groups[groups.length] = group; + groupsByVal[val] = group; + } + + group.rows[group.count++] = r; + } + + return groups; + } - // TODO: lazy totals calculation - function calculateGroupTotals(group) { - if (group.collapsed && !aggregateCollapsed) { - return; - } - - // TODO: try moving iterating over groups into compiled accumulator - var totals = new Slick.GroupTotals(); - var agg, idx = aggregators.length; - while (idx--) { - agg = aggregators[idx]; - agg.init(); - compiledAccumulators[idx].call(agg, group.rows); - agg.storeResult(totals); - } - totals.group = group; - group.totals = totals; - } + // TODO: lazy totals calculation + function calculateGroupTotals(group) { + if (group.collapsed && !aggregateCollapsed) { + return; + } + + // TODO: try moving iterating over groups into compiled accumulator + var totals = new Slick.GroupTotals(); + var agg, idx = aggregators.length; + while (idx--) { + agg = aggregators[idx]; + agg.init(); + compiledAccumulators[idx].call(agg, group.rows); + agg.storeResult(totals); + } + totals.group = group; + group.totals = totals; + } - function calculateTotals(groups) { - var idx = groups.length; - while (idx--) { - calculateGroupTotals(groups[idx]); - } - } + function calculateTotals(groups) { + var idx = groups.length; + while (idx--) { + calculateGroupTotals(groups[idx]); + } + } - function finalizeGroups(groups) { - var idx = groups.length, g; - while (idx--) { - g = groups[idx]; - g.collapsed = (g.value in collapsedGroups); - g.title = groupingFormatter ? groupingFormatter(g) : g.value; - } - } + function finalizeGroups(groups) { + var idx = groups.length, g; + while (idx--) { + g = groups[idx]; + g.collapsed = (g.value in collapsedGroups); + g.title = groupingFormatter ? groupingFormatter(g) : g.value; + } + } - function flattenGroupedRows(groups) { - var groupedRows = [], gl = 0, g; - for (var i = 0, l = groups.length; i < l; i++) { - g = groups[i]; - groupedRows[gl++] = g; - - if (!g.collapsed) { - for (var j = 0, jj = g.rows.length; j < jj; j++) { - groupedRows[gl++] = g.rows[j]; - } - } - - if (g.totals && (!g.collapsed || aggregateCollapsed)) { - groupedRows[gl++] = g.totals; - } - } - return groupedRows; - } + function flattenGroupedRows(groups) { + var groupedRows = [], gl = 0, g; + for (var i = 0, l = groups.length; i < l; i++) { + g = groups[i]; + groupedRows[gl++] = g; - function getFunctionInfo(fn) { - var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; - var matches = fn.toString().match(fnRegex); - return { - params: matches[1].split(","), - body: matches[2] - }; + if (!g.collapsed) { + for (var j = 0, jj = g.rows.length; j < jj; j++) { + groupedRows[gl++] = g.rows[j]; + } } - function compileAccumulatorLoop(aggregator) { - var accumulatorInfo = getFunctionInfo(aggregator.accumulate); - var fn = new Function( - "_items", - "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + - accumulatorInfo.params[0] + " = _items[_i]; " + - accumulatorInfo.body + - "}" - ); - fn.displayName = fn.name = "compiledAccumulatorLoop"; - return fn; - } - - function compileFilter() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false;/gi, "{ continue _coreloop; }") - .replace(/return true;/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }") - .replace(/return ([^;]+?);/gi, - "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args", tpl); - fn.displayName = fn.name = "compiledFilter"; - return fn; + if (g.totals && (!g.collapsed || aggregateCollapsed)) { + groupedRows[gl++] = g.totals; } + } + return groupedRows; + } - function compileFilterWithCaching() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false;/gi, "{ continue _coreloop; }") - .replace(/return true;/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }") - .replace(/return ([^;]+?);/gi, - "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args, _cache) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "if (_cache[_i]) { ", - "_retval[_idx++] = $item$; ", - "continue _coreloop; ", - "} ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args,_cache", tpl); - fn.displayName = fn.name = "compiledFilterWithCaching"; - return fn; - } + function getFunctionInfo(fn) { + var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; + var matches = fn.toString().match(fnRegex); + return { + params:matches[1].split(","), + body:matches[2] + }; + } - function getFilteredAndPagedItems(items) { - if (filter && !refreshHints.isFilterUnchanged) { - if (refreshHints.isFilterNarrowing) { - filteredItems = compiledFilter(filteredItems, filterArgs); - } else if (refreshHints.isFilterExpanding) { - filteredItems = compiledFilterWithCaching(items, filterArgs, filterCache); - } else { - filteredItems = compiledFilter(items, filterArgs); - } - } else { - // special case: if not filtering and not paging, the resulting - // rows collection needs to be a copy so that changes due to sort - // can be caught - filteredItems = pagesize ? items : items.concat(); - } - - // get the current page - var paged; - if (pagesize) { - if (filteredItems.length < pagenum * pagesize) { - pagenum = Math.floor(filteredItems.length / pagesize); - } - paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); - } else { - paged = filteredItems; - } - - return {totalRows:filteredItems.length, rows:paged}; - } + function compileAccumulatorLoop(aggregator) { + var accumulatorInfo = getFunctionInfo(aggregator.accumulate); + var fn = new Function( + "_items", + "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + + accumulatorInfo.params[0] + " = _items[_i]; " + + accumulatorInfo.body + + "}" + ); + fn.displayName = fn.name = "compiledAccumulatorLoop"; + return fn; + } - function getRowDiffs(rows, newRows) { - var item, r, eitherIsNonData, diff = []; - var from = 0, to = newRows.length; - - if (refreshHints && refreshHints.ignoreDiffsBefore) { - from = Math.max(0, - Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); - } - - if (refreshHints && refreshHints.ignoreDiffsAfter) { - to = Math.min(newRows.length, - Math.max(0, refreshHints.ignoreDiffsAfter)); - } - - for (var i = from, rl = rows.length; i < to; i++) { - if (i >= rl) { - diff[diff.length] = i; - } - else { - item = newRows[i]; - r = rows[i]; - - if ((groupingGetter && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && - item.__group !== r.__group || - item.__updated || - item.__group && !item.equals(r)) - || (aggregators && eitherIsNonData && - // no good way to compare totals since they are arbitrary DTOs - // deep object comparison is pretty expensive - // always considering them 'dirty' seems easier for the time being - (item.__groupTotals || r.__groupTotals)) - || item[idProperty] != r[idProperty] - || (updated && updated[item[idProperty]]) - ) { - diff[diff.length] = i; - } - } - } - return diff; - } + function compileFilter() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false;/gi, "{ continue _coreloop; }") + .replace(/return true;/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }") + .replace(/return ([^;]+?);/gi, + "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args", tpl); + fn.displayName = fn.name = "compiledFilter"; + return fn; + } - function recalc(_items) { - rowsById = null; + function compileFilterWithCaching() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false;/gi, "{ continue _coreloop; }") + .replace(/return true;/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }") + .replace(/return ([^;]+?);/gi, + "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args, _cache) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "if (_cache[_i]) { ", + "_retval[_idx++] = $item$; ", + "continue _coreloop; ", + "} ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args,_cache", tpl); + fn.displayName = fn.name = "compiledFilterWithCaching"; + return fn; + } - if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || - refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { - filterCache = []; - } + function getFilteredAndPagedItems(items) { + if (filter && !refreshHints.isFilterUnchanged) { + if (refreshHints.isFilterNarrowing) { + filteredItems = compiledFilter(filteredItems, filterArgs); + } else if (refreshHints.isFilterExpanding) { + filteredItems = compiledFilterWithCaching(items, filterArgs, filterCache); + } else { + filteredItems = compiledFilter(items, filterArgs); + } + } else { + // special case: if not filtering and not paging, the resulting + // rows collection needs to be a copy so that changes due to sort + // can be caught + filteredItems = pagesize ? items : items.concat(); + } + + // get the current page + var paged; + if (pagesize) { + if (filteredItems.length < pagenum * pagesize) { + pagenum = Math.floor(filteredItems.length / pagesize); + } + paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); + } else { + paged = filteredItems; + } + + return {totalRows:filteredItems.length, rows:paged}; + } - var filteredItems = getFilteredAndPagedItems(_items); - totalRows = filteredItems.totalRows; - var newRows = filteredItems.rows; + function getRowDiffs(rows, newRows) { + var item, r, eitherIsNonData, diff = []; + var from = 0, to = newRows.length; + + if (refreshHints && refreshHints.ignoreDiffsBefore) { + from = Math.max(0, + Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); + } + + if (refreshHints && refreshHints.ignoreDiffsAfter) { + to = Math.min(newRows.length, + Math.max(0, refreshHints.ignoreDiffsAfter)); + } + + for (var i = from, rl = rows.length; i < to; i++) { + if (i >= rl) { + diff[diff.length] = i; + } + else { + item = newRows[i]; + r = rows[i]; + + if ((groupingGetter && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && + item.__group !== r.__group || + item.__updated || + item.__group && !item.equals(r)) + || (aggregators && eitherIsNonData && + // no good way to compare totals since they are arbitrary DTOs + // deep object comparison is pretty expensive + // always considering them 'dirty' seems easier for the time being + (item.__groupTotals || r.__groupTotals)) + || item[idProperty] != r[idProperty] + || (updated && updated[item[idProperty]]) + ) { + diff[diff.length] = i; + } + } + } + return diff; + } - groups = []; - if (groupingGetter != null) { - groups = extractGroups(newRows); - if (groups.length) { - finalizeGroups(groups); - if (aggregators) { - calculateTotals(groups); - } - groups.sort(groupingComparer); - newRows = flattenGroupedRows(groups); - } - } + function recalc(_items) { + rowsById = null; - var diff = getRowDiffs(rows, newRows); + if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || + refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { + filterCache = []; + } - rows = newRows; + var filteredItems = getFilteredAndPagedItems(_items); + totalRows = filteredItems.totalRows; + var newRows = filteredItems.rows; - return diff; + groups = []; + if (groupingGetter != null) { + groups = extractGroups(newRows); + if (groups.length) { + finalizeGroups(groups); + if (aggregators) { + calculateTotals(groups); + } + groups.sort(groupingComparer); + newRows = flattenGroupedRows(groups); } + } - function refresh() { - if (suspend) return; + var diff = getRowDiffs(rows, newRows); - var countBefore = rows.length; - var totalRowsBefore = totalRows; + rows = newRows; - var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit - - // if the current page is no longer valid, go to last page and recalc - // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized - if (pagesize && totalRows < pagenum * pagesize) { - pagenum = Math.floor(totalRows / pagesize); - diff = recalc(items, filter); - } - - updated = null; - prevRefreshHints = refreshHints; - refreshHints = {}; + return diff; + } - if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo(), null, self); - if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length}, null, self); - if (diff.length > 0) onRowsChanged.notify({rows:diff}, null, self); - } + function refresh() { + if (suspend) { + return; + } + + var countBefore = rows.length; + var totalRowsBefore = totalRows; + + var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit + + // if the current page is no longer valid, go to last page and recalc + // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized + if (pagesize && totalRows < pagenum * pagesize) { + pagenum = Math.floor(totalRows / pagesize); + diff = recalc(items, filter); + } + + updated = null; + prevRefreshHints = refreshHints; + refreshHints = {}; + + if (totalRowsBefore != totalRows) { + onPagingInfoChanged.notify(getPagingInfo(), null, self); + } + if (countBefore != rows.length) { + onRowCountChanged.notify({previous:countBefore, current:rows.length}, null, self); + } + if (diff.length > 0) { + onRowsChanged.notify({rows:diff}, null, self); + } + } - return { - // methods - "beginUpdate": beginUpdate, - "endUpdate": endUpdate, - "setPagingOptions": setPagingOptions, - "getPagingInfo": getPagingInfo, - "getItems": getItems, - "setItems": setItems, - "setFilter": setFilter, - "sort": sort, - "fastSort": fastSort, - "reSort": reSort, - "groupBy": groupBy, - "setAggregators": setAggregators, - "collapseGroup": collapseGroup, - "expandGroup": expandGroup, - "getGroups": getGroups, - "getIdxById": getIdxById, - "getRowById": getRowById, - "getItemById": getItemById, - "getItemByIdx": getItemByIdx, - "setRefreshHints": setRefreshHints, - "setFilterArgs": setFilterArgs, - "refresh": refresh, - "updateItem": updateItem, - "insertItem": insertItem, - "addItem": addItem, - "deleteItem": deleteItem, - - // data provider methods - "getLength": getLength, - "getItem": getItem, - "getItemMetadata": getItemMetadata, - - // events - "onRowCountChanged": onRowCountChanged, - "onRowsChanged": onRowsChanged, - "onPagingInfoChanged": onPagingInfoChanged - }; - } - - function AvgAggregator(field) { - this.field_ = field; - - this.init = function() { - this.count_ = 0; - this.nonNullCount_ = 0; - this.sum_ = 0; - }; - - this.accumulate = function(item) { - var val = item[this.field_]; - this.count_++; - if (val != null && val != NaN) { - this.nonNullCount_++; - this.sum_ += 1 * val; - } - }; - - this.storeResult = function(groupTotals) { - if (!groupTotals.avg) { - groupTotals.avg = {}; - } - if (this.nonNullCount_ != 0) { - groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; - } - }; - } - - function MinAggregator(field) { - this.field_ = field; - - this.init = function() { - this.min_ = null; - }; - - this.accumulate = function(item) { - var val = item[this.field_]; - if (val != null && val != NaN) { - if (this.min_ == null ||val < this.min_) { - this.min_ = val; - } - } - }; - - this.storeResult = function(groupTotals) { - if (!groupTotals.min) { - groupTotals.min = {}; - } - groupTotals.min[this.field_] = this.min_; - } + return { + // methods + "beginUpdate":beginUpdate, + "endUpdate":endUpdate, + "setPagingOptions":setPagingOptions, + "getPagingInfo":getPagingInfo, + "getItems":getItems, + "setItems":setItems, + "setFilter":setFilter, + "sort":sort, + "fastSort":fastSort, + "reSort":reSort, + "groupBy":groupBy, + "setAggregators":setAggregators, + "collapseGroup":collapseGroup, + "expandGroup":expandGroup, + "getGroups":getGroups, + "getIdxById":getIdxById, + "getRowById":getRowById, + "getItemById":getItemById, + "getItemByIdx":getItemByIdx, + "setRefreshHints":setRefreshHints, + "setFilterArgs":setFilterArgs, + "refresh":refresh, + "updateItem":updateItem, + "insertItem":insertItem, + "addItem":addItem, + "deleteItem":deleteItem, + + // data provider methods + "getLength":getLength, + "getItem":getItem, + "getItemMetadata":getItemMetadata, + + // events + "onRowCountChanged":onRowCountChanged, + "onRowsChanged":onRowsChanged, + "onPagingInfoChanged":onPagingInfoChanged + }; + } + + function AvgAggregator(field) { + this.field_ = field; + + this.init = function () { + this.count_ = 0; + this.nonNullCount_ = 0; + this.sum_ = 0; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + this.count_++; + if (val != null && val != NaN) { + this.nonNullCount_++; + this.sum_ += 1 * val; + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.avg) { + groupTotals.avg = {}; + } + if (this.nonNullCount_ != 0) { + groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; + } + }; + } + + function MinAggregator(field) { + this.field_ = field; + + this.init = function () { + this.min_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val != NaN) { + if (this.min_ == null || val < this.min_) { + this.min_ = val; + } + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.min) { + groupTotals.min = {}; + } + groupTotals.min[this.field_] = this.min_; } + } - function MaxAggregator(field) { - this.field_ = field; + function MaxAggregator(field) { + this.field_ = field; - this.init = function() { - this.max_ = null; - }; + this.init = function () { + this.max_ = null; + }; - this.accumulate = function(item) { - var val = item[this.field_]; - if (val != null && val != NaN) { - if (this.max_ == null ||val > this.max_) { - this.max_ = val; - } - } - }; - - this.storeResult = function(groupTotals) { - if (!groupTotals.max) { - groupTotals.max = {}; - } - groupTotals.max[this.field_] = this.max_; + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val != NaN) { + if (this.max_ == null || val > this.max_) { + this.max_ = val; } + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.max) { + groupTotals.max = {}; + } + groupTotals.max[this.field_] = this.max_; } + } - // TODO: add more built-in aggregators - // TODO: merge common aggregators in one to prevent needles iterating + // TODO: add more built-in aggregators + // TODO: merge common aggregators in one to prevent needles iterating })(jQuery);
\ No newline at end of file diff --git a/slick.editors.js b/slick.editors.js index a458195..3101da7 100644 --- a/slick.editors.js +++ b/slick.editors.js @@ -1,616 +1,626 @@ /* THESE FORMATTERS & EDITORS ARE JUST SAMPLES! */ +(function ($) { + var SlickEditor = { + SelectorCellFormatter:function (row, cell, value, columnDef, dataContext) { + return (!dataContext ? "" : row); + }, + + PercentCompleteCellFormatter:function (row, cell, value, columnDef, dataContext) { + if (value == null || value === "") { + return "-"; + } else if (value < 50) { + return "<span style='color:red;font-weight:bold;'>" + value + "%</span>"; + } else { + return "<span style='color:green'>" + value + "%</span>"; + } + }, + + GraphicalPercentCompleteCellFormatter:function (row, cell, value, columnDef, dataContext) { + if (value == null || value === "") { + return ""; + } + + var color; + + if (value < 30) { + color = "red"; + } else if (value < 70) { + color = "silver"; + } else { + color = "green"; + } + + return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>"; + }, + + YesNoCellFormatter:function (row, cell, value, columnDef, dataContext) { + return value ? "Yes" : "No"; + }, + + BoolCellFormatter:function (row, cell, value, columnDef, dataContext) { + return value ? "<img src='../images/tick.png'>" : ""; + }, + + TaskNameFormatter:function (row, cell, value, columnDef, dataContext) { + // todo: html encode + var spacer = "<span style='display:inline-block;height:1px;width:" + (2 + 15 * dataContext["indent"]) + "px'></span>"; + return spacer + " <img src='../images/expand.gif'> " + value; + }, + + ResourcesFormatter:function (row, cell, value, columnDef, dataContext) { + var resources = dataContext["resources"]; + + if (!resources || resources.length == 0) { + return ""; + } + + if (columnDef.width < 50) { + return (resources.length > 1 ? "<center><img src='../images/user_identity_plus.gif' " : "<center><img src='../images/user_identity.gif' ") + + " title='" + resources.join(", ") + "'></center>"; + } else { + return resources.join(", "); + } + }, + + StarFormatter:function (row, cell, value, columnDef, dataContext) { + return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : ""; + }, + + + TextCellEditor:function (args) { + var $input; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $("<INPUT type=text class='editor-text' />") + .appendTo(args.container) + .bind("keydown.nav", function (e) { + if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { + e.stopImmediatePropagation(); + } + }) + .focus() + .select(); + }; + + this.destroy = function () { + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.getValue = function () { + return $input.val(); + }; + + this.setValue = function (val) { + $input.val(val); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field] || ""; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + if (args.column.validator) { + var validationResults = args.column.validator($input.val()); + if (!validationResults.valid) { + return validationResults; + } + } + + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + IntegerCellEditor:function (args) { + var $input; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $("<INPUT type=text class='editor-text' />"); + + $input.bind("keydown.nav", function (e) { + if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { + e.stopImmediatePropagation(); + } + }); + + $input.appendTo(args.container); + $input.focus().select(); + }; + + this.destroy = function () { + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return parseInt($input.val(), 10) || 0; + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + if (isNaN($input.val())) { + return { + valid:false, + msg:"Please enter a valid integer" + }; + } + + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + DateCellEditor:function (args) { + var $input; + var defaultValue; + var scope = this; + var calendarOpen = false; + + this.init = function () { + $input = $("<INPUT type=text class='editor-text' />"); + $input.appendTo(args.container); + $input.focus().select(); + $input.datepicker({ + showOn:"button", + buttonImageOnly:true, + buttonImage:"../images/calendar.gif", + beforeShow:function () { + calendarOpen = true + }, + onClose:function () { + calendarOpen = false + } + }); + $input.width($input.width() - 18); + }; + + this.destroy = function () { + $.datepicker.dpDiv.stop(true, true); + $input.datepicker("hide"); + $input.datepicker("destroy"); + $input.remove(); + }; + + this.show = function () { + if (calendarOpen) { + $.datepicker.dpDiv.stop(true, true).show(); + } + }; -(function($) { - - var SlickEditor = { - - SelectorCellFormatter : function(row, cell, value, columnDef, dataContext) { - return (!dataContext ? "" : row); - }, - - PercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) { - if (value == null || value === "") - return "-"; - else if (value < 50) - return "<span style='color:red;font-weight:bold;'>" + value + "%</span>"; - else - return "<span style='color:green'>" + value + "%</span>"; - }, - - GraphicalPercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) { - if (value == null || value === "") - return ""; - - var color; - - if (value < 30) - color = "red"; - else if (value < 70) - color = "silver"; - else - color = "green"; - - return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>"; - }, - - YesNoCellFormatter : function(row, cell, value, columnDef, dataContext) { - return value ? "Yes" : "No"; - }, - - BoolCellFormatter : function(row, cell, value, columnDef, dataContext) { - return value ? "<img src='../images/tick.png'>" : ""; - }, - - TaskNameFormatter : function(row, cell, value, columnDef, dataContext) { - // todo: html encode - var spacer = "<span style='display:inline-block;height:1px;width:" + (2 + 15 * dataContext["indent"]) + "px'></span>"; - return spacer + " <img src='../images/expand.gif'> " + value; - }, - - ResourcesFormatter : function(row, cell, value, columnDef, dataContext) { - var resources = dataContext["resources"]; - - if (!resources || resources.length == 0) - return ""; - - if (columnDef.width < 50) - return (resources.length > 1 ? "<center><img src='../images/user_identity_plus.gif' " : "<center><img src='../images/user_identity.gif' ") + - " title='" + resources.join(", ") + "'></center>"; - else - return resources.join(", "); - }, - - StarFormatter : function(row, cell, value, columnDef, dataContext) { - return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : ""; - }, - - - TextCellEditor : function(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function() { - $input = $("<INPUT type=text class='editor-text' />") - .appendTo(args.container) - .bind("keydown.nav", function(e) { - if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }) - .focus() - .select(); - }; - - this.destroy = function() { - $input.remove(); - }; - - this.focus = function() { - $input.focus(); - }; - - this.getValue = function() { - return $input.val(); - }; - - this.setValue = function(val) { - $input.val(val); - }; - - this.loadValue = function(item) { - defaultValue = item[args.column.field] || ""; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function() { - return $input.val(); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function() { - if (args.column.validator) { - var validationResults = args.column.validator($input.val()); - if (!validationResults.valid) - return validationResults; - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - IntegerCellEditor : function(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function() { - $input = $("<INPUT type=text class='editor-text' />"); - - $input.bind("keydown.nav", function(e) { - if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }); - - $input.appendTo(args.container); - $input.focus().select(); - }; - - this.destroy = function() { - $input.remove(); - }; - - this.focus = function() { - $input.focus(); - }; - - this.loadValue = function(item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function() { - return parseInt($input.val(),10) || 0; - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function() { - if (isNaN($input.val())) - return { - valid: false, - msg: "Please enter a valid integer" - }; - - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - DateCellEditor : function(args) { - var $input; - var defaultValue; - var scope = this; - var calendarOpen = false; - - this.init = function() { - $input = $("<INPUT type=text class='editor-text' />"); - $input.appendTo(args.container); - $input.focus().select(); - $input.datepicker({ - showOn: "button", - buttonImageOnly: true, - buttonImage: "../images/calendar.gif", - beforeShow: function() { calendarOpen = true }, - onClose: function() { calendarOpen = false } - }); - $input.width($input.width() - 18); - }; - - this.destroy = function() { - $.datepicker.dpDiv.stop(true,true); - $input.datepicker("hide"); - $input.datepicker("destroy"); - $input.remove(); - }; - - this.show = function() { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true,true).show(); - } - }; - - this.hide = function() { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true,true).hide(); - } - }; - - this.position = function(position) { - if (!calendarOpen) return; - $.datepicker.dpDiv - .css("top", position.top + 30) - .css("left", position.left); - }; - - this.focus = function() { - $input.focus(); - }; - - this.loadValue = function(item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function() { - return $input.val(); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function() { - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - YesNoSelectCellEditor : function(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function() { - $select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>"); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function() { - $select.remove(); - }; - - this.focus = function() { - $select.focus(); - }; - - this.loadValue = function(item) { - $select.val((defaultValue = item[args.column.field]) ? "yes" : "no"); - $select.select(); - }; - - this.serializeValue = function() { - return ($select.val() == "yes"); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return ($select.val() != defaultValue); - }; - - this.validate = function() { - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - YesNoCheckboxCellEditor : function(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function() { - $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>"); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function() { - $select.remove(); - }; - - this.focus = function() { - $select.focus(); - }; - - this.loadValue = function(item) { - defaultValue = item[args.column.field]; - if (defaultValue) - $select.attr("checked", "checked"); - else - $select.removeAttr("checked"); - }; - - this.serializeValue = function() { - return $select.attr("checked"); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return ($select.attr("checked") != defaultValue); - }; - - this.validate = function() { - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - PercentCompleteCellEditor : function(args) { - var $input, $picker; - var defaultValue; - var scope = this; - - this.init = function() { - $input = $("<INPUT type=text class='editor-percentcomplete' />"); - $input.width($(args.container).innerWidth() - 25); - $input.appendTo(args.container); - - $picker = $("<div class='editor-percentcomplete-picker' />").appendTo(args.container); - $picker.append("<div class='editor-percentcomplete-helper'><div class='editor-percentcomplete-wrapper'><div class='editor-percentcomplete-slider' /><div class='editor-percentcomplete-buttons' /></div></div>"); - - $picker.find(".editor-percentcomplete-buttons").append("<button val=0>Not started</button><br/><button val=50>In Progress</button><br/><button val=100>Complete</button>"); - - $input.focus().select(); - - $picker.find(".editor-percentcomplete-slider").slider({ - orientation: "vertical", - range: "min", - value: defaultValue, - slide: function(event, ui) { - $input.val(ui.value) - } - }); - - $picker.find(".editor-percentcomplete-buttons button").bind("click", function(e) { - $input.val($(this).attr("val")); - $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val")); - }) - }; - - this.destroy = function() { - $input.remove(); - $picker.remove(); - }; - - this.focus = function() { - $input.focus(); - }; - - this.loadValue = function(item) { - $input.val(defaultValue = item[args.column.field]); - $input.select(); - }; - - this.serializeValue = function() { - return parseInt($input.val(),10) || 0; - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(),10) || 0) != defaultValue); - }; - - this.validate = function() { - if (isNaN(parseInt($input.val(),10))) - return { - valid: false, - msg: "Please enter a valid positive number" - }; - - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - StarCellEditor : function(args) { - var $input; - var defaultValue; - var scope = this; - - function toggle(e) { - if (e.type == "keydown" && e.which != 32) return; - - if ($input.css("opacity") == "1") - $input.css("opacity", 0.5); - else - $input.css("opacity", 1); - - e.preventDefault(); - e.stopPropagation(); - return false; - } - - this.init = function() { - $input = $("<IMG src='../images/bullet_star.png' align=absmiddle tabIndex=0 title='Click or press Space to toggle' />") - .bind("click keydown", toggle) - .appendTo(args.container) - .focus(); - }; - - this.destroy = function() { - $input.unbind("click keydown", toggle); - $input.remove(); - }; - - this.focus = function() { - $input.focus(); - }; - - this.loadValue = function(item) { - defaultValue = item[args.column.field]; - $input.css("opacity", defaultValue ? 1 : 0.2); - }; - - this.serializeValue = function() { - return ($input.css("opacity") == "1"); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return defaultValue != ($input.css("opacity") == "1"); - }; - - this.validate = function() { - return { - valid: true, - msg: null - }; - }; - - this.init(); - }, - - /* - * An example of a "detached" editor. - * The UI is added onto document BODY and .position(), .show() and .hide() are implemented. - * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. - */ - LongTextCellEditor : function (args) { - var $input, $wrapper; - var defaultValue; - var scope = this; - - this.init = function() { - var $container = $("body"); - - $wrapper = $("<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>") - .appendTo($container); - - $input = $("<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>") - .appendTo($wrapper); - - $("<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>") - .appendTo($wrapper); - - $wrapper.find("button:first").bind("click", this.save); - $wrapper.find("button:last").bind("click", this.cancel); - $input.bind("keydown", this.handleKeyDown); - - scope.position(args.position); - $input.focus().select(); - }; - - this.handleKeyDown = function(e) { - if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) { - scope.save(); - } - else if (e.which == $.ui.keyCode.ESCAPE) { - e.preventDefault(); - scope.cancel(); - } - else if (e.which == $.ui.keyCode.TAB && e.shiftKey) { - e.preventDefault(); - grid.navigatePrev(); - } - else if (e.which == $.ui.keyCode.TAB) { - e.preventDefault(); - grid.navigateNext(); - } - }; - - this.save = function() { - args.commitChanges(); - }; - - this.cancel = function() { - $input.val(defaultValue); - args.cancelChanges(); - }; - - this.hide = function() { - $wrapper.hide(); - }; - - this.show = function() { - $wrapper.show(); - }; - - this.position = function(position) { - $wrapper - .css("top", position.top - 5) - .css("left", position.left - 5) - }; - - this.destroy = function() { - $wrapper.remove(); - }; - - this.focus = function() { - $input.focus(); - }; - - this.loadValue = function(item) { - $input.val(defaultValue = item[args.column.field]); - $input.select(); - }; - - this.serializeValue = function() { - return $input.val(); - }; - - this.applyValue = function(item,state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function() { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function() { - return { - valid: true, - msg: null - }; - }; - - this.init(); + this.hide = function () { + if (calendarOpen) { + $.datepicker.dpDiv.stop(true, true).hide(); } + }; - }; + this.position = function (position) { + if (!calendarOpen) { + return; + } + $.datepicker.dpDiv + .css("top", position.top + 30) + .css("left", position.left); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + YesNoSelectCellEditor:function (args) { + var $select; + var defaultValue; + var scope = this; + + this.init = function () { + $select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>"); + $select.appendTo(args.container); + $select.focus(); + }; + + this.destroy = function () { + $select.remove(); + }; + + this.focus = function () { + $select.focus(); + }; + + this.loadValue = function (item) { + $select.val((defaultValue = item[args.column.field]) ? "yes" : "no"); + $select.select(); + }; + + this.serializeValue = function () { + return ($select.val() == "yes"); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return ($select.val() != defaultValue); + }; + + this.validate = function () { + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + YesNoCheckboxCellEditor:function (args) { + var $select; + var defaultValue; + var scope = this; + + this.init = function () { + $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>"); + $select.appendTo(args.container); + $select.focus(); + }; + + this.destroy = function () { + $select.remove(); + }; + + this.focus = function () { + $select.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + if (defaultValue) { + $select.attr("checked", "checked"); + } else { + $select.removeAttr("checked"); + } + }; + + this.serializeValue = function () { + return $select.attr("checked"); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return ($select.attr("checked") != defaultValue); + }; + + this.validate = function () { + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + PercentCompleteCellEditor:function (args) { + var $input, $picker; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $("<INPUT type=text class='editor-percentcomplete' />"); + $input.width($(args.container).innerWidth() - 25); + $input.appendTo(args.container); + + $picker = $("<div class='editor-percentcomplete-picker' />").appendTo(args.container); + $picker.append("<div class='editor-percentcomplete-helper'><div class='editor-percentcomplete-wrapper'><div class='editor-percentcomplete-slider' /><div class='editor-percentcomplete-buttons' /></div></div>"); + + $picker.find(".editor-percentcomplete-buttons").append("<button val=0>Not started</button><br/><button val=50>In Progress</button><br/><button val=100>Complete</button>"); + + $input.focus().select(); + + $picker.find(".editor-percentcomplete-slider").slider({ + orientation:"vertical", + range:"min", + value:defaultValue, + slide:function (event, ui) { + $input.val(ui.value) + } + }); + + $picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) { + $input.val($(this).attr("val")); + $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val")); + }) + }; + + this.destroy = function () { + $input.remove(); + $picker.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + $input.val(defaultValue = item[args.column.field]); + $input.select(); + }; + + this.serializeValue = function () { + return parseInt($input.val(), 10) || 0; + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue); + }; + + this.validate = function () { + if (isNaN(parseInt($input.val(), 10))) { + return { + valid:false, + msg:"Please enter a valid positive number" + }; + } - $.extend(window, SlickEditor); + return { + valid:true, + msg:null + }; + }; + this.init(); + }, + + StarCellEditor:function (args) { + var $input; + var defaultValue; + var scope = this; + + function toggle(e) { + if (e.type == "keydown" && e.which != 32) { + return; + } + + if ($input.css("opacity") == "1") { + $input.css("opacity", 0.5); + } else { + $input.css("opacity", 1); + } + + e.preventDefault(); + e.stopPropagation(); + return false; + } + + this.init = function () { + $input = $("<IMG src='../images/bullet_star.png' align=absmiddle tabIndex=0 title='Click or press Space to toggle' />") + .bind("click keydown", toggle) + .appendTo(args.container) + .focus(); + }; + + this.destroy = function () { + $input.unbind("click keydown", toggle); + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + $input.css("opacity", defaultValue ? 1 : 0.2); + }; + + this.serializeValue = function () { + return ($input.css("opacity") == "1"); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return defaultValue != ($input.css("opacity") == "1"); + }; + + this.validate = function () { + return { + valid:true, + msg:null + }; + }; + + this.init(); + }, + + /* + * An example of a "detached" editor. + * The UI is added onto document BODY and .position(), .show() and .hide() are implemented. + * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. + */ + LongTextCellEditor:function (args) { + var $input, $wrapper; + var defaultValue; + var scope = this; + + this.init = function () { + var $container = $("body"); + + $wrapper = $("<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>") + .appendTo($container); + + $input = $("<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>") + .appendTo($wrapper); + + $("<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>") + .appendTo($wrapper); + + $wrapper.find("button:first").bind("click", this.save); + $wrapper.find("button:last").bind("click", this.cancel); + $input.bind("keydown", this.handleKeyDown); + + scope.position(args.position); + $input.focus().select(); + }; + + this.handleKeyDown = function (e) { + if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) { + scope.save(); + } else if (e.which == $.ui.keyCode.ESCAPE) { + e.preventDefault(); + scope.cancel(); + } else if (e.which == $.ui.keyCode.TAB && e.shiftKey) { + e.preventDefault(); + grid.navigatePrev(); + } else if (e.which == $.ui.keyCode.TAB) { + e.preventDefault(); + grid.navigateNext(); + } + }; + + this.save = function () { + args.commitChanges(); + }; + + this.cancel = function () { + $input.val(defaultValue); + args.cancelChanges(); + }; + + this.hide = function () { + $wrapper.hide(); + }; + + this.show = function () { + $wrapper.show(); + }; + + this.position = function (position) { + $wrapper + .css("top", position.top - 5) + .css("left", position.left - 5) + }; + + this.destroy = function () { + $wrapper.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + $input.val(defaultValue = item[args.column.field]); + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + return { + valid:true, + msg:null + }; + }; + + this.init(); + } + }; + + $.extend(window, SlickEditor); })(jQuery); diff --git a/slick.grid.css b/slick.grid.css index b817638..db7fca1 100644 --- a/slick.grid.css +++ b/slick.grid.css @@ -5,154 +5,153 @@ No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) classes should alter those! */ - .slick-header.ui-state-default, .slick-headerrow.ui-state-default { - width: 100%; - overflow: hidden; - border-left: 0px; + width: 100%; + overflow: hidden; + border-left: 0px; } .slick-header-columns, .slick-headerrow-columns { - width: 999999px; - position: relative; - white-space: nowrap; - cursor: default; - overflow: hidden; + width: 999999px; + position: relative; + white-space: nowrap; + cursor: default; + overflow: hidden; } .slick-header-column.ui-state-default { - position: relative; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - height: 16px; - line-height: 16px; - margin: 0; - padding: 4px; - border-right: 1px solid silver; - border-left: 0px; - border-top: 0px; - border-bottom: 0px; - float: left; + position: relative; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + height: 16px; + line-height: 16px; + margin: 0; + padding: 4px; + border-right: 1px solid silver; + border-left: 0px; + border-top: 0px; + border-bottom: 0px; + float: left; } .slick-headerrow-column.ui-state-default { - padding: 4px; + padding: 4px; } .slick-header-column-sorted { - font-style: italic; + font-style: italic; } .slick-sort-indicator { - display: inline-block; - width: 8px; - height: 5px; - margin-left: 4px; + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; } .slick-sort-indicator-desc { - background: url(images/sort-desc.gif); + background: url(images/sort-desc.gif); } .slick-sort-indicator-asc { - background: url(images/sort-asc.gif); + background: url(images/sort-asc.gif); } .slick-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - cursor: col-resize; - width: 4px; - right: 0px; - top: 0; - height: 100%; + position: absolute; + font-size: 0.1px; + display: block; + cursor: col-resize; + width: 4px; + right: 0px; + top: 0; + height: 100%; } .slick-sortable-placeholder { - background: silver; + background: silver; } .grid-canvas { - position: relative; - outline: 0; + position: relative; + outline: 0; } .slick-row.ui-widget-content, .slick-row.ui-state-active { - position: absolute; - border: 0px; + position: absolute; + border: 0px; } .slick-cell, .slick-headerrow-column { - position: absolute; + position: absolute; - border: 1px solid transparent; - border-right: 1px dotted silver; - border-bottom-color: silver; + border: 1px solid transparent; + border-right: 1px dotted silver; + border-bottom-color: silver; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: middle; - z-index: 1; - padding: 1px 2px 2px 1px; - margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; + z-index: 1; + padding: 1px 2px 2px 1px; + margin: 0; - white-space: nowrap; + white-space: nowrap; - cursor: default; + cursor: default; } .slick-group { } .slick-group-toggle { - display: inline-block; + display: inline-block; } .slick-cell.highlighted { - background: lightskyblue; - background: rgba(0,0,255,0.2); - -webkit-transition: all 0.5s; - -moz-transition: all 0.5s; - transition: all 0.5s; + background: lightskyblue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + transition: all 0.5s; } .slick-cell.flashing { - border: 1px solid red !important; + border: 1px solid red !important; } .slick-cell.editable { - z-index: 11; - overflow: visible; - background: white; - border-color: black; - border-style: solid; + z-index: 11; + overflow: visible; + background: white; + border-color: black; + border-style: solid; } .slick-cell:focus { - outline: none; + outline: none; } .slick-reorder-proxy { - display: inline-block; - background: blue; - opacity: 0.15; - filter: alpha(opacity=15); - cursor: move; + display: inline-block; + background: blue; + opacity: 0.15; + filter: alpha(opacity = 15); + cursor: move; } .slick-reorder-guide { - display: inline-block; - height: 2px; - background: blue; - opacity: 0.7; - filter: alpha(opacity=70); + display: inline-block; + height: 2px; + background: blue; + opacity: 0.7; + filter: alpha(opacity = 70); } .slick-selection { - z-index: 10; - position: absolute; - border: 2px dashed black; + z-index: 10; + position: absolute; + border: 2px dashed black; }
\ No newline at end of file diff --git a/slick.grid.js b/slick.grid.js index 37500e3..408c068 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -1,8 +1,9 @@ /** * @license - * (c) 2009-2010 Michael Leibman + * (c) 2009-2012 Michael Leibman * michael{dot}leibman{at}gmail{dot}com * http://github.com/mleibman/slickgrid + * * Distributed under MIT license. * All rights reserved. * @@ -17,2605 +18,2658 @@ // make sure required JavaScript modules are loaded if (typeof jQuery === "undefined") { - throw "SlickGrid requires jquery module to be loaded"; + throw "SlickGrid requires jquery module to be loaded"; } if (!jQuery.fn.drag) { - throw "SlickGrid requires jquery.event.drag module to be loaded"; + throw "SlickGrid requires jquery.event.drag module to be loaded"; } if (typeof Slick === "undefined") { - throw "slick.core.js not loaded"; + throw "slick.core.js not loaded"; } -(function($) { - // Slick.Grid - $.extend(true, window, { - Slick: { - Grid: SlickGrid - } - }); +(function ($) { + // Slick.Grid + $.extend(true, window, { + Slick:{ + Grid:SlickGrid + } + }); + + // shared across all grids on the page + var scrollbarDimensions; + var maxSupportedCssHeight; // browser's breaking point + + ////////////////////////////////////////////////////////////////////////////////////////////// + // SlickGrid class implementation (available as Slick.Grid) + + /** + * Creates a new instance of the grid. + * @class SlickGrid + * @constructor + * @param {Node} container Container node to create the grid in. + * @param {Array,Object} data An array of objects for databinding. + * @param {Array} columns An array of column definitions. + * @param {Object} options Grid options. + **/ + function SlickGrid(container, data, columns, options) { + // settings + var defaults = { + headerHeight:25, + rowHeight:25, + defaultColumnWidth:80, + enableAddRow:false, + leaveSpaceForNewRows:false, + editable:false, + autoEdit:true, + enableCellNavigation:true, + enableColumnReorder:true, + asyncEditorLoading:false, + asyncEditorLoadDelay:100, + forceFitColumns:false, + enableAsyncPostRender:false, + asyncPostRenderDelay:60, + autoHeight:false, + editorLock:Slick.GlobalEditorLock, + showHeaderRow:false, + headerRowHeight:25, + showTopPanel:false, + topPanelHeight:25, + formatterFactory:null, + editorFactory:null, + cellFlashingCssClass:"flashing", + selectedCellCssClass:"selected", + multiSelect:true, + enableTextSelectionOnCells:false, + dataItemColumnValueExtractor:null + }; + + var columnDefaults = { + name:"", + resizable:true, + sortable:false, + minWidth:30, + rerenderOnResize:false, + headerCssClass:null + }; + + // scroller + var th; // virtual height + var h; // real scrollable height + var ph; // page height + var n; // number of pages + var cj; // "jumpiness" coefficient + + var page = 0; // current page + var offset = 0; // current page offset + var scrollDir = 1; + + // private + var $container; + var uid = "slickgrid_" + Math.round(1000000 * Math.random()); + var self = this; + var $headerScroller; + var $headers; + var $headerRow, $headerRowScroller; + var $topPanelScroller; + var $topPanel; + var $viewport; + var $canvas; + var $style; + var stylesheet; + var viewportH, viewportW; + var viewportHasHScroll; + var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff; // padding+border + var absoluteColumnMinWidth; + + var activePosX; + var activeRow, activeCell; + var activeCellNode = null; + var currentEditor = null; + var serializedEditorValue; + var editController; + + var rowsCache = {}; + var renderedRows = 0; + var numVisibleRows; + var prevScrollTop = 0; + var scrollTop = 0; + var lastRenderedScrollTop = 0; + var prevScrollLeft = 0; + var avgRowRenderTime = 10; + + var selectionModel; + var selectedRows = []; + + var plugins = []; + var cellCssClasses = {}; + + var columnsById = {}; + var sortColumnId; + var sortAsc = true; + + // async call handles + var h_editorLoader = null; + var h_render = null; + var h_postrender = null; + var postProcessedRows = {}; + var postProcessToRow = null; + var postProcessFromRow = null; + + // perf counters + var counter_rows_rendered = 0; + var counter_rows_removed = 0; - // shared across all grids on the page - var scrollbarDimensions; - var maxSupportedCssHeight; // browser's breaking point ////////////////////////////////////////////////////////////////////////////////////////////// - // SlickGrid class implementation (available as Slick.Grid) - - /** - * @param {Node} container Container node to create the grid in. - * @param {Array,Object} data An array of objects for databinding. - * @param {Array} columns An array of column definitions. - * @param {Object} options Grid options. - **/ - function SlickGrid(container,data,columns,options) { - /// <summary> - /// Create and manage virtual grid in the specified $container, - /// connecting it to the specified data source. Data is presented - /// as a grid with the specified columns and data.length rows. - /// Options alter behaviour of the grid. - /// </summary> - - // settings - var defaults = { - headerHeight: 25, - rowHeight: 25, - defaultColumnWidth: 80, - enableAddRow: false, - leaveSpaceForNewRows: false, - editable: false, - autoEdit: true, - enableCellNavigation: true, - enableColumnReorder: true, - asyncEditorLoading: false, - asyncEditorLoadDelay: 100, - forceFitColumns: false, - enableAsyncPostRender: false, - asyncPostRenderDelay: 60, - autoHeight: false, - editorLock: Slick.GlobalEditorLock, - showHeaderRow: false, - headerRowHeight: 25, - showTopPanel: false, - topPanelHeight: 25, - formatterFactory: null, - editorFactory: null, - cellFlashingCssClass: "flashing", - selectedCellCssClass: "selected", - multiSelect: true, - enableTextSelectionOnCells: false, - dataItemColumnValueExtractor: null - }; - - var columnDefaults = { - name: "", - resizable: true, - sortable: false, - minWidth: 30, - rerenderOnResize: false, - headerCssClass: null - }; - - // scroller - var th; // virtual height - var h; // real scrollable height - var ph; // page height - var n; // number of pages - var cj; // "jumpiness" coefficient - - var page = 0; // current page - var offset = 0; // current page offset - var scrollDir = 1; - - // private - var $container; - var uid = "slickgrid_" + Math.round(1000000 * Math.random()); - var self = this; - var $headerScroller; - var $headers; - var $headerRow, $headerRowScroller; - var $topPanelScroller; - var $topPanel; - var $viewport; - var $canvas; - var $style; - var stylesheet; - var viewportH, viewportW; - var viewportHasHScroll; - var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff; // padding+border - var absoluteColumnMinWidth; - - var activePosX; - var activeRow, activeCell; - var activeCellNode = null; - var currentEditor = null; - var serializedEditorValue; - var editController; - - var rowsCache = {}; - var renderedRows = 0; - var numVisibleRows; - var prevScrollTop = 0; - var scrollTop = 0; - var lastRenderedScrollTop = 0; - var prevScrollLeft = 0; - var avgRowRenderTime = 10; - - var selectionModel; - var selectedRows = []; - - var plugins = []; - var cellCssClasses = {}; - - var columnsById = {}; - var sortColumnId; - var sortAsc = true; - - // async call handles - var h_editorLoader = null; - var h_render = null; - var h_postrender = null; - var postProcessedRows = {}; - var postProcessToRow = null; - var postProcessFromRow = null; - - // perf counters - var counter_rows_rendered = 0; - var counter_rows_removed = 0; - - - ////////////////////////////////////////////////////////////////////////////////////////////// - // Initialization - - function init() { - /// <summary> - /// Initialize 'this' (self) instance of a SlickGrid. - /// This function is called by the constructor. - /// </summary> - - $container = $(container); - if($container.length < 1) { - throw new Error("SlickGrid requires a valid container, "+container+" does not exist in the DOM."); - } - - maxSupportedCssHeight = getMaxSupportedCssHeight(); - - scrollbarDimensions = scrollbarDimensions || measureScrollbar(); // skip measurement if already have dimensions - options = $.extend({},defaults,options); - columnDefaults.width = options.defaultColumnWidth; - - // validate loaded JavaScript modules against requested options - if (options.enableColumnReorder && !$.fn.sortable) { - throw new Error("SlickGrid's \"enableColumnReorder = true\" option requires jquery-ui.sortable module to be loaded"); - } - - editController = { - "commitCurrentEdit": commitCurrentEdit, - "cancelCurrentEdit": cancelCurrentEdit - }; - - $container - .empty() - .attr("tabIndex",0) - .attr("hideFocus",true) - .css("overflow","hidden") - .css("outline",0) - .addClass(uid) - .addClass("ui-widget"); + // Initialization + + function init() { + $container = $(container); + if ($container.length < 1) { + throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); + } + + // calculate these only once and share between grid instances + maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); + scrollbarDimensions = scrollbarDimensions || measureScrollbar(); + + options = $.extend({}, defaults, options); + columnDefaults.width = options.defaultColumnWidth; + + // validate loaded JavaScript modules against requested options + if (options.enableColumnReorder && !$.fn.sortable) { + throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded"); + } + + editController = { + "commitCurrentEdit":commitCurrentEdit, + "cancelCurrentEdit":cancelCurrentEdit + }; + + $container + .empty() + .attr("tabIndex", 0) + .attr("hideFocus", true) + .css("overflow", "hidden") + .css("outline", 0) + .addClass(uid) + .addClass("ui-widget"); + + // set up a positioning container if needed + if (!/relative|absolute|fixed/.test($container.css("position"))) { + $container.css("position", "relative"); + } + + $headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); + $headers = $("<div class='slick-header-columns' style='width:10000px; left:-1000px' />").appendTo($headerScroller); + + $headerRowScroller = $("<div class='slick-headerrow ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); + $headerRow = $("<div class='slick-headerrow-columns' />").appendTo($headerRowScroller); + + $topPanelScroller = $("<div class='slick-top-panel-scroller ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); + $topPanel = $("<div class='slick-top-panel' style='width:10000px' />").appendTo($topPanelScroller); + + if (!options.showTopPanel) { + $topPanelScroller.hide(); + } + + if (!options.showHeaderRow) { + $headerRowScroller.hide(); + } + + $viewport = $("<div class='slick-viewport' tabIndex='0' hideFocus style='width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;'>").appendTo($container); + $canvas = $("<div class='grid-canvas' tabIndex='0' hideFocus />").appendTo($viewport); + + // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?) + // calculate the diff so we can set consistent sizes + measureCellPaddingAndBorder(); + + // for usability reasons, all text selection in SlickGrid is disabled + // with the exception of input and textarea elements (selection must + // be enabled there so that editors work as expected); note that + // selection in grid cells (grid body) is already unavailable in + // all browsers except IE + disableSelection($headers); // disable all text selection in header (including input and textarea) + + if (!options.enableTextSelectionOnCells) { + // disable text selection in grid cells except in input and textarea elements + // (this is IE-specific, because selectstart event will only fire in IE) + $viewport.bind("selectstart.ui", function (event) { + return $(event.target).is("input,textarea"); + }); + } + + viewportW = parseFloat($.css($container[0], "width", true)); + + createColumnHeaders(); + setupColumnSort(); + createCssRules(); + resizeAndRender(); + + bindAncestorScrollEvents(); + $viewport.bind("scroll.slickgrid", handleScroll); + $container.bind("resize.slickgrid", resizeAndRender); + $headerScroller + .bind("contextmenu.slickgrid", handleHeaderContextMenu) + .bind("click.slickgrid", handleHeaderClick); + + $canvas + .bind("keydown.slickgrid", handleKeyDown) + .bind("click.slickgrid", handleClick) + .bind("dblclick.slickgrid", handleDblClick) + .bind("contextmenu.slickgrid", handleContextMenu) + .bind("draginit", handleDragInit) + .bind("dragstart", handleDragStart) + .bind("drag", handleDrag) + .bind("dragend", handleDragEnd); + + $canvas.delegate(".slick-cell", "mouseenter", handleMouseEnter); + $canvas.delegate(".slick-cell", "mouseleave", handleMouseLeave); + } - // set up a positioning container if needed - if (!/relative|absolute|fixed/.test($container.css("position"))) - $container.css("position","relative"); + function registerPlugin(plugin) { + plugins.unshift(plugin); + plugin.init(self); + } - $headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); - $headers = $("<div class='slick-header-columns' style='width:10000px; left:-1000px' />").appendTo($headerScroller); + function unregisterPlugin(plugin) { + for (var i = plugins.length; i >= 0; i--) { + if (plugins[i] === plugin) { + if (plugins[i].destroy) { + plugins[i].destroy(); + } + plugins.splice(i, 1); + break; + } + } + } - $headerRowScroller = $("<div class='slick-headerrow ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); - $headerRow = $("<div class='slick-headerrow-columns' />").appendTo($headerRowScroller); + function setSelectionModel(model) { + if (selectionModel) { + selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); + if (selectionModel.destroy) { + selectionModel.destroy(); + } + } - $topPanelScroller = $("<div class='slick-top-panel-scroller ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); - $topPanel = $("<div class='slick-top-panel' style='width:10000px' />").appendTo($topPanelScroller); + selectionModel = model; + if (selectionModel) { + selectionModel.init(self); + selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); + } + } - if (!options.showTopPanel) { - $topPanelScroller.hide(); - } + function getSelectionModel() { + return selectionModel; + } - if (!options.showHeaderRow) { - $headerRowScroller.hide(); - } + function getCanvasNode() { + return $canvas[0]; + } - $viewport = $("<div class='slick-viewport' tabIndex='0' hideFocus style='width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;'>").appendTo($container); - $canvas = $("<div class='grid-canvas' tabIndex='0' hideFocus />").appendTo($viewport); - - // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?) - // calculate the diff so we can set consistent sizes - measureCellPaddingAndBorder(); - - // for usability reasons, all text selection in SlickGrid is disabled - // with the exception of input and textarea elements (selection must - // be enabled there so that editors work as expected); note that - // selection in grid cells (grid body) is already unavailable in - // all browsers except IE - disableSelection($headers); // disable all text selection in header (including input and textarea) - - if (!options.enableTextSelectionOnCells) { - // disable text selection in grid cells except in input and textarea elements - // (this is IE-specific, because selectstart event will only fire in IE) - $viewport.bind("selectstart.ui", function (event) { - return $(event.target).is("input,textarea"); - }); - } + function measureScrollbar() { + var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body"); + var dim = { + width:$c.width() - $c[0].clientWidth, + height:$c.height() - $c[0].clientHeight + }; + $c.remove(); + return dim; + } - viewportW = parseFloat($.css($container[0], "width", true)); + function getRowWidth() { + var rowWidth = 0; + var i = columns.length; + while (i--) { + rowWidth += (columns[i].width || columnDefaults.width); + } + return rowWidth; + } - createColumnHeaders(); - setupColumnSort(); - createCssRules(); - resizeAndRender(); + function setCanvasWidth(width) { + $canvas.width(width); + $headerRow.width(width); + viewportHasHScroll = (width > viewportW - scrollbarDimensions.width); + } - bindAncestorScrollEvents(); - $viewport.bind("scroll.slickgrid", handleScroll); - $container.bind("resize.slickgrid", resizeAndRender); - $headerScroller - .bind("contextmenu.slickgrid", handleHeaderContextMenu) - .bind("click.slickgrid", handleHeaderClick); + function disableSelection($target) { + if ($target && $target.jquery) { + $target + .attr("unselectable", "on") + .css("MozUserSelect", "none") + .bind("selectstart.ui", function () { + return false; + }); // from jquery:ui.core.js 1.7.2 + } + } - $canvas - .bind("keydown.slickgrid", handleKeyDown) - .bind("click.slickgrid", handleClick) - .bind("dblclick.slickgrid", handleDblClick) - .bind("contextmenu.slickgrid", handleContextMenu) - .bind("draginit", handleDragInit) - .bind("dragstart", handleDragStart) - .bind("drag", handleDrag) - .bind("dragend", handleDragEnd); + function getMaxSupportedCssHeight() { + var increment = 1000000; + var supportedHeight = increment; + // FF reports the height back but still renders blank after ~6M px + var testUpTo = ($.browser.mozilla) ? 5000000 : 1000000000; + var div = $("<div style='display:none' />").appendTo(document.body); - $canvas.delegate(".slick-cell", "mouseenter", handleMouseEnter); - $canvas.delegate(".slick-cell", "mouseleave", handleMouseLeave); + while (supportedHeight <= testUpTo) { + div.css("height", supportedHeight + increment); + if (div.height() !== supportedHeight + increment) { + break; + } else { + supportedHeight += increment; } + } - function registerPlugin(plugin) { - plugins.unshift(plugin); - plugin.init(self); - } + div.remove(); + return supportedHeight; + } - function unregisterPlugin(plugin) { - for (var i = plugins.length; i >= 0; i--) { - if (plugins[i] === plugin) { - if (plugins[i].destroy) { - plugins[i].destroy(); - } - plugins.splice(i, 1); - break; - } - } + // TODO: this is static. need to handle page mutation. + function bindAncestorScrollEvents() { + var elem = $canvas[0]; + while ((elem = elem.parentNode) != document.body && elem != null) { + // bind to scroll containers only + if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight) { + $(elem).bind("scroll.slickgrid", handleActiveCellPositionChange); } + } + } - function setSelectionModel(model) { - if (selectionModel) { - selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); - if (selectionModel.destroy) { - selectionModel.destroy(); - } - } + function unbindAncestorScrollEvents() { + $canvas.parents().unbind("scroll.slickgrid"); + } - selectionModel = model; - if (selectionModel) { - selectionModel.init(self); - selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); - } - } + function updateColumnHeader(columnId, title, toolTip) { + var idx = getColumnIndex(columnId); + var $header = $headers.children().eq(idx); + if ($header) { + columns[idx].name = title; + columns[idx].toolTip = toolTip; + $header + .attr("title", toolTip || title || "") + .children().eq(0).html(title); + } + } - function getSelectionModel() { - return selectionModel; - } + function getHeaderRow() { + return $headerRow[0]; + } - function getCanvasNode() { - return $canvas[0]; - } + function getHeaderRowColumn(columnId) { + var idx = getColumnIndex(columnId); + var $header = $headerRow.children().eq(idx); + return $header && $header[0]; + } - function measureScrollbar() { - /// <summary> - /// Measure width of a vertical scrollbar - /// and height of a horizontal scrollbar. - /// </summary - /// <returns> - /// { width: pixelWidth, height: pixelHeight } - /// </returns> - var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body"); - var dim = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight }; - $c.remove(); - return dim; - } + function createColumnHeaders() { + var i; - function getRowWidth() { - var rowWidth = 0; - var i = columns.length; - while (i--) { - rowWidth += (columns[i].width || columnDefaults.width); - } - return rowWidth; - } + function hoverBegin() { + $(this).addClass("ui-state-hover"); + } - function setCanvasWidth(width) { - $canvas.width(width); - $headerRow.width(width); - viewportHasHScroll = (width > viewportW - scrollbarDimensions.width); - } + function hoverEnd() { + $(this).removeClass("ui-state-hover"); + } - function disableSelection($target) { - /// <summary> - /// Disable text selection (using mouse) in - /// the specified target. - /// </summary - if ($target && $target.jquery) { - $target - .attr('unselectable', 'on') - .css('MozUserSelect', 'none') - .bind('selectstart.ui', function() { return false; }); // from jquery:ui.core.js 1.7.2 - } - } + $headers.empty(); + $headerRow.empty(); + columnsById = {}; - function getMaxSupportedCssHeight() { - // return cached value if already calculated - if (maxSupportedCssHeight) { - return maxSupportedCssHeight; - } + for (i = 0; i < columns.length; i++) { + var m = columns[i] = $.extend({}, columnDefaults, columns[i]); + columnsById[m.id] = i; - var increment = 1000000; - var supportedHeight = increment; - // FF reports the height back but still renders blank after ~6M px - var testUpTo = ($.browser.mozilla) ? 5000000 : 1000000000; - var div = $("<div style='display:none' />").appendTo(document.body); - - while (supportedHeight <= testUpTo) { - div.css("height", supportedHeight + increment); - if (div.height() !== supportedHeight + increment) - break; - else - supportedHeight += increment; - } + var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />") + .html("<span class='slick-column-name'>" + m.name + "</span>") + .width(m.width - headerColumnWidthDiff) + .attr("title", m.toolTip || m.name || "") + .data("fieldId", m.id) + .addClass(m.headerCssClass || "") + .appendTo($headers); - div.remove(); - return supportedHeight; + if (options.enableColumnReorder || m.sortable) { + header.hover(hoverBegin, hoverEnd); } - // TODO: this is static. need to handle page mutation. - function bindAncestorScrollEvents() { - var elem = $canvas[0]; - while ((elem = elem.parentNode) != document.body) { - // bind to scroll containers only - if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight) - $(elem).bind("scroll.slickgrid", handleActiveCellPositionChange); - } + if (m.sortable) { + header.append("<span class='slick-sort-indicator' />"); } - function unbindAncestorScrollEvents() { - $canvas.parents().unbind("scroll.slickgrid"); + if (options.showHeaderRow) { + $("<div class='ui-state-default slick-headerrow-column l" + i + " r" + i + "'></div>") + .appendTo($headerRow); } + } - function updateColumnHeader(columnId, title, toolTip) { - var idx = getColumnIndex(columnId); - var $header = $headers.children().eq(idx); - if ($header) { - columns[idx].name = title; - columns[idx].toolTip = toolTip; - $header - .attr("title", toolTip || title || "") - .children().eq(0).html(title); - } - } + setSortColumn(sortColumnId, sortAsc); + setupColumnResize(); + if (options.enableColumnReorder) { + setupColumnReorder(); + } + } - function getHeaderRow() { - return $headerRow[0]; + function setupColumnSort() { + $headers.click(function (e) { + if ($(e.target).hasClass("slick-resizable-handle")) { + return; } - function getHeaderRowColumn(columnId) { - var idx = getColumnIndex(columnId); - var $header = $headerRow.children().eq(idx); - return $header && $header[0]; + var $col = $(e.target).closest(".slick-header-column"); + if (!$col.length) { + return; } - function createColumnHeaders() { - var i; - - function hoverBegin() { - $(this).addClass("ui-state-hover"); - } - function hoverEnd() { - $(this).removeClass("ui-state-hover"); - } - - $headers.empty(); - $headerRow.empty(); - columnsById = {}; - - for (i = 0; i < columns.length; i++) { - var m = columns[i] = $.extend({},columnDefaults,columns[i]); - columnsById[m.id] = i; + var column = columns[getColumnIndex($col.data("fieldId"))]; + if (column.sortable) { + if (!getEditorLock().commitCurrentEdit()) { + return; + } - var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />") - .html("<span class='slick-column-name'>" + m.name + "</span>") - .width(m.width - headerColumnWidthDiff) - .attr("title", m.toolTip || m.name || "") - .data("fieldId", m.id) - .addClass(m.headerCssClass || "") - .appendTo($headers); + if (column.id === sortColumnId) { + sortAsc = !sortAsc; + } + else { + sortColumnId = column.id; + sortAsc = true; + } - if (options.enableColumnReorder || m.sortable) { - header.hover(hoverBegin, hoverEnd); - } - - if (m.sortable) { - header.append("<span class='slick-sort-indicator' />"); - } - - if (options.showHeaderRow) { - $("<div class='ui-state-default slick-headerrow-column l" + i + " r" + i +"'></div>") - .appendTo($headerRow); - } - } - - setSortColumn(sortColumnId,sortAsc); - setupColumnResize(); - if (options.enableColumnReorder) { - setupColumnReorder(); - } + setSortColumn(sortColumnId, sortAsc); + trigger(self.onSort, {sortCol:column, sortAsc:sortAsc}, e); } + }); + } - function setupColumnSort() { - $headers.click(function(e) { - if ($(e.target).hasClass("slick-resizable-handle")) { - return; - } - - var $col = $(e.target).closest(".slick-header-column"); - if (!$col.length) - return; - - var column = columns[getColumnIndex($col.data("fieldId"))]; - if (column.sortable) { - if (!getEditorLock().commitCurrentEdit()) - return; + function setupColumnReorder() { + $headers.sortable({ + containment:"parent", + axis:"x", + cursor:"default", + tolerance:"intersection", + helper:"clone", + placeholder:"slick-sortable-placeholder ui-state-default slick-header-column", + forcePlaceholderSize:true, + start:function (e, ui) { + $(ui.helper).addClass("slick-header-column-active"); + }, + beforeStop:function (e, ui) { + $(ui.helper).removeClass("slick-header-column-active"); + }, + stop:function (e) { + if (!getEditorLock().commitCurrentEdit()) { + $(this).sortable("cancel"); + return; + } + + var reorderedIds = $headers.sortable("toArray"); + var reorderedColumns = []; + for (var i = 0; i < reorderedIds.length; i++) { + reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid, ""))]); + } + setColumns(reorderedColumns); + + trigger(self.onColumnsReordered, {}); + e.stopPropagation(); + setupColumnResize(); + } + }); + } - if (column.id === sortColumnId) { - sortAsc = !sortAsc; + function setupColumnResize() { + var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable, originalCanvasWidth; + columnElements = $headers.children(); + columnElements.find(".slick-resizable-handle").remove(); + columnElements.each(function (i, e) { + if (columns[i].resizable) { + if (firstResizable === undefined) { + firstResizable = i; + } + lastResizable = i; + } + }); + if (firstResizable === undefined) { + return; + } + columnElements.each(function (i, e) { + if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) { + return; + } + $col = $(e); + $("<div class='slick-resizable-handle' />") + .appendTo(e) + .bind("dragstart", function (e, dd) { + if (!getEditorLock().commitCurrentEdit()) { + return false; + } + pageX = e.pageX; + $(this).parent().addClass("slick-header-column-active"); + var shrinkLeewayOnRight = null, stretchLeewayOnRight = null; + // lock each column's width option to current width + columnElements.each(function (i, e) { + columns[i].previousWidth = $(e).outerWidth(); + }); + if (options.forceFitColumns) { + shrinkLeewayOnRight = 0; + stretchLeewayOnRight = 0; + // colums on right affect maxPageX/minPageX + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnRight !== null) { + if (c.maxWidth) { + stretchLeewayOnRight += c.maxWidth - c.previousWidth; + } + else { + stretchLeewayOnRight = null; + } + } + shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + } + } + var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0; + for (j = 0; j <= i; j++) { + // columns on left only affect minPageX + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnLeft !== null) { + if (c.maxWidth) { + stretchLeewayOnLeft += c.maxWidth - c.previousWidth; } else { - sortColumnId = column.id; - sortAsc = true; + stretchLeewayOnLeft = null; } - - setSortColumn(sortColumnId,sortAsc); - trigger(self.onSort, {sortCol:column,sortAsc:sortAsc}, e); + } + shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); } - }); - } - - function setupColumnReorder() { - $headers.sortable({ - containment: "parent", - axis: "x", - cursor: "default", - tolerance: "intersection", - helper: "clone", - placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", - forcePlaceholderSize: true, - start: function(e, ui) { $(ui.helper).addClass("slick-header-column-active"); }, - beforeStop: function(e, ui) { $(ui.helper).removeClass("slick-header-column-active"); }, - stop: function(e) { - if (!getEditorLock().commitCurrentEdit()) { - $(this).sortable("cancel"); - return; + } + if (shrinkLeewayOnRight === null) { + shrinkLeewayOnRight = 100000; + } + if (shrinkLeewayOnLeft === null) { + shrinkLeewayOnLeft = 100000; + } + if (stretchLeewayOnRight === null) { + stretchLeewayOnRight = 100000; + } + if (stretchLeewayOnLeft === null) { + stretchLeewayOnLeft = 100000; + } + maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft); + minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight); + originalCanvasWidth = $canvas.width(); + }) + .bind("drag", function (e, dd) { + var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x; + if (d < 0) { // shrink column + x = d; + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; } + } + } - var reorderedIds = $headers.sortable("toArray"); - var reorderedColumns = []; - for (var i=0; i<reorderedIds.length; i++) { - reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid,""))]); + if (options.forceFitColumns) { + x = -d; + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } } - setColumns(reorderedColumns); - - trigger(self.onColumnsReordered, {}); - e.stopPropagation(); - setupColumnResize(); + } + } else if (options.syncColumnCellResize) { + setCanvasWidth(originalCanvasWidth + d); } - }); - } - - function setupColumnResize() { - var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable, originalCanvasWidth; - columnElements = $headers.children(); - columnElements.find(".slick-resizable-handle").remove(); - columnElements.each(function(i,e) { - if (columns[i].resizable) { - if (firstResizable === undefined) { firstResizable = i; } - lastResizable = i; + } else { // stretch column + x = d; + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } } - }); - if (firstResizable === undefined) { - return; - } - columnElements.each(function(i,e) { - if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) { return; } - $col = $(e); - $("<div class='slick-resizable-handle' />") - .appendTo(e) - .bind("dragstart", function(e,dd) { - if (!getEditorLock().commitCurrentEdit()) { return false; } - pageX = e.pageX; - $(this).parent().addClass("slick-header-column-active"); - var shrinkLeewayOnRight = null, stretchLeewayOnRight = null; - // lock each column's width option to current width - columnElements.each(function(i,e) { columns[i].previousWidth = $(e).outerWidth(); }); - if (options.forceFitColumns) { - shrinkLeewayOnRight = 0; - stretchLeewayOnRight = 0; - // colums on right affect maxPageX/minPageX - for (j = i + 1; j < columnElements.length; j++) { - c = columns[j]; - if (c.resizable) { - if (stretchLeewayOnRight !== null) { - if (c.maxWidth) { - stretchLeewayOnRight += c.maxWidth - c.previousWidth; - } - else { - stretchLeewayOnRight = null; - } - } - shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); - } - } - } - var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0; - for (j = 0; j <= i; j++) { - // columns on left only affect minPageX - c = columns[j]; - if (c.resizable) { - if (stretchLeewayOnLeft !== null) { - if (c.maxWidth) { - stretchLeewayOnLeft += c.maxWidth - c.previousWidth; - } - else { - stretchLeewayOnLeft = null; - } - } - shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); - } - } - if (shrinkLeewayOnRight === null) { shrinkLeewayOnRight = 100000; } - if (shrinkLeewayOnLeft === null) { shrinkLeewayOnLeft = 100000; } - if (stretchLeewayOnRight === null) { stretchLeewayOnRight = 100000; } - if (stretchLeewayOnLeft === null) { stretchLeewayOnLeft = 100000; } - maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft); - minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight); - originalCanvasWidth = $canvas.width(); - }) - .bind("drag", function(e,dd) { - var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x, ci; - if (d < 0) { // shrink column - x = d; - for (j = i; j >= 0; j--) { - c = columns[j]; - if (c.resizable) { - actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); - if (x && c.previousWidth + x < actualMinWidth) { - x += c.previousWidth - actualMinWidth; - c.width = actualMinWidth; - } else { - c.width = c.previousWidth + x; - x = 0; - } - } - } - - if (options.forceFitColumns) { - x = -d; - for (j = i + 1; j < columnElements.length; j++) { - c = columns[j]; - if (c.resizable) { - if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { - x -= c.maxWidth - c.previousWidth; - c.width = c.maxWidth; - } else { - c.width = c.previousWidth + x; - x = 0; - } - } - } - } else if (options.syncColumnCellResize) { - setCanvasWidth(originalCanvasWidth + d); - } - } else { // stretch column - x = d; - for (j = i; j >= 0; j--) { - c = columns[j]; - if (c.resizable) { - if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { - x -= c.maxWidth - c.previousWidth; - c.width = c.maxWidth; - } else { - c.width = c.previousWidth + x; - x = 0; - } - } - } - - if (options.forceFitColumns) { - x = -d; - for (j = i + 1; j < columnElements.length; j++) { - c = columns[j]; - if (c.resizable) { - actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); - if (x && c.previousWidth + x < actualMinWidth) { - x += c.previousWidth - actualMinWidth; - c.width = actualMinWidth; - } else { - c.width = c.previousWidth + x; - x = 0; - } - } - } - } else if (options.syncColumnCellResize) { - setCanvasWidth(originalCanvasWidth + d); - } - } - applyColumnHeaderWidths(); - if (options.syncColumnCellResize) { - applyColumnWidths(); - } - }) - .bind("dragend", function(e,dd) { - var newWidth; - $(this).parent().removeClass("slick-header-column-active"); - for (j = 0; j < columnElements.length; j++) { - c = columns[j]; - newWidth = $(columnElements[j]).outerWidth(); - - if (c.previousWidth !== newWidth && c.rerenderOnResize) { - invalidateAllRows(); - } - } - applyColumnWidths(); - resizeCanvas(); - trigger(self.onColumnsResized, {}); - }); - }); - } - - function getVBoxDelta($el) { - var p = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; - var delta = 0; - $.each(p, function(n,val) { delta += parseFloat($el.css(val)) || 0; }); - return delta; - } - - function measureCellPaddingAndBorder() { - var el; - var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"]; - var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; - - el = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers); - headerColumnWidthDiff = headerColumnHeightDiff = 0; - $.each(h, function(n,val) { headerColumnWidthDiff += parseFloat(el.css(val)) || 0; }); - $.each(v, function(n,val) { headerColumnHeightDiff += parseFloat(el.css(val)) || 0; }); - el.remove(); - - var r = $("<div class='slick-row' />").appendTo($canvas); - el = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r); - cellWidthDiff = cellHeightDiff = 0; - $.each(h, function(n,val) { cellWidthDiff += parseFloat(el.css(val)) || 0; }); - $.each(v, function(n,val) { cellHeightDiff += parseFloat(el.css(val)) || 0; }); - r.remove(); - - absoluteColumnMinWidth = Math.max(headerColumnWidthDiff,cellWidthDiff); - } - - function createCssRules() { - $style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head")); - var rowHeight = (options.rowHeight - cellHeightDiff); - - var rules = [ - "." + uid + " .slick-header-column { left: 1000px; }", - "." + uid + " .slick-top-panel { height:" + options.topPanelHeight + "px; }", - "." + uid + " .slick-headerrow-columns { height:" + options.headerRowHeight + "px; }", - "." + uid + " .slick-cell { height:" + rowHeight + "px; }", - "." + uid + " .slick-row { width:" + getRowWidth() + "px; height:" + options.rowHeight + "px; }" - ]; - - var rowWidth = getRowWidth(); - var x = 0, w; - for (var i=0; i<columns.length; i++) { - w = columns[i].width; - rules.push("." + uid + " .l" + i + " { left: " + x + "px; }"); - rules.push("." + uid + " .r" + i + " { right: " + (rowWidth - x - w) + "px; }"); - x += columns[i].width; - } - if ($style[0].styleSheet) { // IE - $style[0].styleSheet.cssText = rules.join(" "); - } - else { - $style[0].appendChild(document.createTextNode(rules.join(" "))); - } - - var sheets = document.styleSheets; - for (var i=0; i<sheets.length; i++) { - if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) { - stylesheet = sheets[i]; - break; + if (options.forceFitColumns) { + x = -d; + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + } else if (options.syncColumnCellResize) { + setCanvasWidth(originalCanvasWidth + d); } - } - } - - function findCssRule(selector) { - var rules = (stylesheet.cssRules || stylesheet.rules); - - for (var i=0; i<rules.length; i++) { - if (rules[i].selectorText == selector) - return rules[i]; - } - - return null; - } - - function removeCssRules() { - $style.remove(); - } - - function destroy() { - getEditorLock().cancelCurrentEdit(); - - trigger(self.onBeforeDestroy, {}); + } + applyColumnHeaderWidths(); + if (options.syncColumnCellResize) { + applyColumnWidths(); + } + }) + .bind("dragend", function (e, dd) { + var newWidth; + $(this).parent().removeClass("slick-header-column-active"); + for (j = 0; j < columnElements.length; j++) { + c = columns[j]; + newWidth = $(columnElements[j]).outerWidth(); + + if (c.previousWidth !== newWidth && c.rerenderOnResize) { + invalidateAllRows(); + } + } + applyColumnWidths(); + resizeCanvas(); + trigger(self.onColumnsResized, {}); + }); + }); + } - for (var i = 0; i < plugins.length; i++) { - unregisterPlugin(plugins[i]); - } + function getVBoxDelta($el) { + var p = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + var delta = 0; + $.each(p, function (n, val) { + delta += parseFloat($el.css(val)) || 0; + }); + return delta; + } - if (options.enableColumnReorder && $headers.sortable) - $headers.sortable("destroy"); + function measureCellPaddingAndBorder() { + var el; + var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"]; + var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + + el = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers); + headerColumnWidthDiff = headerColumnHeightDiff = 0; + $.each(h, function (n, val) { + headerColumnWidthDiff += parseFloat(el.css(val)) || 0; + }); + $.each(v, function (n, val) { + headerColumnHeightDiff += parseFloat(el.css(val)) || 0; + }); + el.remove(); + + var r = $("<div class='slick-row' />").appendTo($canvas); + el = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r); + cellWidthDiff = cellHeightDiff = 0; + $.each(h, function (n, val) { + cellWidthDiff += parseFloat(el.css(val)) || 0; + }); + $.each(v, function (n, val) { + cellHeightDiff += parseFloat(el.css(val)) || 0; + }); + r.remove(); + + absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); + } - unbindAncestorScrollEvents(); - $container.unbind(".slickgrid"); - removeCssRules(); + function createCssRules() { + $style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head")); + var rowHeight = (options.rowHeight - cellHeightDiff); + var rowWidth = getRowWidth(); + var rules = [ + "." + uid + " .slick-header-column { left: 1000px; }", + "." + uid + " .slick-top-panel { height:" + options.topPanelHeight + "px; }", + "." + uid + " .slick-headerrow-columns { height:" + options.headerRowHeight + "px; }", + "." + uid + " .slick-cell { height:" + rowHeight + "px; }", + "." + uid + " .slick-row { width:" + getRowWidth() + "px; height:" + options.rowHeight + "px; }" + ]; + + var x = 0, w; + for (var i = 0; i < columns.length; i++) { + w = columns[i].width; + rules.push("." + uid + " .l" + i + " { left: " + x + "px; }"); + rules.push("." + uid + " .r" + i + " { right: " + (rowWidth - x - w) + "px; }"); + x += columns[i].width; + } + + if ($style[0].styleSheet) { // IE + $style[0].styleSheet.cssText = rules.join(" "); + } + else { + $style[0].appendChild(document.createTextNode(rules.join(" "))); + } + + var sheets = document.styleSheets; + for (var i = 0; i < sheets.length; i++) { + if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) { + stylesheet = sheets[i]; + break; + } + } + } - $canvas.unbind("draginit dragstart dragend drag"); - $container.empty().removeClass(uid); + function findCssRule(selector) { + var rules = (stylesheet.cssRules || stylesheet.rules); + for (var i = 0; i < rules.length; i++) { + if (rules[i].selectorText == selector) { + return rules[i]; } + } + return null; + } + function removeCssRules() { + $style.remove(); + } - ////////////////////////////////////////////////////////////////////////////////////////////// - // General + function destroy() { + getEditorLock().cancelCurrentEdit(); - function trigger(evt, args, e) { - e = e || new Slick.EventData(); - args = args || {}; - args.grid = self; - return evt.notify(args, e, self); - } + trigger(self.onBeforeDestroy, {}); - function getEditorLock() { - return options.editorLock; - } + for (var i = 0; i < plugins.length; i++) { + unregisterPlugin(plugins[i]); + } - function getEditController() { - return editController; - } + if (options.enableColumnReorder && $headers.sortable) { + $headers.sortable("destroy"); + } - function getColumnIndex(id) { - return columnsById[id]; - } + unbindAncestorScrollEvents(); + $container.unbind(".slickgrid"); + removeCssRules(); - function autosizeColumns() { - var i, c, - widths = [], - shrinkLeeway = 0, - availWidth = (options.autoHeight ? viewportW : viewportW - scrollbarDimensions.width), // with AutoHeight, we do not need to accomodate the vertical scroll bar - total = 0, - existingTotal = 0; + $canvas.unbind("draginit dragstart dragend drag"); + $container.empty().removeClass(uid); + } - for (i = 0; i < columns.length; i++) { - c = columns[i]; - widths.push(c.width); - existingTotal += c.width; - shrinkLeeway += c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth); - } - total = existingTotal; + ////////////////////////////////////////////////////////////////////////////////////////////// + // General - invalidateAllRows(); + function trigger(evt, args, e) { + e = e || new Slick.EventData(); + args = args || {}; + args.grid = self; + return evt.notify(args, e, self); + } - // shrink - while (total > availWidth) { - if (!shrinkLeeway) { return; } - var shrinkProportion = (total - availWidth) / shrinkLeeway; - for (i = 0; i < columns.length && total > availWidth; i++) { - c = columns[i]; - if (!c.resizable || c.minWidth === c.width || c.width === absoluteColumnMinWidth) { continue; } - var shrinkSize = Math.floor(shrinkProportion * (c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth))) || 1; - total -= shrinkSize; - widths[i] -= shrinkSize; - } - } + function getEditorLock() { + return options.editorLock; + } - // grow - var previousTotal = total; - while (total < availWidth) { - var growProportion = availWidth / total; - for (i = 0; i < columns.length && total < availWidth; i++) { - c = columns[i]; - if (!c.resizable || c.maxWidth <= c.width) { continue; } - var growSize = Math.min(Math.floor(growProportion * c.width) - c.width, (c.maxWidth - c.width) || 1000000) || 1; - total += growSize; - widths[i] += growSize; - } - if (previousTotal == total) break; // if total is not changing, will result in infinite loop - previousTotal = total; - } + function getEditController() { + return editController; + } - for (i=0; i<columns.length; i++) { - columns[i].width = widths[i]; - } + function getColumnIndex(id) { + return columnsById[id]; + } - applyColumnHeaderWidths(); - applyColumnWidths(); - resizeCanvas(); - } + function autosizeColumns() { + var i, c, + widths = [], + shrinkLeeway = 0, + // with AutoHeight, we do not need to accommodate the vertical scroll bar + availWidth = (options.autoHeight ? viewportW : viewportW - scrollbarDimensions.width), + total = 0, + existingTotal = 0; + + for (i = 0; i < columns.length; i++) { + c = columns[i]; + widths.push(c.width); + existingTotal += c.width; + shrinkLeeway += c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + + total = existingTotal; + + invalidateAllRows(); + + // shrink + while (total > availWidth) { + if (!shrinkLeeway) { + return; + } + var shrinkProportion = (total - availWidth) / shrinkLeeway; + for (i = 0; i < columns.length && total > availWidth; i++) { + c = columns[i]; + if (!c.resizable || c.minWidth === c.width || c.width === absoluteColumnMinWidth) { + continue; + } + var shrinkSize = Math.floor(shrinkProportion * (c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth))) || 1; + total -= shrinkSize; + widths[i] -= shrinkSize; + } + } + + // grow + var previousTotal = total; + while (total < availWidth) { + var growProportion = availWidth / total; + for (i = 0; i < columns.length && total < availWidth; i++) { + c = columns[i]; + if (!c.resizable || c.maxWidth <= c.width) { + continue; + } + var growSize = Math.min(Math.floor(growProportion * c.width) - c.width, (c.maxWidth - c.width) || 1000000) || 1; + total += growSize; + widths[i] += growSize; + } + if (previousTotal == total) { + break; + } // if total is not changing, will result in infinite loop + previousTotal = total; + } + + for (i = 0; i < columns.length; i++) { + columns[i].width = widths[i]; + } + + applyColumnHeaderWidths(); + applyColumnWidths(); + resizeCanvas(); + } - function applyColumnHeaderWidths() { - var h; - for (var i = 0, headers = $headers.children(), ii = headers.length; i < ii; i++) { - h = $(headers[i]); - if (h.width() !== columns[i].width - headerColumnWidthDiff) { - h.width(columns[i].width - headerColumnWidthDiff); - } - } + function applyColumnHeaderWidths() { + var h; + for (var i = 0, headers = $headers.children(), ii = headers.length; i < ii; i++) { + h = $(headers[i]); + if (h.width() !== columns[i].width - headerColumnWidthDiff) { + h.width(columns[i].width - headerColumnWidthDiff); } + } + } - function applyColumnWidths() { - var rowWidth = getRowWidth(); - var x = 0, w, rule; - for (var i = 0; i < columns.length; i++) { - w = columns[i].width; + function applyColumnWidths() { + var rowWidth = getRowWidth(); + var x = 0, w, rule; + for (var i = 0; i < columns.length; i++) { + w = columns[i].width; - rule = findCssRule("." + uid + " .l" + i); - rule.style.left = x + "px"; + rule = findCssRule("." + uid + " .l" + i); + rule.style.left = x + "px"; - rule = findCssRule("." + uid + " .r" + i); - rule.style.right = (rowWidth - x - w) + "px"; + rule = findCssRule("." + uid + " .r" + i); + rule.style.right = (rowWidth - x - w) + "px"; - x += columns[i].width; - } + x += columns[i].width; + } - rule = findCssRule("." + uid + " .slick-row"); - rule.style.width = rowWidth + "px"; - } + rule = findCssRule("." + uid + " .slick-row"); + rule.style.width = rowWidth + "px"; + } - function setSortColumn(columnId, ascending) { - sortColumnId = columnId; - sortAsc = ascending; - var columnIndex = getColumnIndex(sortColumnId); + function setSortColumn(columnId, ascending) { + sortColumnId = columnId; + sortAsc = ascending; + var columnIndex = getColumnIndex(sortColumnId); - $headers.children().removeClass("slick-header-column-sorted"); - $headers.find(".slick-sort-indicator").removeClass("slick-sort-indicator-asc slick-sort-indicator-desc"); + $headers.children().removeClass("slick-header-column-sorted"); + $headers.find(".slick-sort-indicator").removeClass("slick-sort-indicator-asc slick-sort-indicator-desc"); - if (columnIndex != null) { - $headers.children().eq(columnIndex) - .addClass("slick-header-column-sorted") - .find(".slick-sort-indicator") - .addClass(sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc"); - } - } + if (columnIndex != null) { + $headers.children().eq(columnIndex) + .addClass("slick-header-column-sorted") + .find(".slick-sort-indicator") + .addClass(sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc"); + } + } - function handleSelectedRangesChanged(e, ranges) { - selectedRows = []; - var hash = {}; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - if (!hash[j]) { // prevent duplicates - selectedRows.push(j); - } - hash[j] = {}; - for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { - if (canCellBeSelected(j, k)) { - hash[j][columns[k].id] = options.selectedCellCssClass; - } - } - } + function handleSelectedRangesChanged(e, ranges) { + selectedRows = []; + var hash = {}; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + if (!hash[j]) { // prevent duplicates + selectedRows.push(j); + } + hash[j] = {}; + for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { + if (canCellBeSelected(j, k)) { + hash[j][columns[k].id] = options.selectedCellCssClass; } - - setCellCssStyles(options.selectedCellCssClass, hash); - - trigger(self.onSelectedRowsChanged, {rows:getSelectedRows()}, e); - } - - function getColumns() { - return columns; - } - - function setColumns(columnDefinitions) { - columns = columnDefinitions; - invalidateAllRows(); - createColumnHeaders(); - removeCssRules(); - createCssRules(); - resizeAndRender(); - handleScroll(); + } } + } - function getOptions() { - return options; - } + setCellCssStyles(options.selectedCellCssClass, hash); - function setOptions(args) { - if (!getEditorLock().commitCurrentEdit()) { - return; - } + trigger(self.onSelectedRowsChanged, {rows:getSelectedRows()}, e); + } - makeActiveCellNormal(); + function getColumns() { + return columns; + } - if (options.enableAddRow !== args.enableAddRow) { - invalidateRow(getDataLength()); - } + function setColumns(columnDefinitions) { + columns = columnDefinitions; + invalidateAllRows(); + createColumnHeaders(); + removeCssRules(); + createCssRules(); + resizeAndRender(); + handleScroll(); + } - options = $.extend(options,args); + function getOptions() { + return options; + } - render(); - } + function setOptions(args) { + if (!getEditorLock().commitCurrentEdit()) { + return; + } - function setData(newData,scrollToTop) { - invalidateAllRows(); - data = newData; - if (scrollToTop) - scrollTo(0); - } + makeActiveCellNormal(); - function getData() { - return data; - } + if (options.enableAddRow !== args.enableAddRow) { + invalidateRow(getDataLength()); + } - function getDataLength() { - if (data.getLength) { - return data.getLength(); - } - else { - return data.length; - } - } + options = $.extend(options, args); - function getDataItem(i) { - if (data.getItem) { - return data.getItem(i); - } - else { - return data[i]; - } - } - - function getTopPanel() { - return $topPanel[0]; - } - - function showTopPanel() { - options.showTopPanel = true; - $topPanelScroller.slideDown("fast", resizeCanvas); - } + render(); + } - function hideTopPanel() { - options.showTopPanel = false; - $topPanelScroller.slideUp("fast", resizeCanvas); - } + function setData(newData, scrollToTop) { + invalidateAllRows(); + data = newData; + if (scrollToTop) { + scrollTo(0); + } + } - function showHeaderRowColumns() { - options.showHeaderRow = true; - $headerRowScroller.slideDown("fast", resizeCanvas); - } + function getData() { + return data; + } - function hideHeaderRowColumns() { - options.showHeaderRow = false; - $headerRowScroller.slideUp("fast", resizeCanvas); - } + function getDataLength() { + if (data.getLength) { + return data.getLength(); + } else { + return data.length; + } + } - ////////////////////////////////////////////////////////////////////////////////////////////// - // Rendering / Scrolling + function getDataItem(i) { + if (data.getItem) { + return data.getItem(i); + } else { + return data[i]; + } + } - function scrollTo(y) { - var oldOffset = offset; + function getTopPanel() { + return $topPanel[0]; + } - page = Math.min(n-1, Math.floor(y / ph)); - offset = Math.round(page * cj); - var newScrollTop = y - offset; + function showTopPanel() { + options.showTopPanel = true; + $topPanelScroller.slideDown("fast", resizeCanvas); + } - if (offset != oldOffset) { - var range = getVisibleRange(newScrollTop); - cleanupRows(range.top,range.bottom); - updateRowPositions(); - } + function hideTopPanel() { + options.showTopPanel = false; + $topPanelScroller.slideUp("fast", resizeCanvas); + } - if (prevScrollTop != newScrollTop) { - scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1; - $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop); + function showHeaderRowColumns() { + options.showHeaderRow = true; + $headerRowScroller.slideDown("fast", resizeCanvas); + } - trigger(self.onViewportChanged, {}); - } - } + function hideHeaderRowColumns() { + options.showHeaderRow = false; + $headerRowScroller.slideUp("fast", resizeCanvas); + } - function defaultFormatter(row, cell, value, columnDef, dataContext) { - return (value === null || value === undefined) ? "" : value; - } + ////////////////////////////////////////////////////////////////////////////////////////////// + // Rendering / Scrolling - function getFormatter(row, column) { - var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); + function scrollTo(y) { + var oldOffset = offset; - // look up by id, then index - var columnOverrides = rowMetadata && - rowMetadata.columns && - (rowMetadata.columns[column.id] || rowMetadata.columns[getColumnIndex(column.id)]); + page = Math.min(n - 1, Math.floor(y / ph)); + offset = Math.round(page * cj); + var newScrollTop = y - offset; - return (columnOverrides && columnOverrides.formatter) || - (rowMetadata && rowMetadata.formatter) || - column.formatter || - (options.formatterFactory && options.formatterFactory.getFormatter(column)) || - defaultFormatter; - } + if (offset != oldOffset) { + var range = getVisibleRange(newScrollTop); + cleanupRows(range.top, range.bottom); + updateRowPositions(); + } - function getEditor(row, cell) { - var column = columns[cell]; - var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); - var columnMetadata = rowMetadata && rowMetadata.columns; + if (prevScrollTop != newScrollTop) { + scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1; + $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop); - if (columnMetadata && columnMetadata[column.id] && columnMetadata[column.id].editor !== undefined) { - return columnMetadata[column.id].editor; - } - if (columnMetadata && columnMetadata[cell] && columnMetadata[cell].editor !== undefined) { - return columnMetadata[cell].editor; - } + trigger(self.onViewportChanged, {}); + } + } - return column.editor || (options.editorFactory && options.editorFactory.getEditor(column)); - } + function defaultFormatter(row, cell, value, columnDef, dataContext) { + return (value === null || value === undefined) ? "" : value; + } - function getDataItemValueForColumn(item, columnDef) { - if (options.dataItemColumnValueExtractor) { - return options.dataItemColumnValueExtractor(item, columnDef); - } - return item[columnDef.field]; - } + function getFormatter(row, column) { + var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); - function appendRowHtml(stringArray, row) { - var d = getDataItem(row); - var dataLoading = row < getDataLength() && !d; - var cellCss; - var rowCss = "slick-row " + - (dataLoading ? " loading" : "") + - (row % 2 == 1 ? ' odd' : ' even'); + // look up by id, then index + var columnOverrides = rowMetadata && + rowMetadata.columns && + (rowMetadata.columns[column.id] || rowMetadata.columns[getColumnIndex(column.id)]); - var metadata = data.getItemMetadata && data.getItemMetadata(row); + return (columnOverrides && columnOverrides.formatter) || + (rowMetadata && rowMetadata.formatter) || + column.formatter || + (options.formatterFactory && options.formatterFactory.getFormatter(column)) || + defaultFormatter; + } - if (metadata && metadata.cssClasses) { - rowCss += " " + metadata.cssClasses; - } + function getEditor(row, cell) { + var column = columns[cell]; + var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); + var columnMetadata = rowMetadata && rowMetadata.columns; - stringArray.push("<div class='ui-widget-content " + rowCss + "' row='" + row + "' style='top:" + (options.rowHeight*row-offset) + "px'>"); + if (columnMetadata && columnMetadata[column.id] && columnMetadata[column.id].editor !== undefined) { + return columnMetadata[column.id].editor; + } + if (columnMetadata && columnMetadata[cell] && columnMetadata[cell].editor !== undefined) { + return columnMetadata[cell].editor; + } - var colspan, m; - for (var i=0, cols=columns.length; i<cols; i++) { - m = columns[i]; - colspan = getColspan(row, i); // TODO: don't calc unless we have to - cellCss = "slick-cell l" + i + " r" + Math.min(columns.length -1, i + colspan - 1) + (m.cssClass ? " " + m.cssClass : ""); - if (row === activeRow && i === activeCell) { - cellCss += (" active"); - } + return column.editor || (options.editorFactory && options.editorFactory.getEditor(column)); + } - // TODO: merge them together in the setter - for (var key in cellCssClasses) { - if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) { - cellCss += (" " + cellCssClasses[key][row][m.id]); - } - } + function getDataItemValueForColumn(item, columnDef) { + if (options.dataItemColumnValueExtractor) { + return options.dataItemColumnValueExtractor(item, columnDef); + } + return item[columnDef.field]; + } - stringArray.push("<div class='" + cellCss + "'>"); + function appendRowHtml(stringArray, row) { + var d = getDataItem(row); + var dataLoading = row < getDataLength() && !d; + var cellCss; + var rowCss = "slick-row " + + (dataLoading ? " loading" : "") + + (row % 2 == 1 ? " odd" : " even"); - // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) - if (d) { - stringArray.push(getFormatter(row, m)(row, i, getDataItemValueForColumn(d, m), m, d)); - } + var metadata = data.getItemMetadata && data.getItemMetadata(row); - stringArray.push("</div>"); + if (metadata && metadata.cssClasses) { + rowCss += " " + metadata.cssClasses; + } - if (colspan) { - i += (colspan - 1); - } - } + stringArray.push("<div class='ui-widget-content " + rowCss + "' row='" + row + "' style='top:" + (options.rowHeight * row - offset) + "px'>"); - stringArray.push("</div>"); + var colspan, m; + for (var i = 0, cols = columns.length; i < cols; i++) { + m = columns[i]; + colspan = getColspan(row, i); // TODO: don't calc unless we have to + cellCss = "slick-cell l" + i + " r" + Math.min(columns.length - 1, i + colspan - 1) + (m.cssClass ? " " + m.cssClass : ""); + if (row === activeRow && i === activeCell) { + cellCss += (" active"); } - function cleanupRows(rangeToKeep) { - for (var i in rowsCache) { - if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) { - removeRowFromCache(i); - } - } + // TODO: merge them together in the setter + for (var key in cellCssClasses) { + if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) { + cellCss += (" " + cellCssClasses[key][row][m.id]); + } } - function invalidate() { - updateRowCount(); - invalidateAllRows(); - render(); - } + stringArray.push("<div class='" + cellCss + "'>"); - function invalidateAllRows() { - if (currentEditor) { - makeActiveCellNormal(); - } - for (var row in rowsCache) { - removeRowFromCache(row); - } + // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) + if (d) { + stringArray.push(getFormatter(row, m)(row, i, getDataItemValueForColumn(d, m), m, d)); } - function removeRowFromCache(row) { - var node = rowsCache[row]; - if (!node) { return; } - $canvas[0].removeChild(node); + stringArray.push("</div>"); - delete rowsCache[row]; - delete postProcessedRows[row]; - renderedRows--; - counter_rows_removed++; + if (colspan) { + i += (colspan - 1); } + } - function invalidateRows(rows) { - var i, rl; - if (!rows || !rows.length) { return; } - scrollDir = 0; - for (i=0, rl=rows.length; i<rl; i++) { - if (currentEditor && activeRow === i) { - makeActiveCellNormal(); - } - - if (rowsCache[rows[i]]) { - removeRowFromCache(rows[i]); - } - } - } + stringArray.push("</div>"); + } - function invalidateRow(row) { - invalidateRows([row]); + function cleanupRows(rangeToKeep) { + for (var i in rowsCache) { + if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) { + removeRowFromCache(i); } + } + } - function updateCell(row,cell) { - var cellNode = getCellNode(row,cell); - if (!cellNode) { - return; - } - - var m = columns[cell], d = getDataItem(row); - if (currentEditor && activeRow === row && activeCell === cell) { - currentEditor.loadValue(d); - } - else { - cellNode.innerHTML = d ? getFormatter(row, m)(row, cell, getDataItemValueForColumn(d, m), m, d) : ""; - invalidatePostProcessingResults(row); - } - } + function invalidate() { + updateRowCount(); + invalidateAllRows(); + render(); + } - function updateRow(row) { - if (!rowsCache[row]) { return; } + function invalidateAllRows() { + if (currentEditor) { + makeActiveCellNormal(); + } + for (var row in rowsCache) { + removeRowFromCache(row); + } + } - $(rowsCache[row]).children().each(function(i) { - var m = columns[i], d = getDataItem(row); - if (row === activeRow && i === activeCell && currentEditor) { - currentEditor.loadValue(getDataItem(activeRow)); - } - else if (d) { - this.innerHTML = getFormatter(row, m)(row, i, getDataItemValueForColumn(d, m), m, getDataItem(row)); - } - else { - this.innerHTML = ""; - } - }); + function removeRowFromCache(row) { + var node = rowsCache[row]; + if (!node) { + return; + } + $canvas[0].removeChild(node); + delete rowsCache[row]; + delete postProcessedRows[row]; + renderedRows--; + counter_rows_removed++; + } - invalidatePostProcessingResults(row); + function invalidateRows(rows) { + var i, rl; + if (!rows || !rows.length) { + return; + } + scrollDir = 0; + for (i = 0, rl = rows.length; i < rl; i++) { + if (currentEditor && activeRow === i) { + makeActiveCellNormal(); } - - function getViewportHeight() { - return parseFloat($.css($container[0], "height", true)) - - options.headerHeight - - getVBoxDelta($headers) - - (options.showTopPanel ? options.topPanelHeight + getVBoxDelta($topPanelScroller) : 0) - - (options.showHeaderRow ? options.headerRowHeight + getVBoxDelta($headerRowScroller) : 0); + if (rowsCache[rows[i]]) { + removeRowFromCache(rows[i]); } + } + } - function resizeCanvas() { - if (options.autoHeight) { - viewportH = options.rowHeight * (getDataLength() + (options.enableAddRow ? 1 : 0) + (options.leaveSpaceForNewRows? numVisibleRows - 1 : 0)); - } - else { - viewportH = getViewportHeight(); - } + function invalidateRow(row) { + invalidateRows([row]); + } - numVisibleRows = Math.ceil(viewportH / options.rowHeight); - viewportW = parseFloat($.css($container[0], "width", true)); - $viewport.height(viewportH); + function updateCell(row, cell) { + var cellNode = getCellNode(row, cell); + if (!cellNode) { + return; + } + + var m = columns[cell], d = getDataItem(row); + if (currentEditor && activeRow === row && activeCell === cell) { + currentEditor.loadValue(d); + } else { + cellNode.innerHTML = d ? getFormatter(row, m)(row, cell, getDataItemValueForColumn(d, m), m, d) : ""; + invalidatePostProcessingResults(row); + } + } - var w = 0, i = columns.length; - while (i--) { - w += columns[i].width; - } - setCanvasWidth(w); + function updateRow(row) { + if (!rowsCache[row]) { + return; + } - updateRowCount(); - render(); + $(rowsCache[row]).children().each(function (i) { + var m = columns[i], d = getDataItem(row); + if (row === activeRow && i === activeCell && currentEditor) { + currentEditor.loadValue(getDataItem(activeRow)); + } else if (d) { + this.innerHTML = getFormatter(row, m)(row, i, getDataItemValueForColumn(d, m), m, getDataItem(row)); + } else { + this.innerHTML = ""; } + }); - function resizeAndRender() { - if (options.forceFitColumns) { - autosizeColumns(); - } else { - resizeCanvas(); - } - } - - function updateRowCount() { - var newRowCount = getDataLength() + (options.enableAddRow?1:0) + (options.leaveSpaceForNewRows?numVisibleRows-1:0); - var oldH = h; - - // remove the rows that are now outside of the data range - // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows - var l = options.enableAddRow ? getDataLength() : getDataLength() - 1; - for (var i in rowsCache) { - if (i >= l) { - removeRowFromCache(i); - } - } - th = Math.max(options.rowHeight * newRowCount, viewportH - scrollbarDimensions.height); - if (th < maxSupportedCssHeight) { - // just one page - h = ph = th; - n = 1; - cj = 0; - } - else { - // break into pages - h = maxSupportedCssHeight; - ph = h / 100; - n = Math.floor(th / ph); - cj = (th - h) / (n - 1); - } + invalidatePostProcessingResults(row); + } - if (h !== oldH) { - $canvas.css("height",h); - scrollTop = $viewport[0].scrollTop; - } + function getViewportHeight() { + return parseFloat($.css($container[0], "height", true)) - + options.headerHeight - + getVBoxDelta($headers) - + (options.showTopPanel ? options.topPanelHeight + getVBoxDelta($topPanelScroller) : 0) - + (options.showHeaderRow ? options.headerRowHeight + getVBoxDelta($headerRowScroller) : 0); + } - var oldScrollTopInRange = (scrollTop + offset <= th - viewportH); + function resizeCanvas() { + if (options.autoHeight) { + viewportH = options.rowHeight * (getDataLength() + (options.enableAddRow ? 1 : 0) + (options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0)); + } else { + viewportH = getViewportHeight(); + } + + numVisibleRows = Math.ceil(viewportH / options.rowHeight); + viewportW = parseFloat($.css($container[0], "width", true)); + $viewport.height(viewportH); + + var w = 0, i = columns.length; + while (i--) { + w += columns[i].width; + } + setCanvasWidth(w); + updateRowCount(); + render(); + } - if (th == 0 || scrollTop == 0) { - page = offset = 0; - } - else if (oldScrollTopInRange) { - // maintain virtual position - scrollTo(scrollTop+offset); - } - else { - // scroll to bottom - scrollTo(th-viewportH); - } + function resizeAndRender() { + if (options.forceFitColumns) { + autosizeColumns(); + } else { + resizeCanvas(); + } + } - if (h != oldH && options.autoHeight) { - resizeCanvas(); - } - } + function updateRowCount() { + var newRowCount = getDataLength() + (options.enableAddRow ? 1 : 0) + + (options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0); + var oldH = h; + + // remove the rows that are now outside of the data range + // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows + var l = options.enableAddRow ? getDataLength() : getDataLength() - 1; + for (var i in rowsCache) { + if (i >= l) { + removeRowFromCache(i); + } + } + th = Math.max(options.rowHeight * newRowCount, viewportH - scrollbarDimensions.height); + if (th < maxSupportedCssHeight) { + // just one page + h = ph = th; + n = 1; + cj = 0; + } else { + // break into pages + h = maxSupportedCssHeight; + ph = h / 100; + n = Math.floor(th / ph); + cj = (th - h) / (n - 1); + } + + if (h !== oldH) { + $canvas.css("height", h); + scrollTop = $viewport[0].scrollTop; + } + + var oldScrollTopInRange = (scrollTop + offset <= th - viewportH); + + if (th == 0 || scrollTop == 0) { + page = offset = 0; + } else if (oldScrollTopInRange) { + // maintain virtual position + scrollTo(scrollTop + offset); + } else { + // scroll to bottom + scrollTo(th - viewportH); + } + + if (h != oldH && options.autoHeight) { + resizeCanvas(); + } + } - function getVisibleRange(viewportTop) { - if (viewportTop == null) - viewportTop = scrollTop; + function getVisibleRange(viewportTop) { + if (viewportTop == null) { + viewportTop = scrollTop; + } - return { - top: Math.floor((scrollTop+offset)/options.rowHeight), - bottom: Math.ceil((scrollTop+offset+viewportH)/options.rowHeight) - }; - } + return { + top:Math.floor((viewportTop + offset) / options.rowHeight), + bottom:Math.ceil((viewportTop + offset + viewportH) / options.rowHeight) + }; + } - function getRenderedRange(viewportTop) { - var range = getVisibleRange(viewportTop); - var buffer = Math.round(viewportH/options.rowHeight); - var minBuffer = 3; + function getRenderedRange(viewportTop) { + var range = getVisibleRange(viewportTop); + var buffer = Math.round(viewportH / options.rowHeight); + var minBuffer = 3; + + if (scrollDir == -1) { + range.top -= buffer; + range.bottom += minBuffer; + } else if (scrollDir == 1) { + range.top -= minBuffer; + range.bottom += buffer; + } else { + range.top -= minBuffer; + range.bottom += minBuffer; + } + + range.top = Math.max(0, range.top); + range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, range.bottom); + + return range; + } - if (scrollDir == -1) { - range.top -= buffer; - range.bottom += minBuffer; - } - else if (scrollDir == 1) { - range.top -= minBuffer; - range.bottom += buffer; - } - else { - range.top -= minBuffer; - range.bottom += minBuffer; - } + function renderRows(range) { + var i, l, + parentNode = $canvas[0], + rowsBefore = renderedRows, + stringArray = [], + rows = [], + startTimestamp = new Date(), + needToReselectCell = false; + + for (i = range.top; i <= range.bottom; i++) { + if (rowsCache[i]) { + continue; + } + renderedRows++; + rows.push(i); + appendRowHtml(stringArray, i); + if (activeCellNode && activeRow === i) { + needToReselectCell = true; + } + counter_rows_rendered++; + } + + var x = document.createElement("div"); + x.innerHTML = stringArray.join(""); + + for (i = 0, l = x.childNodes.length; i < l; i++) { + rowsCache[rows[i]] = parentNode.appendChild(x.firstChild); + } + + if (needToReselectCell) { + activeCellNode = getCellNode(activeRow, activeCell); + } + + if (renderedRows - rowsBefore > 5) { + avgRowRenderTime = (new Date() - startTimestamp) / (renderedRows - rowsBefore); + } + } - range.top = Math.max(0,range.top); - range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1,range.bottom); + function startPostProcessing() { + if (!options.enableAsyncPostRender) { + return; + } + clearTimeout(h_postrender); + h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); + } - return range; - } + function invalidatePostProcessingResults(row) { + delete postProcessedRows[row]; + postProcessFromRow = Math.min(postProcessFromRow, row); + postProcessToRow = Math.max(postProcessToRow, row); + startPostProcessing(); + } - function renderRows(range) { - var i, l, - parentNode = $canvas[0], - rowsBefore = renderedRows, - stringArray = [], - rows = [], - startTimestamp = new Date(), - needToReselectCell = false; + function updateRowPositions() { + for (var row in rowsCache) { + rowsCache[row].style.top = (row * options.rowHeight - offset) + "px"; + } + } - for (i = range.top; i <= range.bottom; i++) { - if (rowsCache[i]) { continue; } - renderedRows++; - rows.push(i); - appendRowHtml(stringArray,i); - if (activeCellNode && activeRow === i) { - needToReselectCell = true; - } - counter_rows_rendered++; - } + function render() { + var visible = getVisibleRange(); + var rendered = getRenderedRange(); - var x = document.createElement("div"); - x.innerHTML = stringArray.join(""); + // remove rows no longer in the viewport + cleanupRows(rendered); - for (i = 0, l = x.childNodes.length; i < l; i++) { - rowsCache[rows[i]] = parentNode.appendChild(x.firstChild); - } + // add new rows + renderRows(rendered); - if (needToReselectCell) { - activeCellNode = getCellNode(activeRow,activeCell); - } + postProcessFromRow = visible.top; + postProcessToRow = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, visible.bottom); + startPostProcessing(); - if (renderedRows - rowsBefore > 5) { - avgRowRenderTime = (new Date() - startTimestamp) / (renderedRows - rowsBefore); - } - } + lastRenderedScrollTop = scrollTop; + h_render = null; + } - function startPostProcessing() { - if (!options.enableAsyncPostRender) { return; } - clearTimeout(h_postrender); - h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); + function handleScroll() { + scrollTop = $viewport[0].scrollTop; + var scrollLeft = $viewport[0].scrollLeft; + var scrollDist = Math.abs(scrollTop - prevScrollTop); + + if (scrollLeft !== prevScrollLeft) { + prevScrollLeft = scrollLeft; + $headerScroller[0].scrollLeft = scrollLeft; + $topPanelScroller[0].scrollLeft = scrollLeft; + $headerRowScroller[0].scrollLeft = scrollLeft; + } + + if (scrollDist) { + scrollDir = prevScrollTop < scrollTop ? 1 : -1; + prevScrollTop = scrollTop; + + // switch virtual pages if needed + if (scrollDist < viewportH) { + scrollTo(scrollTop + offset); + } else { + var oldOffset = offset; + page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph))); + offset = Math.round(page * cj); + if (oldOffset != offset) { + invalidateAllRows(); + } } - function invalidatePostProcessingResults(row) { - delete postProcessedRows[row]; - postProcessFromRow = Math.min(postProcessFromRow,row); - postProcessToRow = Math.max(postProcessToRow,row); - startPostProcessing(); + if (h_render) { + clearTimeout(h_render); } - function updateRowPositions() { - for (var row in rowsCache) { - rowsCache[row].style.top = (row*options.rowHeight-offset) + "px"; - } + if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH) { + render(); + } else { + h_render = setTimeout(render, 50); } - function render() { - var visible = getVisibleRange(); - var rendered = getRenderedRange(); - - // remove rows no longer in the viewport - cleanupRows(rendered); - - // add new rows - renderRows(rendered); + trigger(self.onViewportChanged, {}); + } - postProcessFromRow = visible.top; - postProcessToRow = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, visible.bottom); - startPostProcessing(); + trigger(self.onScroll, {scrollLeft:scrollLeft, scrollTop:scrollTop}); + } - lastRenderedScrollTop = scrollTop; - h_render = null; + function asyncPostProcessRows() { + while (postProcessFromRow <= postProcessToRow) { + var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; + var rowNode = rowsCache[row]; + if (!rowNode || postProcessedRows[row] || row >= getDataLength()) { + continue; } - function handleScroll() { - scrollTop = $viewport[0].scrollTop; - var scrollLeft = $viewport[0].scrollLeft; - var scrollDist = Math.abs(scrollTop - prevScrollTop); - - if (scrollLeft !== prevScrollLeft) { - prevScrollLeft = scrollLeft; - $headerScroller[0].scrollLeft = scrollLeft; - $topPanelScroller[0].scrollLeft = scrollLeft; - $headerRowScroller[0].scrollLeft = scrollLeft; - } - - if (scrollDist) { - scrollDir = prevScrollTop < scrollTop ? 1 : -1; - prevScrollTop = scrollTop; - - // switch virtual pages if needed - if (scrollDist < viewportH) { - scrollTo(scrollTop + offset); - } - else { - var oldOffset = offset; - page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph))); - offset = Math.round(page * cj); - if (oldOffset != offset) - invalidateAllRows(); - } - - if (h_render) - clearTimeout(h_render); - - if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH) - render(); - else - h_render = setTimeout(render, 50); - - trigger(self.onViewportChanged, {}); - } - - trigger(self.onScroll, {scrollLeft:scrollLeft, scrollTop:scrollTop}); + var d = getDataItem(row), cellNodes = rowNode.childNodes; + for (var i = 0, j = 0, l = columns.length; i < l; ++i) { + var m = columns[i]; + if (m.asyncPostRender) { + m.asyncPostRender(cellNodes[j], postProcessFromRow, d, m); + } + ++j; } - function asyncPostProcessRows() { - while (postProcessFromRow <= postProcessToRow) { - var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; - var rowNode = rowsCache[row]; - if (!rowNode || postProcessedRows[row] || row>=getDataLength()) { continue; } - - var d = getDataItem(row), cellNodes = rowNode.childNodes; - for (var i=0, j=0, l=columns.length; i<l; ++i) { - var m = columns[i]; - if (m.asyncPostRender) { m.asyncPostRender(cellNodes[j], postProcessFromRow, d, m); } - ++j; - } - - postProcessedRows[row] = true; - h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); - return; - } - } + postProcessedRows[row] = true; + h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); + return; + } + } - function addCellCssStyles(key,hash) { - if (cellCssClasses[key]) { - throw "addCellCssStyles: cell CSS hash with key '" + key + "' already exists."; - } + function addCellCssStyles(key, hash) { + if (cellCssClasses[key]) { + throw "addCellCssStyles: cell CSS hash with key '" + key + "' already exists."; + } - cellCssClasses[key] = hash; + cellCssClasses[key] = hash; - var node; - for (var row in rowsCache) { - if (hash[row]) { - for (var columnId in hash[row]) { - node = getCellNode(row, getColumnIndex(columnId)); - if (node) { - $(node).addClass(hash[row][columnId]); - } - } - } + var node; + for (var row in rowsCache) { + if (hash[row]) { + for (var columnId in hash[row]) { + node = getCellNode(row, getColumnIndex(columnId)); + if (node) { + $(node).addClass(hash[row][columnId]); } + } } + } + } - function removeCellCssStyles(key) { - if (!cellCssClasses[key]) { - return; - } + function removeCellCssStyles(key) { + if (!cellCssClasses[key]) { + return; + } - var node; - for (var row in rowsCache) { - if (cellCssClasses[key][row]) { - for (var columnId in cellCssClasses[key][row]) { - node = getCellNode(row, getColumnIndex(columnId)); - if (node) { - $(node).removeClass(cellCssClasses[key][row][columnId]); - } - } - } + var node; + for (var row in rowsCache) { + if (cellCssClasses[key][row]) { + for (var columnId in cellCssClasses[key][row]) { + node = getCellNode(row, getColumnIndex(columnId)); + if (node) { + $(node).removeClass(cellCssClasses[key][row][columnId]); } - - delete cellCssClasses[key]; - } - - function setCellCssStyles(key,hash) { - removeCellCssStyles(key); - addCellCssStyles(key,hash); + } } + } - function flashCell(row, cell, speed) { - speed = speed || 100; - if (rowsCache[row]) { - var $cell = $(getCellNode(row,cell)); + delete cellCssClasses[key]; + } - function toggleCellClass(times) { - if (!times) return; - setTimeout(function() { - $cell.queue(function() { - $cell.toggleClass(options.cellFlashingCssClass).dequeue(); - toggleCellClass(times-1); - }); - }, - speed); - } + function setCellCssStyles(key, hash) { + removeCellCssStyles(key); + addCellCssStyles(key, hash); + } - toggleCellClass(4); - } + function flashCell(row, cell, speed) { + speed = speed || 100; + if (rowsCache[row]) { + var $cell = $(getCellNode(row, cell)); + + function toggleCellClass(times) { + if (!times) { + return; + } + setTimeout(function () { + $cell.queue(function () { + $cell.toggleClass(options.cellFlashingCssClass).dequeue(); + toggleCellClass(times - 1); + }); + }, + speed); } - ////////////////////////////////////////////////////////////////////////////////////////////// - // Interactivity - - function handleDragInit(e,dd) { - var cell = getCellFromEvent(e); - if (!cell || !cellExists(cell.row, cell.cell)) { - return false; - } - - retval = trigger(self.onDragInit, dd, e); - if (e.isImmediatePropagationStopped()) { - return retval; - } - - // if nobody claims to be handling drag'n'drop by stopping immediate propagation, - // cancel out of it - return false; - } + toggleCellClass(4); + } + } - function handleDragStart(e,dd) { - var cell = getCellFromEvent(e); - if (!cell || !cellExists(cell.row, cell.cell)) { - return false; - } + ////////////////////////////////////////////////////////////////////////////////////////////// + // Interactivity + + function handleDragInit(e, dd) { + var cell = getCellFromEvent(e); + if (!cell || !cellExists(cell.row, cell.cell)) { + return false; + } + + retval = trigger(self.onDragInit, dd, e); + if (e.isImmediatePropagationStopped()) { + return retval; + } + + // if nobody claims to be handling drag'n'drop by stopping immediate propagation, + // cancel out of it + return false; + } - var retval = trigger(self.onDragStart, dd, e); - if (e.isImmediatePropagationStopped()) { - return retval; - } + function handleDragStart(e, dd) { + var cell = getCellFromEvent(e); + if (!cell || !cellExists(cell.row, cell.cell)) { + return false; + } - return false; - } + var retval = trigger(self.onDragStart, dd, e); + if (e.isImmediatePropagationStopped()) { + return retval; + } - function handleDrag(e,dd) { - return trigger(self.onDrag, dd, e); - } + return false; + } - function handleDragEnd(e,dd) { - trigger(self.onDragEnd, dd, e); - } + function handleDrag(e, dd) { + return trigger(self.onDrag, dd, e); + } - function handleKeyDown(e) { - trigger(self.onKeyDown, {}, e); - var handled = e.isImmediatePropagationStopped(); + function handleDragEnd(e, dd) { + trigger(self.onDragEnd, dd, e); + } - if (!handled) { - if (!e.shiftKey && !e.altKey && !e.ctrlKey) { - if (e.which == 27) { - if (!getEditorLock().isActive()) { - return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) - } - cancelEditAndSetFocus(); - } - else if (e.which == 37) { - navigateLeft(); - } - else if (e.which == 39) { - navigateRight(); - } - else if (e.which == 38) { - navigateUp(); - } - else if (e.which == 40) { - navigateDown(); - } - else if (e.which == 9) { - navigateNext(); - } - else if (e.which == 13) { - if (options.editable) { - if (currentEditor) { - // adding new row - if (activeRow === getDataLength()) { - navigateDown(); - } - else { - commitEditAndSetFocus(); - } - } else { - if (getEditorLock().commitCurrentEdit()) { - makeActiveCellEditable(); - } - } - } - } - else - return; + function handleKeyDown(e) { + trigger(self.onKeyDown, {}, e); + var handled = e.isImmediatePropagationStopped(); + + if (!handled) { + if (!e.shiftKey && !e.altKey && !e.ctrlKey) { + if (e.which == 27) { + if (!getEditorLock().isActive()) { + return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) + } + cancelEditAndSetFocus(); + } else if (e.which == 37) { + navigateLeft(); + } else if (e.which == 39) { + navigateRight(); + } else if (e.which == 38) { + navigateUp(); + } else if (e.which == 40) { + navigateDown(); + } else if (e.which == 9) { + navigateNext(); + } else if (e.which == 13) { + if (options.editable) { + if (currentEditor) { + // adding new row + if (activeRow === getDataLength()) { + navigateDown(); } - else if (e.which == 9 && e.shiftKey && !e.ctrlKey && !e.altKey) { - navigatePrev(); + else { + commitEditAndSetFocus(); } - else - return; - } - - // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it - e.stopPropagation(); - e.preventDefault(); - try { - e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.) - } - catch (error) {} // ignore exceptions - setting the original event's keycode throws access denied exception for "Ctrl" (hitting control key only, nothing else), "Shift" (maybe others) - } - - function handleClick(e) { - var cell = getCellFromEvent(e); - if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) { - return; - } - - trigger(self.onClick, {row:cell.row, cell:cell.cell}, e); - if (e.isImmediatePropagationStopped()) { - return; - } - - if (canCellBeActive(cell.row, cell.cell)) { - if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) { - scrollRowIntoView(cell.row,false); - setActiveCellInternal(getCellNode(cell.row,cell.cell), (cell.row === getDataLength()) || options.autoEdit); + } else { + if (getEditorLock().commitCurrentEdit()) { + makeActiveCellEditable(); } - } - } - - function handleContextMenu(e) { - var $cell = $(e.target).closest(".slick-cell", $canvas); - if ($cell.length === 0) { return; } - - // are we editing this cell? - if (activeCellNode === $cell[0] && currentEditor !== null) { return; } - - trigger(self.onContextMenu, {}, e); - } - - function handleDblClick(e) { - var cell = getCellFromEvent(e); - if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) { - return; - } - - trigger(self.onDblClick, {row:cell.row, cell:cell.cell}, e); - if (e.isImmediatePropagationStopped()) { - return; - } - - if (options.editable) { - gotoCell(cell.row, cell.cell, true); - } - } - - function handleHeaderContextMenu(e) { - var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns"); - var column = $header && columns[self.getColumnIndex($header.data("fieldId"))]; - trigger(self.onHeaderContextMenu, {column: column}, e); - } - - function handleHeaderClick(e) { - var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns"); - var column = $header && columns[self.getColumnIndex($header.data("fieldId"))]; - trigger(self.onHeaderClick, {column: column}, e); - } + } + } + } else { + return; + } + } else if (e.which == 9 && e.shiftKey && !e.ctrlKey && !e.altKey) { + navigatePrev(); + } else { + return; + } + } + + // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it + e.stopPropagation(); + e.preventDefault(); + try { + e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.) + } + // ignore exceptions - setting the original event's keycode throws access denied exception for "Ctrl" + // (hitting control key only, nothing else), "Shift" (maybe others) + catch (error) { + } + } - function handleMouseEnter(e) { - trigger(self.onMouseEnter, {}, e); - } + function handleClick(e) { + var cell = getCellFromEvent(e); + if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) { + return; + } - function handleMouseLeave(e) { - trigger(self.onMouseLeave, {}, e); - } + trigger(self.onClick, {row:cell.row, cell:cell.cell}, e); + if (e.isImmediatePropagationStopped()) { + return; + } - function cellExists(row,cell) { - return !(row < 0 || row >= getDataLength() || cell < 0 || cell >= columns.length); + if (canCellBeActive(cell.row, cell.cell)) { + if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) { + scrollRowIntoView(cell.row, false); + setActiveCellInternal(getCellNode(cell.row, cell.cell), (cell.row === getDataLength()) || options.autoEdit); } + } + } - function getCellFromPoint(x,y) { - var row = Math.floor((y+offset)/options.rowHeight); - var cell = 0; - - var w = 0; - for (var i=0; i<columns.length && w<x; i++) { - w += columns[i].width; - cell++; - } - - if (cell < 0) { - cell = 0; - } - - return {row:row,cell:cell-1}; - } + function handleContextMenu(e) { + var $cell = $(e.target).closest(".slick-cell", $canvas); + if ($cell.length === 0) { + return; + } - function getCellFromNode(node) { - // read column number from .l1 or .c1 CSS classes - var cls = /l\d+/.exec(node.className) || /c\d+/.exec(node.className); - if (!cls) - throw "getCellFromNode: cannot get cell - " + node.className; - return parseInt(cls[0].substr(1, cls[0].length-1), 10); - } + // are we editing this cell? + if (activeCellNode === $cell[0] && currentEditor !== null) { + return; + } - function getCellFromEvent(e) { - var $cell = $(e.target).closest(".slick-cell", $canvas); - if (!$cell.length) - return null; + trigger(self.onContextMenu, {}, e); + } - return { - row: $cell.parent().attr("row") | 0, - cell: getCellFromNode($cell[0]) - }; - } + function handleDblClick(e) { + var cell = getCellFromEvent(e); + if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) { + return; + } - function getCellNodeBox(row,cell) { - if (!cellExists(row,cell)) - return null; + trigger(self.onDblClick, {row:cell.row, cell:cell.cell}, e); + if (e.isImmediatePropagationStopped()) { + return; + } - var y1 = row * options.rowHeight - offset; - var y2 = y1 + options.rowHeight - 1; - var x1 = 0; - for (var i=0; i<cell; i++) { - x1 += columns[i].width; - } - var x2 = x1 + columns[cell].width; + if (options.editable) { + gotoCell(cell.row, cell.cell, true); + } + } - return { - top: y1, - left: x1, - bottom: y2, - right: x2 - }; - } + function handleHeaderContextMenu(e) { + var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns"); + var column = $header && columns[self.getColumnIndex($header.data("fieldId"))]; + trigger(self.onHeaderContextMenu, {column:column}, e); + } - ////////////////////////////////////////////////////////////////////////////////////////////// - // Cell switching + function handleHeaderClick(e) { + var $header = $(e.target).closest(".slick-header-column", ".slick-header-columns"); + var column = $header && columns[self.getColumnIndex($header.data("fieldId"))]; + trigger(self.onHeaderClick, {column:column}, e); + } - function resetActiveCell() { - setActiveCellInternal(null,false); - } + function handleMouseEnter(e) { + trigger(self.onMouseEnter, {}, e); + } - function setFocus() { - // IE tries to scroll the viewport so that the item being focused is aligned to the left border - // IE-specific .setActive() sets the focus, but doesn't scroll - if ($.browser.msie) { - $canvas[0].setActive(); - } - else { - $canvas[0].focus(); - } - } + function handleMouseLeave(e) { + trigger(self.onMouseLeave, {}, e); + } - function scrollActiveCellIntoView() { - if (activeCellNode) { - var left = $(activeCellNode).position().left, - right = left + $(activeCellNode).outerWidth(), - scrollLeft = $viewport.scrollLeft(), - scrollRight = scrollLeft + $viewport.width(); + function cellExists(row, cell) { + return !(row < 0 || row >= getDataLength() || cell < 0 || cell >= columns.length); + } - if (left < scrollLeft) - $viewport.scrollLeft(left); - else if (right > scrollRight) - $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth)); - } - } + function getCellFromPoint(x, y) { + var row = Math.floor((y + offset) / options.rowHeight); + var cell = 0; - function setActiveCellInternal(newCell, editMode) { - if (activeCellNode !== null) { - makeActiveCellNormal(); - $(activeCellNode).removeClass("active"); - } + var w = 0; + for (var i = 0; i < columns.length && w < x; i++) { + w += columns[i].width; + cell++; + } - var activeCellChanged = (activeCellNode !== newCell); - activeCellNode = newCell; + if (cell < 0) { + cell = 0; + } - if (activeCellNode != null) { - activeRow = parseInt($(activeCellNode).parent().attr("row")); - activeCell = activePosX = getCellFromNode(activeCellNode); + return {row:row, cell:cell - 1}; + } - $(activeCellNode).addClass("active"); + function getCellFromNode(node) { + // read column number from .l1 or .c1 CSS classes + var cls = /l\d+/.exec(node.className) || /c\d+/.exec(node.className); + if (!cls) { + throw "getCellFromNode: cannot get cell - " + node.className; + } + return parseInt(cls[0].substr(1, cls[0].length - 1), 10); + } - if (options.editable && editMode && isCellPotentiallyEditable(activeRow,activeCell)) { - clearTimeout(h_editorLoader); + function getCellFromEvent(e) { + var $cell = $(e.target).closest(".slick-cell", $canvas); + if (!$cell.length) { + return null; + } - if (options.asyncEditorLoading) { - h_editorLoader = setTimeout(function() { makeActiveCellEditable(); }, options.asyncEditorLoadDelay); - } - else { - makeActiveCellEditable(); - } - } - else { - setFocus(); - } - } - else { - activeRow = activeCell = null; - } + return { + row:$cell.parent().attr("row") | 0, + cell:getCellFromNode($cell[0]) + }; + } - if (activeCellChanged) { - scrollActiveCellIntoView(); - trigger(self.onActiveCellChanged, getActiveCell()); - } - } + function getCellNodeBox(row, cell) { + if (!cellExists(row, cell)) { + return null; + } + + var y1 = row * options.rowHeight - offset; + var y2 = y1 + options.rowHeight - 1; + var x1 = 0; + for (var i = 0; i < cell; i++) { + x1 += columns[i].width; + } + var x2 = x1 + columns[cell].width; + + return { + top:y1, + left:x1, + bottom:y2, + right:x2 + }; + } - function clearTextSelection() { - if (document.selection && document.selection.empty) { - document.selection.empty(); - } - else if (window.getSelection) { - var sel = window.getSelection(); - if (sel && sel.removeAllRanges) { - sel.removeAllRanges(); - } - } - } + ////////////////////////////////////////////////////////////////////////////////////////////// + // Cell switching - function isCellPotentiallyEditable(row, cell) { - // is the data for this row loaded? - if (row < getDataLength() && !getDataItem(row)) { - return false; - } + function resetActiveCell() { + setActiveCellInternal(null, false); + } - // are we in the Add New row? can we create new from this cell? - if (columns[cell].cannotTriggerInsert && row >= getDataLength()) { - return false; - } + function setFocus() { + // IE tries to scroll the viewport so that the item being focused is aligned to the left border + // IE-specific .setActive() sets the focus, but doesn't scroll + if ($.browser.msie) { + $canvas[0].setActive(); + } else { + $canvas[0].focus(); + } + } - // does this cell have an editor? - if (!getEditor(row, cell)) { - return false; - } + function scrollActiveCellIntoView() { + if (activeCellNode) { + var left = $(activeCellNode).position().left, + right = left + $(activeCellNode).outerWidth(), + scrollLeft = $viewport.scrollLeft(), + scrollRight = scrollLeft + $viewport.width(); - return true; + if (left < scrollLeft) { + $viewport.scrollLeft(left); + } else if (right > scrollRight) { + $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth)); } + } + } - function makeActiveCellNormal() { - if (!currentEditor) { return; } - trigger(self.onBeforeCellEditorDestroy, {editor:currentEditor}); - currentEditor.destroy(); - currentEditor = null; - - if (activeCellNode) { - var d = getDataItem(activeRow); - - $(activeCellNode).removeClass("editable invalid"); - - if (d) { - var column = columns[activeCell]; - activeCellNode.innerHTML = getFormatter(activeRow, column)(activeRow, activeCell, getDataItemValueForColumn(d, column), column, getDataItem(activeRow)); - invalidatePostProcessingResults(activeRow); - } - } - - // if there previously was text selected on a page (such as selected text in the edit cell just removed), - // IE can't set focus to anything else correctly - if ($.browser.msie) { clearTextSelection(); } + function setActiveCellInternal(newCell, editMode) { + if (activeCellNode !== null) { + makeActiveCellNormal(); + $(activeCellNode).removeClass("active"); + } + + var activeCellChanged = (activeCellNode !== newCell); + activeCellNode = newCell; + + if (activeCellNode != null) { + activeRow = parseInt($(activeCellNode).parent().attr("row")); + activeCell = activePosX = getCellFromNode(activeCellNode); + + $(activeCellNode).addClass("active"); + + if (options.editable && editMode && isCellPotentiallyEditable(activeRow, activeCell)) { + clearTimeout(h_editorLoader); + + if (options.asyncEditorLoading) { + h_editorLoader = setTimeout(function () { + makeActiveCellEditable(); + }, options.asyncEditorLoadDelay); + } else { + makeActiveCellEditable(); + } + } else { + setFocus(); + } + } else { + activeRow = activeCell = null; + } + + if (activeCellChanged) { + scrollActiveCellIntoView(); + trigger(self.onActiveCellChanged, getActiveCell()); + } + } - getEditorLock().deactivate(editController); + function clearTextSelection() { + if (document.selection && document.selection.empty) { + document.selection.empty(); + } else if (window.getSelection) { + var sel = window.getSelection(); + if (sel && sel.removeAllRanges) { + sel.removeAllRanges(); } + } + } - function makeActiveCellEditable(editor) { - if (!activeCellNode) { return; } - if (!options.editable) { - throw "Grid : makeActiveCellEditable : should never get called when options.editable is false"; - } - - // cancel pending async call if there is one - clearTimeout(h_editorLoader); - - if (!isCellPotentiallyEditable(activeRow,activeCell)) { - return; - } + function isCellPotentiallyEditable(row, cell) { + // is the data for this row loaded? + if (row < getDataLength() && !getDataItem(row)) { + return false; + } - var columnDef = columns[activeCell]; - var item = getDataItem(activeRow); + // are we in the Add New row? can we create new from this cell? + if (columns[cell].cannotTriggerInsert && row >= getDataLength()) { + return false; + } - if (trigger(self.onBeforeEditCell, {row:activeRow, cell:activeCell, item:item, column:columnDef}) === false) { - setFocus(); - return; - } + // does this cell have an editor? + if (!getEditor(row, cell)) { + return false; + } - getEditorLock().activate(editController); - $(activeCellNode).addClass("editable"); - - // don't clear the cell if a custom editor is passed through - if (!editor) { - activeCellNode.innerHTML = ""; - } - - currentEditor = new (editor || getEditor(activeRow, activeCell))({ - grid: self, - gridPosition: absBox($container[0]), - position: absBox(activeCellNode), - container: activeCellNode, - column: columnDef, - item: item || {}, - commitChanges: commitEditAndSetFocus, - cancelChanges: cancelEditAndSetFocus - }); + return true; + } - if (item) - currentEditor.loadValue(item); + function makeActiveCellNormal() { + if (!currentEditor) { + return; + } + trigger(self.onBeforeCellEditorDestroy, {editor:currentEditor}); + currentEditor.destroy(); + currentEditor = null; + + if (activeCellNode) { + var d = getDataItem(activeRow); + $(activeCellNode).removeClass("editable invalid"); + if (d) { + var column = columns[activeCell]; + var formatter = getFormatter(activeRow, column); + activeCellNode.innerHTML = formatter(activeRow, activeCell, getDataItemValueForColumn(d, column), column, getDataItem(activeRow)); + invalidatePostProcessingResults(activeRow); + } + } + + // if there previously was text selected on a page (such as selected text in the edit cell just removed), + // IE can't set focus to anything else correctly + if ($.browser.msie) { + clearTextSelection(); + } + + getEditorLock().deactivate(editController); + } - serializedEditorValue = currentEditor.serializeValue(); + function makeActiveCellEditable(editor) { + if (!activeCellNode) { + return; + } + if (!options.editable) { + throw "Grid : makeActiveCellEditable : should never get called when options.editable is false"; + } + + // cancel pending async call if there is one + clearTimeout(h_editorLoader); + + if (!isCellPotentiallyEditable(activeRow, activeCell)) { + return; + } + + var columnDef = columns[activeCell]; + var item = getDataItem(activeRow); + + if (trigger(self.onBeforeEditCell, {row:activeRow, cell:activeCell, item:item, column:columnDef}) === false) { + setFocus(); + return; + } + + getEditorLock().activate(editController); + $(activeCellNode).addClass("editable"); + + // don't clear the cell if a custom editor is passed through + if (!editor) { + activeCellNode.innerHTML = ""; + } + + currentEditor = new (editor || getEditor(activeRow, activeCell))({ + grid:self, + gridPosition:absBox($container[0]), + position:absBox(activeCellNode), + container:activeCellNode, + column:columnDef, + item:item || {}, + commitChanges:commitEditAndSetFocus, + cancelChanges:cancelEditAndSetFocus + }); + + if (item) { + currentEditor.loadValue(item); + } + + serializedEditorValue = currentEditor.serializeValue(); + + if (currentEditor.position) { + handleActiveCellPositionChange(); + } + } - if (currentEditor.position) - handleActiveCellPositionChange(); + function commitEditAndSetFocus() { + // if the commit fails, it would do so due to a validation error + // if so, do not steal the focus from the editor + if (getEditorLock().commitCurrentEdit()) { + setFocus(); + if (options.autoEdit) { + navigateDown(); } + } + } - function commitEditAndSetFocus() { - // if the commit fails, it would do so due to a validation error - // if so, do not steal the focus from the editor - if (getEditorLock().commitCurrentEdit()) { - setFocus(); + function cancelEditAndSetFocus() { + if (getEditorLock().cancelCurrentEdit()) { + setFocus(); + } + } - if (options.autoEdit) { - navigateDown(); - } - } - } + function absBox(elem) { + var box = { + top:elem.offsetTop, + left:elem.offsetLeft, + bottom:0, + right:0, + width:$(elem).outerWidth(), + height:$(elem).outerHeight(), + visible:true}; + box.bottom = box.top + box.height; + box.right = box.left + box.width; - function cancelEditAndSetFocus() { - if (getEditorLock().cancelCurrentEdit()) { - setFocus(); - } + // walk up the tree + var offsetParent = elem.offsetParent; + while ((elem = elem.parentNode) != document.body) { + if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css("overflowY") != "visible") { + box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight; } - function absBox(elem) { - var box = {top:elem.offsetTop, left:elem.offsetLeft, bottom:0, right:0, width:$(elem).outerWidth(), height:$(elem).outerHeight(), visible:true}; - box.bottom = box.top + box.height; - box.right = box.left + box.width; - - // walk up the tree - var offsetParent = elem.offsetParent; - while ((elem = elem.parentNode) != document.body) { - if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css("overflowY") != "visible") - box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight; - - if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css("overflowX") != "visible") - box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth; - - box.left -= elem.scrollLeft; - box.top -= elem.scrollTop; - - if (elem === offsetParent) { - box.left += elem.offsetLeft; - box.top += elem.offsetTop; - offsetParent = elem.offsetParent; - } - - box.bottom = box.top + box.height; - box.right = box.left + box.width; - } - - return box; + if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css("overflowX") != "visible") { + box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth; } - function getActiveCellPosition(){ - return absBox(activeCellNode); - } + box.left -= elem.scrollLeft; + box.top -= elem.scrollTop; - function getGridPosition(){ - return absBox($container[0]) + if (elem === offsetParent) { + box.left += elem.offsetLeft; + box.top += elem.offsetTop; + offsetParent = elem.offsetParent; } - function handleActiveCellPositionChange() { - if (!activeCellNode) return; - var cellBox; + box.bottom = box.top + box.height; + box.right = box.left + box.width; + } - trigger(self.onActiveCellPositionChanged, {}); + return box; + } - if (currentEditor) { - cellBox = cellBox || getActiveCellPosition(); - if (currentEditor.show && currentEditor.hide) { - if (!cellBox.visible) - currentEditor.hide(); - else - currentEditor.show(); - } + function getActiveCellPosition() { + return absBox(activeCellNode); + } - if (currentEditor.position) - currentEditor.position(cellBox); - } - } + function getGridPosition() { + return absBox($container[0]) + } - function getCellEditor() { - return currentEditor; - } + function handleActiveCellPositionChange() { + if (!activeCellNode) { + return; + } - function getActiveCell() { - if (!activeCellNode) - return null; - else - return {row: activeRow, cell: activeCell}; - } + trigger(self.onActiveCellPositionChanged, {}); - function getActiveCellNode() { - return activeCellNode; + if (currentEditor) { + var cellBox = getActiveCellPosition(); + if (currentEditor.show && currentEditor.hide) { + if (!cellBox.visible) { + currentEditor.hide(); + } else { + currentEditor.show(); + } } - function scrollRowIntoView(row, doPaging) { - var rowAtTop = row * options.rowHeight; - var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll?scrollbarDimensions.height:0); - - // need to page down? - if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) { - scrollTo(doPaging ? rowAtTop : rowAtBottom); - render(); - } - - // or page up? - else if (row * options.rowHeight < scrollTop + offset) { - scrollTo(doPaging ? rowAtBottom : rowAtTop); - render(); - } + if (currentEditor.position) { + currentEditor.position(cellBox); } + } + } - function getColspan(row, cell) { - var metadata = data.getItemMetadata && data.getItemMetadata(row); - if (!metadata || !metadata.columns) { - return 1; - } + function getCellEditor() { + return currentEditor; + } - var columnData = metadata.columns[columns[cell].id] || metadata.columns[cell]; - var colspan = (columnData && columnData.colspan); - if (colspan === "*") { - colspan = columns.length - cell; - } - return (colspan || 1); - } + function getActiveCell() { + if (!activeCellNode) { + return null; + } else { + return {row:activeRow, cell:activeCell}; + } + } - function findFirstFocusableCell(row) { - var cell = 0; - while (cell < columns.length) { - if (canCellBeActive(row, cell)) { - return cell; - } - cell += getColspan(row, cell); - } - return null; - } + function getActiveCellNode() { + return activeCellNode; + } - function findLastFocusableCell(row) { - var cell = 0; - var lastFocusableCell = null; - while (cell < columns.length) { - if (canCellBeActive(row, cell)) { - lastFocusableCell = cell; - } - cell += getColspan(row, cell); - } - return lastFocusableCell; - } + function scrollRowIntoView(row, doPaging) { + var rowAtTop = row * options.rowHeight; + var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll ? scrollbarDimensions.height : 0); + + // need to page down? + if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) { + scrollTo(doPaging ? rowAtTop : rowAtBottom); + render(); + } + // or page up? + else if (row * options.rowHeight < scrollTop + offset) { + scrollTo(doPaging ? rowAtBottom : rowAtTop); + render(); + } + } - function gotoRight(row, cell, posX) { - if (cell >= columns.length) { - return null; - } + function getColspan(row, cell) { + var metadata = data.getItemMetadata && data.getItemMetadata(row); + if (!metadata || !metadata.columns) { + return 1; + } + + var columnData = metadata.columns[columns[cell].id] || metadata.columns[cell]; + var colspan = (columnData && columnData.colspan); + if (colspan === "*") { + colspan = columns.length - cell; + } + return (colspan || 1); + } - do { - cell += getColspan(row, cell); - } - while (cell < columns.length && !canCellBeActive(row, cell)); - - if (cell < columns.length) { - return { - "row": row, - "cell": cell, - "posX": cell - }; - } - return null; + function findFirstFocusableCell(row) { + var cell = 0; + while (cell < columns.length) { + if (canCellBeActive(row, cell)) { + return cell; } + cell += getColspan(row, cell); + } + return null; + } - function gotoLeft(row, cell, posX) { - if (cell <= 0) { - return null; - } - - var firstFocusableCell = findFirstFocusableCell(row); - if (firstFocusableCell === null || firstFocusableCell >= cell) { - return null; - } - - var prev = { - "row": row, - "cell": firstFocusableCell, - "posX": firstFocusableCell - }; - var pos; - while (true) { - pos = gotoRight(prev.row, prev.cell, prev.posX); - if (!pos) { - return null; - } - if (pos.cell >= cell) { - return prev; - } - prev = pos; - } + function findLastFocusableCell(row) { + var cell = 0; + var lastFocusableCell = null; + while (cell < columns.length) { + if (canCellBeActive(row, cell)) { + lastFocusableCell = cell; } + cell += getColspan(row, cell); + } + return lastFocusableCell; + } - function gotoDown(row, cell, posX) { - var prevCell; - while (true) { - if (++row >= getDataLength() + (options.enableAddRow ? 1 : 0)) { - return null; - } + function gotoRight(row, cell, posX) { + if (cell >= columns.length) { + return null; + } + + do { + cell += getColspan(row, cell); + } + while (cell < columns.length && !canCellBeActive(row, cell)); + + if (cell < columns.length) { + return { + "row":row, + "cell":cell, + "posX":cell + }; + } + return null; + } - prevCell = cell = 0; - while (cell <= posX) { - prevCell = cell; - cell += getColspan(row, cell); - } + function gotoLeft(row, cell, posX) { + if (cell <= 0) { + return null; + } + + var firstFocusableCell = findFirstFocusableCell(row); + if (firstFocusableCell === null || firstFocusableCell >= cell) { + return null; + } + + var prev = { + "row":row, + "cell":firstFocusableCell, + "posX":firstFocusableCell + }; + var pos; + while (true) { + pos = gotoRight(prev.row, prev.cell, prev.posX); + if (!pos) { + return null; + } + if (pos.cell >= cell) { + return prev; + } + prev = pos; + } + } - if (canCellBeActive(row, prevCell)) { - return { - "row": row, - "cell": prevCell, - "posX": posX - }; - } - } + function gotoDown(row, cell, posX) { + var prevCell; + while (true) { + if (++row >= getDataLength() + (options.enableAddRow ? 1 : 0)) { + return null; } - function gotoUp(row, cell, posX) { - var prevCell; - while (true) { - if (--row < 0) { - return null; - } - - prevCell = cell = 0; - while (cell <= posX) { - prevCell = cell; - cell += getColspan(row, cell); - } - - if (canCellBeActive(row, prevCell)) { - return { - "row": row, - "cell": prevCell, - "posX": posX - }; - } - } + prevCell = cell = 0; + while (cell <= posX) { + prevCell = cell; + cell += getColspan(row, cell); } - function gotoNext(row, cell, posX) { - var pos = gotoRight(row, cell, posX); - if (pos) { - return pos; - } - - var firstFocusableCell = null; - while (++row < getDataLength() + (options.enableAddRow ? 1 : 0)) { - firstFocusableCell = findFirstFocusableCell(row); - if (firstFocusableCell !== null) { - return { - "row": row, - "cell": firstFocusableCell, - "posX": firstFocusableCell - }; - } - } - return null; + if (canCellBeActive(row, prevCell)) { + return { + "row":row, + "cell":prevCell, + "posX":posX + }; } + } + } - function gotoPrev(row, cell, posX) { - var pos; - var lastSelectableCell; - while (!pos) { - pos = gotoLeft(row, cell, posX); - if (pos) { - break; - } - if (--row < 0) { - return null; - } - - cell = 0; - lastSelectableCell = findLastFocusableCell(row); - if (lastSelectableCell !== null) { - pos = { - "row": row, - "cell": lastSelectableCell, - "posX": lastSelectableCell - }; - } - } - return pos; + function gotoUp(row, cell, posX) { + var prevCell; + while (true) { + if (--row < 0) { + return null; } - function navigateRight() { - navigate("right"); + prevCell = cell = 0; + while (cell <= posX) { + prevCell = cell; + cell += getColspan(row, cell); } - function navigateLeft() { - navigate("left"); + if (canCellBeActive(row, prevCell)) { + return { + "row":row, + "cell":prevCell, + "posX":posX + }; } + } + } - function navigateDown() { - navigate("down"); - } + function gotoNext(row, cell, posX) { + var pos = gotoRight(row, cell, posX); + if (pos) { + return pos; + } + + var firstFocusableCell = null; + while (++row < getDataLength() + (options.enableAddRow ? 1 : 0)) { + firstFocusableCell = findFirstFocusableCell(row); + if (firstFocusableCell !== null) { + return { + "row":row, + "cell":firstFocusableCell, + "posX":firstFocusableCell + }; + } + } + return null; + } - function navigateUp() { - navigate("up"); - } + function gotoPrev(row, cell, posX) { + var pos; + var lastSelectableCell; + while (!pos) { + pos = gotoLeft(row, cell, posX); + if (pos) { + break; + } + if (--row < 0) { + return null; + } + + cell = 0; + lastSelectableCell = findLastFocusableCell(row); + if (lastSelectableCell !== null) { + pos = { + "row":row, + "cell":lastSelectableCell, + "posX":lastSelectableCell + }; + } + } + return pos; + } - function navigateNext() { - navigate("next"); - } + function navigateRight() { + navigate("right"); + } - function navigatePrev() { - navigate("prev"); - } + function navigateLeft() { + navigate("left"); + } - function navigate(dir) { - if (!activeCellNode || !options.enableCellNavigation) { return; } - if (!getEditorLock().commitCurrentEdit()) { return; } + function navigateDown() { + navigate("down"); + } - var stepFunctions = { - "up": gotoUp, - "down": gotoDown, - "left": gotoLeft, - "right": gotoRight, - "prev": gotoPrev, - "next": gotoNext - }; - var stepFn = stepFunctions[dir]; - var pos = stepFn(activeRow, activeCell, activePosX); - if (pos) { - var isAddNewRow = (pos.row == getDataLength()); - scrollRowIntoView(pos.row, !isAddNewRow); - setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit); - activePosX = pos.posX; - } else { - setActiveCellInternal(getCellNode(activeRow, activeCell), (activeRow == getDataLength()) || options.autoEdit); - } - } + function navigateUp() { + navigate("up"); + } - function getCellNode(row, cell) { - if (rowsCache[row]) { - var cells = $(rowsCache[row]).children(); - var nodeCell; - for (var i = 0; i < cells.length; i++) { - nodeCell = getCellFromNode(cells[i]); - if (nodeCell === cell) { - return cells[i]; - } - else if (nodeCell > cell) { - return null; - } + function navigateNext() { + navigate("next"); + } - } - } - return null; - } + function navigatePrev() { + navigate("prev"); + } - function setActiveCell(row, cell) { - if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) { - return; - } + function navigate(dir) { + if (!activeCellNode || !options.enableCellNavigation) { + return; + } + if (!getEditorLock().commitCurrentEdit()) { + return; + } + + var stepFunctions = { + "up":gotoUp, + "down":gotoDown, + "left":gotoLeft, + "right":gotoRight, + "prev":gotoPrev, + "next":gotoNext + }; + var stepFn = stepFunctions[dir]; + var pos = stepFn(activeRow, activeCell, activePosX); + if (pos) { + var isAddNewRow = (pos.row == getDataLength()); + scrollRowIntoView(pos.row, !isAddNewRow); + setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit); + activePosX = pos.posX; + } else { + setActiveCellInternal(getCellNode(activeRow, activeCell), (activeRow == getDataLength()) || options.autoEdit); + } + } - if (!options.enableCellNavigation) { - return; - } + function getCellNode(row, cell) { + if (rowsCache[row]) { + var cells = $(rowsCache[row]).children(); + var nodeCell; + for (var i = 0; i < cells.length; i++) { + nodeCell = getCellFromNode(cells[i]); + if (nodeCell === cell) { + return cells[i]; + } else if (nodeCell > cell) { + return null; + } - scrollRowIntoView(row,false); - setActiveCellInternal(getCellNode(row,cell),false); } + } + return null; + } - function canCellBeActive(row, cell) { - if (!options.enableCellNavigation || row >= getDataLength() + (options.enableAddRow ? 1 : 0) || row < 0 || cell >= columns.length || cell < 0) { - return false; - } + function setActiveCell(row, cell) { + if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) { + return; + } - var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); - if (rowMetadata && typeof rowMetadata.focusable === "boolean") { - return rowMetadata.focusable; - } + if (!options.enableCellNavigation) { + return; + } - var columnMetadata = rowMetadata && rowMetadata.columns; - if (columnMetadata && columnMetadata[columns[cell].id] && typeof columnMetadata[columns[cell].id].focusable === "boolean") { - return columnMetadata[columns[cell].id].focusable; - } - if (columnMetadata && columnMetadata[cell] && typeof columnMetadata[cell].focusable === "boolean") { - return columnMetadata[cell].focusable; - } + scrollRowIntoView(row, false); + setActiveCellInternal(getCellNode(row, cell), false); + } - if (typeof columns[cell].focusable === "boolean") { - return columns[cell].focusable; - } + function canCellBeActive(row, cell) { + if (!options.enableCellNavigation || row >= getDataLength() + (options.enableAddRow ? 1 : 0) || + row < 0 || cell >= columns.length || cell < 0) { + return false; + } + + var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); + if (rowMetadata && typeof rowMetadata.focusable === "boolean") { + return rowMetadata.focusable; + } + + var columnMetadata = rowMetadata && rowMetadata.columns; + if (columnMetadata && columnMetadata[columns[cell].id] && typeof columnMetadata[columns[cell].id].focusable === "boolean") { + return columnMetadata[columns[cell].id].focusable; + } + if (columnMetadata && columnMetadata[cell] && typeof columnMetadata[cell].focusable === "boolean") { + return columnMetadata[cell].focusable; + } + + if (typeof columns[cell].focusable === "boolean") { + return columns[cell].focusable; + } + + return true; + } - return true; - } + function canCellBeSelected(row, cell) { + if (row >= getDataLength() || row < 0 || cell >= columns.length || cell < 0) { + return false; + } - function canCellBeSelected(row, cell) { - if (row >= getDataLength() || row < 0 || cell >= columns.length || cell < 0) { - return false; - } + var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); + if (rowMetadata && typeof rowMetadata.selectable === "boolean") { + return rowMetadata.selectable; + } - var rowMetadata = data.getItemMetadata && data.getItemMetadata(row); - if (rowMetadata && typeof rowMetadata.selectable === "boolean") { - return rowMetadata.selectable; - } + var columnMetadata = rowMetadata && rowMetadata.columns && (rowMetadata.columns[columns[cell].id] || rowMetadata.columns[cell]); + if (columnMetadata && typeof columnMetadata.selectable === "boolean") { + return columnMetadata.selectable; + } - var columnMetadata = rowMetadata && rowMetadata.columns && (rowMetadata.columns[columns[cell].id] || rowMetadata.columns[cell]); - if (columnMetadata && typeof columnMetadata.selectable === "boolean") { - return columnMetadata.selectable; - } + if (typeof columns[cell].selectable === "boolean") { + return columns[cell].selectable; + } - if (typeof columns[cell].selectable === "boolean") { - return columns[cell].selectable; - } + return true; + } - return true; - } + function gotoCell(row, cell, forceEdit) { + if (!canCellBeActive(row, cell)) { + return; + } - function gotoCell(row, cell, forceEdit) { - if (!canCellBeActive(row, cell)) { - return; - } + if (!getEditorLock().commitCurrentEdit()) { + return; + } - if (!getEditorLock().commitCurrentEdit()) { return; } + scrollRowIntoView(row, false); - scrollRowIntoView(row,false); + var newCell = getCellNode(row, cell); - var newCell = getCellNode(row, cell); + // if selecting the 'add new' row, start editing right away + setActiveCellInternal(newCell, forceEdit || (row === getDataLength()) || options.autoEdit); - // if selecting the 'add new' row, start editing right away - setActiveCellInternal(newCell, forceEdit || (row === getDataLength()) || options.autoEdit); + // if no editor was created, set the focus back on the grid + if (!currentEditor) { + setFocus(); + } + } - // if no editor was created, set the focus back on the grid - if (!currentEditor) { - setFocus(); - } - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - // IEditor implementation for the editor lock - - function commitCurrentEdit() { - var item = getDataItem(activeRow); - var column = columns[activeCell]; - - if (currentEditor) { - if (currentEditor.isValueChanged()) { - var validationResults = currentEditor.validate(); - - if (validationResults.valid) { - if (activeRow < getDataLength()) { - var editCommand = { - row: activeRow, - cell: activeCell, - editor: currentEditor, - serializedValue: currentEditor.serializeValue(), - prevSerializedValue: serializedEditorValue, - execute: function() { - this.editor.applyValue(item,this.serializedValue); - updateRow(this.row); - }, - undo: function() { - this.editor.applyValue(item,this.prevSerializedValue); - updateRow(this.row); - } - }; - - if (options.editCommandHandler) { - makeActiveCellNormal(); - options.editCommandHandler(item,column,editCommand); - - } - else { - editCommand.execute(); - makeActiveCellNormal(); - } - - trigger(self.onCellChange, { - row: activeRow, - cell: activeCell, - item: item - }); - } - else { - var newItem = {}; - currentEditor.applyValue(newItem,currentEditor.serializeValue()); - makeActiveCellNormal(); - trigger(self.onAddNewRow, {item:newItem, column:column}); - } - - // check whether the lock has been re-acquired by event handlers - return !getEditorLock().isActive(); - } - else { - // TODO: remove and put in onValidationError handlers in examples - $(activeCellNode).addClass("invalid"); - $(activeCellNode).stop(true,true).effect("highlight", {color:"red"}, 300); - - trigger(self.onValidationError, { - editor: currentEditor, - cellNode: activeCellNode, - validationResults: validationResults, - row: activeRow, - cell: activeCell, - column: column - }); - - currentEditor.focus(); - return false; - } + ////////////////////////////////////////////////////////////////////////////////////////////// + // IEditor implementation for the editor lock + + function commitCurrentEdit() { + var item = getDataItem(activeRow); + var column = columns[activeCell]; + + if (currentEditor) { + if (currentEditor.isValueChanged()) { + var validationResults = currentEditor.validate(); + + if (validationResults.valid) { + if (activeRow < getDataLength()) { + var editCommand = { + row:activeRow, + cell:activeCell, + editor:currentEditor, + serializedValue:currentEditor.serializeValue(), + prevSerializedValue:serializedEditorValue, + execute:function () { + this.editor.applyValue(item, this.serializedValue); + updateRow(this.row); + }, + undo:function () { + this.editor.applyValue(item, this.prevSerializedValue); + updateRow(this.row); } + }; + if (options.editCommandHandler) { makeActiveCellNormal(); - } - return true; - } + options.editCommandHandler(item, column, editCommand); + } else { + editCommand.execute(); + makeActiveCellNormal(); + } - function cancelCurrentEdit() { - makeActiveCellNormal(); - return true; - } + trigger(self.onCellChange, { + row:activeRow, + cell:activeCell, + item:item + }); + } else { + var newItem = {}; + currentEditor.applyValue(newItem, currentEditor.serializeValue()); + makeActiveCellNormal(); + trigger(self.onAddNewRow, {item:newItem, column:column}); + } + + // check whether the lock has been re-acquired by event handlers + return !getEditorLock().isActive(); + } else { + // TODO: remove and put in onValidationError handlers in examples + $(activeCellNode).addClass("invalid"); + $(activeCellNode).stop(true, true).effect("highlight", {color:"red"}, 300); + + trigger(self.onValidationError, { + editor:currentEditor, + cellNode:activeCellNode, + validationResults:validationResults, + row:activeRow, + cell:activeCell, + column:column + }); - function rowsToRanges(rows) { - var ranges = []; - var lastCell = columns.length - 1; - for (var i = 0; i < rows.length; i++) { - ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); - } - return ranges; + currentEditor.focus(); + return false; + } } - function getSelectedRows() { - if (!selectionModel) { - throw "Selection model is not set"; - } - return selectedRows; - } + makeActiveCellNormal(); + } + return true; + } - function setSelectedRows(rows) { - if (!selectionModel) { - throw "Selection model is not set"; - } - selectionModel.setSelectedRanges(rowsToRanges(rows)); - } + function cancelCurrentEdit() { + makeActiveCellNormal(); + return true; + } + function rowsToRanges(rows) { + var ranges = []; + var lastCell = columns.length - 1; + for (var i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } - ////////////////////////////////////////////////////////////////////////////////////////////// - // Debug + function getSelectedRows() { + if (!selectionModel) { + throw "Selection model is not set"; + } + return selectedRows; + } - this.debug = function() { - var s = ""; + function setSelectedRows(rows) { + if (!selectionModel) { + throw "Selection model is not set"; + } + selectionModel.setSelectedRanges(rowsToRanges(rows)); + } - s += ("\n" + "counter_rows_rendered: " + counter_rows_rendered); - s += ("\n" + "counter_rows_removed: " + counter_rows_removed); - s += ("\n" + "renderedRows: " + renderedRows); - s += ("\n" + "numVisibleRows: " + numVisibleRows); - s += ("\n" + "maxSupportedCssHeight: " + maxSupportedCssHeight); - s += ("\n" + "n(umber of pages): " + n); - s += ("\n" + "(current) page: " + page); - s += ("\n" + "page height (ph): " + ph); - s += ("\n" + "scrollDir: " + scrollDir); - alert(s); - }; + ////////////////////////////////////////////////////////////////////////////////////////////// + // Debug - // a debug helper to be able to access private members - this.eval = function(expr) { - return eval(expr); - }; + this.debug = function () { + var s = ""; - ////////////////////////////////////////////////////////////////////////////////////////////// - // Public API - - $.extend(this, { - "slickGridVersion": "2.0a1", - - // Events - "onScroll": new Slick.Event(), - "onSort": new Slick.Event(), - "onHeaderContextMenu": new Slick.Event(), - "onHeaderClick": new Slick.Event(), - "onMouseEnter": new Slick.Event(), - "onMouseLeave": new Slick.Event(), - "onClick": new Slick.Event(), - "onDblClick": new Slick.Event(), - "onContextMenu": new Slick.Event(), - "onKeyDown": new Slick.Event(), - "onAddNewRow": new Slick.Event(), - "onValidationError": new Slick.Event(), - "onViewportChanged": new Slick.Event(), - "onColumnsReordered": new Slick.Event(), - "onColumnsResized": new Slick.Event(), - "onCellChange": new Slick.Event(), - "onBeforeEditCell": new Slick.Event(), - "onBeforeCellEditorDestroy": new Slick.Event(), - "onBeforeDestroy": new Slick.Event(), - "onActiveCellChanged": new Slick.Event(), - "onActiveCellPositionChanged": new Slick.Event(), - "onDragInit": new Slick.Event(), - "onDragStart": new Slick.Event(), - "onDrag": new Slick.Event(), - "onDragEnd": new Slick.Event(), - "onSelectedRowsChanged": new Slick.Event(), - - // Methods - "registerPlugin": registerPlugin, - "unregisterPlugin": unregisterPlugin, - "getColumns": getColumns, - "setColumns": setColumns, - "getColumnIndex": getColumnIndex, - "updateColumnHeader": updateColumnHeader, - "setSortColumn": setSortColumn, - "autosizeColumns": autosizeColumns, - "getOptions": getOptions, - "setOptions": setOptions, - "getData": getData, - "getDataLength": getDataLength, - "getDataItem": getDataItem, - "setData": setData, - "getSelectionModel": getSelectionModel, - "setSelectionModel": setSelectionModel, - "getSelectedRows": getSelectedRows, - "setSelectedRows": setSelectedRows, - - "render": render, - "invalidate": invalidate, - "invalidateRow": invalidateRow, - "invalidateRows": invalidateRows, - "invalidateAllRows": invalidateAllRows, - "updateCell": updateCell, - "updateRow": updateRow, - "getViewport": getVisibleRange, - "getRenderedRange": getRenderedRange, - "resizeCanvas": resizeCanvas, - "updateRowCount": updateRowCount, - "scrollRowIntoView": scrollRowIntoView, - "getCanvasNode": getCanvasNode, - - "getCellFromPoint": getCellFromPoint, - "getCellFromEvent": getCellFromEvent, - "getActiveCell": getActiveCell, - "setActiveCell": setActiveCell, - "getActiveCellNode": getActiveCellNode, - "getActiveCellPosition": getActiveCellPosition, - "resetActiveCell": resetActiveCell, - "editActiveCell": makeActiveCellEditable, - "getCellEditor": getCellEditor, - "getCellNode": getCellNode, - "getCellNodeBox": getCellNodeBox, - "canCellBeSelected": canCellBeSelected, - "canCellBeActive": canCellBeActive, - "navigatePrev": navigatePrev, - "navigateNext": navigateNext, - "navigateUp": navigateUp, - "navigateDown": navigateDown, - "navigateLeft": navigateLeft, - "navigateRight": navigateRight, - "gotoCell": gotoCell, - "getTopPanel": getTopPanel, - "showTopPanel": showTopPanel, - "hideTopPanel": hideTopPanel, - "showHeaderRowColumns": showHeaderRowColumns, - "hideHeaderRowColumns": hideHeaderRowColumns, - "getHeaderRow": getHeaderRow, - "getHeaderRowColumn": getHeaderRowColumn, - "getGridPosition": getGridPosition, - "flashCell": flashCell, - "addCellCssStyles": addCellCssStyles, - "setCellCssStyles": setCellCssStyles, - "removeCellCssStyles": removeCellCssStyles, - - "destroy": destroy, - - // IEditor implementation - "getEditorLock": getEditorLock, - "getEditController": getEditController - }); + s += ("\n" + "counter_rows_rendered: " + counter_rows_rendered); + s += ("\n" + "counter_rows_removed: " + counter_rows_removed); + s += ("\n" + "renderedRows: " + renderedRows); + s += ("\n" + "numVisibleRows: " + numVisibleRows); + s += ("\n" + "maxSupportedCssHeight: " + maxSupportedCssHeight); + s += ("\n" + "n(umber of pages): " + n); + s += ("\n" + "(current) page: " + page); + s += ("\n" + "page height (ph): " + ph); + s += ("\n" + "scrollDir: " + scrollDir); - init(); - } + alert(s); + }; + + // a debug helper to be able to access private members + this.eval = function (expr) { + return eval(expr); + }; + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Public API + + $.extend(this, { + "slickGridVersion":"2.0a1", + + // Events + "onScroll":new Slick.Event(), + "onSort":new Slick.Event(), + "onHeaderContextMenu":new Slick.Event(), + "onHeaderClick":new Slick.Event(), + "onMouseEnter":new Slick.Event(), + "onMouseLeave":new Slick.Event(), + "onClick":new Slick.Event(), + "onDblClick":new Slick.Event(), + "onContextMenu":new Slick.Event(), + "onKeyDown":new Slick.Event(), + "onAddNewRow":new Slick.Event(), + "onValidationError":new Slick.Event(), + "onViewportChanged":new Slick.Event(), + "onColumnsReordered":new Slick.Event(), + "onColumnsResized":new Slick.Event(), + "onCellChange":new Slick.Event(), + "onBeforeEditCell":new Slick.Event(), + "onBeforeCellEditorDestroy":new Slick.Event(), + "onBeforeDestroy":new Slick.Event(), + "onActiveCellChanged":new Slick.Event(), + "onActiveCellPositionChanged":new Slick.Event(), + "onDragInit":new Slick.Event(), + "onDragStart":new Slick.Event(), + "onDrag":new Slick.Event(), + "onDragEnd":new Slick.Event(), + "onSelectedRowsChanged":new Slick.Event(), + + // Methods + "registerPlugin":registerPlugin, + "unregisterPlugin":unregisterPlugin, + "getColumns":getColumns, + "setColumns":setColumns, + "getColumnIndex":getColumnIndex, + "updateColumnHeader":updateColumnHeader, + "setSortColumn":setSortColumn, + "autosizeColumns":autosizeColumns, + "getOptions":getOptions, + "setOptions":setOptions, + "getData":getData, + "getDataLength":getDataLength, + "getDataItem":getDataItem, + "setData":setData, + "getSelectionModel":getSelectionModel, + "setSelectionModel":setSelectionModel, + "getSelectedRows":getSelectedRows, + "setSelectedRows":setSelectedRows, + + "render":render, + "invalidate":invalidate, + "invalidateRow":invalidateRow, + "invalidateRows":invalidateRows, + "invalidateAllRows":invalidateAllRows, + "updateCell":updateCell, + "updateRow":updateRow, + "getViewport":getVisibleRange, + "getRenderedRange":getRenderedRange, + "resizeCanvas":resizeCanvas, + "updateRowCount":updateRowCount, + "scrollRowIntoView":scrollRowIntoView, + "getCanvasNode":getCanvasNode, + + "getCellFromPoint":getCellFromPoint, + "getCellFromEvent":getCellFromEvent, + "getActiveCell":getActiveCell, + "setActiveCell":setActiveCell, + "getActiveCellNode":getActiveCellNode, + "getActiveCellPosition":getActiveCellPosition, + "resetActiveCell":resetActiveCell, + "editActiveCell":makeActiveCellEditable, + "getCellEditor":getCellEditor, + "getCellNode":getCellNode, + "getCellNodeBox":getCellNodeBox, + "canCellBeSelected":canCellBeSelected, + "canCellBeActive":canCellBeActive, + "navigatePrev":navigatePrev, + "navigateNext":navigateNext, + "navigateUp":navigateUp, + "navigateDown":navigateDown, + "navigateLeft":navigateLeft, + "navigateRight":navigateRight, + "gotoCell":gotoCell, + "getTopPanel":getTopPanel, + "showTopPanel":showTopPanel, + "hideTopPanel":hideTopPanel, + "showHeaderRowColumns":showHeaderRowColumns, + "hideHeaderRowColumns":hideHeaderRowColumns, + "getHeaderRow":getHeaderRow, + "getHeaderRowColumn":getHeaderRowColumn, + "getGridPosition":getGridPosition, + "flashCell":flashCell, + "addCellCssStyles":addCellCssStyles, + "setCellCssStyles":setCellCssStyles, + "removeCellCssStyles":removeCellCssStyles, + + "destroy":destroy, + + // IEditor implementation + "getEditorLock":getEditorLock, + "getEditController":getEditController + }); + + init(); + } }(jQuery)); diff --git a/slick.groupitemmetadataprovider.js b/slick.groupitemmetadataprovider.js index 68430eb..2ad0137 100644 --- a/slick.groupitemmetadataprovider.js +++ b/slick.groupitemmetadataprovider.js @@ -1,140 +1,139 @@ -(function($) { - $.extend(true, window, { - Slick: { - Data: { - GroupItemMetadataProvider: GroupItemMetadataProvider - } - } - }); - - - /*** - * Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView. - * This metadata overrides the default behavior and formatting of those rows so that they appear and function - * correctly when processed by the grid. - * - * This class also acts as a grid plugin providing event handlers to expand & collapse groups. - * If "grid.registerPlugin(...)" is not called, expand & collapse will not work. - * - * @class GroupItemMetadataProvider - * @module Data - * @namespace Slick.Data - * @constructor - * @param options - */ - function GroupItemMetadataProvider(options) { - var _grid; - var _defaults = { - groupCssClass: "slick-group", - totalsCssClass: "slick-group-totals", - groupFocusable: true, - totalsFocusable: false, - toggleCssClass: "slick-group-toggle", - toggleExpandedCssClass: "expanded", - toggleCollapsedCssClass: "collapsed", - enableExpandCollapse: true - }; - - options = $.extend(true, {}, _defaults, options); - - - function defaultGroupCellFormatter(row, cell, value, columnDef, item) { - if (!options.enableExpandCollapse) { - return item.title; - } +(function ($) { + $.extend(true, window, { + Slick:{ + Data:{ + GroupItemMetadataProvider:GroupItemMetadataProvider + } + } + }); + + + /*** + * Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView. + * This metadata overrides the default behavior and formatting of those rows so that they appear and function + * correctly when processed by the grid. + * + * This class also acts as a grid plugin providing event handlers to expand & collapse groups. + * If "grid.registerPlugin(...)" is not called, expand & collapse will not work. + * + * @class GroupItemMetadataProvider + * @module Data + * @namespace Slick.Data + * @constructor + * @param options + */ + function GroupItemMetadataProvider(options) { + var _grid; + var _defaults = { + groupCssClass:"slick-group", + totalsCssClass:"slick-group-totals", + groupFocusable:true, + totalsFocusable:false, + toggleCssClass:"slick-group-toggle", + toggleExpandedCssClass:"expanded", + toggleCollapsedCssClass:"collapsed", + enableExpandCollapse:true + }; + + options = $.extend(true, {}, _defaults, options); + + + function defaultGroupCellFormatter(row, cell, value, columnDef, item) { + if (!options.enableExpandCollapse) { + return item.title; + } + + return "<span class='" + options.toggleCssClass + " " + + (item.collapsed ? options.toggleCollapsedCssClass : options.toggleExpandedCssClass) + + "'></span>" + item.title; + } - return "<span class='" + options.toggleCssClass + " " + - (item.collapsed ? options.toggleCollapsedCssClass : options.toggleExpandedCssClass) + - "'></span>" + item.title; - } + function defaultTotalsCellFormatter(row, cell, value, columnDef, item) { + return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || ""; + } - function defaultTotalsCellFormatter(row, cell, value, columnDef, item) { - return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || ""; - } + function init(grid) { + _grid = grid; + _grid.onClick.subscribe(handleGridClick); + _grid.onKeyDown.subscribe(handleGridKeyDown); - function init(grid) { - _grid = grid; - _grid.onClick.subscribe(handleGridClick); - _grid.onKeyDown.subscribe(handleGridKeyDown); + } - } + function destroy() { + if (_grid) { + _grid.onClick.unsubscribe(handleGridClick); + _grid.onKeyDown.unsubscribe(handleGridKeyDown); + } + } - function destroy() { - if (_grid) { - _grid.onClick.unsubscribe(handleGridClick); - _grid.onKeyDown.unsubscribe(handleGridKeyDown); - } + function handleGridClick(e, args) { + var item = this.getDataItem(args.row); + if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) { + if (item.collapsed) { + this.getData().expandGroup(item.value); } - - function handleGridClick(e, args) { - var item = this.getDataItem(args.row); - if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) { - if (item.collapsed) { - this.getData().expandGroup(item.value); - } - else { - this.getData().collapseGroup(item.value); - } - - e.stopImmediatePropagation(); - e.preventDefault(); - } + else { + this.getData().collapseGroup(item.value); } - // TODO: add -/+ handling - function handleGridKeyDown(e, args) { - if (options.enableExpandCollapse && (e.which == $.ui.keyCode.SPACE)) { - var activeCell = this.getActiveCell(); - if (activeCell) { - var item = this.getDataItem(activeCell.row); - if (item && item instanceof Slick.Group) { - if (item.collapsed) { - this.getData().expandGroup(item.value); - } - else { - this.getData().collapseGroup(item.value); - } - - e.stopImmediatePropagation(); - e.preventDefault(); - } - } + e.stopImmediatePropagation(); + e.preventDefault(); + } + } + + // TODO: add -/+ handling + function handleGridKeyDown(e, args) { + if (options.enableExpandCollapse && (e.which == $.ui.keyCode.SPACE)) { + var activeCell = this.getActiveCell(); + if (activeCell) { + var item = this.getDataItem(activeCell.row); + if (item && item instanceof Slick.Group) { + if (item.collapsed) { + this.getData().expandGroup(item.value); + } + else { + this.getData().collapseGroup(item.value); } - } - function getGroupRowMetadata(item) { - return { - selectable: false, - focusable: options.groupFocusable, - cssClasses: options.groupCssClass, - columns: { - 0: { - colspan: "*", - formatter: defaultGroupCellFormatter, - editor: null - } - } - }; + e.stopImmediatePropagation(); + e.preventDefault(); + } } + } + } - function getTotalsRowMetadata(item) { - return { - selectable: false, - focusable: options.totalsFocusable, - cssClasses: options.totalsCssClass, - formatter: defaultTotalsCellFormatter, - editor: null - }; + function getGroupRowMetadata(item) { + return { + selectable:false, + focusable:options.groupFocusable, + cssClasses:options.groupCssClass, + columns:{ + 0:{ + colspan:"*", + formatter:defaultGroupCellFormatter, + editor:null + } } + }; + } - - - return { - "init": init, - "destroy": destroy, - "getGroupRowMetadata": getGroupRowMetadata, - "getTotalsRowMetadata": getTotalsRowMetadata - }; + function getTotalsRowMetadata(item) { + return { + selectable:false, + focusable:options.totalsFocusable, + cssClasses:options.totalsCssClass, + formatter:defaultTotalsCellFormatter, + editor:null + }; } + + + return { + "init":init, + "destroy":destroy, + "getGroupRowMetadata":getGroupRowMetadata, + "getTotalsRowMetadata":getTotalsRowMetadata + }; + } })(jQuery);
\ No newline at end of file diff --git a/slick.remotemodel.js b/slick.remotemodel.js index dc41b64..aba7688 100644 --- a/slick.remotemodel.js +++ b/slick.remotemodel.js @@ -1,161 +1,164 @@ -(function($) { - /*** - * A sample AJAX data store implementation. - * Right now, it's hooked up to load all Apple-related Digg stories, but can - * easily be extended to support and JSONP-compatible backend that accepts paging parameters. - */ - function RemoteModel() { - // private - var PAGESIZE = 50; - var data = {length:0}; - var searchstr = "apple"; - var sortcol = null; - var sortdir = 1; - var h_request = null; - var req = null; // ajax request - - // events - var onDataLoading = new Slick.Event(); - var onDataLoaded = new Slick.Event(); - - - function init() { - } - +(function ($) { + /*** + * A sample AJAX data store implementation. + * Right now, it's hooked up to load all Apple-related Digg stories, but can + * easily be extended to support and JSONP-compatible backend that accepts paging parameters. + */ + function RemoteModel() { + // private + var PAGESIZE = 50; + var data = {length:0}; + var searchstr = "apple"; + var sortcol = null; + var sortdir = 1; + var h_request = null; + var req = null; // ajax request + + // events + var onDataLoading = new Slick.Event(); + var onDataLoaded = new Slick.Event(); + + + function init() { + } + + + function isDataLoaded(from, to) { + for (var i = from; i <= to; i++) { + if (data[i] == undefined || data[i] == null) { + return false; + } + } + + return true; + } - function isDataLoaded(from,to) { - for (var i=from; i<=to; i++) { - if (data[i] == undefined || data[i] == null) - return false; - } - return true; - } + function clear() { + for (var key in data) { + delete data[key]; + } + data.length = 0; + } + + function ensureData(from, to) { + if (req) { + req.abort(); + for (var i = req.fromPage; i <= req.toPage; i++) + data[i * PAGESIZE] = undefined; + } - function clear() { - for (var key in data) { - delete data[key]; - } - data.length = 0; - } + if (from < 0) { + from = 0; + } + var fromPage = Math.floor(from / PAGESIZE); + var toPage = Math.floor(to / PAGESIZE); + + while (data[fromPage * PAGESIZE] !== undefined && fromPage < toPage) + fromPage++; + + while (data[toPage * PAGESIZE] !== undefined && fromPage < toPage) + toPage--; + + if (fromPage > toPage || ((fromPage == toPage) && data[fromPage * PAGESIZE] !== undefined)) { + // TODO: look-ahead + return; + } + + var url = "http://services.digg.com/search/stories?query=" + searchstr + "&offset=" + (fromPage * PAGESIZE) + "&count=" + (((toPage - fromPage) * PAGESIZE) + PAGESIZE) + "&appkey=http://slickgrid.googlecode.com&type=javascript"; + + switch (sortcol) { + case "diggs": + url += ("&sort=" + ((sortdir > 0) ? "digg_count-asc" : "digg_count-desc")); + break; + } + + if (h_request != null) { + clearTimeout(h_request); + } + + h_request = setTimeout(function () { + for (var i = fromPage; i <= toPage; i++) + data[i * PAGESIZE] = null; // null indicates a 'requested but not available yet' + + onDataLoading.notify({from:from, to:to}); + + req = $.jsonp({ + url:url, + callbackParameter:"callback", + cache:true, // Digg doesn't accept the autogenerated cachebuster param + success:onSuccess, + error:function () { + onError(fromPage, toPage) + } + }); + req.fromPage = fromPage; + req.toPage = toPage; + }, 50); + } + + + function onError(fromPage, toPage) { + alert("error loading pages " + fromPage + " to " + toPage); + } - function ensureData(from,to) { - if (req) { - req.abort(); - for (var i=req.fromPage; i<=req.toPage; i++) - data[i*PAGESIZE] = undefined; - } + function onSuccess(resp) { + var from = this.fromPage * PAGESIZE, to = from + resp.count; + data.length = parseInt(resp.total); - if (from < 0) - from = 0; + for (var i = 0; i < resp.stories.length; i++) { + data[from + i] = resp.stories[i]; + data[from + i].index = from + i; + } - var fromPage = Math.floor(from / PAGESIZE); - var toPage = Math.floor(to / PAGESIZE); - - while (data[fromPage * PAGESIZE] !== undefined && fromPage < toPage) - fromPage++; + req = null; - while (data[toPage * PAGESIZE] !== undefined && fromPage < toPage) - toPage--; - - if (fromPage > toPage || ((fromPage == toPage) && data[fromPage*PAGESIZE] !== undefined)) { - // TODO: look-ahead - return; - } - - var url = "http://services.digg.com/search/stories?query=" + searchstr + "&offset=" + (fromPage * PAGESIZE) + "&count=" + (((toPage - fromPage) * PAGESIZE) + PAGESIZE) + "&appkey=http://slickgrid.googlecode.com&type=javascript"; - - switch (sortcol) { - case "diggs": - url += ("&sort=" + ((sortdir>0)?"digg_count-asc":"digg_count-desc")); - break; - } - - if (h_request != null) - clearTimeout(h_request); - - h_request = setTimeout(function() { - for (var i=fromPage; i<=toPage; i++) - data[i*PAGESIZE] = null; // null indicates a 'requested but not available yet' - - onDataLoading.notify({from:from, to:to}); - - req = $.jsonp({ - url: url, - callbackParameter: "callback", - cache: true, // Digg doesn't accept the autogenerated cachebuster param - success: onSuccess, - error: function(){ - onError(fromPage, toPage) - } - }); - req.fromPage = fromPage; - req.toPage = toPage; - }, 50); - } - - - function onError(fromPage,toPage) { - alert("error loading pages " + fromPage + " to " + toPage); - } + onDataLoaded.notify({from:from, to:to}); + } - function onSuccess(resp) { - var from = this.fromPage*PAGESIZE, to = from + resp.count; - data.length = parseInt(resp.total); - for (var i = 0; i < resp.stories.length; i++) { - data[from + i] = resp.stories[i]; - data[from + i].index = from + i; - } + function reloadData(from, to) { + for (var i = from; i <= to; i++) + delete data[i]; - req = null; + ensureData(from, to); + } - onDataLoaded.notify({from:from, to:to}); - } + function setSort(column, dir) { + sortcol = column; + sortdir = dir; + clear(); + } - function reloadData(from,to) { - for (var i=from; i<=to; i++) - delete data[i]; + function setSearch(str) { + searchstr = str; + clear(); + } - ensureData(from,to); - } + init(); - function setSort(column,dir) { - sortcol = column; - sortdir = dir; - clear(); - } + return { + // properties + "data":data, - function setSearch(str) { - searchstr = str; - clear(); - } + // methods + "clear":clear, + "isDataLoaded":isDataLoaded, + "ensureData":ensureData, + "reloadData":reloadData, + "setSort":setSort, + "setSearch":setSearch, + // events + "onDataLoading":onDataLoading, + "onDataLoaded":onDataLoaded + }; + } - init(); - - return { - // properties - "data": data, - - // methods - "clear": clear, - "isDataLoaded": isDataLoaded, - "ensureData": ensureData, - "reloadData": reloadData, - "setSort": setSort, - "setSearch": setSearch, - - // events - "onDataLoading": onDataLoading, - "onDataLoaded": onDataLoaded - }; - } - - // Slick.Data.RemoteModel - $.extend(true, window, { Slick: { Data: { RemoteModel: RemoteModel }}}); + // Slick.Data.RemoteModel + $.extend(true, window, { Slick:{ Data:{ RemoteModel:RemoteModel }}}); })(jQuery);
\ No newline at end of file |