diff options
26 files changed, 5482 insertions, 5482 deletions
diff --git a/examples/example1-simple.html b/examples/example1-simple.html index 4f73269..2c1f93f 100644 --- a/examples/example1-simple.html +++ b/examples/example1-simple.html @@ -1,89 +1,89 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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>
- </style>
- </head>
- <body>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <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>basic grid with minimal configuration</li>
- <li>grid handles container resizing</li>
- </ul>
-
- </td>
- </tr>
- </table>
-
-
- <script>
-
- var grid;
-
- var data = [];
-
- 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 = {
- editable: false,
- enableAddRow: false,
- enableCellNavigation: false
- };
-
-
-
- $(function()
- {
- for (var i=0; i<500; i++) {
- var d = (data[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);
- }
-
-
-
- grid = new SlickGrid($("#myGrid"), data, columns, options);
-
- $("#myGrid").resizable();
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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> + </style> + </head> + <body> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <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>basic grid with minimal configuration</li> + <li>grid handles container resizing</li> + </ul> + + </td> + </tr> + </table> + + + <script> + + var grid; + + var data = []; + + 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 = { + editable: false, + enableAddRow: false, + enableCellNavigation: false + }; + + + + $(function() + { + for (var i=0; i<500; i++) { + var d = (data[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); + } + + + + grid = new SlickGrid($("#myGrid"), data, columns, options); + + $("#myGrid").resizable(); + }) + + </script> + + </body> +</html> diff --git a/examples/example10-async-post-render.html b/examples/example10-async-post-render.html index 9cc9eb2..2d072e0 100644 --- a/examples/example10-async-post-render.html +++ b/examples/example10-async-post-render.html @@ -1,134 +1,134 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
- <script language="JavaScript" src="../lib/jquery.sparkline.min.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
- <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>onPostProcessRowNode(rowNode,row,context)</u> event which you can subscribe to and implement your own rendering there.
- 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>
- 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...";
- }
-
-
- 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}
-
- ];
-
- var options = {
- editable: true,
- enableAddRow: false,
- enableCellNavigation: true,
- asyncEditorLoading: false
- };
-
-
-
- $(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 SlickGrid($("#myGrid"), data, columns, options);
-
-
- grid.onPostProcessRowNode = function(rowNode, row, dataContext) {
- var vals = [
- dataContext["n1"],
- dataContext["n2"],
- dataContext["n3"],
- dataContext["n4"],
- dataContext["n5"]
- ];
-
- $(rowNode).children().eq(grid.getColumnIndex("chart")).sparkline(vals, {width:"100%"});
- }
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + <script language="JavaScript" src="../lib/jquery.sparkline.min.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + <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>onPostProcessRowNode(rowNode,row,context)</u> event which you can subscribe to and implement your own rendering there. + 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> + 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..."; + } + + + 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} + + ]; + + var options = { + editable: true, + enableAddRow: false, + enableCellNavigation: true, + asyncEditorLoading: false + }; + + + + $(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 SlickGrid($("#myGrid"), data, columns, options); + + + grid.onPostProcessRowNode = function(rowNode, row, dataContext) { + var vals = [ + dataContext["n1"], + dataContext["n2"], + dataContext["n3"], + dataContext["n4"], + dataContext["n5"] + ]; + + $(rowNode).children().eq(grid.getColumnIndex("chart")).sparkline(vals, {width:"100%"}); + } + }) + + </script> + + </body> +</html> diff --git a/examples/example2-formatters.html b/examples/example2-formatters.html index e26dd4d..10c3538 100644 --- a/examples/example2-formatters.html +++ b/examples/example2-formatters.html @@ -1,99 +1,99 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <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>
- 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 options = {
- editable: false,
- enableAddRow: false,
- enableCellNavigation: false
- };
-
-
-
- $(function()
- {
- for (var i=0; i<500; i++) {
- var d = (data[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);
- }
-
-
- grid = new SlickGrid($("#myGrid"), data, columns, options);
-
- grid.onHeaderContextMenu = function() {
- console.log(1)
-
- }
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <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> + 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 options = { + editable: false, + enableAddRow: false, + enableCellNavigation: false + }; + + + + $(function() + { + for (var i=0; i<500; i++) { + var d = (data[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); + } + + + grid = new SlickGrid($("#myGrid"), data, columns, options); + + grid.onHeaderContextMenu = function() { + console.log(1) + + } + }) + + </script> + + </body> +</html> diff --git a/examples/example3-editing.html b/examples/example3-editing.html index 71d89bb..298a6fc 100644 --- a/examples/example3-editing.html +++ b/examples/example3-editing.html @@ -1,103 +1,103 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <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>adding basic keyboard navigation and editing</li>
- <li>custom editors and validators</li>
- </ul>
-
- </td>
- </tr>
- </table>
-
-
-
- <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:"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
- };
-
-
-
- $(function()
- {
- for (var i=0; i<500; i++) {
- var d = (data[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);
- }
-
-
- grid = new SlickGrid($("#myGrid"), data, columns, options);
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <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>adding basic keyboard navigation and editing</li> + <li>custom editors and validators</li> + </ul> + + </td> + </tr> + </table> + + + + <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:"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 + }; + + + + $(function() + { + for (var i=0; i<500; i++) { + var d = (data[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); + } + + + grid = new SlickGrid($("#myGrid"), data, columns, options); + }) + + </script> + + </body> +</html> diff --git a/examples/example4-model.html b/examples/example4-model.html index 9b7fdc2..31b6466 100644 --- a/examples/example4-model.html +++ b/examples/example4-model.html @@ -1,297 +1,297 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../slick.pager.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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="../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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
- <script language="JavaScript" src="../slick.model.js"></script>
- <script language="JavaScript" src="../slick.pager.js"></script>
- <script language="JavaScript" src="../slick.columnpicker.js"></script>
-
-
- <div style="width:600px;float:left;">
- <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" style="width:320px;margin-left:650px;">
- <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>
- </div>
- </div>
-
-
- <div style="margin-left:650px;margin-top:40px;;">
- <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: setValueHandler, cannotTriggerInsert</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>
- </ul>
- </div>
-
-
- <script>
- var dataView;
- var grid;
- var data = [];
- var selectedRowIds = [];
-
- var columns = [
- {id:"sel", name:"#", cssClass:"cell-selection", width:40, cannotTriggerInsert:true, resizable:false, sortable:false, unselectable:true },
- {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator, setValueHandler:updateItem},
- {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem},
- {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem},
- {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, cannotTriggerInsert:true}
- ];
-
- var options = {
- editable: true,
- enableAddRow: true,
- enableCellNavigation: true,
- asyncEditorLoading: true,
- forceFitColumns: true
- };
-
- 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) {
- if (item["percentComplete"] < percentCompleteThreshold)
- return false;
-
- if (searchString != "" && item["title"].indexOf(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 sortdir * (x == y ? 0 : (x > y ? 1 : -1));
- }
-
- function updateItem(value,columnDef,item) {
- item[columnDef.field] = value;
- dataView.updateItem(item.id,item);
- }
-
- function addItem(columnDef,value) {
- var item = {"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};
- item[columnDef.field] = value;
- dataView.addItem(item);
- }
-
-
-
-
- $(function()
- {
- // prepare the data
- for (var i=0; i<50000; i++) {
- var d = (data[i] = {});
-
- d["id"] = "id_" + 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 DataView();
- grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options);
- var pager = new SlickGridPager(dataView, grid, $("#pager"));
- var columnpicker = new SlickColumnPicker(columns, grid);
-
- grid.onAddNewRow = addItem;
-
- grid.onKeyDown = 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.rows.length; i++) {
- rows.push(i);
- selectedRowIds.push(dataView.rows[i].id);
- }
-
- grid.setSelectedRows(rows);
-
- return true;
- }
-
- grid.onSelectedRowsChanged = function() {
- selectedRowIds = [];
- var rows = grid.getSelectedRows();
- for (var i=0, l=rows.length; i<l; i++) {
- selectedRowIds.push(rows[i].id);
- }
- }
-
- grid.onSort = function(sortCol,sortAsc) {
- sortdir = sortAsc?1:-1;
- sortcol = sortCol.field;
- dataView.sort(comparer);
- }
-
- // wire up model events to drive the grid
- dataView.onRowCountChanged.subscribe(function(args) {
- grid.updateRowCount();
- });
-
- dataView.onRowsChanged.subscribe(function(rows) {
- grid.removeRows(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 rows = [];
- for (var i = 0; i < selectedRowIds.length; i++)
- {
- var idx = dataView.getRowById(selectedRowIds[i]);
- if (idx != undefined)
- rows.push(idx);
- }
-
- grid.setSelectedRows(rows);
- }
- });
-
- dataView.onPagingInfoChanged.subscribe(function(pagingInfo) {
- var isLastPage = pagingInfo.pageSize*(pagingInfo.pageNum+1)-1 >= pagingInfo.totalRows;
- grid.setOptions({enableAddRow:isLastPage||pagingInfo.pageSize==0});
- });
-
-
-
- var h_runfilters = null;
-
- // wire up the slider to apply the filter to the model
- $("#pcSlider").slider({
- "range": "min",
- "slide": function(event,ui) {
- if (GlobalEditorLock.isEditing())
- 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) {
- if (GlobalEditorLock.isEditing())
- GlobalEditorLock.cancelCurrentEdit();
-
- // clear on Esc
- if (e.which == 27)
- this.value = "";
-
- searchString = this.value;
- dataView.refresh();
- });
-
- $("#btnSelectRows").click(function() {
- var rows = [];
- selectedRowIds = [];
-
- for (var i=0; i<10 && i<dataView.rows.length; i++) {
- rows.push(i);
- selectedRowIds.push(dataView.rows[i].id);
- }
-
- grid.setSelectedRows(rows);
- });
-
-
-
-
- // initialize the model after all the events have been hooked up
- dataView.beginUpdate();
- dataView.setItems(data);
- dataView.setFilter(myFilter);
- //dataView.setPagingOptions({pageSize:25});
- dataView.endUpdate();
-
- $("#gridContainer").resizable();
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../slick.pager.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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="../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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + <script language="JavaScript" src="../slick.model.js"></script> + <script language="JavaScript" src="../slick.pager.js"></script> + <script language="JavaScript" src="../slick.columnpicker.js"></script> + + + <div style="width:600px;float:left;"> + <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" style="width:320px;margin-left:650px;"> + <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> + </div> + </div> + + + <div style="margin-left:650px;margin-top:40px;;"> + <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: setValueHandler, cannotTriggerInsert</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> + </ul> + </div> + + + <script> + var dataView; + var grid; + var data = []; + var selectedRowIds = []; + + var columns = [ + {id:"sel", name:"#", cssClass:"cell-selection", width:40, cannotTriggerInsert:true, resizable:false, sortable:false, unselectable:true }, + {id:"title", name:"Title", field:"title", width:120, minWidth:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator, setValueHandler:updateItem}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, cannotTriggerInsert:true} + ]; + + var options = { + editable: true, + enableAddRow: true, + enableCellNavigation: true, + asyncEditorLoading: true, + forceFitColumns: true + }; + + 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) { + if (item["percentComplete"] < percentCompleteThreshold) + return false; + + if (searchString != "" && item["title"].indexOf(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 sortdir * (x == y ? 0 : (x > y ? 1 : -1)); + } + + function updateItem(value,columnDef,item) { + item[columnDef.field] = value; + dataView.updateItem(item.id,item); + } + + function addItem(columnDef,value) { + var item = {"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}; + item[columnDef.field] = value; + dataView.addItem(item); + } + + + + + $(function() + { + // prepare the data + for (var i=0; i<50000; i++) { + var d = (data[i] = {}); + + d["id"] = "id_" + 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 DataView(); + grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options); + var pager = new SlickGridPager(dataView, grid, $("#pager")); + var columnpicker = new SlickColumnPicker(columns, grid); + + grid.onAddNewRow = addItem; + + grid.onKeyDown = 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.rows.length; i++) { + rows.push(i); + selectedRowIds.push(dataView.rows[i].id); + } + + grid.setSelectedRows(rows); + + return true; + } + + grid.onSelectedRowsChanged = function() { + selectedRowIds = []; + var rows = grid.getSelectedRows(); + for (var i=0, l=rows.length; i<l; i++) { + selectedRowIds.push(rows[i].id); + } + } + + grid.onSort = function(sortCol,sortAsc) { + sortdir = sortAsc?1:-1; + sortcol = sortCol.field; + dataView.sort(comparer); + } + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function(args) { + grid.updateRowCount(); + }); + + dataView.onRowsChanged.subscribe(function(rows) { + grid.removeRows(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 rows = []; + for (var i = 0; i < selectedRowIds.length; i++) + { + var idx = dataView.getRowById(selectedRowIds[i]); + if (idx != undefined) + rows.push(idx); + } + + grid.setSelectedRows(rows); + } + }); + + dataView.onPagingInfoChanged.subscribe(function(pagingInfo) { + var isLastPage = pagingInfo.pageSize*(pagingInfo.pageNum+1)-1 >= pagingInfo.totalRows; + grid.setOptions({enableAddRow:isLastPage||pagingInfo.pageSize==0}); + }); + + + + var h_runfilters = null; + + // wire up the slider to apply the filter to the model + $("#pcSlider").slider({ + "range": "min", + "slide": function(event,ui) { + if (GlobalEditorLock.isEditing()) + 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) { + if (GlobalEditorLock.isEditing()) + GlobalEditorLock.cancelCurrentEdit(); + + // clear on Esc + if (e.which == 27) + this.value = ""; + + searchString = this.value; + dataView.refresh(); + }); + + $("#btnSelectRows").click(function() { + var rows = []; + selectedRowIds = []; + + for (var i=0; i<10 && i<dataView.rows.length; i++) { + rows.push(i); + selectedRowIds.push(dataView.rows[i].id); + } + + grid.setSelectedRows(rows); + }); + + + + + // initialize the model after all the events have been hooked up + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + //dataView.setPagingOptions({pageSize:25}); + dataView.endUpdate(); + + $("#gridContainer").resizable(); + }) + + </script> + + </body> +</html> diff --git a/examples/example5-collapsing.html b/examples/example5-collapsing.html index 6c883fc..4c820a7 100644 --- a/examples/example5-collapsing.html +++ b/examples/example5-collapsing.html @@ -1,294 +1,294 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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;
- }
-
- .sort-asc {
- background: silver url('../images/sort-asc.png') no-repeat right center !important;
- }
-
- .sort-desc {
- background: silver url('../images/sort-desc.png') no-repeat right center !important;
- }
-
- .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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
- <script language="JavaScript" src="../slick.model.js"></script>
-
-
- <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>
- 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, setValueHandler:updateItem},
- {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem},
- {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem},
- {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, 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 updateItem(value,columnDef,item) {
- item[columnDef.field] = value;
- dataView.updateItem(item.id,item);
- }
-
- function addItem(columnDef,value) {
- 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};
- item[columnDef.field] = value;
- dataView.addItem(item);
- }
-
-
- $(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 DataView();
- dataView.beginUpdate();
- dataView.setItems(data);
- dataView.setFilter(myFilter);
- dataView.endUpdate();
-
-
- // initialize the grid
- grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options);
-
- grid.onAddNewRow = addItem;
-
- grid.onClick = function(e,row,cell) {
- if ($(e.target).hasClass("toggle")) {
-
- var item = dataView.rows[row];
- if (item) {
- if (!item._collapsed)
- item._collapsed = true;
- else
- item._collapsed = false;
-
- dataView.updateItem(item.id,item);
- }
-
- return true;
- }
-
- return false;
- }
-
-
- // wire up model events to drive the grid
- dataView.onRowCountChanged.subscribe(function(args) {
- grid.updateRowCount();
- });
-
- dataView.onRowsChanged.subscribe(function(rows) {
- grid.removeRows(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) {
- if (GlobalEditorLock.isEditing())
- 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) {
- if (GlobalEditorLock.isEditing())
- GlobalEditorLock.cancelCurrentEdit();
-
- // clear on Esc
- if (e.which == 27)
- this.value = "";
-
- searchString = this.value;
- dataView.refresh();
- })
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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; + } + + .sort-asc { + background: silver url('../images/sort-asc.png') no-repeat right center !important; + } + + .sort-desc { + background: silver url('../images/sort-desc.png') no-repeat right center !important; + } + + .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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + <script language="JavaScript" src="../slick.model.js"></script> + + + <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> + 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, setValueHandler:updateItem}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, 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 updateItem(value,columnDef,item) { + item[columnDef.field] = value; + dataView.updateItem(item.id,item); + } + + function addItem(columnDef,value) { + 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}; + item[columnDef.field] = value; + dataView.addItem(item); + } + + + $(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 DataView(); + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + dataView.endUpdate(); + + + // initialize the grid + grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options); + + grid.onAddNewRow = addItem; + + grid.onClick = function(e,row,cell) { + if ($(e.target).hasClass("toggle")) { + + var item = dataView.rows[row]; + if (item) { + if (!item._collapsed) + item._collapsed = true; + else + item._collapsed = false; + + dataView.updateItem(item.id,item); + } + + return true; + } + + return false; + } + + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function(args) { + grid.updateRowCount(); + }); + + dataView.onRowsChanged.subscribe(function(rows) { + grid.removeRows(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) { + if (GlobalEditorLock.isEditing()) + 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) { + if (GlobalEditorLock.isEditing()) + 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 bed8cad..c7e597a 100644 --- a/examples/example6-ajax-loading.html +++ b/examples/example6-ajax-loading.html @@ -1,161 +1,161 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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;
- }
-
- .sort-asc {
- background: silver url('../images/sort-asc.png') no-repeat right center !important;
- }
-
- .sort-desc {
- background: silver url('../images/sort-desc.png') no-repeat right center !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: 0px 0px 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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
- <script language="JavaScript" src="../lib/jquery.jsonp-1.1.0.min.js"></script>
-
- <script language="JavaScript" src="../slick.remotemodel.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
- <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>
- </div>
-
- <script>
- var grid;
- var data = [];
- var loader = new 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, sortable:false},
- {id:"story", name:"Story", width:580, formatter:storyTitleFormatter, cssClass:"cell-story", sortable:false},
- {id:"diggs", name:"Diggs", field:"diggs", width:60}
- ];
-
- var options = {
- rowHeight: 64,
- editable: false,
- enableAddRow: false,
- enableCellNavigation: false
- };
-
- var loadingIndicator = null;
-
-
-
- $(function()
- {
- grid = new SlickGrid($("#myGrid"), loader.data, columns, options);
-
- grid.onViewportChanged = function() {
- var vp = grid.getViewport();
- loader.ensureData(vp.top, vp.bottom);
- }
-
- grid.onSort = function(sortCol,sortAsc) {
- loader.setSort(sortCol.field,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(args) {
- for (var i = args.from; i <= args.to; i++) {
- grid.removeRow(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();
- })
- </script>
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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; + } + + .sort-asc { + background: silver url('../images/sort-asc.png') no-repeat right center !important; + } + + .sort-desc { + background: silver url('../images/sort-desc.png') no-repeat right center !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: 0px 0px 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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + <script language="JavaScript" src="../lib/jquery.jsonp-1.1.0.min.js"></script> + + <script language="JavaScript" src="../slick.remotemodel.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + <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> + </div> + + <script> + var grid; + var data = []; + var loader = new 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, sortable:false}, + {id:"story", name:"Story", width:580, formatter:storyTitleFormatter, cssClass:"cell-story", sortable:false}, + {id:"diggs", name:"Diggs", field:"diggs", width:60} + ]; + + var options = { + rowHeight: 64, + editable: false, + enableAddRow: false, + enableCellNavigation: false + }; + + var loadingIndicator = null; + + + + $(function() + { + grid = new SlickGrid($("#myGrid"), loader.data, columns, options); + + grid.onViewportChanged = function() { + var vp = grid.getViewport(); + loader.ensureData(vp.top, vp.bottom); + } + + grid.onSort = function(sortCol,sortAsc) { + loader.setSort(sortCol.field,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(args) { + for (var i = args.from; i <= args.to; i++) { + grid.removeRow(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(); + }) + </script> + </body> +</html> diff --git a/examples/example7-events.html b/examples/example7-events.html index 3a325a3..3660cc2 100644 --- a/examples/example7-events.html +++ b/examples/example7-events.html @@ -1,159 +1,159 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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;
- }
-
-
- #contextMenu
- {
- background: #e1efc7;
- border: 1px solid gray;
- padding: 2px;
- display: inline-block;
- min-width: 100px;
- -moz-box-shadow: 2px 2px 2px silver;
- -webkit-box-shadow: 2px 2px 2px silver;
- z-index: 99999;
- }
-
- #contextMenu li
- {
- padding: 4px;
- list-style: none;
- cursor: pointer;
- background: url("../images/arrow_right_peppermint.png") no-repeat center left;
- padding-left: 14px;
- }
-
- #contextMenu li:hover
- {
- background-color: white;
- }
- </style>
- </head>
- <body>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <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>handling events from the grid:</li>
- <li>Right-click the row to open the context menu</li>
- <li>Click the priority cell to toggle values</li>
- </ul>
-
- </td>
- </tr>
- </table>
-
-
-
-
- <ul id="contextMenu" style="display:none;position:absolute">
- <b>Set priority:</b>
- <li data="Low">Low</li>
- <li data="Medium">Medium</li>
- <li data="High">High</li>
- </ul>
-
-
-
- <script>
- var grid;
- var data = [];
-
- var columns = [
- {id:"title", name:"Title", field:"title", width:200, cssClass:"cell-title", editor:TextCellEditor},
- {id:"priority", name:"Priority", field:"priority", width:80, unselectable:true, resizable:false}
- ];
-
- var options = {
- editable: true,
- enableAddRow: false,
- enableCellNavigation: true,
- asyncEditorLoading: false,
- rowHeight: 30
- };
-
-
-
-
- $(function()
- {
- for (var i=0; i<100; i++) {
- var d = (data[i] = {});
-
- d["title"] = "Task " + i;
- d["priority"] = "Medium";
- }
-
-
- grid = new SlickGrid($("#myGrid"), data, columns, options);
-
- grid.onContextMenu = function (e, row, cell)
- {
- $("#contextMenu")
- .data("row", row)
- .css("top", e.pageY)
- .css("left", e.pageX)
- .show();
-
- $("body").one("click", function() { $("#contextMenu").hide(); })
-
- return true;
- }
-
- grid.onClick = function (e, row, cell)
- {
- if (columns[cell].id == "priority")
- {
- var states = { "Low": "Medium", "Medium": "High", "High": "Low" };
-
- data[row].priority = states[data[row].priority];
- grid.updateRow(row);
-
- return true;
- }
- }
- })
-
-
- $("#contextMenu").click(function(e) {
- if (!$(e.target).is("li"))
- return;
-
- var row = $(this).data("row");
- data[row].priority = $(e.target).attr("data");
- grid.updateRow(row);
- });
-
-
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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; + } + + + #contextMenu + { + background: #e1efc7; + border: 1px solid gray; + padding: 2px; + display: inline-block; + min-width: 100px; + -moz-box-shadow: 2px 2px 2px silver; + -webkit-box-shadow: 2px 2px 2px silver; + z-index: 99999; + } + + #contextMenu li + { + padding: 4px; + list-style: none; + cursor: pointer; + background: url("../images/arrow_right_peppermint.png") no-repeat center left; + padding-left: 14px; + } + + #contextMenu li:hover + { + background-color: white; + } + </style> + </head> + <body> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <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>handling events from the grid:</li> + <li>Right-click the row to open the context menu</li> + <li>Click the priority cell to toggle values</li> + </ul> + + </td> + </tr> + </table> + + + + + <ul id="contextMenu" style="display:none;position:absolute"> + <b>Set priority:</b> + <li data="Low">Low</li> + <li data="Medium">Medium</li> + <li data="High">High</li> + </ul> + + + + <script> + var grid; + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", width:200, cssClass:"cell-title", editor:TextCellEditor}, + {id:"priority", name:"Priority", field:"priority", width:80, unselectable:true, resizable:false} + ]; + + var options = { + editable: true, + enableAddRow: false, + enableCellNavigation: true, + asyncEditorLoading: false, + rowHeight: 30 + }; + + + + + $(function() + { + for (var i=0; i<100; i++) { + var d = (data[i] = {}); + + d["title"] = "Task " + i; + d["priority"] = "Medium"; + } + + + grid = new SlickGrid($("#myGrid"), data, columns, options); + + grid.onContextMenu = function (e, row, cell) + { + $("#contextMenu") + .data("row", row) + .css("top", e.pageY) + .css("left", e.pageX) + .show(); + + $("body").one("click", function() { $("#contextMenu").hide(); }) + + return true; + } + + grid.onClick = function (e, row, cell) + { + if (columns[cell].id == "priority") + { + var states = { "Low": "Medium", "Medium": "High", "High": "Low" }; + + data[row].priority = states[data[row].priority]; + grid.updateRow(row); + + return true; + } + } + }) + + + $("#contextMenu").click(function(e) { + if (!$(e.target).is("li")) + return; + + var row = $(this).data("row"); + data[row].priority = $(e.target).attr("data"); + grid.updateRow(row); + }); + + + + </script> + + </body> +</html> diff --git a/examples/example8-alternative-display.html b/examples/example8-alternative-display.html index 3a5cfe5..fdd44dd 100644 --- a/examples/example8-alternative-display.html +++ b/examples/example8-alternative-display.html @@ -1,178 +1,178 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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>
- .r .c {
- background: white!important;
- border-color: transparent!important;
- }
-
- /* alternating offsets */
- .r .cell-inner {
- margin-right: 60px;
- }
-
- .r[row$="1"] .cell-inner, .r[row$="3"] .cell-inner, .r[row$="5"] .cell-inner,
- .r[row$="7"] .cell-inner, .r[row$="9"] .cell-inner {
- margin-left: 60px;
- margin-right: 0px;
- }
-
- .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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <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>
- // 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
- };
-
- 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 SlickGrid($("#myGrid"), data, columns, options);
- })
-
- </script>
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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> + .r .c { + background: white!important; + border-color: transparent!important; + } + + /* alternating offsets */ + .r .cell-inner { + margin-right: 60px; + } + + .r[row$="1"] .cell-inner, .r[row$="3"] .cell-inner, .r[row$="5"] .cell-inner, + .r[row$="7"] .cell-inner, .r[row$="9"] .cell-inner { + margin-left: 60px; + margin-right: 0px; + } + + .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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <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> + // 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 + }; + + 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 SlickGrid($("#myGrid"), data, columns, options); + }) + + </script> + </body> +</html> diff --git a/examples/example9-row-reordering.html b/examples/example9-row-reordering.html index 8c45adc..a245f94 100644 --- a/examples/example9-row-reordering.html +++ b/examples/example9-row-reordering.html @@ -1,166 +1,166 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid example</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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;
- }
- .cell-reorder {
- cursor: move;
- background-image: url("../images/bullet_blue.png");
- background-repeat: no-repeat;
- background-position: center center;
- }
- </style>
- </head>
- <body>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <div style="width:600px;float:left;">
- <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" style="width:320px;margin-left:650px;">
- <b>Tools:</b>
- <hr/>
- <div style="padding:6px;">
- <br/><br/>
- <button id="btnSelectRows" onclick="grid.setSelectedRows([0,1,2])">Select first 3 rows</button>
- </div>
- </div>
-
-
- <script>
- var grid;
-
- var data = [];
-
- var columns = [{
- id: "#",
- name: "",
- width: 40,
- behavior: "move",
- unselectable: true,
- sortable: false,
- resizable: false,
- cssClass: "cell-reorder"
- }, {
- id: "name",
- name: "Name",
- field: "name",
- width: 500,
- cssClass: "cell-title",
- editor: TextCellEditor,
- validator: requiredFieldValidator
- }, {
- id: "complete",
- name: "Complete",
- sortable: false,
- width: 60,
- cssClass: "cell-effort-driven",
- field: "complete",
- cannotTriggerInsert: true,
- formatter: BoolCellFormatter,
- editor: YesNoCheckboxCellEditor
- }];
-
- var options = {
- editable: true,
- enableAddRow: true,
- enableCellNavigation: true,
- forceFitColumns: true
- };
-
- 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 SlickGrid($("#myGrid"), data, columns, options);
-
- grid.onAddNewRow = function addItem(columnDef,value) {
- var item = {name:"New task", complete: false};
- item[columnDef.field] = value;
- data.push(item);
- grid.removeRows([data.length-1]);
- grid.updateRowCount();
- grid.render();
- }
-
- grid.onBeforeMoveRows = function(rows,insertBefore) {
- for (var i=0; i<rows.length; i++) {
- // no point in moving before or after itself
- if (rows[i] == insertBefore || rows[i] == insertBefore - 1) return false;
- }
-
- return true;
- }
-
- grid.onMoveRows = function(rows,insertBefore) {
- var extractedRows = [], left, right;
- 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.setData(data);
- grid.setSelectedRows(selectedRows);
- grid.render();
- }
- })
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid example</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.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; + } + .cell-reorder { + cursor: move; + background-image: url("../images/bullet_blue.png"); + background-repeat: no-repeat; + background-position: center center; + } + </style> + </head> + <body> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <div style="width:600px;float:left;"> + <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" style="width:320px;margin-left:650px;"> + <b>Tools:</b> + <hr/> + <div style="padding:6px;"> + <br/><br/> + <button id="btnSelectRows" onclick="grid.setSelectedRows([0,1,2])">Select first 3 rows</button> + </div> + </div> + + + <script> + var grid; + + var data = []; + + var columns = [{ + id: "#", + name: "", + width: 40, + behavior: "move", + unselectable: true, + sortable: false, + resizable: false, + cssClass: "cell-reorder" + }, { + id: "name", + name: "Name", + field: "name", + width: 500, + cssClass: "cell-title", + editor: TextCellEditor, + validator: requiredFieldValidator + }, { + id: "complete", + name: "Complete", + sortable: false, + width: 60, + cssClass: "cell-effort-driven", + field: "complete", + cannotTriggerInsert: true, + formatter: BoolCellFormatter, + editor: YesNoCheckboxCellEditor + }]; + + var options = { + editable: true, + enableAddRow: true, + enableCellNavigation: true, + forceFitColumns: true + }; + + 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 SlickGrid($("#myGrid"), data, columns, options); + + grid.onAddNewRow = function addItem(columnDef,value) { + var item = {name:"New task", complete: false}; + item[columnDef.field] = value; + data.push(item); + grid.removeRows([data.length-1]); + grid.updateRowCount(); + grid.render(); + } + + grid.onBeforeMoveRows = function(rows,insertBefore) { + for (var i=0; i<rows.length; i++) { + // no point in moving before or after itself + if (rows[i] == insertBefore || rows[i] == insertBefore - 1) return false; + } + + return true; + } + + grid.onMoveRows = function(rows,insertBefore) { + var extractedRows = [], left, right; + 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.setData(data); + grid.setSelectedRows(selectedRows); + grid.render(); + } + }) + + </script> + + </body> +</html> diff --git a/examples/examples.css b/examples/examples.css index 089ba65..8d46143 100644 --- a/examples/examples.css +++ b/examples/examples.css @@ -1,221 +1,221 @@ - * {
- font-family: arial;
- font-size: 8pt;
-}
-
-body {
- background: beige;
-}
-
-h2 {
- font-size: 10pt;
- border-bottom: 1px dotted gray;
-}
-
-ul {
- margin-left: 0px;
- padding: 0px;
- cursor: default;
-}
-
-li {
- background: url("../images/arrow_right_spearmint.png") no-repeat center left;
- padding: 0px;
- padding-left: 14px;
- list-style: none;
- margin: 0px;
-}
-
-#myGrid {
- background: white;
- outline: 0px;
- border: 1px solid gray;
-}
-
-.grid-header {
- border: 1px solid gray;
- border-bottom: 0px;
- border-top: 0px;
- 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;
- margin-left: 6px;
-}
-
-.grid-header #txtSearch {
- margin: 0px 4px 0px 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;
-}
-
-
-/* Individual cell styles */
-.grid-canvas .r .c.task-name {
- font-weight: bold;
- text-align: right;
-}
-
-
-.grid-canvas .r .c.task-percent {
- text-align: right;
-}
-
-
-.grid-canvas .r .c.cell-move-handle {
- font-weight: bold;
- text-align: right;
- border-right-style: solid;
- border-right-color: gray;
-
- background: #efefef;
- cursor: move;
-}
-
-.grid-canvas .r .cell-move-handle:hover {
- background: #b6b9bd;
-}
-
-.grid-canvas .r.selected .cell-move-handle {
- background: #D5DC8D;
-}
-
-.grid-canvas .r .cell-actions {
- text-align: left;
-}
-
-.percent-complete-bar {
- display: inline-block;
- height: 6px;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
-}
-
-
-/* TextCellEditor, DateCellEditor */
-
-.grid-canvas .r .c input.editor-text {
- width: 100%;
- height: 100%;
- border: 0px;
- margin: 0px;
- background: transparent;
- outline: 0px;
- padding: 0px;
- padding-top: 3px;
-}
-
-.grid-canvas .r .c .ui-datepicker-trigger {
- margin-top: 2px;
- padding: 0px;
- vertical-align: top;
-}
-
-/* PercentCompleteCellEditor */
-
-.grid-canvas .r .c input.editor-percentcomplete {
- width: 100%;
- height: 100%;
- border: 0px;
- margin: 0px;
- background: transparent;
- outline: 0px;
- padding: 0px;
- padding-top: 3px;
- float: left;
-}
-
-.grid-canvas .r .c .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;
-}
-
-.grid-canvas .r .c .editor-percentcomplete-helper {
- border: 1px solid gray;
- position: absolute;
- top: -2px;
- left: -9px;
- background: url("../images/editor-helper-bg.gif") no-repeat top left;
- padding-left: 9px;
- border: 0px;
- width: 120px;
- height: 140px;
- display: none;
- overflow: visible;
-}
-
-
-.grid-canvas .r .c .editor-percentcomplete-wrapper {
- background:beige;
- padding:8px;
- padding-top:20px;
- padding-bottom:20px;
- width:100%;
- height:98px;
- border:1px solid gray;
- border-left:0px;
-}
-
-.grid-canvas .r .c .editor-percentcomplete-buttons {
- float: right;
-}
-
-.grid-canvas .r .c .editor-percentcomplete-buttons button {
- width: 80px;
-}
-
-
-.grid-canvas .r .c .editor-percentcomplete-slider {
- float: left;
-}
-
-.grid-canvas .r .c .editor-percentcomplete-picker:hover .editor-percentcomplete-helper {
- display: block;
-}
-
-.grid-canvas .r .c .editor-percentcomplete-helper:hover {
- display: block;
-}
-
-
-
-/* YesNoSelectCellEditor */
-
-.grid-canvas .r .c select.editor-yesno {
- width: 100%;
- margin: 0px;
- vertical-align: middle;
-}
-
-/* YesNoCheckboxCellEditor */
-.grid-canvas .r .c input.editor-checkbox {
- margin: 0px;
- height: 100%;
- padding: 0px;
- border: 0px;
-}
-
-
+ * { + font-family: arial; + font-size: 8pt; +} + +body { + background: beige; +} + +h2 { + font-size: 10pt; + border-bottom: 1px dotted gray; +} + +ul { + margin-left: 0px; + padding: 0px; + cursor: default; +} + +li { + background: url("../images/arrow_right_spearmint.png") no-repeat center left; + padding: 0px; + padding-left: 14px; + list-style: none; + margin: 0px; +} + +#myGrid { + background: white; + outline: 0px; + border: 1px solid gray; +} + +.grid-header { + border: 1px solid gray; + border-bottom: 0px; + border-top: 0px; + 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; + margin-left: 6px; +} + +.grid-header #txtSearch { + margin: 0px 4px 0px 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; +} + + +/* Individual cell styles */ +.grid-canvas .r .c.task-name { + font-weight: bold; + text-align: right; +} + + +.grid-canvas .r .c.task-percent { + text-align: right; +} + + +.grid-canvas .r .c.cell-move-handle { + font-weight: bold; + text-align: right; + border-right-style: solid; + border-right-color: gray; + + background: #efefef; + cursor: move; +} + +.grid-canvas .r .cell-move-handle:hover { + background: #b6b9bd; +} + +.grid-canvas .r.selected .cell-move-handle { + background: #D5DC8D; +} + +.grid-canvas .r .cell-actions { + text-align: left; +} + +.percent-complete-bar { + display: inline-block; + height: 6px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + + +/* TextCellEditor, DateCellEditor */ + +.grid-canvas .r .c input.editor-text { + width: 100%; + height: 100%; + border: 0px; + margin: 0px; + background: transparent; + outline: 0px; + padding: 0px; + padding-top: 3px; +} + +.grid-canvas .r .c .ui-datepicker-trigger { + margin-top: 2px; + padding: 0px; + vertical-align: top; +} + +/* PercentCompleteCellEditor */ + +.grid-canvas .r .c input.editor-percentcomplete { + width: 100%; + height: 100%; + border: 0px; + margin: 0px; + background: transparent; + outline: 0px; + padding: 0px; + padding-top: 3px; + float: left; +} + +.grid-canvas .r .c .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; +} + +.grid-canvas .r .c .editor-percentcomplete-helper { + border: 1px solid gray; + position: absolute; + top: -2px; + left: -9px; + background: url("../images/editor-helper-bg.gif") no-repeat top left; + padding-left: 9px; + border: 0px; + width: 120px; + height: 140px; + display: none; + overflow: visible; +} + + +.grid-canvas .r .c .editor-percentcomplete-wrapper { + background:beige; + padding:8px; + padding-top:20px; + padding-bottom:20px; + width:100%; + height:98px; + border:1px solid gray; + border-left:0px; +} + +.grid-canvas .r .c .editor-percentcomplete-buttons { + float: right; +} + +.grid-canvas .r .c .editor-percentcomplete-buttons button { + width: 80px; +} + + +.grid-canvas .r .c .editor-percentcomplete-slider { + float: left; +} + +.grid-canvas .r .c .editor-percentcomplete-picker:hover .editor-percentcomplete-helper { + display: block; +} + +.grid-canvas .r .c .editor-percentcomplete-helper:hover { + display: block; +} + + + +/* YesNoSelectCellEditor */ + +.grid-canvas .r .c select.editor-yesno { + width: 100%; + margin: 0px; + vertical-align: middle; +} + +/* YesNoCheckboxCellEditor */ +.grid-canvas .r .c input.editor-checkbox { + margin: 0px; + height: 100%; + padding: 0px; + border: 0px; +} + + diff --git a/examples/grid.html b/examples/grid.html index 6cbd746..4bf45d2 100644 --- a/examples/grid.html +++ b/examples/grid.html @@ -1,361 +1,361 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>Spreadsheet prototype</title>
- <link rel="stylesheet" href="simpledropdown.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="../css/custom-theme/jquery-ui-1.7.2.custom.css" type="text/css" media="screen" charset="utf-8" />
- <style>
- *
- {
- font-family: arial;
- font-size: 8pt;
- }
-
- hr {
- height: 1px;
- border: 0;
- background: silver;
- }
-
- #grid {
- background: white;
- outline: 0px;
- }
-
- ul {
- padding: 0px;
- cursor: default;
- }
-
- li {
- background: url("../images/arrow_right_spearmint.png") no-repeat 0px center;
- padding-left: 12px;
- list-style: none;
- margin: 0px;
- }
-
- li:hover {
- background: url("../images/arrow_right_peppermint.png") no-repeat 0px center;
- }
-
- .listview-header {
- height: 24px;
- line-height: 24px;
- width: 100%;
- background: url("../images/listview.gif") no-repeat top left;
- }
-
- .listview-header .r {
- float:right;
- text-align: right;
- width: 50%;
- height: 24px;
- background: white url("../images/listview.gif") no-repeat top right;
- line-height: 24px;
- }
-
- .listview-header input {
- border: 1px solid silver;
- }
- .listview-footer {
- height: 24px;
- line-height: 24px;
- width: 100%;
- background: url("../images/listview.gif") no-repeat left -24px;
- }
-
- .listview-footer .r {
- float:right;
- text-align: right;
- width: 50%;
- height: 24px;
- background: white url("../images/listview.gif") no-repeat right -24px;
- line-height: 24px;
- }
- </style>
- </head>
- <body>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
-
-
- <div class="listview-header">
- <div style="float:left;padding-left:14px;">
-
- <span class="simple-dropdown">
- <em>Reload grid</em>
- <div>
- <a href="javascript:reinitGrid(10)">Load 10 rows</a>
- <a href="javascript:reinitGrid(100)">Load 100 rows</a>
- <a href="javascript:reinitGrid(500)">Load 500 rows</a>
- <a href="javascript:reinitGrid(1000)">Load 1'000 rows</a>
- <a href="javascript:reinitGrid(5000)">Load 5'000 rows</a>
- <a href="javascript:reinitGrid(50000)">Load 50'000 rows</a>
- </div>
- </span>
-
- <span class="simple-dropdown">
- <em>Change column styles</em>
- <div>
- <a href="javascript:updateModel('%', {formatter:PercentCompleteCellFormatter, cssClass:'task-percent'})">Render <b>% Complete</b> as text</a>
- <a href="javascript:updateModel('%', {formatter:GraphicalPercentCompleteCellFormatter, cssClass:null})">Render <b>% Complete</b> as graph</a>
- </div>
- </span>
-
- <span class="simple-dropdown">
- <em>Select rows</em>
- <div>
- <a href="javascript:grid.setSelectedRows([0,1,2,3,4])">0 - 5</a>
- </div>
- </span>
-
- <span class="simple-dropdown">
- <em>Options</em>
- <div>
- <a href="javascript:grid.setOptions({enableAddRow:true})">enableAddRow = true</a>
- <a href="javascript:grid.setOptions({enableAddRow:false})">enableAddRow = false</a>
- <hr/>
- <a href="javascript:grid.setOptions({editable:true})">editable = true</a>
- <a href="javascript:grid.setOptions({editable:false})">editable = false</a>
- <hr/>
- <a href="javascript:grid.setOptions({editOnDoubleClick:true})">editOnDoubleClick = true</a>
- <a href="javascript:grid.setOptions({editOnDoubleClick:false})">editOnDoubleClick = false</a>
- <hr/>
- <a href="javascript:grid.setOptions({enableCellNavigation:true})">enableCellNavigation = true</a>
- <a href="javascript:grid.setOptions({enableCellNavigation:false})">enableCellNavigation = false</a>
- <hr/>
- <a href="javascript:grid.setOptions({asyncEditorLoading:true})">asyncEditorLoading = true</a>
- <a href="javascript:grid.setOptions({asyncEditorLoading:false})">asyncEditorLoading = false</a>
- </div>
- </span>
-
- <span class="simple-dropdown">
- <em>Debug</em>
- <div>
- <a href="javascript:grid.benchmarkFn('removeAllRows')">benchmark removeAllRows</a>
- <a href="javascript:grid.benchmarkFn('render')">benchmark render</a>
- <a href="javascript:grid.benchmarkFn('benchmark_render_200')">benchmark render 200</a>
- <hr/>
- <a href="javascript:grid.stressTest()">stress test (infinite render/clear cycle)</a>
- <hr/>
- <a href="javascript:grid.debug()">internal state</a>
- </div>
- </span>
-
- <span class="simple-dropdown">
- <em style="padding-left:16px;background:url('../images/info.gif') no-repeat center left;">Quick info</em>
- <div style="max-width:600px">
-
- This page demonstrate the use of a SlickGrid control.
- <br><br>
- <b>Key features:</b>
- <ul>
- <li>Virtual rendering/scrolling (hundreds of thousands of rows)</li>
- <li>Extremely fast rendering speed</li>
- <li>Support for a Model data source</li>
- <li>Support for Ajax-loaded data</li>
- <li>Multiple row selection</li>
- <li>Edit and add new rows</li>
- <li>Keyboard navigation for cell selection</li>
- <li>Custom renderers for cells with conditional formatting</li>
- <li>Formatters adaptive to column width</li>
- <li>Custom editors for cells</li>
- <li>Resizable and reorderable columns</li>
- <li>Highly customizable & configurable</li>
- <li>Built-in validators</li>
- <li>Callbacks for events</li>
- <li>Much much more...</li>
- </ul>
-
- </div>
- </span>
-
- </div>
-
- <div class="r">
- <div style="padding-right:14px;">
- <label>Filter:</label> <input type=text>
-
- </div>
- </div>
- </div>
- <div id="myGrid" style="width:100%;height:500px;"></div>
- <div class="listview-footer">
- <div style="float:left;padding-left:14px;">
- </div>
-
- <div class="r">
- <div style="padding-right:14px;">
- </div>
- </div>
- </div>
-
- <br/>
-
- <script>
-
- $(".simple-dropdown a").click(function(e) {
- var dd = $(this).closest(".simple-dropdown > div");
-
- dd.css("display", "none");
- window.setTimeout(function () { dd.css("display", ""); }, 10);
- });
-
-
- function nonEmptyValidator(value) {
- if (value == null || value == undefined || !value.length)
- return {valid:false, msg:"This is a required field"};
- else
- return {valid:true, msg:null};
- }
-
- function setTaskResources(resourcesNamesArray, cellInfo, dataContext)
- {
- dataContext["resources"] = resourcesNamesArray.length > 0 ? resourcesNamesArray.concat() : null;
- }
-
- var data = [];
- var grid;
- var model = [
- {id:"#", name:"#", cssClass:"cell-move-handle", width:60, resizable:false, unselectable:true, formatter:SelectorCellFormatter},
- {id:"title", name:"Title", field:"title", formatter:TaskNameFormatter, width:300, editor:TextCellEditor, validator:nonEmptyValidator},
- {id:"star", name:"<img src='../images/bullet_star.png' align='absmiddle'>", field:"starred", formatter:StarFormatter, editor:StarCellEditor, width:16, resizable:false, cannotTriggerInsert:true},
- {id:"duration", name:"Duration", field:"duration", width:80, editor:TextCellEditor},
- {id:"%", name:"% Complete", field:"percentComplete", formatter:GraphicalPercentCompleteCellFormatter, width:60, editor:PercentCompleteCellEditor},
- {id:"start", name:"Start", field:"start", width:100, editor:DateCellEditor},
- {id:"finish", name:"Finish", field:"finish", width:100, editor:DateCellEditor},
- {id:"resources", name:"Resources <img src='../images/help.png' align='absmiddle' title='This column has an adaptive formatter. Resize to a smaller size to see alternative data representation.'>", formatter:ResourcesFormatter, rerenderOnResize:true, width:200, editor:ResourcesCellEditor, setValueHandler:setTaskResources, cannotTriggerInsert:true, minWidth:16, maxWidth:200},
- {id:"preds", name:"Predecessors", width:100, editor:TextCellEditor, cannotTriggerInsert:true}
- ,
- {id:"e", name:"Deliverable", field:4, formatter:YesNoCellFormatter, width:60, editor:YesNoSelectCellEditor, cannotTriggerInsert:true},
- {id:"f", name:"Effort-driven", field:5, formatter:YesNoCellFormatter, width:50, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true}
- ];
-
-
-
- function updateModel(id, args) {
- if (GlobalEditorLock.isEditing() && !GlobalEditorLock.commitCurrentEdit()) return;
-
- for (var i=0; i<model.length; i++)
- {
- if (model[i].id == id) {
- model[i] = $.extend(model[i], args);
- grid.removeAllRows();
- grid.render();
- return;
- }
- }
- }
-
-
- $(function()
- {
- $("#myGrid")[0].unselectable = true;
-
- reinitGrid(10);
- })
-
-
- function enterEdit() {
-
- GlobalEditorLock.enterEditMode({
- commitCurrentEdit: function() { alert('validation error'); return false },
- cancelCurrentEdit: function() {}
- });
-
- }
-
-
- function doSomething() {
-
- if (GlobalEditorLock.isEditing() && !GlobalEditorLock.commitCurrentEdit()) return;
-
-/*
- if (GlobalEditorLock.isEditing())
- {
- if (confirm("Cancel current edit?"))
- GlobalEditorLock.cancelCurrentEdit();
- else
- return;
- }
-*/
-
- alert("Editor lock acquired")
-
- }
-
-
- function reinitGrid(loadedRows,totalRows) {
-
- data = [];
-
- for (var i = 0; i < loadedRows; i++)
- {
- var d = (data[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["indent"] = i % 5;
- d["resources"] = (i % 7 == 0) ? ["Boris The Blade", "Bullet Tooth"] : (i % 11 == 0 ? ["Bricktop"] : null);
- }
-
-
- if (grid) grid.destroy();
-
-
- grid = new SlickGrid($("#myGrid"), data, model, {});
-
- grid.onValidationError = function(elem, validationResults, row, cell, cellInfo) {
- console.warn(validationResults.msg);
- }
-
- grid.onAddNewRow = function(cellInfo, value) {
- var item = {title:"New task", indent:0, duration:"1 day", percentComplete:0, start:"01/01/2009", finish:"01/01/2009"};
-
- item[cellInfo.field] = value;
-
- data[data.length] = item;
-
- grid.updateRowCount();
- grid.render();
- grid.updateRow(data.length-1);
- }
-
- grid.onClick = function(e, row, cell) {
- // toggle expand/collapse icon
- if (model[cell].id == "title" && $(e.target).is("img"))
- {
- $(e.target).attr("src", $(e.target).attr("src") != "../images/collapse.gif" ? "../images/collapse.gif" : "../images/expand.gif");
- return true;
- }
-
- if (model[cell].id == "#")
- {
- grid.setSelectedRows([row]);
- }
-
- // pass the event through
- return false;
- }
- }
-
-
- </script>
-
-
- <button onclick="doSomething()">Test GlobalEditorLock lock</button>
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>Spreadsheet prototype</title> + <link rel="stylesheet" href="simpledropdown.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="../css/custom-theme/jquery-ui-1.7.2.custom.css" type="text/css" media="screen" charset="utf-8" /> + <style> + * + { + font-family: arial; + font-size: 8pt; + } + + hr { + height: 1px; + border: 0; + background: silver; + } + + #grid { + background: white; + outline: 0px; + } + + ul { + padding: 0px; + cursor: default; + } + + li { + background: url("../images/arrow_right_spearmint.png") no-repeat 0px center; + padding-left: 12px; + list-style: none; + margin: 0px; + } + + li:hover { + background: url("../images/arrow_right_peppermint.png") no-repeat 0px center; + } + + .listview-header { + height: 24px; + line-height: 24px; + width: 100%; + background: url("../images/listview.gif") no-repeat top left; + } + + .listview-header .r { + float:right; + text-align: right; + width: 50%; + height: 24px; + background: white url("../images/listview.gif") no-repeat top right; + line-height: 24px; + } + + .listview-header input { + border: 1px solid silver; + } + .listview-footer { + height: 24px; + line-height: 24px; + width: 100%; + background: url("../images/listview.gif") no-repeat left -24px; + } + + .listview-footer .r { + float:right; + text-align: right; + width: 50%; + height: 24px; + background: white url("../images/listview.gif") no-repeat right -24px; + line-height: 24px; + } + </style> + </head> + <body> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + + + <div class="listview-header"> + <div style="float:left;padding-left:14px;"> + + <span class="simple-dropdown"> + <em>Reload grid</em> + <div> + <a href="javascript:reinitGrid(10)">Load 10 rows</a> + <a href="javascript:reinitGrid(100)">Load 100 rows</a> + <a href="javascript:reinitGrid(500)">Load 500 rows</a> + <a href="javascript:reinitGrid(1000)">Load 1'000 rows</a> + <a href="javascript:reinitGrid(5000)">Load 5'000 rows</a> + <a href="javascript:reinitGrid(50000)">Load 50'000 rows</a> + </div> + </span> + + <span class="simple-dropdown"> + <em>Change column styles</em> + <div> + <a href="javascript:updateModel('%', {formatter:PercentCompleteCellFormatter, cssClass:'task-percent'})">Render <b>% Complete</b> as text</a> + <a href="javascript:updateModel('%', {formatter:GraphicalPercentCompleteCellFormatter, cssClass:null})">Render <b>% Complete</b> as graph</a> + </div> + </span> + + <span class="simple-dropdown"> + <em>Select rows</em> + <div> + <a href="javascript:grid.setSelectedRows([0,1,2,3,4])">0 - 5</a> + </div> + </span> + + <span class="simple-dropdown"> + <em>Options</em> + <div> + <a href="javascript:grid.setOptions({enableAddRow:true})">enableAddRow = true</a> + <a href="javascript:grid.setOptions({enableAddRow:false})">enableAddRow = false</a> + <hr/> + <a href="javascript:grid.setOptions({editable:true})">editable = true</a> + <a href="javascript:grid.setOptions({editable:false})">editable = false</a> + <hr/> + <a href="javascript:grid.setOptions({editOnDoubleClick:true})">editOnDoubleClick = true</a> + <a href="javascript:grid.setOptions({editOnDoubleClick:false})">editOnDoubleClick = false</a> + <hr/> + <a href="javascript:grid.setOptions({enableCellNavigation:true})">enableCellNavigation = true</a> + <a href="javascript:grid.setOptions({enableCellNavigation:false})">enableCellNavigation = false</a> + <hr/> + <a href="javascript:grid.setOptions({asyncEditorLoading:true})">asyncEditorLoading = true</a> + <a href="javascript:grid.setOptions({asyncEditorLoading:false})">asyncEditorLoading = false</a> + </div> + </span> + + <span class="simple-dropdown"> + <em>Debug</em> + <div> + <a href="javascript:grid.benchmarkFn('removeAllRows')">benchmark removeAllRows</a> + <a href="javascript:grid.benchmarkFn('render')">benchmark render</a> + <a href="javascript:grid.benchmarkFn('benchmark_render_200')">benchmark render 200</a> + <hr/> + <a href="javascript:grid.stressTest()">stress test (infinite render/clear cycle)</a> + <hr/> + <a href="javascript:grid.debug()">internal state</a> + </div> + </span> + + <span class="simple-dropdown"> + <em style="padding-left:16px;background:url('../images/info.gif') no-repeat center left;">Quick info</em> + <div style="max-width:600px"> + + This page demonstrate the use of a SlickGrid control. + <br><br> + <b>Key features:</b> + <ul> + <li>Virtual rendering/scrolling (hundreds of thousands of rows)</li> + <li>Extremely fast rendering speed</li> + <li>Support for a Model data source</li> + <li>Support for Ajax-loaded data</li> + <li>Multiple row selection</li> + <li>Edit and add new rows</li> + <li>Keyboard navigation for cell selection</li> + <li>Custom renderers for cells with conditional formatting</li> + <li>Formatters adaptive to column width</li> + <li>Custom editors for cells</li> + <li>Resizable and reorderable columns</li> + <li>Highly customizable & configurable</li> + <li>Built-in validators</li> + <li>Callbacks for events</li> + <li>Much much more...</li> + </ul> + + </div> + </span> + + </div> + + <div class="r"> + <div style="padding-right:14px;"> + <label>Filter:</label> <input type=text> + + </div> + </div> + </div> + <div id="myGrid" style="width:100%;height:500px;"></div> + <div class="listview-footer"> + <div style="float:left;padding-left:14px;"> + </div> + + <div class="r"> + <div style="padding-right:14px;"> + </div> + </div> + </div> + + <br/> + + <script> + + $(".simple-dropdown a").click(function(e) { + var dd = $(this).closest(".simple-dropdown > div"); + + dd.css("display", "none"); + window.setTimeout(function () { dd.css("display", ""); }, 10); + }); + + + function nonEmptyValidator(value) { + if (value == null || value == undefined || !value.length) + return {valid:false, msg:"This is a required field"}; + else + return {valid:true, msg:null}; + } + + function setTaskResources(resourcesNamesArray, cellInfo, dataContext) + { + dataContext["resources"] = resourcesNamesArray.length > 0 ? resourcesNamesArray.concat() : null; + } + + var data = []; + var grid; + var model = [ + {id:"#", name:"#", cssClass:"cell-move-handle", width:60, resizable:false, unselectable:true, formatter:SelectorCellFormatter}, + {id:"title", name:"Title", field:"title", formatter:TaskNameFormatter, width:300, editor:TextCellEditor, validator:nonEmptyValidator}, + {id:"star", name:"<img src='../images/bullet_star.png' align='absmiddle'>", field:"starred", formatter:StarFormatter, editor:StarCellEditor, width:16, resizable:false, cannotTriggerInsert:true}, + {id:"duration", name:"Duration", field:"duration", width:80, editor:TextCellEditor}, + {id:"%", name:"% Complete", field:"percentComplete", formatter:GraphicalPercentCompleteCellFormatter, width:60, editor:PercentCompleteCellEditor}, + {id:"start", name:"Start", field:"start", width:100, editor:DateCellEditor}, + {id:"finish", name:"Finish", field:"finish", width:100, editor:DateCellEditor}, + {id:"resources", name:"Resources <img src='../images/help.png' align='absmiddle' title='This column has an adaptive formatter. Resize to a smaller size to see alternative data representation.'>", formatter:ResourcesFormatter, rerenderOnResize:true, width:200, editor:ResourcesCellEditor, setValueHandler:setTaskResources, cannotTriggerInsert:true, minWidth:16, maxWidth:200}, + {id:"preds", name:"Predecessors", width:100, editor:TextCellEditor, cannotTriggerInsert:true} + , + {id:"e", name:"Deliverable", field:4, formatter:YesNoCellFormatter, width:60, editor:YesNoSelectCellEditor, cannotTriggerInsert:true}, + {id:"f", name:"Effort-driven", field:5, formatter:YesNoCellFormatter, width:50, editor:YesNoCheckboxCellEditor, cannotTriggerInsert:true} + ]; + + + + function updateModel(id, args) { + if (GlobalEditorLock.isEditing() && !GlobalEditorLock.commitCurrentEdit()) return; + + for (var i=0; i<model.length; i++) + { + if (model[i].id == id) { + model[i] = $.extend(model[i], args); + grid.removeAllRows(); + grid.render(); + return; + } + } + } + + + $(function() + { + $("#myGrid")[0].unselectable = true; + + reinitGrid(10); + }) + + + function enterEdit() { + + GlobalEditorLock.enterEditMode({ + commitCurrentEdit: function() { alert('validation error'); return false }, + cancelCurrentEdit: function() {} + }); + + } + + + function doSomething() { + + if (GlobalEditorLock.isEditing() && !GlobalEditorLock.commitCurrentEdit()) return; + +/* + if (GlobalEditorLock.isEditing()) + { + if (confirm("Cancel current edit?")) + GlobalEditorLock.cancelCurrentEdit(); + else + return; + } +*/ + + alert("Editor lock acquired") + + } + + + function reinitGrid(loadedRows,totalRows) { + + data = []; + + for (var i = 0; i < loadedRows; i++) + { + var d = (data[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["indent"] = i % 5; + d["resources"] = (i % 7 == 0) ? ["Boris The Blade", "Bullet Tooth"] : (i % 11 == 0 ? ["Bricktop"] : null); + } + + + if (grid) grid.destroy(); + + + grid = new SlickGrid($("#myGrid"), data, model, {}); + + grid.onValidationError = function(elem, validationResults, row, cell, cellInfo) { + console.warn(validationResults.msg); + } + + grid.onAddNewRow = function(cellInfo, value) { + var item = {title:"New task", indent:0, duration:"1 day", percentComplete:0, start:"01/01/2009", finish:"01/01/2009"}; + + item[cellInfo.field] = value; + + data[data.length] = item; + + grid.updateRowCount(); + grid.render(); + grid.updateRow(data.length-1); + } + + grid.onClick = function(e, row, cell) { + // toggle expand/collapse icon + if (model[cell].id == "title" && $(e.target).is("img")) + { + $(e.target).attr("src", $(e.target).attr("src") != "../images/collapse.gif" ? "../images/collapse.gif" : "../images/expand.gif"); + return true; + } + + if (model[cell].id == "#") + { + grid.setSelectedRows([row]); + } + + // pass the event through + return false; + } + } + + + </script> + + + <button onclick="doSomething()">Test GlobalEditorLock lock</button> + </body> +</html> diff --git a/examples/simpledropdown.css b/examples/simpledropdown.css index 8553c99..ffd71f7 100644 --- a/examples/simpledropdown.css +++ b/examples/simpledropdown.css @@ -1,83 +1,83 @@ -.simple-dropdown {
- z-index: 100;
- overflow: visible;
- display: inline-block;
- line-height: normal;
-
- border: 1px solid silver;
- background: #eee url("images/down.gif") no-repeat center right;
- padding: 2px;
- padding-left: 8px;
- padding-right: 20px;
- margin: 2px;
-
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
-}
-
-.simple-dropdown > em {
- display: block;
-
- text-decoration: none;
- font-style: normal;
- cursor: default;
-}
-
-.simple-dropdown:hover {
- background-color: #777;
- color: white;
- border-color: gray;
-}
-
-.simple-dropdown:hover > div {
- -display: block;
- visibility: visible;
-
- opacity: 1;
- -webkit-transition: opacity 0.5s;
-}
-
-.simple-dropdown > div {
- z-index: 100;
- -display: none;
- visibility: hidden;
- position: absolute;
- margin-left: -9px;
- min-width: 120px;
- max-width: 200px;
-
- color: black;
- text-align: left;
- padding: 4px;
- background: #fafafa;
- border: 1px solid gray;
-
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- -moz-border-radius-topleft: 0px;
- -webkit-border-top-left-radius: 0px;
- -moz-box-shadow: 2px 2px 2px silver;
- -webkit-box-shadow: 2px 2px 2px silver;
-
- opacity: 0;
-}
-
-.simple-dropdown > div a {
- display: block;
- padding: 2px;
- outline: 0px;
- text-decoration: none;
- color: black;
- cursor: default;
- zbackground: #fafafa url("images/arrow_right_spearmint.png") no-repeat center left;
- padding-left: 12px;
- padding-right: 6px;
-}
-
-
-.simple-dropdown > div a:hover {
- background: skyblue url("images/arrow_right_peppermint.png") no-repeat center left;
-
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
+.simple-dropdown { + z-index: 100; + overflow: visible; + display: inline-block; + line-height: normal; + + border: 1px solid silver; + background: #eee url("images/down.gif") no-repeat center right; + padding: 2px; + padding-left: 8px; + padding-right: 20px; + margin: 2px; + + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +.simple-dropdown > em { + display: block; + + text-decoration: none; + font-style: normal; + cursor: default; +} + +.simple-dropdown:hover { + background-color: #777; + color: white; + border-color: gray; +} + +.simple-dropdown:hover > div { + -display: block; + visibility: visible; + + opacity: 1; + -webkit-transition: opacity 0.5s; +} + +.simple-dropdown > div { + z-index: 100; + -display: none; + visibility: hidden; + position: absolute; + margin-left: -9px; + min-width: 120px; + max-width: 200px; + + color: black; + text-align: left; + padding: 4px; + background: #fafafa; + border: 1px solid gray; + + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius-topleft: 0px; + -webkit-border-top-left-radius: 0px; + -moz-box-shadow: 2px 2px 2px silver; + -webkit-box-shadow: 2px 2px 2px silver; + + opacity: 0; +} + +.simple-dropdown > div a { + display: block; + padding: 2px; + outline: 0px; + text-decoration: none; + color: black; + cursor: default; + zbackground: #fafafa url("images/arrow_right_spearmint.png") no-repeat center left; + padding-left: 12px; + padding-right: 6px; +} + + +.simple-dropdown > div a:hover { + background: skyblue url("images/arrow_right_peppermint.png") no-repeat center left; + + -moz-border-radius: 3px; + -webkit-border-radius: 3px; }
\ No newline at end of file diff --git a/lib/jquery.event.drag.custom.js b/lib/jquery.event.drag.custom.js index 8b08c2d..cdfc9a4 100644 --- a/lib/jquery.event.drag.custom.js +++ b/lib/jquery.event.drag.custom.js @@ -1,141 +1,141 @@ -/*!
-jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
-Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
-*/
-;(function($){ // secure $ jQuery alias
-/*******************************************************************************************/
-// Created: 2008-06-04 | Updated: 2009-03-24
-/*******************************************************************************************/
-// Events: drag, dragstart, dragend
-/*******************************************************************************************/
-
-// jquery method
-$.fn.drag = function( fn1, fn2, fn3 ){
- if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args
- if ( fn3 ) this.bind('dragend', fn3 ); // 3 args
- return !fn1 ? this.trigger('drag') // 0 args
- : this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args
- };
-
-// local refs
-var $event = $.event, $special = $event.special,
-
-// special event configuration
-drag = $special.drag = {
- not: ':input', // don't begin to drag on event.targets that match this selector
- distance: 0, // distance dragged before dragstart
- which: 1, // mouse button pressed to start drag sequence
- dragging: false, // hold the active target element
- setup: function( data ){
- data = $.extend({
- distance: drag.distance,
- which: drag.which,
- not: drag.not
- }, data || {});
- data.distance = squared( data.distance ); // xІ + yІ = distanceІ
- $event.add( this, "mousedown", handler, data );
- if ( this.attachEvent ) this.attachEvent("ondragstart", dontStart ); // prevent image dragging in IE...
- },
- teardown: function(){
- $event.remove( this, "mousedown", handler );
- if ( this === drag.dragging ) drag.dragging = drag.proxy = false; // deactivate element
- selectable( this, true ); // enable text selection
- if ( this.detachEvent ) this.detachEvent("ondragstart", dontStart ); // prevent image dragging in IE...
- }
- };
-
-// prevent normal event binding...
-$special.dragstart = $special.dragend = { setup:function(){}, teardown:function(){} };
-
-// handle drag-releatd DOM events
-function handler ( event ){
- var elem = this, returned, data = event.data || {};
- // mousemove or mouseup
- if ( data.elem ){
- // update event properties...
- elem = event.dragTarget = data.elem; // drag source element
- event.dragProxy = drag.proxy || elem; // proxy element or source
- event.cursorOffsetX = data.pageX - data.left; // mousedown offset
- event.cursorOffsetY = data.pageY - data.top; // mousedown offset
- event.offsetX = event.pageX - event.cursorOffsetX; // element offset
- event.offsetY = event.pageY - event.cursorOffsetY; // element offset
- }
- // mousedown, check some initial props to avoid the switch statement
- else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) ||
- $( event.target ).is( data.not ) ) return;
- // handle various events
- switch ( event.type ){
- // mousedown, left click, event.target is not restricted, init dragging
- case 'mousedown':
- $.extend( data, $( elem ).offset(), {
- elem: elem, target: event.target,
- pageX: event.pageX, pageY: event.pageY
- }); // store some initial attributes
- returned = hijack(event, "beforedragstart", elem);
- if (returned === false)
- return true;
- $event.add( document, "mousemove mouseup", handler, data );
- selectable( elem, false ); // disable text selection
- drag.dragging = null; // pending state
- return false; // prevents text selection in safari
- // mousemove, check distance, start dragging
- case !drag.dragging && 'mousemove':
- if ( squared( event.pageX-data.pageX )
- + squared( event.pageY-data.pageY ) // xІ + yІ = distanceІ
- < data.distance ) break; // distance tolerance not reached
- event.target = data.target; // force target from "mousedown" event (fix distance issue)
- returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element
- if ( returned !== false ){ // "dragstart" not rejected
- drag.dragging = elem; // activate element
- drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy
- }
- // mousemove, dragging
- case 'mousemove':
- if ( drag.dragging ){
- returned = hijack( event, "drag", elem ); // trigger "drag"
- if ( $special.drop ){ // manage drop events
- $special.drop.allowed = ( returned !== false ); // prevent drop
- $special.drop.handler( event ); // "dropstart", "dropend"
- }
- if ( returned !== false ) break; // "drag" not rejected, stop
- event.type = "mouseup"; // helps "drop" handler behave
- }
- // mouseup, stop dragging
- case 'mouseup':
- if (drag.dragging === false) break;
- $event.remove( document, "mousemove mouseup", handler ); // remove page events
- if ( drag.dragging ){
- if ( $special.drop ) $special.drop.handler( event ); // "drop"
- hijack( event, "dragend", elem ); // trigger "dragend"
- }
- selectable( elem, true ); // enable text selection
- drag.dragging = drag.proxy = data.elem = false; // deactivate element
- break;
- }
- return true;
- };
-
-// set event type to custom value, and handle it
-function hijack ( event, type, elem ){
- event.type = type; // force the event type
- var result = $.event.handle.call( elem, event );
- return result===false ? false : result || event.result;
- };
-
-// return the value squared
-function squared ( value ){ return Math.pow( value, 2 ); };
-
-// suppress default dragstart IE events...
-function dontStart(){ return ( drag.dragging === false ); };
-
-// toggles text selection attributes
-function selectable ( elem, bool ){
- if ( !elem ) return; // maybe element was removed ?
- elem.unselectable = bool ? "off" : "on"; // IE
- elem.onselectstart = function(){ return bool; }; // IE
- //if ( document.selection && document.selection.empty ) document.selection.empty(); // IE
- if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF
- };
-
-/*******************************************************************************************/
+/*! +jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) +Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt +*/ +;(function($){ // secure $ jQuery alias +/*******************************************************************************************/ +// Created: 2008-06-04 | Updated: 2009-03-24 +/*******************************************************************************************/ +// Events: drag, dragstart, dragend +/*******************************************************************************************/ + +// jquery method +$.fn.drag = function( fn1, fn2, fn3 ){ + if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args + if ( fn3 ) this.bind('dragend', fn3 ); // 3 args + return !fn1 ? this.trigger('drag') // 0 args + : this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args + }; + +// local refs +var $event = $.event, $special = $event.special, + +// special event configuration +drag = $special.drag = { + not: ':input', // don't begin to drag on event.targets that match this selector + distance: 0, // distance dragged before dragstart + which: 1, // mouse button pressed to start drag sequence + dragging: false, // hold the active target element + setup: function( data ){ + data = $.extend({ + distance: drag.distance, + which: drag.which, + not: drag.not + }, data || {}); + data.distance = squared( data.distance ); // xІ + yІ = distanceІ + $event.add( this, "mousedown", handler, data ); + if ( this.attachEvent ) this.attachEvent("ondragstart", dontStart ); // prevent image dragging in IE... + }, + teardown: function(){ + $event.remove( this, "mousedown", handler ); + if ( this === drag.dragging ) drag.dragging = drag.proxy = false; // deactivate element + selectable( this, true ); // enable text selection + if ( this.detachEvent ) this.detachEvent("ondragstart", dontStart ); // prevent image dragging in IE... + } + }; + +// prevent normal event binding... +$special.dragstart = $special.dragend = { setup:function(){}, teardown:function(){} }; + +// handle drag-releatd DOM events +function handler ( event ){ + var elem = this, returned, data = event.data || {}; + // mousemove or mouseup + if ( data.elem ){ + // update event properties... + elem = event.dragTarget = data.elem; // drag source element + event.dragProxy = drag.proxy || elem; // proxy element or source + event.cursorOffsetX = data.pageX - data.left; // mousedown offset + event.cursorOffsetY = data.pageY - data.top; // mousedown offset + event.offsetX = event.pageX - event.cursorOffsetX; // element offset + event.offsetY = event.pageY - event.cursorOffsetY; // element offset + } + // mousedown, check some initial props to avoid the switch statement + else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) || + $( event.target ).is( data.not ) ) return; + // handle various events + switch ( event.type ){ + // mousedown, left click, event.target is not restricted, init dragging + case 'mousedown': + $.extend( data, $( elem ).offset(), { + elem: elem, target: event.target, + pageX: event.pageX, pageY: event.pageY + }); // store some initial attributes + returned = hijack(event, "beforedragstart", elem); + if (returned === false) + return true; + $event.add( document, "mousemove mouseup", handler, data ); + selectable( elem, false ); // disable text selection + drag.dragging = null; // pending state + return false; // prevents text selection in safari + // mousemove, check distance, start dragging + case !drag.dragging && 'mousemove': + if ( squared( event.pageX-data.pageX ) + + squared( event.pageY-data.pageY ) // xІ + yІ = distanceІ + < data.distance ) break; // distance tolerance not reached + event.target = data.target; // force target from "mousedown" event (fix distance issue) + returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element + if ( returned !== false ){ // "dragstart" not rejected + drag.dragging = elem; // activate element + drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy + } + // mousemove, dragging + case 'mousemove': + if ( drag.dragging ){ + returned = hijack( event, "drag", elem ); // trigger "drag" + if ( $special.drop ){ // manage drop events + $special.drop.allowed = ( returned !== false ); // prevent drop + $special.drop.handler( event ); // "dropstart", "dropend" + } + if ( returned !== false ) break; // "drag" not rejected, stop + event.type = "mouseup"; // helps "drop" handler behave + } + // mouseup, stop dragging + case 'mouseup': + if (drag.dragging === false) break; + $event.remove( document, "mousemove mouseup", handler ); // remove page events + if ( drag.dragging ){ + if ( $special.drop ) $special.drop.handler( event ); // "drop" + hijack( event, "dragend", elem ); // trigger "dragend" + } + selectable( elem, true ); // enable text selection + drag.dragging = drag.proxy = data.elem = false; // deactivate element + break; + } + return true; + }; + +// set event type to custom value, and handle it +function hijack ( event, type, elem ){ + event.type = type; // force the event type + var result = $.event.handle.call( elem, event ); + return result===false ? false : result || event.result; + }; + +// return the value squared +function squared ( value ){ return Math.pow( value, 2 ); }; + +// suppress default dragstart IE events... +function dontStart(){ return ( drag.dragging === false ); }; + +// toggles text selection attributes +function selectable ( elem, bool ){ + if ( !elem ) return; // maybe element was removed ? + elem.unselectable = bool ? "off" : "on"; // IE + elem.onselectstart = function(){ return bool; }; // IE + //if ( document.selection && document.selection.empty ) document.selection.empty(); // IE + if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF + }; + +/*******************************************************************************************/ })( jQuery ); // confine scope
\ No newline at end of file diff --git a/lib/jquery.rule-1.0.1-min.js b/lib/jquery.rule-1.0.1-min.js index 8eeca12..bd0b033 100644 --- a/lib/jquery.rule-1.0.1-min.js +++ b/lib/jquery.rule-1.0.1-min.js @@ -1,9 +1,9 @@ -/**
- * jQuery.Rule - Css Rules manipulation, the jQuery way.
- * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Dual licensed under MIT and GPL.
- * Date: 02/27/2008
- * @author Ariel Flesler
- * @version 1.0.1
- */
+/** + * jQuery.Rule - Css Rules manipulation, the jQuery way. + * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 02/27/2008 + * @author Ariel Flesler + * @version 1.0.1 + */ ;(function($){var d=$('<style rel="alternate stylesheet" type="text/css" />').appendTo('head')[0],g=d.sheet?'sheet':'styleSheet',h=d[g],j=h.rules?'rules':'cssRules',l=h.deleteRule?'deleteRule':'removeRule',q=h.ownerNode?'ownerNode':'owningElement',t=/^([^{]+)\{([^}]*)\}/m,u=/([^:]+):([^;}]+)/;h.disabled=!0;var w=$.rule=function(r,c){if(!(this instanceof w))return new w(r,c);this.sheets=w.sheets(c);if(r&&t.test(r))r=w.clean(r);if(typeof r=='object'&&!r.exec)return this.setArray(r.get?r.get():r.splice?r:[r]);this.setArray(this.sheets.cssRules().get());return r?this.filter(r):this};$.extend(w,{sheets:function(c){var o=c;if(typeof o!='object')o=$.makeArray(document.styleSheets);o=$(o).not(h);if(typeof c=='string')o=o.ownerNode().filter(c).sheet();return o},rule:function(a){if(a.selectorText)return['',a.selectorText,a.style.cssText];return t.exec(a)},appendTo:function(r,a,b){switch(typeof a){case'string':a=this.sheets(a);case'object':if(a[0])a=a[0];if(a[g])a=a[g];if(a[j])break;default:if(typeof r=='object')return r;a=h}var p;if(!b&&(p=this.parent(r)))r=this.remove(r,p);var c=this.rule(r);if(a.addRule)a.addRule(c[1],c[2]||';');else if(a.insertRule)a.insertRule(c[1]+'{'+c[2]+'}',a[j].length);return a[j][a[j].length-1]},remove:function(r,p){p=p||this.parent(r);if(p!=h){var i=p?$.inArray(r,p[j]):-1;if(i!=-1){r=this.appendTo(r,0,1);p[l](i)}}return r},clean:function(r){return $.map(r.split('}'),function(a){if(a)return w.appendTo(a+'}')})},parent:function(r){if(typeof r=='string'||!$.browser.msie)return r.parentStyleSheet;var a;this.sheets().each(function(){if($.inArray(r,this[j])!=-1){a=this;return !1}});return a},outerText:function(a){return!a?'':[a.selectorText+'{','\t'+a.style.cssText,'}'].join('\n').toLowerCase()},text:function(a,b){if(b!==undefined)a.style.cssText=b;return!a?'':a.style.cssText.toLowerCase()}});w.fn=w.prototype={pushStack:function(a,b){var c=w(a,b||this.sheets);c.prevObject=this;return c},end:function(){return this.prevObject||w(0,[])},filter:function(s){var o;if(!s)s=/./;if(s.split){o=$.trim(s).toLowerCase().split(/\s*,\s*/);s=function(){return!!$.grep(this.selectorText.toLowerCase().split(/\s*,\s*/),function(a){return $.inArray(a,o)!=-1}).length}}else if(s.exec){o=s;s=function(){return o.test(this.selectorText)}}return this.pushStack($.grep(this,function(e,i){return s.call(e,i)}))},add:function(a,c){return this.pushStack($.merge(this.get(),w(a,c)))},is:function(s){return!!(s&&this.filter(s).length)},not:function(n,c){n=w(n,c);return this.filter(function(){return $.inArray(this,n)==-1})},append:function(s){var a=this,b;$.each(s.split(/\s*;\s*/),function(i,v){if((b=u.exec(v)))a.css(b[1],b[2])});return this},text:function(a){return!arguments.length?w.text(this[0]):this.each(function(){w.text(this,a)})},outerText:function(){return w.outerText(this[0])}};$.each({ownerNode:q,sheet:g,cssRules:j},function(m,a){var b=a==j;$.fn[m]=function(){return this.map(function(){return b?$.makeArray(this[a]):this[a]})}});$.fn.cssText=function(){return this.filter('link,style').eq(0).sheet().cssRules().map(function(){return w.outerText(this)}).get().join('\n')};$.each('remove,appendTo,parent'.split(','),function(k,f){w.fn[f]=function(){var a=$.makeArray(arguments),b=this;a.unshift(0);return this.each(function(i){a[0]=this;b[i]=w[f].apply(w,a)||b[i]})}});$.each(('each,index,setArray,get,size,eq,slice,map,attr,andSelf,css,show,hide,toggle,'+'queue,dequeue,stop,animate,fadeIn,fadeOut,fadeTo').split(','),function(k,f){w.fn[f]=$.fn[f]});var x=$.curCSS;$.curCSS=function(e,a){return('selectorText'in e)?e.style[a]||$.prop(e,a=='opacity'?1:0,'curCSS',0,a):x.apply(this,arguments)};w.cache={};var y=function(c){return function(a){var b=a.selectorText;if(b)arguments[0]=w.cache[b]=w.cache[b]||{};return c.apply($,arguments)}};$.data=y($.data);$.removeData=y($.removeData);$(window).unload(function(){$(h).cssRules().remove()})})(jQuery);
\ No newline at end of file diff --git a/slick.columnpicker.css b/slick.columnpicker.css index cc120e8..075bbea 100644 --- a/slick.columnpicker.css +++ b/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;
-}
-
-.slick-columnpicker li {
- list-style: none;
- margin: 0px;
- padding: 0px;
- background: none;
-}
-
-.slick-columnpicker input {
- margin: 4px;
-}
-
-.slick-columnpicker li a {
- display: block;
- padding: 4px;
- font-weight: bold;
-}
-
-.slick-columnpicker li a:hover {
- background: white;
+.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; +} + +.slick-columnpicker li { + list-style: none; + margin: 0px; + padding: 0px; + background: none; +} + +.slick-columnpicker input { + margin: 4px; +} + +.slick-columnpicker li a { + display: block; + padding: 4px; + font-weight: bold; +} + +.slick-columnpicker li a:hover { + background: white; }
\ No newline at end of file diff --git a/slick.columnpicker.js b/slick.columnpicker.js index ab57778..012394c 100644 --- a/slick.columnpicker.js +++ b/slick.columnpicker.js @@ -1,71 +1,71 @@ -function SlickColumnPicker(columns,grid)
-{
- var $menu;
-
- function init() {
- grid.onHeaderContextMenu = displayContextMenu;
-
- $menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body);
-
- $menu.bind("mouseleave", function(e) { $(this).fadeOut() });
- $menu.bind("click", updateColumn);
-
- }
-
- function displayContextMenu(e)
- {
- $menu.empty();
-
- for (var i=0; i<columns.length; i++) {
- var $li = $("<li />").appendTo($menu);
-
- var $input = $("<input type='checkbox' />")
- .attr("id", "columnpicker_" + i)
- .data("id", columns[i].id)
- .appendTo($li)
-
- if (!columns[i].hidden)
- $input.attr("checked","checked");
-
- $("<label for='columnpicker_" + i + "' />")
- .text(columns[i].name)
- .appendTo($li);
- }
-
- $("<hr/><li><a id='autoresize'>Autosize</a></li>").appendTo($menu);
-
- $menu
- .css("top", e.pageY - 10)
- .css("left", e.pageX - 10)
- .fadeIn();
- }
-
- function updateColumn(e)
- {
- if ($(e.target).is("a")) {
- grid.autosizeColumns();
- $menu.fadeOut();
- return;
- }
-
- if ($(e.target).is(":checkbox")) {
- if ($menu.find(":checkbox:checked").length == 0) {
- $(e.target).attr("checked","checked");
- return;
- }
-
- var id =$(e.target).data("id");
- for (var i=0; i<columns.length; i++) {
- if (columns[i].id == id) {
- columns[i].hidden = !$(e.target).is(":checked");
- grid.setColumnVisibility(columns[i], $(e.target).is(":checked"));
- return;
- }
- }
- }
- }
-
-
- init();
-}
-
+function SlickColumnPicker(columns,grid) +{ + var $menu; + + function init() { + grid.onHeaderContextMenu = displayContextMenu; + + $menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body); + + $menu.bind("mouseleave", function(e) { $(this).fadeOut() }); + $menu.bind("click", updateColumn); + + } + + function displayContextMenu(e) + { + $menu.empty(); + + for (var i=0; i<columns.length; i++) { + var $li = $("<li />").appendTo($menu); + + var $input = $("<input type='checkbox' />") + .attr("id", "columnpicker_" + i) + .data("id", columns[i].id) + .appendTo($li) + + if (!columns[i].hidden) + $input.attr("checked","checked"); + + $("<label for='columnpicker_" + i + "' />") + .text(columns[i].name) + .appendTo($li); + } + + $("<hr/><li><a id='autoresize'>Autosize</a></li>").appendTo($menu); + + $menu + .css("top", e.pageY - 10) + .css("left", e.pageX - 10) + .fadeIn(); + } + + function updateColumn(e) + { + if ($(e.target).is("a")) { + grid.autosizeColumns(); + $menu.fadeOut(); + return; + } + + if ($(e.target).is(":checkbox")) { + if ($menu.find(":checkbox:checked").length == 0) { + $(e.target).attr("checked","checked"); + return; + } + + var id =$(e.target).data("id"); + for (var i=0; i<columns.length; i++) { + if (columns[i].id == id) { + columns[i].hidden = !$(e.target).is(":checked"); + grid.setColumnVisibility(columns[i], $(e.target).is(":checked")); + return; + } + } + } + } + + + init(); +} + diff --git a/slick.editors.js b/slick.editors.js index 9ad928b..a59d2b7 100644 --- a/slick.editors.js +++ b/slick.editors.js @@ -1,622 +1,622 @@ -var SelectorCellFormatter = function(row, cell, value, columnDef, dataContext) {
- return (!dataContext ? "" : row);
-};
-
-var 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>";
-};
-
-var 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>";
-};
-
-var YesNoCellFormatter = function(row, cell, value, columnDef, dataContext) {
- return value ? "Yes" : "No";
-};
-
-var BoolCellFormatter = function(row, cell, value, columnDef, dataContext) {
- return value ? "<img src='../images/tick.png'>" : "";
-};
-
-var 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;
-};
-
-var 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(", ");
-};
-
-var StarFormatter = function(row, cell, value, columnDef, dataContext) {
- return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : "";
-};
-
-
-
-
-
-
-var TextCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
-
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
-
- $input.appendTo($container);
- $input.focus().select();
- }
-
- this.destroy = function() {
- $input.remove();
- }
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- return $input.val();
- }
-
- this.isValueChanged = function() {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
- }
-
- this.validate = function() {
- if (columnDef.validator)
- {
- var validationResults = columnDef.validator(scope.getValue());
- if (!validationResults.valid)
- return validationResults;
- }
-
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var IntegerCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
-
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
-
- $input.appendTo($container);
- $input.focus().select();
- }
-
-
- this.destroy = function() {
- $input.remove();
- }
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- var val = $.trim($input.val());
- return (val == "") ? 0 : parseInt($input.val(), 10);
- }
-
- 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();
-}
-
-
-var DateCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
-
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
-
- $input.appendTo($container);
- $input.focus().select();
- $input.datepicker({
- showOn: "button",
- buttonImageOnly: true,
- buttonImage: "../images/calendar.gif"
- });
- $input.width($input.width() - 18);
- }
-
-
- this.destroy = function() {
- $input.datepicker("hide");
- $input.datepicker("destroy");
- $input.remove();
- }
-
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- return $input.val();
- }
-
- this.isValueChanged = function() {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
- }
-
- this.validate = function() {
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var YesNoSelectCellEditor = function($container, columnDef, value, dataContext) {
- var $select;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>");
-
- if (defaultValue)
- $select.val('yes');
- else
- $select.val('no');
-
- $select.appendTo($container);
-
- $select.focus();
- }
-
-
- this.destroy = function() {
- $select.remove();
- }
-
-
- this.focus = function() {
- $select.focus();
- }
-
- this.setValue = function(value) {
- $select.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- return ($select.val() == 'yes');
- }
-
- this.isValueChanged = function() {
- return ($select.val() != defaultValue);
- }
-
- this.validate = function() {
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var YesNoCheckboxCellEditor = function($container, columnDef, value, dataContext) {
- var $select;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>");
-
- if (defaultValue)
- $select.attr("checked", "checked");
-
- $select.appendTo($container);
- $select.focus();
- }
-
-
- this.destroy = function() {
- $select.remove();
- }
-
-
- this.focus = function() {
- $select.focus();
- }
-
- this.setValue = function(value) {
- if (value)
- $select.attr("checked", "checked");
- else
- $select.removeAttr("checked");
-
- defaultValue = value;
- }
-
- this.getValue = function() {
- return $select.attr("checked");
- }
-
- this.isValueChanged = function() {
- return (scope.getValue() != defaultValue);
- }
-
- this.validate = function() {
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var PercentCompleteCellEditor = function($container, columnDef, value, dataContext) {
- var $input, $picker;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-percentcomplete' />");
-
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
-
- $input.width($container.innerWidth() - 20);
- $input.appendTo($container);
-
- $picker = $("<div class='editor-percentcomplete-picker' />").appendTo($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.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- var val = $.trim($input.val());
- return (val == "") ? 0 : parseInt($input.val(), 10);
- }
-
- 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 positive number"
- };
-
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var TaskNameCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
-
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
-
- $input.appendTo($container);
- $input.focus().select();
- }
-
- this.destroy = function() {
- $input.remove();
- }
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- }
-
- this.getValue = function() {
- return $input.val();
- }
-
- this.isValueChanged = function() {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
- }
-
- this.validate = function() {
- if (columnDef.validator)
- {
- var validationResults = columnDef.validator(scope.getValue());
- if (!validationResults.valid)
- return validationResults;
- }
-
- if ($input.val() == "")
- return {
- valid: false,
- msg: "This field cannot be empty"
- };
-
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var ResourcesCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = [];
- var scope = this;
-
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
-
- var resources = dataContext ? dataContext["resources"] : null;
-
- defaultValue = resources ? resources.concat() : [];
-
- if (resources != null)
- {
- $input[0].defaultValue = defaultValue.join(", ");
- $input.val(defaultValue.join(", "));
- }
-
- $input.appendTo($container);
- $input.focus().select();
- }
-
- this.destroy = function() {
- $input.remove();
- }
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- defaultValue = value ? value : [];
- $input.val(defaultValue.join(", "));
- }
-
- this.getValue = function() {
- if ($input.val() == "")
- return [];
-
- var names = $input.val().split(",");
-
- for (var i = 0; i < names.length; i++)
- names[i] = $.trim(names[i]);
-
- return names;
- }
-
- this.isValueChanged = function() {
- // todo: implement
- return true;
- }
-
- this.validate = function() {
- if (columnDef.validator)
- {
- var validationResults = columnDef.validator(scope.getValue());
- if (!validationResults.valid)
- return validationResults;
- }
-
- // todo: implement
-
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
-
-var StarCellEditor = function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- 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' />");
-
- if (defaultValue)
- $input.css("opacity", 1);
- else
- $input.css("opacity", 0.5);
-
- $input.bind("click keydown", toggle);
-
- $input.appendTo($container);
- $input.focus();
- }
-
- this.destroy = function() {
- $input.unbind("click keydown", toggle);
- $input.remove();
- }
-
- this.focus = function() {
- $input.focus();
- }
-
- this.setValue = function(value) {
- defaultValue = value;
-
- if (defaultValue)
- $input.css("opacity", 1);
- else
- $input.css("opacity", 0.2);
- }
-
- this.getValue = function() {
- return $input.css("opacity") == "1";
- }
-
- this.isValueChanged = function() {
- return (defaultValue == true) != scope.getValue();
- }
-
- this.validate = function() {
- return {
- valid: true,
- msg: null
- };
- }
-
- this.init();
-}
+var SelectorCellFormatter = function(row, cell, value, columnDef, dataContext) { + return (!dataContext ? "" : row); +}; + +var 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>"; +}; + +var 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>"; +}; + +var YesNoCellFormatter = function(row, cell, value, columnDef, dataContext) { + return value ? "Yes" : "No"; +}; + +var BoolCellFormatter = function(row, cell, value, columnDef, dataContext) { + return value ? "<img src='../images/tick.png'>" : ""; +}; + +var 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; +}; + +var 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(", "); +}; + +var StarFormatter = function(row, cell, value, columnDef, dataContext) { + return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : ""; +}; + + + + + + +var TextCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = value; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-text' />"); + + if (value != null) + { + $input[0].defaultValue = value; + $input.val(defaultValue); + } + + $input.appendTo($container); + $input.focus().select(); + } + + this.destroy = function() { + $input.remove(); + } + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + $input.val(value); + defaultValue = value; + } + + this.getValue = function() { + return $input.val(); + } + + this.isValueChanged = function() { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + } + + this.validate = function() { + if (columnDef.validator) + { + var validationResults = columnDef.validator(scope.getValue()); + if (!validationResults.valid) + return validationResults; + } + + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var IntegerCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = value; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-text' />"); + + if (value != null) + { + $input[0].defaultValue = value; + $input.val(defaultValue); + } + + $input.appendTo($container); + $input.focus().select(); + } + + + this.destroy = function() { + $input.remove(); + } + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + $input.val(value); + defaultValue = value; + } + + this.getValue = function() { + var val = $.trim($input.val()); + return (val == "") ? 0 : parseInt($input.val(), 10); + } + + 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(); +} + + +var DateCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = value; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-text' />"); + + if (value != null) + { + $input[0].defaultValue = value; + $input.val(defaultValue); + } + + $input.appendTo($container); + $input.focus().select(); + $input.datepicker({ + showOn: "button", + buttonImageOnly: true, + buttonImage: "../images/calendar.gif" + }); + $input.width($input.width() - 18); + } + + + this.destroy = function() { + $input.datepicker("hide"); + $input.datepicker("destroy"); + $input.remove(); + } + + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + $input.val(value); + defaultValue = value; + } + + this.getValue = function() { + return $input.val(); + } + + this.isValueChanged = function() { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + } + + this.validate = function() { + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var YesNoSelectCellEditor = function($container, columnDef, value, dataContext) { + var $select; + var defaultValue = value; + var scope = this; + + this.init = function() { + $select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>"); + + if (defaultValue) + $select.val('yes'); + else + $select.val('no'); + + $select.appendTo($container); + + $select.focus(); + } + + + this.destroy = function() { + $select.remove(); + } + + + this.focus = function() { + $select.focus(); + } + + this.setValue = function(value) { + $select.val(value); + defaultValue = value; + } + + this.getValue = function() { + return ($select.val() == 'yes'); + } + + this.isValueChanged = function() { + return ($select.val() != defaultValue); + } + + this.validate = function() { + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var YesNoCheckboxCellEditor = function($container, columnDef, value, dataContext) { + var $select; + var defaultValue = value; + var scope = this; + + this.init = function() { + $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>"); + + if (defaultValue) + $select.attr("checked", "checked"); + + $select.appendTo($container); + $select.focus(); + } + + + this.destroy = function() { + $select.remove(); + } + + + this.focus = function() { + $select.focus(); + } + + this.setValue = function(value) { + if (value) + $select.attr("checked", "checked"); + else + $select.removeAttr("checked"); + + defaultValue = value; + } + + this.getValue = function() { + return $select.attr("checked"); + } + + this.isValueChanged = function() { + return (scope.getValue() != defaultValue); + } + + this.validate = function() { + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var PercentCompleteCellEditor = function($container, columnDef, value, dataContext) { + var $input, $picker; + var defaultValue = value; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-percentcomplete' />"); + + if (value != null) + { + $input[0].defaultValue = value; + $input.val(defaultValue); + } + + $input.width($container.innerWidth() - 20); + $input.appendTo($container); + + $picker = $("<div class='editor-percentcomplete-picker' />").appendTo($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.setValue = function(value) { + $input.val(value); + defaultValue = value; + } + + this.getValue = function() { + var val = $.trim($input.val()); + return (val == "") ? 0 : parseInt($input.val(), 10); + } + + 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 positive number" + }; + + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var TaskNameCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = value; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-text' />"); + + if (value != null) + { + $input[0].defaultValue = value; + $input.val(defaultValue); + } + + $input.appendTo($container); + $input.focus().select(); + } + + this.destroy = function() { + $input.remove(); + } + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + $input.val(value); + defaultValue = value; + } + + this.getValue = function() { + return $input.val(); + } + + this.isValueChanged = function() { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + } + + this.validate = function() { + if (columnDef.validator) + { + var validationResults = columnDef.validator(scope.getValue()); + if (!validationResults.valid) + return validationResults; + } + + if ($input.val() == "") + return { + valid: false, + msg: "This field cannot be empty" + }; + + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var ResourcesCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = []; + var scope = this; + + this.init = function() { + $input = $("<INPUT type=text class='editor-text' />"); + + var resources = dataContext ? dataContext["resources"] : null; + + defaultValue = resources ? resources.concat() : []; + + if (resources != null) + { + $input[0].defaultValue = defaultValue.join(", "); + $input.val(defaultValue.join(", ")); + } + + $input.appendTo($container); + $input.focus().select(); + } + + this.destroy = function() { + $input.remove(); + } + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + defaultValue = value ? value : []; + $input.val(defaultValue.join(", ")); + } + + this.getValue = function() { + if ($input.val() == "") + return []; + + var names = $input.val().split(","); + + for (var i = 0; i < names.length; i++) + names[i] = $.trim(names[i]); + + return names; + } + + this.isValueChanged = function() { + // todo: implement + return true; + } + + this.validate = function() { + if (columnDef.validator) + { + var validationResults = columnDef.validator(scope.getValue()); + if (!validationResults.valid) + return validationResults; + } + + // todo: implement + + return { + valid: true, + msg: null + }; + } + + this.init(); +} + +var StarCellEditor = function($container, columnDef, value, dataContext) { + var $input; + var defaultValue = value; + 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' />"); + + if (defaultValue) + $input.css("opacity", 1); + else + $input.css("opacity", 0.5); + + $input.bind("click keydown", toggle); + + $input.appendTo($container); + $input.focus(); + } + + this.destroy = function() { + $input.unbind("click keydown", toggle); + $input.remove(); + } + + this.focus = function() { + $input.focus(); + } + + this.setValue = function(value) { + defaultValue = value; + + if (defaultValue) + $input.css("opacity", 1); + else + $input.css("opacity", 0.2); + } + + this.getValue = function() { + return $input.css("opacity") == "1"; + } + + this.isValueChanged = function() { + return (defaultValue == true) != scope.getValue(); + } + + this.validate = function() { + return { + valid: true, + msg: null + }; + } + + this.init(); +} diff --git a/slick.globaleditorlock.js b/slick.globaleditorlock.js index 222de48..de6bc26 100644 --- a/slick.globaleditorlock.js +++ b/slick.globaleditorlock.js @@ -1,54 +1,54 @@ -/***
- * A singleton for controlling access to the editing functionality for multiple components capable of editing the same data.
- */
-var GlobalEditorLock = new function()
-{
- var currentEditor = null;
-
- this.isEditing = function()
- {
- return (currentEditor != null);
- }
-
- this.hasLock = function(editor)
- {
- return (currentEditor == editor);
- }
-
- this.enterEditMode = function(editor)
- {
- if (currentEditor != null)
- throw "GlobalEditorLock : enterEditMode : currentEditor == null";
-
- if (!editor.commitCurrentEdit)
- throw "GlobalEditorLock : enterEditMode : editor must implement .commitCurrentEdit()";
-
- if (!editor.cancelCurrentEdit)
- throw "GlobalEditorLock : enterEditMode : editor must implement .cancelCurrentEdit()";
-
- currentEditor = editor;
- }
-
- this.leaveEditMode = function(editor)
- {
- if (currentEditor != editor)
- throw "GlobalEditorLock : leaveEditMode() : currentEditor != editor";
-
- currentEditor = null;
- }
-
- this.commitCurrentEdit = function()
- {
- if (currentEditor)
- return currentEditor.commitCurrentEdit();
-
- return true;
- }
-
- this.cancelCurrentEdit = function()
- {
- if (currentEditor)
- currentEditor.cancelCurrentEdit();
- }
-};
-
+/*** + * A singleton for controlling access to the editing functionality for multiple components capable of editing the same data. + */ +var GlobalEditorLock = new function() +{ + var currentEditor = null; + + this.isEditing = function() + { + return (currentEditor != null); + } + + this.hasLock = function(editor) + { + return (currentEditor == editor); + } + + this.enterEditMode = function(editor) + { + if (currentEditor != null) + throw "GlobalEditorLock : enterEditMode : currentEditor == null"; + + if (!editor.commitCurrentEdit) + throw "GlobalEditorLock : enterEditMode : editor must implement .commitCurrentEdit()"; + + if (!editor.cancelCurrentEdit) + throw "GlobalEditorLock : enterEditMode : editor must implement .cancelCurrentEdit()"; + + currentEditor = editor; + } + + this.leaveEditMode = function(editor) + { + if (currentEditor != editor) + throw "GlobalEditorLock : leaveEditMode() : currentEditor != editor"; + + currentEditor = null; + } + + this.commitCurrentEdit = function() + { + if (currentEditor) + return currentEditor.commitCurrentEdit(); + + return true; + } + + this.cancelCurrentEdit = function() + { + if (currentEditor) + currentEditor.cancelCurrentEdit(); + } +}; + diff --git a/slick.grid.js b/slick.grid.js index c6e3b52..19f737e 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -1,1363 +1,1363 @@ -/***
- *
- * (c) 2009 Michael Leibman (michael.leibman@gmail.com)
- * All rights reserved.
- *
- *
- * TODO:
- * - frozen columns
- * - consistent events (EventHelper? jQuery events?)
- *
- *
- * KNOWN ISSUES:
- * - keyboard navigation doesn't "jump" over unselectable cells for now
-*
- *
- * OPTIONS:
- * rowHeight - Row height in pixels.
- * enableAddRow - If true, a blank row will be displayed at the bottom - typing values in that row will add a new one.
- * manualScrolling - Disable automatic rerender on scroll. Client will take care of calling Grid.onScroll().
- * editable - If false, no cells will be switched into edit mode.
- * editOnDoubleClick - Cell will not automatically go into edit mode without being double-clicked.
- * enableCellNavigation - If false, no cells will be selectable.
- * defaultColumnWidth - Default column width in pixels (if columns[cell].width is not specified).
- * enableColumnReorder - Allows the user to reorder columns.
- * asyncEditorLoading - Makes cell editors load asynchronously after a small delay.
- * This greatly increases keyboard navigation speed.
- * forceFitColumns - Force column sizes to fit into the viewport (avoid horizontal scrolling).
- *
- *
- * COLUMN DEFINITION (columns) OPTIONS:
- * id - Column ID.
- * name - Column name to put in the header.
- * field - Property of the data context to bind to.
- * formatter - Function responsible for rendering the contents of a cell.
- * editor - An Editor class.
- * validator - An extra validation function to be passed to the editor.
- * unselectable - If true, the cell cannot be selected (and therefore edited).
- * cannotTriggerInsert - If true, a new row cannot be created from just the value of this cell.
- * setValueHandler - If true, this handler will be called to set field value instead of context[field].
- * width - Width of the column in pixels.
- * resizable - If false, the column cannot be resized.
- * sortable - If true, the column can be sorted (onSort will be called).
- * minWidth - Minimum allowed column width for resizing.
- * maxWidth - Maximum allowed column width for resizing.
- * cssClass - A CSS class to add to the cell.
- * rerenderOnResize - Rerender the column when it is resized (useful for columns relying on cell width or adaptive formatters).
- *
- *
- * EVENTS:
- *
- * ...
- *
- *
- * NOTES:
- *
- * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods.
- * This increases the speed dramatically, but can only be done safely because there are no event handlers
- * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy()
- * and do proper cleanup.
- *
- *
- * @param {jQuery} $container Container object to create the grid in.
- * @param {Array} 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 = {
- rowHeight: 25,
- defaultColumnWidth: 80,
- enableAddRow: true,
- leaveSpaceForNewRows: false,
- manualScrolling: false,
- editable: true,
- editOnDoubleClick: false,
- enableCellNavigation: true,
- enableColumnReorder: true,
- asyncEditorLoading: false,
- forceFitColumns: false
- };
-
- var columnDefaults = {
- resizable: true,
- sortable: true,
- formatter: defaultFormatter
- }
-
- // consts
- var CAPACITY = 50;
- var MIN_BUFFER = 5;
- var BUFFER = MIN_BUFFER; // will be set to equal one page
- var POSTPROCESSING_DELAY = 50;
-
- // private
- var uid = "slickgrid_" + Math.round(1000000 * Math.random());
- var self = this;
- var $divHeadersScroller;
- var $divHeaders;
- var $divMainScroller;
- var $divMain;
- var viewportH, viewportW;
- var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff; // padding+border
-
- var currentRow, currentCell;
- var currentCellNode = null;
- var currentEditor = null;
-
- var rowsCache = {};
- var renderedRows = 0;
- var numVisibleRows;
- var lastRenderedScrollTop = 0;
- var currentScrollTop = 0;
- var currentScrollLeft = 0;
- var scrollDir = 1;
- var avgRowRenderTime = 10;
-
- var selectedRows = [];
- var selectedRowsLookup = {};
- var columnsById = {};
-
- // async call handles
- var h_editorLoader = null;
- var h_render = null;
- var h_postrender = null;
- var postProcessedRows = {};
- var rowsToPostProcess = [];
-
- // perf counters
- var counter_rows_rendered = 0;
- var counter_rows_removed = 0;
-
-
- function init() {
- options = $.extend({},defaults,options);
- columnDefaults.width = options.defaultColumnWidth;
-
- $container
- .empty()
- .attr("tabIndex",0)
- .attr("hideFocus",true)
- .css("overflow","hidden")
- .css("outline",0)
- .css("position","relative")
- .addClass(uid);
-
- $divHeadersScroller = $("<div class='slick-header' style='overflow:hidden;position:relative;' />").appendTo($container);
- $divHeaders = $("<div class='slick-header-columns' style='width:100000px' />").appendTo($divHeadersScroller);
- $divMainScroller = $("<div tabIndex='0' hideFocus style='width:100%;overflow:scroll;outline:0;position:relative;outline:0px;'>").appendTo($container);
- $divMain = $("<div class='grid-canvas' tabIndex='0' hideFocus />").appendTo($divMainScroller);
-
- // 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();
-
- $divMainScroller.height($container.innerHeight() - $divHeadersScroller.outerHeight());
-
- if ($.browser.msie)
- $divMainScroller[0].onselectstart = function() {
- if (event.srcElement.tagName != "INPUT" && event.srcElement.tagName != "TEXTAREA")
- return false;
- };
-
- $divHeaders.disableSelection();
-
- createColumnHeaders();
- setupMoveEvents();
- createCssRules();
- resizeCanvas();
- if (options.forceFitColumns)
- autosizeColumns();
- render();
-
- if (!options.manualScrolling)
- $divMainScroller.bind("scroll", handleScroll);
-
- $container.bind("resize", resizeCanvas);
-
- $divMain.bind("keydown", handleKeyDown);
- $divMain.bind("click", handleClick);
- $divMain.bind("dblclick", handleDblClick);
- $divMain.bind("contextmenu", handleContextMenu)
- $divHeadersScroller.bind("contextmenu", handleHeaderContextMenu);
- }
-
- function createColumnHeaders() {
- for (var i = 0; i < columns.length; i++) {
- var m = columns[i] = $.extend({},columnDefaults,columns[i]);
- columnsById[m.id] = i;
-
- var header = $("<div class='slick-header-column' cell=" + i + " id='" + m.id + "' />")
- .html(m.name)
- .width(m.width - headerColumnWidthDiff)
- .appendTo($divHeaders);
-
- if (m.sortable) header.append("<span class='slick-sort-indicator' />")
- if (m.resizable) header.append("<div class='slick-resizable-handle' />");
- }
-
- setupColumnSort();
- setupColumnResizeEvents();
- if (options.enableColumnReorder)
- setupColumnReorderEvents();
- }
-
- function setupColumnSort() {
- $divHeaders.click(function(e) {
- var $col = $(e.target);
- if (!$col.hasClass("slick-header-column") || !columns[columnsById[$col.attr("id")]].sortable)
- return;
-
- if (currentEditor && !commitCurrentEdit()) return;
-
- if ($col.is(".slick-header-column-sorted"))
- {
- $col.find(".slick-sort-indicator").toggleClass("slick-sort-indicator-asc").toggleClass("slick-sort-indicator-desc");
- }
- else
- {
- $divHeaders.children().removeClass("slick-header-column-sorted");
- $divHeaders.find(".slick-sort-indicator").removeClass("slick-sort-indicator-asc slick-sort-indicator-desc");
- $col.addClass("slick-header-column-sorted");
- $col.find(".slick-sort-indicator").addClass("slick-sort-indicator-asc");
- }
-
- if (self.onSort)
- self.onSort(columns[columnsById[$col.attr("id")]], $col.find(".slick-sort-indicator").hasClass("slick-sort-indicator-asc"));
- })
- }
-
- function setupColumnReorderEvents() {
- $divHeaders.sortable({
- axis: "x",
- cursor: "default",
- tolerance: "intersect",
- helper: "clone",
- placeholder: "slick-sortable-placeholder 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, ui) {
- if (currentEditor && !commitCurrentEdit()) {
- $(this).sortable("cancel");
- return;
- }
-
- var newOrder = $divHeaders.sortable("toArray"), lookup = {};
- for (var i=0; i<columns.length; i++) {
- lookup[columns[i].id] = columns[i];
- }
-
- for (var i=0; i<newOrder.length; i++) {
- columnsById[newOrder[i]] = i;
- columns[i] = lookup[newOrder[i]];
- }
-
- removeAllRows();
- removeCssRules();
- createCssRules();
- render();
-
- if (self.onColumnsReordered)
- self.onColumnsReordered();
-
- e.stopPropagation();
- }
- })
- }
-
- function setupColumnResizeEvents() {
- $divHeaders
- .find(".slick-resizable-handle")
- .bind('dragstart', function(e) {
- var $col = $(this).parent();
- var colId = $col.attr("id");
- if (!columns[columnsById[colId]].resizable) return false;
- if (currentEditor && !commitCurrentEdit()) return false;
-
- $col
- .data("colId", colId)
- .data("width", $col.width())
- .data("pageX", e.pageX)
- .addClass("slick-header-column-active");
- })
- .bind('drag', function(e) {
- var $col = $(this).parent(), w = $col.data("width") - $col.data("pageX") + e.pageX;
- var cell = columnsById[$col.data("colId")];
- var m = columns[cell];
- if (m.minWidth) w = Math.max(m.minWidth - headerColumnWidthDiff,w);
- if (m.maxWidth) w = Math.min(m.maxWidth - headerColumnWidthDiff,w);
- $col.css({ width: Math.max(0, w) });
- })
- .bind('dragend', function(e) {
- var $col = $(this).parent();
- var cell = columnsById[$col.data("colId")];
- $col.removeClass("slick-header-column-active");
- columns[cell].width = $col.outerWidth();
-
- if (options.forceFitColumns)
- autosizeColumns(columns[cell]);
- else {
- updateColumnWidth(cell, $col.outerWidth());
- resizeCanvas();
- }
-
- if (columns[cell].rerenderOnResize)
- removeAllRows();
-
- render();
- })
- }
-
- function setupMoveEvents() {
- $divMain
- .bind("beforedragstart", function(e) {
- var $cell = $(e.target).closest(".c");
- if ($cell.length == 0) return false;
- if (parseInt($cell.parent().attr("row")) >= data.length) return false;
- var colDef = columns[$cell.attr("cell")];
- if (colDef.behavior != "move") return false;
- })
- .bind("dragstart", function(e) {
- if (currentEditor && !commitCurrentEdit()) return false;
-
- var row = parseInt($(e.target).closest(".r").attr("row"));
-
- if (!selectedRowsLookup[row])
- setSelectedRows([row]);
-
- var $selectionProxy = $("<div class='slick-reorder-proxy'/>");
- $selectionProxy
- .css("position", "absolute")
- .css("zIndex", "99999")
- .css("width", $(this).innerWidth())
- .css("height", options.rowHeight*selectedRows.length)
- .appendTo($divMainScroller);
-
- $(this)
- .data("selectionProxy", $selectionProxy)
- .data("insertBefore", -1);
-
- var $guide = $("<div class='slick-reorder-guide'/>");
- $guide
- .css("position", "absolute")
- .css("zIndex", "99998")
- .css("width", $(this).innerWidth())
- .css("top", -1000)
- .appendTo($divMainScroller);
-
- return $guide;
- })
- .bind("drag", function(e) {
- var top = e.clientY - $(this).offset().top;
- $(this).data("selectionProxy").css("top",top-5);
-
- var insertBefore = Math.max(0,Math.min(Math.round(top/options.rowHeight),data.length));
- if (insertBefore != $(this).data("insertBefore")) {
- if (self.onBeforeMoveRows && self.onBeforeMoveRows(selectedRows.concat(),insertBefore) === false)
- $(e.dragProxy).css("top", -1000);
- else
- $(e.dragProxy).css("top",insertBefore*options.rowHeight);
- $(this).data("insertBefore", insertBefore);
- }
- })
- .bind("dragend", function(e) {
- $(e.dragProxy).remove();
- $(this).data("selectionProxy").remove();
- var insertBefore = $(this).data("insertBefore");
- $(this).removeData("selectionProxy").removeData("insertBefore");
- if (self.onMoveRows) self.onMoveRows(selectedRows.concat(),insertBefore);
- })
- }
-
- function measureCellPaddingAndBorder() {
- var tmp = $("<div class='slick-header-column cell='' id='' style='visibility:hidden'>-</div>").appendTo($divHeaders);
- headerColumnWidthDiff = tmp.outerWidth() - tmp.width();
- headerColumnHeightDiff = tmp.outerHeight() - tmp.height();
- tmp.remove();
-
- var r = $("<div class='r' />").appendTo($divMain);
- tmp = $("<div class='c' cell='' id='' style='visibility:hidden'>-</div>").appendTo(r);
- cellWidthDiff = tmp.outerWidth() - tmp.width();
- cellHeightDiff = tmp.outerHeight() - tmp.height();
- r.remove();
- }
-
- function createCssRules() {
- var $style = $("<style type='text/css' rel='stylesheet' lib='slickgrid' />").appendTo($("head"));
- $.rule(".grid-canvas .r .c { height:" + (options.rowHeight - cellHeightDiff) + "px;}").appendTo($style);
-
- for (var i = 0; i < columns.length; i++) {
- $.rule(
- "." + uid + " .grid-canvas .c" + i + " { " +
- "width:" + (columns[i].width - cellWidthDiff) + "px; " +
- "display: " + (columns[i].hidden ? "none" : "block") +
- " }").appendTo($style);
- }
- }
-
- function removeCssRules() {
- $("style[lib=slickgrid]").remove();
- }
-
- function destroy() {
- if (currentEditor)
- cancelCurrentEdit();
-
- $divHeaders.sortable("destroy");
- $container.unbind("resize", resizeCanvas);
- removeCssRules();
-
- $container.empty().removeClass(uid);
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // General
-
- function getColumnIndex(id) {
- return columnsById[id];
- }
-
- function autosizeColumns(columnToHold) {
- var availWidth = viewportW-$.getScrollbarWidth();
- var total = 0;
- var existingTotal = 0;
- var minWidth = Math.max(headerColumnWidthDiff,cellWidthDiff);
-
- for (var i = 0; i < columns.length; i++) {
- if (!columns[i].hidden)
- existingTotal += columns[i].width;
- }
-
- total = existingTotal;
-
- removeAllRows();
-
- // shrink
- var workdone = true;
- while (total > availWidth && workdone) {
- workdone = false;
- for (var i = 0; i < columns.length && total > availWidth; i++) {
- var c = columns[i];
- if (c.hidden || !c.resizable || c.minWidth == c.width || c.width == minWidth || (columnToHold && columnToHold.id == c.id)) continue;
- total -= 1;
- c.width -= 1;
- workdone = true;
- }
- }
-
- // shrink the column being "held" as a last resort
- if (total > availWidth && columnToHold && columnToHold.resizable && !columnToHold.hidden) {
- while (total > availWidth) {
- if (columnToHold.minWidth == columnToHold.width || columnToHold.width == minWidth) break;
- total -= 1;
- columnToHold.width -= 1;
- }
- }
-
- // grow
- workdone = true;
- while (total < availWidth && workdone) {
- workdone = false;
- for (var i = 0; i < columns.length && total < availWidth; i++) {
- var c = columns[i];
- if (c.hidden || !c.resizable || c.maxWidth == c.width || (columnToHold && columnToHold.id == c.id)) continue;
- total += 1;
- c.width += 1;
- workdone = true;
- }
- }
-
- // grow the column being "held" as a last resort
- if (total < availWidth && columnToHold && columnToHold.resizable && !columnToHold.hidden) {
- while (total < availWidth) {
- if (columnToHold.maxWidth == columnToHold.width) break;
- total += 1;
- columnToHold.width += 1;
- }
- }
-
- for (var i=0; i<columns.length; i++) {
- updateColumnWidth(i, columns[i].width);
- }
-
- resizeCanvas();
- }
-
- function updateColumnWidth(index,width) {
- columns[index].width = width;
- $divHeaders.find(".slick-header-column[id=" + columns[index].id + "]").css("width",width - headerColumnWidthDiff);
- $.rule("." + uid + " .grid-canvas .c" + index, "style[lib=slickgrid]").css("width", (columns[index].width - cellWidthDiff) + "px");
- }
-
- function setColumnVisibility(column,visible) {
- var index = columnsById[column.id];
- columns[index].hidden = !visible;
- resizeCanvas();
- var header = $divHeaders.find("[id=" + columns[index].id + "]");
- header.css("display", visible?"block":"none");
- $.rule("." + uid + " .grid-canvas .c" + index, "style[lib=slickgrid]").css("display", visible?"block":"none");
-
- if (options.forceFitColumns)
- autosizeColumns(columns[index]);
- }
-
- function getSelectedRows() {
- return selectedRows.concat();
- }
-
- function setSelectedRows(rows) {
- if (GlobalEditorLock.isEditing() && !GlobalEditorLock.hasLock(self))
- throw "Grid : setSelectedRows : cannot set selected rows when somebody else has an edit lock";
-
- var lookup = {};
- for (var i=0; i<rows.length; i++)
- lookup[rows[i]] = true;
-
- // unselect old rows
- for (var i=0; i<selectedRows.length; i++) {
- var row = selectedRows[i];
- if (rowsCache[row] && !lookup[row])
- $(rowsCache[row]).removeClass("selected");
- }
-
- // select new ones
- for (var i=0; i<rows.length; i++) {
- var row = rows[i];
- if (rowsCache[row] && !selectedRowsLookup[row])
- $(rowsCache[row]).addClass("selected");
- }
-
- selectedRows = rows.concat();
- selectedRowsLookup = lookup;
- }
-
- function setOptions(args) {
- if (currentEditor && !commitCurrentEdit())
- return;
-
- makeSelectedCellNormal();
-
- if (options.enableAddRow != args.enableAddRow)
- removeRow(data.length);
-
- options = $.extend(options,args);
-
- render();
- }
-
- function setData(newData,scrollToTop)
- {
- removeAllRows();
- data = newData;
- if (scrollToTop)
- $divMainScroller.scrollTop(0);
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Rendering / Scrolling
-
- function defaultFormatter(row, cell, value, columnDef, dataContext) {
- return (value == null || value == undefined) ? "" : value;
- }
-
- function appendRowHtml(stringArray,row) {
- var d = data[row];
- var dataLoading = row < data.length && !d;
- var css = "r" + (dataLoading ? " loading" : "") + (selectedRowsLookup[row] ? " selected" : "");
-
- stringArray.push("<div class='" + css + "' row='" + row + "' style='top:" + (options.rowHeight*row) + "px'>");
-
- for (var i=0, cols=columns.length; i<cols; i++) {
- var m = columns[i];
-
- stringArray.push("<div " + (m.unselectable ? "" : "hideFocus tabIndex=0 ") + "class='c c" + i + (m.cssClass ? " " + m.cssClass : "") + "' cell=" + i + ">");
-
- // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
- if (d && row < data.length)
- stringArray.push(m.formatter(row, i, d[m.field], m, d));
-
- stringArray.push("</div>");
- }
-
- stringArray.push("</div>");
- }
-
- function getRowHtml(row) {
- var html = [];
- appendRowHtml(html,row);
- return html.join("");
- }
-
- function cleanupRows(visibleFrom,visibleTo) {
- var rowsBefore = renderedRows;
- var parentNode = $divMain[0];
- for (var i in rowsCache) {
- if ((i < visibleFrom || i > visibleTo) && i != currentRow) {
- parentNode.removeChild(rowsCache[i]);
- delete rowsCache[i];
- delete postProcessedRows[i];
- renderedRows--;
- counter_rows_removed++;
- }
- }
- }
-
- function removeAllRows() {
- $divMain[0].innerHTML = "";
- rowsCache= {};
- postProcessedRows = {};
- counter_rows_removed += renderedRows;
- renderedRows = 0;
- }
-
- function removeRow(row) {
- var node = rowsCache[row];
- if (!node) return;
-
- if (currentEditor && currentRow == row)
- throw "Grid : removeRow : Cannot remove a row that is currently in edit mode";
-
- // if we're removing rows, we're probably not scrolling
- scrollDir = 0;
-
- node.parentNode.removeChild(node);
- node = null;
- delete rowsCache[row];
- delete postProcessedRows[row];
- renderedRows--;
- counter_rows_removed++;
- }
-
- function removeRows(rows) {
- if (!rows || !rows.length) return;
- scrollDir = 0;
- var nodes = [];
- for (var i=0, rl=rows.length; i<rl; i++) {
- if (currentEditor && currentRow == i)
- throw "Grid : removeRow : Cannot remove a row that is currently in edit mode";
-
- if (rowsCache[rows[i]])
- nodes.push(rows[i]);
- }
-
- if (renderedRows > 10 && nodes.length == renderedRows) {
- $divMain[0].innerHTML = "";
- rowsCache= {};
- postProcessedRows = {};
- counter_rows_removed += renderedRows;
- renderedRows = 0;
- } else {
- for (var i=0, nl=nodes.length; i<nl; i++) {
- var node = rowsCache[nodes[i]];
- node.parentNode.removeChild(node);
- delete rowsCache[nodes[i]];
- delete postProcessedRows[nodes[i]];
- renderedRows--;
- counter_rows_removed++;
- }
- }
- }
-
- function updateCell(row,cell) {
- if (!rowsCache[row]) return;
- var $cell = $(rowsCache[row]).find(".c[cell=" + cell + "]");
- if ($cell.length == 0) return;
-
- var m = columns[cell], d = data[row];
- if (currentEditor && currentRow == row && currentCell == cell)
- currentEditor.setValue(d[m.field]);
- else {
- $cell[0].innerHTML = d ? m.formatter(row, cell, d[m.field], m, d) : "";
- invalidatePostProcessingResults(row);
- }
- }
-
- function updateRow(row) {
- if (!rowsCache[row]) return;
-
- // todo: perf: iterate over direct children?
- $(rowsCache[row]).find(".c").each(function(i) {
- var m = columns[i];
- if (row == currentRow && i == currentCell && currentEditor)
- currentEditor.setValue(data[currentRow][m.field]);
- else if (data[row])
- this.innerHTML = m.formatter(row, i, data[row][m.field], m, data[row]);
- else
- this.innerHTML = "";
- });
-
- invalidatePostProcessingResults(row);
- }
-
- function resizeCanvas() {
- viewportW = $divMainScroller.innerWidth();
- viewportH = $divMainScroller.innerHeight();
- BUFFER = numVisibleRows = Math.ceil(viewportH / options.rowHeight);
- CAPACITY = Math.max(50, numVisibleRows + 2*BUFFER);
-
- var totalWidth = 0;
- for (var i=0; i<columns.length; i++) {
- if (columns[i].hidden != true)
- totalWidth += columns[i].width;
- }
- $divMain.width(totalWidth);
-
- var newHeight = Math.max(options.rowHeight * (data.length - 1 + (options.leaveSpaceForNewRows?numVisibleRows-1:0)), viewportH - $.getScrollbarWidth());
- $divMainScroller.height( $container.innerHeight() - $divHeadersScroller.outerHeight() );
-
- // browsers sometimes do not adjust scrollTop/scrollHeight when the height of contained objects changes
- if ($divMainScroller.scrollTop() > newHeight - $divMainScroller.height() + $.getScrollbarWidth()) {
- $divMainScroller.scrollTop(newHeight - $divMainScroller.height() + $.getScrollbarWidth());
- }
- $divMain.height(newHeight);
-
- render();
- }
-
- function updateRowCount() {
- // 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 parentNode = $divMain[0];
- var l = options.enableAddRow ? data.length : data.length - 1;
- for (var i in rowsCache) {
- if (i >= l) {
- parentNode.removeChild(rowsCache[i]);
- delete rowsCache[i];
- delete postProcessedRows[i];
- renderedRows--;
- counter_rows_removed++;
- }
- }
-
- var newHeight = Math.max(options.rowHeight * (data.length - 1 + (options.leaveSpaceForNewRows?numVisibleRows-1:0)), viewportH - $.getScrollbarWidth());
-
- // browsers sometimes do not adjust scrollTop/scrollHeight when the height of contained objects changes
- if ($divMainScroller.scrollTop() > newHeight - $divMainScroller.height() + $.getScrollbarWidth())
- $divMainScroller.scrollTop(newHeight - $divMainScroller.height() + $.getScrollbarWidth());
- $divMain.height(newHeight);
- }
-
- function getViewport()
- {
- return {
- top: Math.floor(currentScrollTop / options.rowHeight),
- bottom: Math.floor((currentScrollTop + viewportH) / options.rowHeight)
- };
- }
-
- function renderRows(from,to) {
- var parentNode = $divMain[0];
- var rowsBefore = renderedRows;
- var stringArray = [], rows =[];
- var _start = new Date();
-
- for (var i = from; i <= to; i++) {
- if (rowsCache[i]) continue;
- renderedRows++;
- rows.push(i);
- appendRowHtml(stringArray,i);
- counter_rows_rendered++;
- }
-
- var x = document.createElement("div");
- x.innerHTML = stringArray.join("");
-
- for (var i = 0, l = x.childNodes.length; i < l; i++)
- rowsCache[rows[i]] = parentNode.appendChild(x.firstChild);
-
- if (renderedRows - rowsBefore > MIN_BUFFER)
- avgRowRenderTime = (new Date() - _start) / (renderedRows - rowsBefore);
- }
-
- function startPostProcessing() {
- window.clearTimeout(h_postrender);
- h_postrender = window.setTimeout(processPostRenderChunk, POSTPROCESSING_DELAY);
- }
-
- function invalidatePostProcessingResults(row) {
- delete postProcessedRows[row];
- rowsToPostProcess.unshift(row);
- startPostProcessing();
- }
-
- function render() {
- var vp = getViewport();
- var from = Math.max(0, vp.top - (scrollDir >= 0 ? MIN_BUFFER : BUFFER));
- var to = Math.min(options.enableAddRow ? data.length : data.length - 1, vp.bottom + (scrollDir > 0 ? BUFFER : MIN_BUFFER));
-
- if (renderedRows > 10 && Math.abs(lastRenderedScrollTop - currentScrollTop) > options.rowHeight*CAPACITY)
- removeAllRows();
- else
- cleanupRows(from,to);
-
- renderRows(from,to);
-
- rowsToPostProcess = [];
- from = Math.max(0,vp.top-MIN_BUFFER);
- to = Math.min(options.enableAddRow ? data.length : data.length - 1, vp.bottom+MIN_BUFFER);
- for (var i=from; i<=to; i++) {
- rowsToPostProcess.push(i);
- }
-
- startPostProcessing();
-
- lastRenderedScrollTop = currentScrollTop;
- h_render = null;
- }
-
- function handleScroll() {
- currentScrollTop = $divMainScroller[0].scrollTop;
- var scrollDistance = Math.abs(lastRenderedScrollTop - currentScrollTop);
- var scrollLeft = $divMainScroller[0].scrollLeft;
-
- if (scrollLeft != currentScrollLeft)
- $divHeadersScroller[0].scrollLeft = currentScrollLeft = scrollLeft;
-
- // min scroll distance = 25% of the viewport or MIN_BUFFER rows (whichever is smaller)
- if (scrollDistance < Math.min(viewportH/4, MIN_BUFFER*options.rowHeight)) return;
-
- if (lastRenderedScrollTop == currentScrollTop)
- scrollDir = 0;
- else if (lastRenderedScrollTop < currentScrollTop)
- scrollDir = 1;
- else
- scrollDir = -1;
-
- if (h_render)
- window.clearTimeout(h_render);
-
- if (scrollDistance < numVisibleRows*options.rowHeight)
- render();
- else
- h_render = window.setTimeout(render, 50);
-
- if (self.onViewportChanged)
- self.onViewportChanged();
- }
-
- function processPostRenderChunk() {
- if (rowsToPostProcess.length == 0) return;
- while (rowsToPostProcess.length > 0) {
- var row = rowsToPostProcess.shift();
- if (postProcessedRows[row] || row>=data.length) continue;
- var node = rowsCache[row];
- if (!node) continue;
-
- if (self.onPostProcessRowNode)
- self.onPostProcessRowNode(node, row, data[row]);
- startPostProcessing();
- postProcessedRows[row] = true;
- return;
- }
- }
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Interactivity
-
- function handleKeyDown(e) {
- // do we have any registered handlers?
- if (self.onKeyDown && data[currentRow]) {
- // grid must not be in edit mode
- if (!currentEditor) {
- // handler will return true if the event was handled
- if (self.onKeyDown(e, currentRow, currentCell)) {
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
- }
- }
-
- switch (e.which) {
- case 27: // esc
- if (GlobalEditorLock.isEditing() && GlobalEditorLock.hasLock(self))
- cancelCurrentEdit(self);
-
- if (currentCellNode)
- currentCellNode.focus();
-
- break;
-
- case 9: // tab
- gotoDir(0, (e.shiftKey) ? -1 : 1, true);
- break;
-
- case 37: // left
- gotoDir(0,-1);
- break;
-
- case 39: // right
- gotoDir(0,1);
- break;
-
- case 38: // up
- gotoDir(-1,0);
- break;
-
- case 40: // down
- case 13: // enter
- gotoDir(1,0);
- break;
-
- default:
- // exit without cancelling the event
- return;
- }
-
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
-
- function handleClick(e) {
- var $cell = $(e.target).closest(".c");
- if ($cell.length == 0) return;
-
- // are we editing this cell?
- if (currentCellNode == $cell[0] && currentEditor != null) return;
-
- var row = parseInt($cell.parent().attr("row"));
- var cell = parseInt($cell.attr("cell"));
- var validated = null;
-
- // do we have any registered handlers?
- if (data[row] && self.onClick) {
- // grid must not be in edit mode
- if (!currentEditor || (validated = commitCurrentEdit())) {
- // handler will return true if the event was handled
- if (self.onClick(e, row, cell)) {
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
- }
- }
-
- if (options.enableCellNavigation && !columns[cell].unselectable) {
- // commit current edit before proceeding
- if (validated == true || (validated == null && commitCurrentEdit()))
- setSelectedCellAndRow($cell[0]);
- }
- }
-
- function handleContextMenu(e) {
- var $cell = $(e.target).closest(".c");
- if ($cell.length == 0) return;
-
- // are we editing this cell?
- if (currentCellNode == $cell[0] && currentEditor != null) return;
-
- var row = parseInt($cell.parent().attr("row"));
- var cell = parseInt($cell.attr("cell"));
- var validated = null;
-
- // do we have any registered handlers?
- if (data[row] && self.onContextMenu) {
- // grid must not be in edit mode
- if (!currentEditor || (validated = commitCurrentEdit())) {
- // handler will return true if the event was handled
- if (self.onContextMenu(e, row, cell)) {
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
- }
- }
- }
-
- function handleDblClick(e) {
- var $cell = $(e.target).closest(".c");
- if ($cell.length == 0) return;
-
- // are we editing this cell?
- if (currentCellNode == $cell[0] && currentEditor != null) return;
-
- var row = parseInt($cell.parent().attr("row"));
- var cell = parseInt($cell.attr("cell"));
- var validated = null;
-
- // do we have any registered handlers?
- if (data[row] && self.onDblClick) {
- // grid must not be in edit mode
- if (!currentEditor || (validated = commitCurrentEdit())) {
- // handler will return true if the event was handled
- if (self.onDblClick(e, row, cell)) {
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
- }
- }
-
- if (options.editOnDoubleClick)
- makeSelectedCellEditable();
- }
-
- function handleHeaderContextMenu(e) {
- if (self.onHeaderContextMenu && (!currentEditor || (validated = commitCurrentEdit()))) {
- e.preventDefault();
- // TODO: figure out which column was acted on and pass it as a param to the handler
- self.onHeaderContextMenu(e);
- }
- }
-
- function getCellFromPoint(x,y) {
- var row = Math.floor(y/options.rowHeight);
- var cell = 0;
-
- var w = 0;
- for (var i=0; i<columns.length && w<y; i++) {
- w += columns[i].width;
- cell++;
- }
-
- return {row:row,cell:cell-1};
- }
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Cell switching
-
- function setSelectedCell(newCell,async) {
- if (currentCellNode != null) {
- makeSelectedCellNormal();
- $(currentCellNode).removeClass("selected");
- }
-
- currentCellNode = newCell;
-
- if (currentCellNode != null) {
- currentRow = parseInt($(currentCellNode).parent().attr("row"));
- currentCell = parseInt($(currentCellNode).attr("cell"));
-
- $(currentCellNode).addClass("selected");
-
- scrollSelectedCellIntoView();
-
- if (options.editable && !options.editOnDoubleClick && (data[currentRow] || currentRow == data.length)) {
- window.clearTimeout(h_editorLoader);
-
- if (async)
- h_editorLoader = window.setTimeout(makeSelectedCellEditable, 100);
- else
- makeSelectedCellEditable();
- }
- }
- else {
- currentRow = null;
- currentCell = null;
- }
- }
-
- function setSelectedCellAndRow(newCell,async) {
- setSelectedCell(newCell,async);
-
- if (newCell)
- setSelectedRows([currentRow]);
- else
- setSelectedRows([]);
-
- if (self.onSelectedRowsChanged)
- self.onSelectedRowsChanged();
- }
-
- 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 isCellPotentiallyEditable(row,cell) {
- // is the data for this row loaded?
- if (row < data.length && !data[row])
- return false;
-
- // are we in the Add New row? can we create new from this cell?
- if (columns[cell].cannotTriggerInsert && row >= data.length)
- return false;
-
- // does this cell have an editor?
- if (!columns[cell].editor)
- return false;
-
- return true;
- }
-
- function makeSelectedCellNormal() {
- if (!currentEditor) return;
-
- currentEditor.destroy();
- $(currentCellNode).removeClass("editable invalid");
-
- if (data[currentRow]) {
- currentCellNode.innerHTML = columns[currentCell].formatter(currentRow, currentCell, data[currentRow][columns[currentCell].field], columns[currentCell], data[currentRow]);
- invalidatePostProcessingResults(currentRow);
- }
-
- currentEditor = null;
-
- // 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();
-
- GlobalEditorLock.leaveEditMode(self);
- }
-
- function makeSelectedCellEditable() {
- if (!currentCellNode) return;
- if (!options.editable)
- throw "Grid : makeSelectedCellEditable : should never get called when options.editable is false";
-
- // cancel pending async call if there is one
- window.clearTimeout(h_editorLoader);
-
- if (!isCellPotentiallyEditable(currentRow,currentCell))
- return;
-
- GlobalEditorLock.enterEditMode(self);
-
- $(currentCellNode).addClass("editable");
-
- var value = null;
-
- // if there is a corresponding row
- if (data[currentRow])
- value = data[currentRow][columns[currentCell].field];
-
- currentCellNode.innerHTML = "";
-
- currentEditor = new columns[currentCell].editor($(currentCellNode), columns[currentCell], value, data[currentRow]);
- }
-
- function scrollSelectedCellIntoView() {
- if (!currentCellNode) return;
- var scrollTop = $divMainScroller[0].scrollTop;
-
- // need to page down?
- if ((currentRow + 2) * options.rowHeight > scrollTop + viewportH) {
- $divMainScroller[0].scrollTop = (currentRow ) * options.rowHeight;
- handleScroll();
- }
- // or page up?
- else if (currentRow * options.rowHeight < scrollTop) {
- $divMainScroller[0].scrollTop = (currentRow + 2) * options.rowHeight - viewportH;
- handleScroll();
- }
- }
-
- function gotoDir(dy, dx, rollover) {
- if (!currentCellNode) return;
- if (!options.enableCellNavigation) return;
- if (!GlobalEditorLock.commitCurrentEdit()) return;
-
- var nextRow = rowsCache[currentRow + dy];
- var nextCell = nextRow ? $(nextRow).find(".c[cell=" + (currentCell + dx) + "][tabIndex=0]") : null;
-
- if (rollover && dy == 0 && !(nextRow && nextCell && nextCell.length)) {
- if (!nextCell || !nextCell.length) {
- if (dx > 0) {
- nextRow = rowsCache[currentRow + dy + 1];
- nextCell = nextRow ? $(nextRow).find(".c[cell][tabIndex=0]:first") : null;
- }
- else {
- nextRow = rowsCache[currentRow + dy - 1];
- nextCell = nextRow ? $(nextRow).find(".c[cell][tabIndex=0]:last") : null;
- }
- }
- }
-
-
- if (nextRow && nextCell && nextCell.length) {
- setSelectedCellAndRow(nextCell[0],options.asyncEditorLoading);
-
- // if no editor was created, set the focus back on the cell
- if (!currentEditor)
- currentCellNode.focus();
- }
- else
- currentCellNode.focus();
- }
-
- function gotoCell(row,cell) {
- if (row > data.length || row < 0 || cell >= columns.length || cell < 0) return;
- if (!options.enableCellNavigation || columns[cell].unselectable) return;
-
- if (!GlobalEditorLock.commitCurrentEdit()) return;
-
- if (!rowsCache[row])
- renderRows(row,row);
-
- var cell = $(rowsCache[row]).find(".c[cell=" + cell + "][tabIndex=0]")[0];
-
- setSelectedCellAndRow(cell);
-
- // if no editor was created, set the focus back on the cell
- if (!currentEditor)
- currentCellNode.focus();
- }
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // IEditor implementation for GlobalEditorLock
-
- function commitCurrentEdit() {
- if (currentEditor) {
- if (currentEditor.isValueChanged()) {
- var validationResults = currentEditor.validate();
-
- if (validationResults.valid) {
- var value = currentEditor.getValue();
-
- if (currentRow < data.length) {
- if (columns[currentCell].setValueHandler) {
- makeSelectedCellNormal();
- columns[currentCell].setValueHandler(value, columns[currentCell], data[currentRow]);
- }
- else {
- data[currentRow][columns[currentCell].field] = value;
- makeSelectedCellNormal();
- }
- }
- else if (self.onAddNewRow) {
- makeSelectedCellNormal();
- self.onAddNewRow(columns[currentCell], value);
- }
-
- return true;
- }
- else {
- $(currentCellNode).addClass("invalid");
- $(currentCellNode).stop(true,true).effect("highlight", {color:"red"}, 300);
-
- if (self.onValidationError)
- self.onValidationError(currentCellNode, validationResults, currentRow, currentCell, columns[currentCell]);
-
- currentEditor.focus();
- return false;
- }
- }
-
- makeSelectedCellNormal();
- }
-
-
- return true;
- }
-
- function cancelCurrentEdit() {
- makeSelectedCellNormal();
- }
-
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Debug
-
- this.debug = function() {
- var s = "";
-
- 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" + "CAPACITY: " + CAPACITY);
- s += ("\n" + "BUFFER: " + BUFFER);
- s += ("\n" + "avgRowRenderTime: " + avgRowRenderTime);
-
- alert(s);
- };
-
- this.benchmark_render_200 = function() {
- removeAllRows();
-
- // render 200 rows in the viewport
- renderRows(0, 200);
-
- cleanupRows();
- };
-
- this.stressTest = function() {
- console.time("benchmark-stress");
-
- renderRows(0,500);
-
- cleanupRows();
-
- console.timeEnd("benchmark-stress");
-
- window.setTimeout(self.stressTest, 50);
- };
-
- this.benchmarkFn = function(fn) {
- var s = new Date();
-
- var args = new Array(arguments);
- args.splice(0,1);
-
- self[fn].call(this,args);
-
- alert("Grid : benchmarkFn : " + fn + " : " + (new Date() - s) + "ms");
- };
-
-
-
-
- init();
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Public API
-
- $.extend(this, {
- // Events
- "onSort": null,
- "onHeaderContextMenu": null,
- "onClick": null,
- "onContextMenu": null,
- "onKeyDown": null,
- "onAddNewRow": null,
- "onValidationError": null,
- "onViewportChanged": null,
- "onSelectedRowsChanged": null,
- "onColumnsReordered": null,
- "onBeforeMoveRows" : null,
- "onMoveRows": null,
- "onPostProcessRowNode": null,
-
- // Methods
- "setOptions": setOptions,
- "setData": setData,
- "destroy": destroy,
- "getColumnIndex": getColumnIndex,
- "setColumnVisibility": setColumnVisibility,
- "autosizeColumns": autosizeColumns,
- "updateCell": updateCell,
- "updateRow": updateRow,
- "removeRow": removeRow,
- "removeRows": removeRows,
- "removeAllRows": removeAllRows,
- "render": render,
- "getViewport": getViewport,
- "resizeCanvas": resizeCanvas,
- "updateRowCount": updateRowCount,
- "scroll": scroll, // TODO
- "getCellFromPoint": getCellFromPoint,
- "gotoCell": gotoCell,
- "editCurrentCell": makeSelectedCellEditable,
- "getSelectedRows": getSelectedRows,
- "setSelectedRows": setSelectedRows,
-
- // IEditor implementation
- "commitCurrentEdit": commitCurrentEdit,
- "cancelCurrentEdit": cancelCurrentEdit
- });
-}
+/*** + * + * (c) 2009 Michael Leibman (michael.leibman@gmail.com) + * All rights reserved. + * + * + * TODO: + * - frozen columns + * - consistent events (EventHelper? jQuery events?) + * + * + * KNOWN ISSUES: + * - keyboard navigation doesn't "jump" over unselectable cells for now +* + * + * OPTIONS: + * rowHeight - Row height in pixels. + * enableAddRow - If true, a blank row will be displayed at the bottom - typing values in that row will add a new one. + * manualScrolling - Disable automatic rerender on scroll. Client will take care of calling Grid.onScroll(). + * editable - If false, no cells will be switched into edit mode. + * editOnDoubleClick - Cell will not automatically go into edit mode without being double-clicked. + * enableCellNavigation - If false, no cells will be selectable. + * defaultColumnWidth - Default column width in pixels (if columns[cell].width is not specified). + * enableColumnReorder - Allows the user to reorder columns. + * asyncEditorLoading - Makes cell editors load asynchronously after a small delay. + * This greatly increases keyboard navigation speed. + * forceFitColumns - Force column sizes to fit into the viewport (avoid horizontal scrolling). + * + * + * COLUMN DEFINITION (columns) OPTIONS: + * id - Column ID. + * name - Column name to put in the header. + * field - Property of the data context to bind to. + * formatter - Function responsible for rendering the contents of a cell. + * editor - An Editor class. + * validator - An extra validation function to be passed to the editor. + * unselectable - If true, the cell cannot be selected (and therefore edited). + * cannotTriggerInsert - If true, a new row cannot be created from just the value of this cell. + * setValueHandler - If true, this handler will be called to set field value instead of context[field]. + * width - Width of the column in pixels. + * resizable - If false, the column cannot be resized. + * sortable - If true, the column can be sorted (onSort will be called). + * minWidth - Minimum allowed column width for resizing. + * maxWidth - Maximum allowed column width for resizing. + * cssClass - A CSS class to add to the cell. + * rerenderOnResize - Rerender the column when it is resized (useful for columns relying on cell width or adaptive formatters). + * + * + * EVENTS: + * + * ... + * + * + * NOTES: + * + * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. + * This increases the speed dramatically, but can only be done safely because there are no event handlers + * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy() + * and do proper cleanup. + * + * + * @param {jQuery} $container Container object to create the grid in. + * @param {Array} 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 = { + rowHeight: 25, + defaultColumnWidth: 80, + enableAddRow: true, + leaveSpaceForNewRows: false, + manualScrolling: false, + editable: true, + editOnDoubleClick: false, + enableCellNavigation: true, + enableColumnReorder: true, + asyncEditorLoading: false, + forceFitColumns: false + }; + + var columnDefaults = { + resizable: true, + sortable: true, + formatter: defaultFormatter + } + + // consts + var CAPACITY = 50; + var MIN_BUFFER = 5; + var BUFFER = MIN_BUFFER; // will be set to equal one page + var POSTPROCESSING_DELAY = 50; + + // private + var uid = "slickgrid_" + Math.round(1000000 * Math.random()); + var self = this; + var $divHeadersScroller; + var $divHeaders; + var $divMainScroller; + var $divMain; + var viewportH, viewportW; + var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff; // padding+border + + var currentRow, currentCell; + var currentCellNode = null; + var currentEditor = null; + + var rowsCache = {}; + var renderedRows = 0; + var numVisibleRows; + var lastRenderedScrollTop = 0; + var currentScrollTop = 0; + var currentScrollLeft = 0; + var scrollDir = 1; + var avgRowRenderTime = 10; + + var selectedRows = []; + var selectedRowsLookup = {}; + var columnsById = {}; + + // async call handles + var h_editorLoader = null; + var h_render = null; + var h_postrender = null; + var postProcessedRows = {}; + var rowsToPostProcess = []; + + // perf counters + var counter_rows_rendered = 0; + var counter_rows_removed = 0; + + + function init() { + options = $.extend({},defaults,options); + columnDefaults.width = options.defaultColumnWidth; + + $container + .empty() + .attr("tabIndex",0) + .attr("hideFocus",true) + .css("overflow","hidden") + .css("outline",0) + .css("position","relative") + .addClass(uid); + + $divHeadersScroller = $("<div class='slick-header' style='overflow:hidden;position:relative;' />").appendTo($container); + $divHeaders = $("<div class='slick-header-columns' style='width:100000px' />").appendTo($divHeadersScroller); + $divMainScroller = $("<div tabIndex='0' hideFocus style='width:100%;overflow:scroll;outline:0;position:relative;outline:0px;'>").appendTo($container); + $divMain = $("<div class='grid-canvas' tabIndex='0' hideFocus />").appendTo($divMainScroller); + + // 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(); + + $divMainScroller.height($container.innerHeight() - $divHeadersScroller.outerHeight()); + + if ($.browser.msie) + $divMainScroller[0].onselectstart = function() { + if (event.srcElement.tagName != "INPUT" && event.srcElement.tagName != "TEXTAREA") + return false; + }; + + $divHeaders.disableSelection(); + + createColumnHeaders(); + setupMoveEvents(); + createCssRules(); + resizeCanvas(); + if (options.forceFitColumns) + autosizeColumns(); + render(); + + if (!options.manualScrolling) + $divMainScroller.bind("scroll", handleScroll); + + $container.bind("resize", resizeCanvas); + + $divMain.bind("keydown", handleKeyDown); + $divMain.bind("click", handleClick); + $divMain.bind("dblclick", handleDblClick); + $divMain.bind("contextmenu", handleContextMenu) + $divHeadersScroller.bind("contextmenu", handleHeaderContextMenu); + } + + function createColumnHeaders() { + for (var i = 0; i < columns.length; i++) { + var m = columns[i] = $.extend({},columnDefaults,columns[i]); + columnsById[m.id] = i; + + var header = $("<div class='slick-header-column' cell=" + i + " id='" + m.id + "' />") + .html(m.name) + .width(m.width - headerColumnWidthDiff) + .appendTo($divHeaders); + + if (m.sortable) header.append("<span class='slick-sort-indicator' />") + if (m.resizable) header.append("<div class='slick-resizable-handle' />"); + } + + setupColumnSort(); + setupColumnResizeEvents(); + if (options.enableColumnReorder) + setupColumnReorderEvents(); + } + + function setupColumnSort() { + $divHeaders.click(function(e) { + var $col = $(e.target); + if (!$col.hasClass("slick-header-column") || !columns[columnsById[$col.attr("id")]].sortable) + return; + + if (currentEditor && !commitCurrentEdit()) return; + + if ($col.is(".slick-header-column-sorted")) + { + $col.find(".slick-sort-indicator").toggleClass("slick-sort-indicator-asc").toggleClass("slick-sort-indicator-desc"); + } + else + { + $divHeaders.children().removeClass("slick-header-column-sorted"); + $divHeaders.find(".slick-sort-indicator").removeClass("slick-sort-indicator-asc slick-sort-indicator-desc"); + $col.addClass("slick-header-column-sorted"); + $col.find(".slick-sort-indicator").addClass("slick-sort-indicator-asc"); + } + + if (self.onSort) + self.onSort(columns[columnsById[$col.attr("id")]], $col.find(".slick-sort-indicator").hasClass("slick-sort-indicator-asc")); + }) + } + + function setupColumnReorderEvents() { + $divHeaders.sortable({ + axis: "x", + cursor: "default", + tolerance: "intersect", + helper: "clone", + placeholder: "slick-sortable-placeholder 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, ui) { + if (currentEditor && !commitCurrentEdit()) { + $(this).sortable("cancel"); + return; + } + + var newOrder = $divHeaders.sortable("toArray"), lookup = {}; + for (var i=0; i<columns.length; i++) { + lookup[columns[i].id] = columns[i]; + } + + for (var i=0; i<newOrder.length; i++) { + columnsById[newOrder[i]] = i; + columns[i] = lookup[newOrder[i]]; + } + + removeAllRows(); + removeCssRules(); + createCssRules(); + render(); + + if (self.onColumnsReordered) + self.onColumnsReordered(); + + e.stopPropagation(); + } + }) + } + + function setupColumnResizeEvents() { + $divHeaders + .find(".slick-resizable-handle") + .bind('dragstart', function(e) { + var $col = $(this).parent(); + var colId = $col.attr("id"); + if (!columns[columnsById[colId]].resizable) return false; + if (currentEditor && !commitCurrentEdit()) return false; + + $col + .data("colId", colId) + .data("width", $col.width()) + .data("pageX", e.pageX) + .addClass("slick-header-column-active"); + }) + .bind('drag', function(e) { + var $col = $(this).parent(), w = $col.data("width") - $col.data("pageX") + e.pageX; + var cell = columnsById[$col.data("colId")]; + var m = columns[cell]; + if (m.minWidth) w = Math.max(m.minWidth - headerColumnWidthDiff,w); + if (m.maxWidth) w = Math.min(m.maxWidth - headerColumnWidthDiff,w); + $col.css({ width: Math.max(0, w) }); + }) + .bind('dragend', function(e) { + var $col = $(this).parent(); + var cell = columnsById[$col.data("colId")]; + $col.removeClass("slick-header-column-active"); + columns[cell].width = $col.outerWidth(); + + if (options.forceFitColumns) + autosizeColumns(columns[cell]); + else { + updateColumnWidth(cell, $col.outerWidth()); + resizeCanvas(); + } + + if (columns[cell].rerenderOnResize) + removeAllRows(); + + render(); + }) + } + + function setupMoveEvents() { + $divMain + .bind("beforedragstart", function(e) { + var $cell = $(e.target).closest(".c"); + if ($cell.length == 0) return false; + if (parseInt($cell.parent().attr("row")) >= data.length) return false; + var colDef = columns[$cell.attr("cell")]; + if (colDef.behavior != "move") return false; + }) + .bind("dragstart", function(e) { + if (currentEditor && !commitCurrentEdit()) return false; + + var row = parseInt($(e.target).closest(".r").attr("row")); + + if (!selectedRowsLookup[row]) + setSelectedRows([row]); + + var $selectionProxy = $("<div class='slick-reorder-proxy'/>"); + $selectionProxy + .css("position", "absolute") + .css("zIndex", "99999") + .css("width", $(this).innerWidth()) + .css("height", options.rowHeight*selectedRows.length) + .appendTo($divMainScroller); + + $(this) + .data("selectionProxy", $selectionProxy) + .data("insertBefore", -1); + + var $guide = $("<div class='slick-reorder-guide'/>"); + $guide + .css("position", "absolute") + .css("zIndex", "99998") + .css("width", $(this).innerWidth()) + .css("top", -1000) + .appendTo($divMainScroller); + + return $guide; + }) + .bind("drag", function(e) { + var top = e.clientY - $(this).offset().top; + $(this).data("selectionProxy").css("top",top-5); + + var insertBefore = Math.max(0,Math.min(Math.round(top/options.rowHeight),data.length)); + if (insertBefore != $(this).data("insertBefore")) { + if (self.onBeforeMoveRows && self.onBeforeMoveRows(selectedRows.concat(),insertBefore) === false) + $(e.dragProxy).css("top", -1000); + else + $(e.dragProxy).css("top",insertBefore*options.rowHeight); + $(this).data("insertBefore", insertBefore); + } + }) + .bind("dragend", function(e) { + $(e.dragProxy).remove(); + $(this).data("selectionProxy").remove(); + var insertBefore = $(this).data("insertBefore"); + $(this).removeData("selectionProxy").removeData("insertBefore"); + if (self.onMoveRows) self.onMoveRows(selectedRows.concat(),insertBefore); + }) + } + + function measureCellPaddingAndBorder() { + var tmp = $("<div class='slick-header-column cell='' id='' style='visibility:hidden'>-</div>").appendTo($divHeaders); + headerColumnWidthDiff = tmp.outerWidth() - tmp.width(); + headerColumnHeightDiff = tmp.outerHeight() - tmp.height(); + tmp.remove(); + + var r = $("<div class='r' />").appendTo($divMain); + tmp = $("<div class='c' cell='' id='' style='visibility:hidden'>-</div>").appendTo(r); + cellWidthDiff = tmp.outerWidth() - tmp.width(); + cellHeightDiff = tmp.outerHeight() - tmp.height(); + r.remove(); + } + + function createCssRules() { + var $style = $("<style type='text/css' rel='stylesheet' lib='slickgrid' />").appendTo($("head")); + $.rule(".grid-canvas .r .c { height:" + (options.rowHeight - cellHeightDiff) + "px;}").appendTo($style); + + for (var i = 0; i < columns.length; i++) { + $.rule( + "." + uid + " .grid-canvas .c" + i + " { " + + "width:" + (columns[i].width - cellWidthDiff) + "px; " + + "display: " + (columns[i].hidden ? "none" : "block") + + " }").appendTo($style); + } + } + + function removeCssRules() { + $("style[lib=slickgrid]").remove(); + } + + function destroy() { + if (currentEditor) + cancelCurrentEdit(); + + $divHeaders.sortable("destroy"); + $container.unbind("resize", resizeCanvas); + removeCssRules(); + + $container.empty().removeClass(uid); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // General + + function getColumnIndex(id) { + return columnsById[id]; + } + + function autosizeColumns(columnToHold) { + var availWidth = viewportW-$.getScrollbarWidth(); + var total = 0; + var existingTotal = 0; + var minWidth = Math.max(headerColumnWidthDiff,cellWidthDiff); + + for (var i = 0; i < columns.length; i++) { + if (!columns[i].hidden) + existingTotal += columns[i].width; + } + + total = existingTotal; + + removeAllRows(); + + // shrink + var workdone = true; + while (total > availWidth && workdone) { + workdone = false; + for (var i = 0; i < columns.length && total > availWidth; i++) { + var c = columns[i]; + if (c.hidden || !c.resizable || c.minWidth == c.width || c.width == minWidth || (columnToHold && columnToHold.id == c.id)) continue; + total -= 1; + c.width -= 1; + workdone = true; + } + } + + // shrink the column being "held" as a last resort + if (total > availWidth && columnToHold && columnToHold.resizable && !columnToHold.hidden) { + while (total > availWidth) { + if (columnToHold.minWidth == columnToHold.width || columnToHold.width == minWidth) break; + total -= 1; + columnToHold.width -= 1; + } + } + + // grow + workdone = true; + while (total < availWidth && workdone) { + workdone = false; + for (var i = 0; i < columns.length && total < availWidth; i++) { + var c = columns[i]; + if (c.hidden || !c.resizable || c.maxWidth == c.width || (columnToHold && columnToHold.id == c.id)) continue; + total += 1; + c.width += 1; + workdone = true; + } + } + + // grow the column being "held" as a last resort + if (total < availWidth && columnToHold && columnToHold.resizable && !columnToHold.hidden) { + while (total < availWidth) { + if (columnToHold.maxWidth == columnToHold.width) break; + total += 1; + columnToHold.width += 1; + } + } + + for (var i=0; i<columns.length; i++) { + updateColumnWidth(i, columns[i].width); + } + + resizeCanvas(); + } + + function updateColumnWidth(index,width) { + columns[index].width = width; + $divHeaders.find(".slick-header-column[id=" + columns[index].id + "]").css("width",width - headerColumnWidthDiff); + $.rule("." + uid + " .grid-canvas .c" + index, "style[lib=slickgrid]").css("width", (columns[index].width - cellWidthDiff) + "px"); + } + + function setColumnVisibility(column,visible) { + var index = columnsById[column.id]; + columns[index].hidden = !visible; + resizeCanvas(); + var header = $divHeaders.find("[id=" + columns[index].id + "]"); + header.css("display", visible?"block":"none"); + $.rule("." + uid + " .grid-canvas .c" + index, "style[lib=slickgrid]").css("display", visible?"block":"none"); + + if (options.forceFitColumns) + autosizeColumns(columns[index]); + } + + function getSelectedRows() { + return selectedRows.concat(); + } + + function setSelectedRows(rows) { + if (GlobalEditorLock.isEditing() && !GlobalEditorLock.hasLock(self)) + throw "Grid : setSelectedRows : cannot set selected rows when somebody else has an edit lock"; + + var lookup = {}; + for (var i=0; i<rows.length; i++) + lookup[rows[i]] = true; + + // unselect old rows + for (var i=0; i<selectedRows.length; i++) { + var row = selectedRows[i]; + if (rowsCache[row] && !lookup[row]) + $(rowsCache[row]).removeClass("selected"); + } + + // select new ones + for (var i=0; i<rows.length; i++) { + var row = rows[i]; + if (rowsCache[row] && !selectedRowsLookup[row]) + $(rowsCache[row]).addClass("selected"); + } + + selectedRows = rows.concat(); + selectedRowsLookup = lookup; + } + + function setOptions(args) { + if (currentEditor && !commitCurrentEdit()) + return; + + makeSelectedCellNormal(); + + if (options.enableAddRow != args.enableAddRow) + removeRow(data.length); + + options = $.extend(options,args); + + render(); + } + + function setData(newData,scrollToTop) + { + removeAllRows(); + data = newData; + if (scrollToTop) + $divMainScroller.scrollTop(0); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Rendering / Scrolling + + function defaultFormatter(row, cell, value, columnDef, dataContext) { + return (value == null || value == undefined) ? "" : value; + } + + function appendRowHtml(stringArray,row) { + var d = data[row]; + var dataLoading = row < data.length && !d; + var css = "r" + (dataLoading ? " loading" : "") + (selectedRowsLookup[row] ? " selected" : ""); + + stringArray.push("<div class='" + css + "' row='" + row + "' style='top:" + (options.rowHeight*row) + "px'>"); + + for (var i=0, cols=columns.length; i<cols; i++) { + var m = columns[i]; + + stringArray.push("<div " + (m.unselectable ? "" : "hideFocus tabIndex=0 ") + "class='c c" + i + (m.cssClass ? " " + m.cssClass : "") + "' cell=" + i + ">"); + + // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) + if (d && row < data.length) + stringArray.push(m.formatter(row, i, d[m.field], m, d)); + + stringArray.push("</div>"); + } + + stringArray.push("</div>"); + } + + function getRowHtml(row) { + var html = []; + appendRowHtml(html,row); + return html.join(""); + } + + function cleanupRows(visibleFrom,visibleTo) { + var rowsBefore = renderedRows; + var parentNode = $divMain[0]; + for (var i in rowsCache) { + if ((i < visibleFrom || i > visibleTo) && i != currentRow) { + parentNode.removeChild(rowsCache[i]); + delete rowsCache[i]; + delete postProcessedRows[i]; + renderedRows--; + counter_rows_removed++; + } + } + } + + function removeAllRows() { + $divMain[0].innerHTML = ""; + rowsCache= {}; + postProcessedRows = {}; + counter_rows_removed += renderedRows; + renderedRows = 0; + } + + function removeRow(row) { + var node = rowsCache[row]; + if (!node) return; + + if (currentEditor && currentRow == row) + throw "Grid : removeRow : Cannot remove a row that is currently in edit mode"; + + // if we're removing rows, we're probably not scrolling + scrollDir = 0; + + node.parentNode.removeChild(node); + node = null; + delete rowsCache[row]; + delete postProcessedRows[row]; + renderedRows--; + counter_rows_removed++; + } + + function removeRows(rows) { + if (!rows || !rows.length) return; + scrollDir = 0; + var nodes = []; + for (var i=0, rl=rows.length; i<rl; i++) { + if (currentEditor && currentRow == i) + throw "Grid : removeRow : Cannot remove a row that is currently in edit mode"; + + if (rowsCache[rows[i]]) + nodes.push(rows[i]); + } + + if (renderedRows > 10 && nodes.length == renderedRows) { + $divMain[0].innerHTML = ""; + rowsCache= {}; + postProcessedRows = {}; + counter_rows_removed += renderedRows; + renderedRows = 0; + } else { + for (var i=0, nl=nodes.length; i<nl; i++) { + var node = rowsCache[nodes[i]]; + node.parentNode.removeChild(node); + delete rowsCache[nodes[i]]; + delete postProcessedRows[nodes[i]]; + renderedRows--; + counter_rows_removed++; + } + } + } + + function updateCell(row,cell) { + if (!rowsCache[row]) return; + var $cell = $(rowsCache[row]).find(".c[cell=" + cell + "]"); + if ($cell.length == 0) return; + + var m = columns[cell], d = data[row]; + if (currentEditor && currentRow == row && currentCell == cell) + currentEditor.setValue(d[m.field]); + else { + $cell[0].innerHTML = d ? m.formatter(row, cell, d[m.field], m, d) : ""; + invalidatePostProcessingResults(row); + } + } + + function updateRow(row) { + if (!rowsCache[row]) return; + + // todo: perf: iterate over direct children? + $(rowsCache[row]).find(".c").each(function(i) { + var m = columns[i]; + if (row == currentRow && i == currentCell && currentEditor) + currentEditor.setValue(data[currentRow][m.field]); + else if (data[row]) + this.innerHTML = m.formatter(row, i, data[row][m.field], m, data[row]); + else + this.innerHTML = ""; + }); + + invalidatePostProcessingResults(row); + } + + function resizeCanvas() { + viewportW = $divMainScroller.innerWidth(); + viewportH = $divMainScroller.innerHeight(); + BUFFER = numVisibleRows = Math.ceil(viewportH / options.rowHeight); + CAPACITY = Math.max(50, numVisibleRows + 2*BUFFER); + + var totalWidth = 0; + for (var i=0; i<columns.length; i++) { + if (columns[i].hidden != true) + totalWidth += columns[i].width; + } + $divMain.width(totalWidth); + + var newHeight = Math.max(options.rowHeight * (data.length - 1 + (options.leaveSpaceForNewRows?numVisibleRows-1:0)), viewportH - $.getScrollbarWidth()); + $divMainScroller.height( $container.innerHeight() - $divHeadersScroller.outerHeight() ); + + // browsers sometimes do not adjust scrollTop/scrollHeight when the height of contained objects changes + if ($divMainScroller.scrollTop() > newHeight - $divMainScroller.height() + $.getScrollbarWidth()) { + $divMainScroller.scrollTop(newHeight - $divMainScroller.height() + $.getScrollbarWidth()); + } + $divMain.height(newHeight); + + render(); + } + + function updateRowCount() { + // 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 parentNode = $divMain[0]; + var l = options.enableAddRow ? data.length : data.length - 1; + for (var i in rowsCache) { + if (i >= l) { + parentNode.removeChild(rowsCache[i]); + delete rowsCache[i]; + delete postProcessedRows[i]; + renderedRows--; + counter_rows_removed++; + } + } + + var newHeight = Math.max(options.rowHeight * (data.length - 1 + (options.leaveSpaceForNewRows?numVisibleRows-1:0)), viewportH - $.getScrollbarWidth()); + + // browsers sometimes do not adjust scrollTop/scrollHeight when the height of contained objects changes + if ($divMainScroller.scrollTop() > newHeight - $divMainScroller.height() + $.getScrollbarWidth()) + $divMainScroller.scrollTop(newHeight - $divMainScroller.height() + $.getScrollbarWidth()); + $divMain.height(newHeight); + } + + function getViewport() + { + return { + top: Math.floor(currentScrollTop / options.rowHeight), + bottom: Math.floor((currentScrollTop + viewportH) / options.rowHeight) + }; + } + + function renderRows(from,to) { + var parentNode = $divMain[0]; + var rowsBefore = renderedRows; + var stringArray = [], rows =[]; + var _start = new Date(); + + for (var i = from; i <= to; i++) { + if (rowsCache[i]) continue; + renderedRows++; + rows.push(i); + appendRowHtml(stringArray,i); + counter_rows_rendered++; + } + + var x = document.createElement("div"); + x.innerHTML = stringArray.join(""); + + for (var i = 0, l = x.childNodes.length; i < l; i++) + rowsCache[rows[i]] = parentNode.appendChild(x.firstChild); + + if (renderedRows - rowsBefore > MIN_BUFFER) + avgRowRenderTime = (new Date() - _start) / (renderedRows - rowsBefore); + } + + function startPostProcessing() { + window.clearTimeout(h_postrender); + h_postrender = window.setTimeout(processPostRenderChunk, POSTPROCESSING_DELAY); + } + + function invalidatePostProcessingResults(row) { + delete postProcessedRows[row]; + rowsToPostProcess.unshift(row); + startPostProcessing(); + } + + function render() { + var vp = getViewport(); + var from = Math.max(0, vp.top - (scrollDir >= 0 ? MIN_BUFFER : BUFFER)); + var to = Math.min(options.enableAddRow ? data.length : data.length - 1, vp.bottom + (scrollDir > 0 ? BUFFER : MIN_BUFFER)); + + if (renderedRows > 10 && Math.abs(lastRenderedScrollTop - currentScrollTop) > options.rowHeight*CAPACITY) + removeAllRows(); + else + cleanupRows(from,to); + + renderRows(from,to); + + rowsToPostProcess = []; + from = Math.max(0,vp.top-MIN_BUFFER); + to = Math.min(options.enableAddRow ? data.length : data.length - 1, vp.bottom+MIN_BUFFER); + for (var i=from; i<=to; i++) { + rowsToPostProcess.push(i); + } + + startPostProcessing(); + + lastRenderedScrollTop = currentScrollTop; + h_render = null; + } + + function handleScroll() { + currentScrollTop = $divMainScroller[0].scrollTop; + var scrollDistance = Math.abs(lastRenderedScrollTop - currentScrollTop); + var scrollLeft = $divMainScroller[0].scrollLeft; + + if (scrollLeft != currentScrollLeft) + $divHeadersScroller[0].scrollLeft = currentScrollLeft = scrollLeft; + + // min scroll distance = 25% of the viewport or MIN_BUFFER rows (whichever is smaller) + if (scrollDistance < Math.min(viewportH/4, MIN_BUFFER*options.rowHeight)) return; + + if (lastRenderedScrollTop == currentScrollTop) + scrollDir = 0; + else if (lastRenderedScrollTop < currentScrollTop) + scrollDir = 1; + else + scrollDir = -1; + + if (h_render) + window.clearTimeout(h_render); + + if (scrollDistance < numVisibleRows*options.rowHeight) + render(); + else + h_render = window.setTimeout(render, 50); + + if (self.onViewportChanged) + self.onViewportChanged(); + } + + function processPostRenderChunk() { + if (rowsToPostProcess.length == 0) return; + while (rowsToPostProcess.length > 0) { + var row = rowsToPostProcess.shift(); + if (postProcessedRows[row] || row>=data.length) continue; + var node = rowsCache[row]; + if (!node) continue; + + if (self.onPostProcessRowNode) + self.onPostProcessRowNode(node, row, data[row]); + startPostProcessing(); + postProcessedRows[row] = true; + return; + } + } + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Interactivity + + function handleKeyDown(e) { + // do we have any registered handlers? + if (self.onKeyDown && data[currentRow]) { + // grid must not be in edit mode + if (!currentEditor) { + // handler will return true if the event was handled + if (self.onKeyDown(e, currentRow, currentCell)) { + e.stopPropagation(); + e.preventDefault(); + return false; + } + } + } + + switch (e.which) { + case 27: // esc + if (GlobalEditorLock.isEditing() && GlobalEditorLock.hasLock(self)) + cancelCurrentEdit(self); + + if (currentCellNode) + currentCellNode.focus(); + + break; + + case 9: // tab + gotoDir(0, (e.shiftKey) ? -1 : 1, true); + break; + + case 37: // left + gotoDir(0,-1); + break; + + case 39: // right + gotoDir(0,1); + break; + + case 38: // up + gotoDir(-1,0); + break; + + case 40: // down + case 13: // enter + gotoDir(1,0); + break; + + default: + // exit without cancelling the event + return; + } + + e.stopPropagation(); + e.preventDefault(); + return false; + } + + function handleClick(e) { + var $cell = $(e.target).closest(".c"); + if ($cell.length == 0) return; + + // are we editing this cell? + if (currentCellNode == $cell[0] && currentEditor != null) return; + + var row = parseInt($cell.parent().attr("row")); + var cell = parseInt($cell.attr("cell")); + var validated = null; + + // do we have any registered handlers? + if (data[row] && self.onClick) { + // grid must not be in edit mode + if (!currentEditor || (validated = commitCurrentEdit())) { + // handler will return true if the event was handled + if (self.onClick(e, row, cell)) { + e.stopPropagation(); + e.preventDefault(); + return false; + } + } + } + + if (options.enableCellNavigation && !columns[cell].unselectable) { + // commit current edit before proceeding + if (validated == true || (validated == null && commitCurrentEdit())) + setSelectedCellAndRow($cell[0]); + } + } + + function handleContextMenu(e) { + var $cell = $(e.target).closest(".c"); + if ($cell.length == 0) return; + + // are we editing this cell? + if (currentCellNode == $cell[0] && currentEditor != null) return; + + var row = parseInt($cell.parent().attr("row")); + var cell = parseInt($cell.attr("cell")); + var validated = null; + + // do we have any registered handlers? + if (data[row] && self.onContextMenu) { + // grid must not be in edit mode + if (!currentEditor || (validated = commitCurrentEdit())) { + // handler will return true if the event was handled + if (self.onContextMenu(e, row, cell)) { + e.stopPropagation(); + e.preventDefault(); + return false; + } + } + } + } + + function handleDblClick(e) { + var $cell = $(e.target).closest(".c"); + if ($cell.length == 0) return; + + // are we editing this cell? + if (currentCellNode == $cell[0] && currentEditor != null) return; + + var row = parseInt($cell.parent().attr("row")); + var cell = parseInt($cell.attr("cell")); + var validated = null; + + // do we have any registered handlers? + if (data[row] && self.onDblClick) { + // grid must not be in edit mode + if (!currentEditor || (validated = commitCurrentEdit())) { + // handler will return true if the event was handled + if (self.onDblClick(e, row, cell)) { + e.stopPropagation(); + e.preventDefault(); + return false; + } + } + } + + if (options.editOnDoubleClick) + makeSelectedCellEditable(); + } + + function handleHeaderContextMenu(e) { + if (self.onHeaderContextMenu && (!currentEditor || (validated = commitCurrentEdit()))) { + e.preventDefault(); + // TODO: figure out which column was acted on and pass it as a param to the handler + self.onHeaderContextMenu(e); + } + } + + function getCellFromPoint(x,y) { + var row = Math.floor(y/options.rowHeight); + var cell = 0; + + var w = 0; + for (var i=0; i<columns.length && w<y; i++) { + w += columns[i].width; + cell++; + } + + return {row:row,cell:cell-1}; + } + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Cell switching + + function setSelectedCell(newCell,async) { + if (currentCellNode != null) { + makeSelectedCellNormal(); + $(currentCellNode).removeClass("selected"); + } + + currentCellNode = newCell; + + if (currentCellNode != null) { + currentRow = parseInt($(currentCellNode).parent().attr("row")); + currentCell = parseInt($(currentCellNode).attr("cell")); + + $(currentCellNode).addClass("selected"); + + scrollSelectedCellIntoView(); + + if (options.editable && !options.editOnDoubleClick && (data[currentRow] || currentRow == data.length)) { + window.clearTimeout(h_editorLoader); + + if (async) + h_editorLoader = window.setTimeout(makeSelectedCellEditable, 100); + else + makeSelectedCellEditable(); + } + } + else { + currentRow = null; + currentCell = null; + } + } + + function setSelectedCellAndRow(newCell,async) { + setSelectedCell(newCell,async); + + if (newCell) + setSelectedRows([currentRow]); + else + setSelectedRows([]); + + if (self.onSelectedRowsChanged) + self.onSelectedRowsChanged(); + } + + 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 isCellPotentiallyEditable(row,cell) { + // is the data for this row loaded? + if (row < data.length && !data[row]) + return false; + + // are we in the Add New row? can we create new from this cell? + if (columns[cell].cannotTriggerInsert && row >= data.length) + return false; + + // does this cell have an editor? + if (!columns[cell].editor) + return false; + + return true; + } + + function makeSelectedCellNormal() { + if (!currentEditor) return; + + currentEditor.destroy(); + $(currentCellNode).removeClass("editable invalid"); + + if (data[currentRow]) { + currentCellNode.innerHTML = columns[currentCell].formatter(currentRow, currentCell, data[currentRow][columns[currentCell].field], columns[currentCell], data[currentRow]); + invalidatePostProcessingResults(currentRow); + } + + currentEditor = null; + + // 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(); + + GlobalEditorLock.leaveEditMode(self); + } + + function makeSelectedCellEditable() { + if (!currentCellNode) return; + if (!options.editable) + throw "Grid : makeSelectedCellEditable : should never get called when options.editable is false"; + + // cancel pending async call if there is one + window.clearTimeout(h_editorLoader); + + if (!isCellPotentiallyEditable(currentRow,currentCell)) + return; + + GlobalEditorLock.enterEditMode(self); + + $(currentCellNode).addClass("editable"); + + var value = null; + + // if there is a corresponding row + if (data[currentRow]) + value = data[currentRow][columns[currentCell].field]; + + currentCellNode.innerHTML = ""; + + currentEditor = new columns[currentCell].editor($(currentCellNode), columns[currentCell], value, data[currentRow]); + } + + function scrollSelectedCellIntoView() { + if (!currentCellNode) return; + var scrollTop = $divMainScroller[0].scrollTop; + + // need to page down? + if ((currentRow + 2) * options.rowHeight > scrollTop + viewportH) { + $divMainScroller[0].scrollTop = (currentRow ) * options.rowHeight; + handleScroll(); + } + // or page up? + else if (currentRow * options.rowHeight < scrollTop) { + $divMainScroller[0].scrollTop = (currentRow + 2) * options.rowHeight - viewportH; + handleScroll(); + } + } + + function gotoDir(dy, dx, rollover) { + if (!currentCellNode) return; + if (!options.enableCellNavigation) return; + if (!GlobalEditorLock.commitCurrentEdit()) return; + + var nextRow = rowsCache[currentRow + dy]; + var nextCell = nextRow ? $(nextRow).find(".c[cell=" + (currentCell + dx) + "][tabIndex=0]") : null; + + if (rollover && dy == 0 && !(nextRow && nextCell && nextCell.length)) { + if (!nextCell || !nextCell.length) { + if (dx > 0) { + nextRow = rowsCache[currentRow + dy + 1]; + nextCell = nextRow ? $(nextRow).find(".c[cell][tabIndex=0]:first") : null; + } + else { + nextRow = rowsCache[currentRow + dy - 1]; + nextCell = nextRow ? $(nextRow).find(".c[cell][tabIndex=0]:last") : null; + } + } + } + + + if (nextRow && nextCell && nextCell.length) { + setSelectedCellAndRow(nextCell[0],options.asyncEditorLoading); + + // if no editor was created, set the focus back on the cell + if (!currentEditor) + currentCellNode.focus(); + } + else + currentCellNode.focus(); + } + + function gotoCell(row,cell) { + if (row > data.length || row < 0 || cell >= columns.length || cell < 0) return; + if (!options.enableCellNavigation || columns[cell].unselectable) return; + + if (!GlobalEditorLock.commitCurrentEdit()) return; + + if (!rowsCache[row]) + renderRows(row,row); + + var cell = $(rowsCache[row]).find(".c[cell=" + cell + "][tabIndex=0]")[0]; + + setSelectedCellAndRow(cell); + + // if no editor was created, set the focus back on the cell + if (!currentEditor) + currentCellNode.focus(); + } + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // IEditor implementation for GlobalEditorLock + + function commitCurrentEdit() { + if (currentEditor) { + if (currentEditor.isValueChanged()) { + var validationResults = currentEditor.validate(); + + if (validationResults.valid) { + var value = currentEditor.getValue(); + + if (currentRow < data.length) { + if (columns[currentCell].setValueHandler) { + makeSelectedCellNormal(); + columns[currentCell].setValueHandler(value, columns[currentCell], data[currentRow]); + } + else { + data[currentRow][columns[currentCell].field] = value; + makeSelectedCellNormal(); + } + } + else if (self.onAddNewRow) { + makeSelectedCellNormal(); + self.onAddNewRow(columns[currentCell], value); + } + + return true; + } + else { + $(currentCellNode).addClass("invalid"); + $(currentCellNode).stop(true,true).effect("highlight", {color:"red"}, 300); + + if (self.onValidationError) + self.onValidationError(currentCellNode, validationResults, currentRow, currentCell, columns[currentCell]); + + currentEditor.focus(); + return false; + } + } + + makeSelectedCellNormal(); + } + + + return true; + } + + function cancelCurrentEdit() { + makeSelectedCellNormal(); + } + + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Debug + + this.debug = function() { + var s = ""; + + 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" + "CAPACITY: " + CAPACITY); + s += ("\n" + "BUFFER: " + BUFFER); + s += ("\n" + "avgRowRenderTime: " + avgRowRenderTime); + + alert(s); + }; + + this.benchmark_render_200 = function() { + removeAllRows(); + + // render 200 rows in the viewport + renderRows(0, 200); + + cleanupRows(); + }; + + this.stressTest = function() { + console.time("benchmark-stress"); + + renderRows(0,500); + + cleanupRows(); + + console.timeEnd("benchmark-stress"); + + window.setTimeout(self.stressTest, 50); + }; + + this.benchmarkFn = function(fn) { + var s = new Date(); + + var args = new Array(arguments); + args.splice(0,1); + + self[fn].call(this,args); + + alert("Grid : benchmarkFn : " + fn + " : " + (new Date() - s) + "ms"); + }; + + + + + init(); + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Public API + + $.extend(this, { + // Events + "onSort": null, + "onHeaderContextMenu": null, + "onClick": null, + "onContextMenu": null, + "onKeyDown": null, + "onAddNewRow": null, + "onValidationError": null, + "onViewportChanged": null, + "onSelectedRowsChanged": null, + "onColumnsReordered": null, + "onBeforeMoveRows" : null, + "onMoveRows": null, + "onPostProcessRowNode": null, + + // Methods + "setOptions": setOptions, + "setData": setData, + "destroy": destroy, + "getColumnIndex": getColumnIndex, + "setColumnVisibility": setColumnVisibility, + "autosizeColumns": autosizeColumns, + "updateCell": updateCell, + "updateRow": updateRow, + "removeRow": removeRow, + "removeRows": removeRows, + "removeAllRows": removeAllRows, + "render": render, + "getViewport": getViewport, + "resizeCanvas": resizeCanvas, + "updateRowCount": updateRowCount, + "scroll": scroll, // TODO + "getCellFromPoint": getCellFromPoint, + "gotoCell": gotoCell, + "editCurrentCell": makeSelectedCellEditable, + "getSelectedRows": getSelectedRows, + "setSelectedRows": setSelectedRows, + + // IEditor implementation + "commitCurrentEdit": commitCurrentEdit, + "cancelCurrentEdit": cancelCurrentEdit + }); +} diff --git a/slick.model.js b/slick.model.js index e4cf831..ec2be94 100644 --- a/slick.model.js +++ b/slick.model.js @@ -1,231 +1,231 @@ -/***
- * A simple observer pattern implementation.
- */
-function EventHelper() {
- this.handlers = [];
-
- this.subscribe = function(fn) {
- this.handlers.push(fn);
- }
-
- this.notify = function(args) {
- for (var i = 0; i < this.handlers.length; i++) {
- this.handlers[i].call(this, args);
- }
- }
-
- return this;
-}
-
-
-
-/***
- * 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() {
- var self = this;
-
- // private
- 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 pagesize = 0;
- var pagenum = 0;
- var totalRows = 0;
-
- // events
- var onRowCountChanged = new EventHelper();
- var onRowsChanged = new EventHelper();
- var onPagingInfoChanged = new EventHelper();
-
-
- function beginUpdate() {
- suspend = true;
- }
-
- function endUpdate() {
- suspend = false;
- refresh();
- }
-
- function setItems(data) {
- items = data.concat();
- for (var i=0,l=items.length; i<l; i++) {
- var id = items[i].id;
- if (id == undefined || idxById[id] != undefined)
- throw "Each data element must implement a unique 'id' property";
- idxById[id] = i;
- }
- refresh();
- }
-
- function setPagingOptions(args) {
- if (args.pageSize != undefined)
- pagesize = args.pageSize;
-
- if (args.pageNum != undefined)
- pagenum = Math.min(args.pageNum, Math.ceil(totalRows/pagesize));
-
- onPagingInfoChanged.notify(getPagingInfo());
-
- refresh();
- }
-
- function getPagingInfo() {
- return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows};
- }
-
- function sort(comparer) {
- items.sort(comparer);
- refresh();
- }
-
- function setFilter(filterFn) {
- filter = filterFn;
- refresh();
- }
-
- function getItemByIdx(i) {
- return items[i];
- }
-
- function getIdxById(id) {
- return idxById[id];
- }
-
- // calculate the lookup table on first call
- function getRowById(id) {
- if (!rowsById) {
- rowsById = {};
- for (var i=0, l=rows.length; i<l; ++i) {
- rowsById[rows[i].id] = i;
- }
- }
-
- return rowsById[id];
- }
-
- function getItemById(id) {
- return items[idxById[id]];
- }
-
- function updateItem(id,item) {
- items[idxById[id]] = item;
- if (!updated) updated = {};
- updated[id] = true;
- refresh();
- }
-
- function insertItem(insertBefore,item) {
- items.splice(insertBefore,0,item);
- refresh();
- }
-
- function addItem(item) {
- items.push(item);
- refresh();
- }
-
- function deleteItem(id) {
- items.splice(idxById[id],1);
- refresh();
- }
-
- function recalc(_items,_rows,_filter,_updated) {
- var diff = [];
- var items=_items, rows=_rows, filter=_filter, updated=_updated; // cache as local vars
-
- rowsById = null;
-
- // go over all items remapping them to rows on the fly
- // while keeping track of the differences and updating indexes
- var rl = rows.length;
- var currentRowIndex = 0;
- var currentPageIndex = 0;
- var item,id;
-
- for (var i = 0, il = items.length; i < il; ++i) {
- item = items[i];
- id = item.id;
-
- if (!filter || filter(item)) {
- if (!pagesize || (currentRowIndex >= pagesize * pagenum && currentRowIndex < pagesize * (pagenum + 1))) {
- if (currentPageIndex >= rl || id != rows[currentPageIndex].id || (updated && updated[id]))
- diff[diff.length] = currentPageIndex;
-
- rows[currentPageIndex] = item;
- currentPageIndex++;
- }
-
- currentRowIndex++;
- }
- }
-
- if (rl > currentPageIndex)
- rows.splice(currentPageIndex, rl - currentPageIndex);
-
- totalRows = currentRowIndex;
-
- return diff;
- }
-
- function refresh() {
- if (suspend) return;
-
- var countBefore = rows.length;
- var totalRowsBefore = totalRows;
-
- var diff = recalc(items,rows,filter,updated); // 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,rows,filter,updated);
- }
-
- updated = null;
-
- if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo());
- if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length});
- if (diff.length > 0 || countBefore != rows.length) onRowsChanged.notify(diff);
- }
-
-
-
- return {
- // properties
- "rows": rows, // note: neither the array or the data in it should be modified directly
-
- // methods
- "beginUpdate": beginUpdate,
- "endUpdate": endUpdate,
- "setPagingOptions": setPagingOptions,
- "getPagingInfo": getPagingInfo,
- "setItems": setItems,
- "setFilter": setFilter,
- "sort": sort,
- "getIdxById": getIdxById,
- "getRowById": getRowById,
- "getItemById": getItemById,
- "getItemByIdx": getItemByIdx,
- "refresh": refresh,
- "updateItem": updateItem,
- "insertItem": insertItem,
- "addItem": addItem,
- "deleteItem": deleteItem,
-
- // events
- "onRowCountChanged": onRowCountChanged,
- "onRowsChanged": onRowsChanged,
- "onPagingInfoChanged": onPagingInfoChanged
- };
-}
+/*** + * A simple observer pattern implementation. + */ +function EventHelper() { + this.handlers = []; + + this.subscribe = function(fn) { + this.handlers.push(fn); + } + + this.notify = function(args) { + for (var i = 0; i < this.handlers.length; i++) { + this.handlers[i].call(this, args); + } + } + + return this; +} + + + +/*** + * 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() { + var self = this; + + // private + 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 pagesize = 0; + var pagenum = 0; + var totalRows = 0; + + // events + var onRowCountChanged = new EventHelper(); + var onRowsChanged = new EventHelper(); + var onPagingInfoChanged = new EventHelper(); + + + function beginUpdate() { + suspend = true; + } + + function endUpdate() { + suspend = false; + refresh(); + } + + function setItems(data) { + items = data.concat(); + for (var i=0,l=items.length; i<l; i++) { + var id = items[i].id; + if (id == undefined || idxById[id] != undefined) + throw "Each data element must implement a unique 'id' property"; + idxById[id] = i; + } + refresh(); + } + + function setPagingOptions(args) { + if (args.pageSize != undefined) + pagesize = args.pageSize; + + if (args.pageNum != undefined) + pagenum = Math.min(args.pageNum, Math.ceil(totalRows/pagesize)); + + onPagingInfoChanged.notify(getPagingInfo()); + + refresh(); + } + + function getPagingInfo() { + return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows}; + } + + function sort(comparer) { + items.sort(comparer); + refresh(); + } + + function setFilter(filterFn) { + filter = filterFn; + refresh(); + } + + function getItemByIdx(i) { + return items[i]; + } + + function getIdxById(id) { + return idxById[id]; + } + + // calculate the lookup table on first call + function getRowById(id) { + if (!rowsById) { + rowsById = {}; + for (var i=0, l=rows.length; i<l; ++i) { + rowsById[rows[i].id] = i; + } + } + + return rowsById[id]; + } + + function getItemById(id) { + return items[idxById[id]]; + } + + function updateItem(id,item) { + items[idxById[id]] = item; + if (!updated) updated = {}; + updated[id] = true; + refresh(); + } + + function insertItem(insertBefore,item) { + items.splice(insertBefore,0,item); + refresh(); + } + + function addItem(item) { + items.push(item); + refresh(); + } + + function deleteItem(id) { + items.splice(idxById[id],1); + refresh(); + } + + function recalc(_items,_rows,_filter,_updated) { + var diff = []; + var items=_items, rows=_rows, filter=_filter, updated=_updated; // cache as local vars + + rowsById = null; + + // go over all items remapping them to rows on the fly + // while keeping track of the differences and updating indexes + var rl = rows.length; + var currentRowIndex = 0; + var currentPageIndex = 0; + var item,id; + + for (var i = 0, il = items.length; i < il; ++i) { + item = items[i]; + id = item.id; + + if (!filter || filter(item)) { + if (!pagesize || (currentRowIndex >= pagesize * pagenum && currentRowIndex < pagesize * (pagenum + 1))) { + if (currentPageIndex >= rl || id != rows[currentPageIndex].id || (updated && updated[id])) + diff[diff.length] = currentPageIndex; + + rows[currentPageIndex] = item; + currentPageIndex++; + } + + currentRowIndex++; + } + } + + if (rl > currentPageIndex) + rows.splice(currentPageIndex, rl - currentPageIndex); + + totalRows = currentRowIndex; + + return diff; + } + + function refresh() { + if (suspend) return; + + var countBefore = rows.length; + var totalRowsBefore = totalRows; + + var diff = recalc(items,rows,filter,updated); // 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,rows,filter,updated); + } + + updated = null; + + if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo()); + if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length}); + if (diff.length > 0 || countBefore != rows.length) onRowsChanged.notify(diff); + } + + + + return { + // properties + "rows": rows, // note: neither the array or the data in it should be modified directly + + // methods + "beginUpdate": beginUpdate, + "endUpdate": endUpdate, + "setPagingOptions": setPagingOptions, + "getPagingInfo": getPagingInfo, + "setItems": setItems, + "setFilter": setFilter, + "sort": sort, + "getIdxById": getIdxById, + "getRowById": getRowById, + "getItemById": getItemById, + "getItemByIdx": getItemByIdx, + "refresh": refresh, + "updateItem": updateItem, + "insertItem": insertItem, + "addItem": addItem, + "deleteItem": deleteItem, + + // events + "onRowCountChanged": onRowCountChanged, + "onRowsChanged": onRowsChanged, + "onPagingInfoChanged": onPagingInfoChanged + }; +} diff --git a/slick.pager.css b/slick.pager.css index 851385e..70f0eb3 100644 --- a/slick.pager.css +++ b/slick.pager.css @@ -1,48 +1,48 @@ -.slick-pager
-{
- width: 100%;
- height: 26px;
- border: 1px solid gray;
- border-top: 0px;
- 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 .ui-icon
-{
- display: inline-block;
- margin: 2px;
- border-color: gray;
-}
-
-.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 *
-{
- vertical-align: middle;
-}
-
-.slick-pager .slick-pager-settings a
-{
- padding: 2px;
- text-decoration: underline;
- cursor: pointer;
-}
+.slick-pager +{ + width: 100%; + height: 26px; + border: 1px solid gray; + border-top: 0px; + 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 .ui-icon +{ + display: inline-block; + margin: 2px; + border-color: gray; +} + +.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 * +{ + vertical-align: middle; +} + +.slick-pager .slick-pager-settings a +{ + padding: 2px; + text-decoration: underline; + cursor: pointer; +} diff --git a/slick.pager.js b/slick.pager.js index 79e9bec..c9c45bb 100644 --- a/slick.pager.js +++ b/slick.pager.js @@ -1,138 +1,138 @@ -function SlickGridPager(dataView, grid, $container)
-{
- var $status, $contextMenu;
-
- function init()
- {
- dataView.onPagingInfoChanged.subscribe(function(pagingInfo) {
- updatePager(pagingInfo);
- });
-
- constructPagerUI();
- updatePager(dataView.getPagingInfo());
- }
-
- function getNavState()
- {
- var cannotLeaveEditMode = !(!GlobalEditorLock.isEditing() || 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 setPageSize(n)
- {
- dataView.setPagingOptions({pageSize:n});
- }
-
- function gotoFirst()
- {
- if (getNavState().canGotoFirst)
- dataView.setPagingOptions({pageNum: 0});
- }
-
- 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();
-
- $status = $("<span class='slick-pager-status' />").appendTo($container);
-
- var $nav = $("<span class='slick-pager-nav' />").appendTo($container);
- var $settings = $("<span class='slick-pager-settings' />").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));
- }
- });
-
- $("<span class='ui-icon ui-icon-lightbulb' />")
- .click(function() { $(".slick-pager-settings-expanded").toggle() })
- .appendTo($settings);
-
- $("<span class='ui-icon ui-icon-seek-first' />")
- .click(gotoFirst)
- .appendTo($nav);
-
- $("<span class='ui-icon ui-icon-seek-prev' />")
- .click(gotoPrev)
- .appendTo($nav);
-
- $("<span class='ui-icon ui-icon-seek-next' />")
- .click(gotoNext)
- .appendTo($nav);
-
- $("<span class='ui-icon ui-icon-seek-end' />")
- .click(gotoLast)
- .appendTo($nav);
-
- $container.find(".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") })
-
- $container.children().wrapAll("<div class='slick-pager zui-state-default' />");
- }
-
-
- 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();
-}
+function SlickGridPager(dataView, grid, $container) +{ + var $status, $contextMenu; + + function init() + { + dataView.onPagingInfoChanged.subscribe(function(pagingInfo) { + updatePager(pagingInfo); + }); + + constructPagerUI(); + updatePager(dataView.getPagingInfo()); + } + + function getNavState() + { + var cannotLeaveEditMode = !(!GlobalEditorLock.isEditing() || 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 setPageSize(n) + { + dataView.setPagingOptions({pageSize:n}); + } + + function gotoFirst() + { + if (getNavState().canGotoFirst) + dataView.setPagingOptions({pageNum: 0}); + } + + 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(); + + $status = $("<span class='slick-pager-status' />").appendTo($container); + + var $nav = $("<span class='slick-pager-nav' />").appendTo($container); + var $settings = $("<span class='slick-pager-settings' />").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)); + } + }); + + $("<span class='ui-icon ui-icon-lightbulb' />") + .click(function() { $(".slick-pager-settings-expanded").toggle() }) + .appendTo($settings); + + $("<span class='ui-icon ui-icon-seek-first' />") + .click(gotoFirst) + .appendTo($nav); + + $("<span class='ui-icon ui-icon-seek-prev' />") + .click(gotoPrev) + .appendTo($nav); + + $("<span class='ui-icon ui-icon-seek-next' />") + .click(gotoNext) + .appendTo($nav); + + $("<span class='ui-icon ui-icon-seek-end' />") + .click(gotoLast) + .appendTo($nav); + + $container.find(".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") }) + + $container.children().wrapAll("<div class='slick-pager zui-state-default' />"); + } + + + 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(); +} diff --git a/slick.remotemodel.js b/slick.remotemodel.js index a7e27b2..4dce1b8 100644 --- a/slick.remotemodel.js +++ b/slick.remotemodel.js @@ -1,183 +1,183 @@ -/***
- * A simple observer pattern implementation.
- */
-function EventHelper() {
- this.handlers = [];
-
- this.subscribe = function(fn) {
- this.handlers.push(fn);
- }
-
- this.notify = function(args) {
- for (var i = 0; i < this.handlers.length; i++) {
- this.handlers[i].call(this, args);
- }
- }
-
- return this;
-}
-
-
-
-/***
- * 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
- var req_page;
-
- // events
- var onDataLoading = new EventHelper();
- var onDataLoaded = new EventHelper();
-
-
- 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 clear() {
- for (var key in data) {
- delete data[key];
- }
- data.length = 0;
- }
-
-
- function ensureData(from,to) {
- 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: lookeahead
-
- //if ()
-
- 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 (req) {
- req.abort();
- data[req_page*PAGESIZE] = undefined;
- }
-
- if (h_request != null)
- window.clearTimeout(h_request);
-
- h_request = window.setTimeout(function() {
- for (var i=fromPage; i<=toPage; i++)
- data[i*PAGESIZE] = null; // null indicates a 'requested but not available yet'
-
- req_page = fromPage;
-
- 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)
- }
- });
- }, 50);
- }
-
-
- function onError(fromPage,toPage) {
- alert("error loading pages " + fromPage + " to " + toPage);
- }
-
- function onSuccess(resp) {
- var from = resp.offset, to = resp.offset + 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;
- }
-
- req = null;
-
- onDataLoaded.notify({from:from, to:to});
- }
-
-
- function reloadData(from,to) {
- for (var i=from; i<=to; i++)
- delete data[i];
-
- ensureData(from,to);
- }
-
-
- function setSort(column,dir) {
- sortcol = column;
- sortdir = dir;
- clear();
- }
-
- function setSearch(str) {
- searchstr = str;
- clear();
- }
-
-
- init();
-
- return {
- // properties
- "data": data,
-
- // methods
- "clear": clear,
- "isDataLoaded": isDataLoaded,
- "ensureData": ensureData,
- "reloadData": reloadData,
- "setSort": setSort,
- "setSearch": setSearch,
-
- // events
- "onDataLoading": onDataLoading,
- "onDataLoaded": onDataLoaded
- };
-}
+/*** + * A simple observer pattern implementation. + */ +function EventHelper() { + this.handlers = []; + + this.subscribe = function(fn) { + this.handlers.push(fn); + } + + this.notify = function(args) { + for (var i = 0; i < this.handlers.length; i++) { + this.handlers[i].call(this, args); + } + } + + return this; +} + + + +/*** + * 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 + var req_page; + + // events + var onDataLoading = new EventHelper(); + var onDataLoaded = new EventHelper(); + + + 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 clear() { + for (var key in data) { + delete data[key]; + } + data.length = 0; + } + + + function ensureData(from,to) { + 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: lookeahead + + //if () + + 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 (req) { + req.abort(); + data[req_page*PAGESIZE] = undefined; + } + + if (h_request != null) + window.clearTimeout(h_request); + + h_request = window.setTimeout(function() { + for (var i=fromPage; i<=toPage; i++) + data[i*PAGESIZE] = null; // null indicates a 'requested but not available yet' + + req_page = fromPage; + + 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) + } + }); + }, 50); + } + + + function onError(fromPage,toPage) { + alert("error loading pages " + fromPage + " to " + toPage); + } + + function onSuccess(resp) { + var from = resp.offset, to = resp.offset + 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; + } + + req = null; + + onDataLoaded.notify({from:from, to:to}); + } + + + function reloadData(from,to) { + for (var i=from; i<=to; i++) + delete data[i]; + + ensureData(from,to); + } + + + function setSort(column,dir) { + sortcol = column; + sortdir = dir; + clear(); + } + + function setSearch(str) { + searchstr = str; + clear(); + } + + + init(); + + return { + // properties + "data": data, + + // methods + "clear": clear, + "isDataLoaded": isDataLoaded, + "ensureData": ensureData, + "reloadData": reloadData, + "setSort": setSort, + "setSearch": setSearch, + + // events + "onDataLoading": onDataLoading, + "onDataLoaded": onDataLoaded + }; +} diff --git a/tests/model benchmarks.html b/tests/model benchmarks.html index a0a1f62..4881c38 100644 --- a/tests/model benchmarks.html +++ b/tests/model benchmarks.html @@ -1,63 +1,63 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title></title>
- </head>
- <body>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../slick.model.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
-
- <script>
-
- var filterparam = 0;
-
- function filter(item) {
- // simulate a more expensive filer
- var matches = 0;
- if (item.title.indexOf("ask") != -1) matches++;
- for (var i=0; i<5; i++)
- matches += Math.random();
-
- return item.percentComplete >= filterparam;
- }
-
- var data = [];
-
- for (var i=0; i<20000; i++) {
- var d = (data[i] = {});
-
- d["id"] = i;
- d["title"] = "Task " + i;
- d["percentComplete"] = i % 100;
- }
-
-
- var dv = new DataView();
- dv.beginUpdate();
- dv.setItems(data);
- dv.setFilter(filter);
- //dv.setPagingOptions({pageSize:25});
- dv.endUpdate();
-
-
- var b = new Date();
-
- console.time("refresh");
-
- for (var i = 0; i <= 100; i+=2) {
- filterparam = i;
- dv.refresh();
- }
-
- console.timeEnd("refresh");
-
- var a = new Date();
- alert(a-b);
-
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title></title> + </head> + <body> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../slick.model.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + + <script> + + var filterparam = 0; + + function filter(item) { + // simulate a more expensive filer + var matches = 0; + if (item.title.indexOf("ask") != -1) matches++; + for (var i=0; i<5; i++) + matches += Math.random(); + + return item.percentComplete >= filterparam; + } + + var data = []; + + for (var i=0; i<20000; i++) { + var d = (data[i] = {}); + + d["id"] = i; + d["title"] = "Task " + i; + d["percentComplete"] = i % 100; + } + + + var dv = new DataView(); + dv.beginUpdate(); + dv.setItems(data); + dv.setFilter(filter); + //dv.setPagingOptions({pageSize:25}); + dv.endUpdate(); + + + var b = new Date(); + + console.time("refresh"); + + for (var i = 0; i <= 100; i+=2) { + filterparam = i; + dv.refresh(); + } + + console.timeEnd("refresh"); + + var a = new Date(); + alert(a-b); + + + </script> + + </body> +</html> diff --git a/tests/scrolling benchmarks.html b/tests/scrolling benchmarks.html index ea48fb5..6a0aa98 100644 --- a/tests/scrolling benchmarks.html +++ b/tests/scrolling benchmarks.html @@ -1,188 +1,188 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>SlickGrid</title>
- <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.custom.css" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="../examples/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>
- <script language="JavaScript" src="../lib/firebugx.js"></script>
- <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script>
- <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script>
- <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script>
- <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script>
- <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script>
-
- <script language="JavaScript" src="../slick.editors.js"></script>
- <script language="JavaScript" src="../slick.grid.js"></script>
- <script language="JavaScript" src="../slick.globaleditorlock.js"></script>
- <script language="JavaScript" src="../slick.model.js"></script>
-
- <button onclick="grid.debug()">Debug info</button>
- <button onclick="startBench(100)">Run (+= 100px)</button>
- <button onclick="startBench(300)">Run (+= 300px)</button>
- <button onclick="startBench(550)">Run (+= 550px; simulate paging)</button>
- <button onclick="startBench(1000)">Run (+= 1'000px)</button>
- <button onclick="startBench(5000)">Run (+= 5'000px)</button>
- <hr/>
-
- <div id="myGrid" style="width:600px;height:600px;"></div>
-
-
-
- <script>
-
- var dataView;
- var grid;
-
- var data = [];
-
- var columns = [
- {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, setValueHandler:updateItem},
- {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem},
- {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem},
- {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem},
- {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, cannotTriggerInsert:true}
- ];
-
- var options = {
- editable: true,
- enableAddRow: true,
- enableCellNavigation: true,
- asyncEditorLoading: true
- };
-
-
- var percentCompleteThreshold = 0;
- var searchString = "";
-
- function myFilter(item) {
- if (item["percentComplete"] < percentCompleteThreshold)
- return false;
-
- if (searchString != "" && item["title"].indexOf(searchString) == -1)
- return false;
-
- return true;
- }
-
- function percentCompleteSort(a,b) {
- return a["percentComplete"] - b["percentComplete"];
- }
-
- var sortcol = "title";
- var sortdir = 1;
- function comparer(a,b) {
- var x = a[sortcol], y = b[sortcol];
-
- return sortdir * (x == y ? 0 : (x > y ? 1 : -1));
- }
-
- function updateItem(value,columnDef,item) {
- item[columnDef.field] = value;
- dataView.updateItem(item.id,item);
- }
-
- function addItem(columnDef,value) {
- var item = {"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};
- item[columnDef.field] = value;
- dataView.addItem(item);
- }
-
-
- $(function()
- {
- // prepare the data
- for (var i=0; i<5000; i++) {
- var d = (data[i] = {});
-
- d["id"] = "id_" + 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);
- }
-
-
- // initialize the model
- dataView = new DataView();
- dataView.beginUpdate();
- dataView.setItems(data);
- dataView.setFilter(myFilter);
- dataView.endUpdate();
-
-
- // initialize the grid
- grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options);
-
-
- grid.onPostProcessRowNode = function(rowNode) {
- rowNode.lastChild.style.background = "yellow"
- }
-
- // wire up model events to drive the grid
- dataView.onRowCountChanged.subscribe(function(args) {
- grid.resizeCanvas();
- });
-
- dataView.onRowsChanged.subscribe(function(rows) {
- for (var i=0, rl=rows.length; i<rl; i++) {
- grid.removeRow(rows[i]);
- }
-
- grid.render();
- });
-
- $grid = $("#myGrid div.grid-canvas").parent();
- })
-
-
-
-
-
- var $grid;
- var lastScrollTop = 0;
- var startTime;
- var scrollAmount = 1000;
-
- function startBench(scroll) {
- scrollAmount = scroll;
- lastScrollTop = 0;
- $grid.scrollTop(0);
-
- window.setTimeout(function(){
- startTime = new Date();
- bench();
- }, 500);
- }
-
- function bench() {
- var scrollTop = $grid[0].scrollTop = lastScrollTop + scrollAmount;
-
- if ($grid[0].scrollTop == lastScrollTop + scrollAmount) {
- lastScrollTop = scrollTop;
- window.setTimeout(bench, 10);
- }
- else {
- alert((new Date() - startTime));
- }
- }
-
- </script>
-
- </body>
-</html>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/custom-theme/jquery-ui-1.7.2.custom.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../examples/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> + <script language="JavaScript" src="../lib/firebugx.js"></script> + <script language="JavaScript" src="../lib/jquery-1.3.2.min.js"></script> + <script language="JavaScript" src="../lib/jquery-ui-1.7.2.custom.min.js"></script> + <script language="JavaScript" src="../lib/jquery.getScrollbarWidth.js"></script> + <script language="JavaScript" src="../lib/jquery.rule-1.0.1-min.js"></script> + <script language="JavaScript" src="../lib/jquery.event.drag.custom.js"></script> + + <script language="JavaScript" src="../slick.editors.js"></script> + <script language="JavaScript" src="../slick.grid.js"></script> + <script language="JavaScript" src="../slick.globaleditorlock.js"></script> + <script language="JavaScript" src="../slick.model.js"></script> + + <button onclick="grid.debug()">Debug info</button> + <button onclick="startBench(100)">Run (+= 100px)</button> + <button onclick="startBench(300)">Run (+= 300px)</button> + <button onclick="startBench(550)">Run (+= 550px; simulate paging)</button> + <button onclick="startBench(1000)">Run (+= 1'000px)</button> + <button onclick="startBench(5000)">Run (+= 5'000px)</button> + <hr/> + + <div id="myGrid" style="width:600px;height:600px;"></div> + + + + <script> + + var dataView; + var grid; + + var data = []; + + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, setValueHandler:updateItem}, + {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor, setValueHandler:updateItem}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor, setValueHandler:updateItem}, + {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor, setValueHandler:updateItem}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor, setValueHandler:updateItem, cannotTriggerInsert:true} + ]; + + var options = { + editable: true, + enableAddRow: true, + enableCellNavigation: true, + asyncEditorLoading: true + }; + + + var percentCompleteThreshold = 0; + var searchString = ""; + + function myFilter(item) { + if (item["percentComplete"] < percentCompleteThreshold) + return false; + + if (searchString != "" && item["title"].indexOf(searchString) == -1) + return false; + + return true; + } + + function percentCompleteSort(a,b) { + return a["percentComplete"] - b["percentComplete"]; + } + + var sortcol = "title"; + var sortdir = 1; + function comparer(a,b) { + var x = a[sortcol], y = b[sortcol]; + + return sortdir * (x == y ? 0 : (x > y ? 1 : -1)); + } + + function updateItem(value,columnDef,item) { + item[columnDef.field] = value; + dataView.updateItem(item.id,item); + } + + function addItem(columnDef,value) { + var item = {"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}; + item[columnDef.field] = value; + dataView.addItem(item); + } + + + $(function() + { + // prepare the data + for (var i=0; i<5000; i++) { + var d = (data[i] = {}); + + d["id"] = "id_" + 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); + } + + + // initialize the model + dataView = new DataView(); + dataView.beginUpdate(); + dataView.setItems(data); + dataView.setFilter(myFilter); + dataView.endUpdate(); + + + // initialize the grid + grid = new SlickGrid($("#myGrid"), dataView.rows, columns, options); + + + grid.onPostProcessRowNode = function(rowNode) { + rowNode.lastChild.style.background = "yellow" + } + + // wire up model events to drive the grid + dataView.onRowCountChanged.subscribe(function(args) { + grid.resizeCanvas(); + }); + + dataView.onRowsChanged.subscribe(function(rows) { + for (var i=0, rl=rows.length; i<rl; i++) { + grid.removeRow(rows[i]); + } + + grid.render(); + }); + + $grid = $("#myGrid div.grid-canvas").parent(); + }) + + + + + + var $grid; + var lastScrollTop = 0; + var startTime; + var scrollAmount = 1000; + + function startBench(scroll) { + scrollAmount = scroll; + lastScrollTop = 0; + $grid.scrollTop(0); + + window.setTimeout(function(){ + startTime = new Date(); + bench(); + }, 500); + } + + function bench() { + var scrollTop = $grid[0].scrollTop = lastScrollTop + scrollAmount; + + if ($grid[0].scrollTop == lastScrollTop + scrollAmount) { + lastScrollTop = scrollTop; + window.setTimeout(bench, 10); + } + else { + alert((new Date() - startTime)); + } + } + + </script> + + </body> +</html> |