summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/example3-editing.html37
-rw-r--r--examples/example4-model.html3
-rw-r--r--slick.editors.js1049
-rw-r--r--slick.grid.js180
4 files changed, 715 insertions, 554 deletions
diff --git a/examples/example3-editing.html b/examples/example3-editing.html
index e88aa71..3258ed5 100644
--- a/examples/example3-editing.html
+++ b/examples/example3-editing.html
@@ -17,20 +17,25 @@
</style>
</head>
<body>
- <table width="100%">
- <tr>
- <td valign="top" width="50%">
- <div id="myGrid" style="width:600px;height:500px;"></div>
- </td>
- <td valign="top">
- <h2>Demonstrates:</h2>
- <ul>
- <li>adding basic keyboard navigation and editing</li>
- <li>custom editors and validators</li>
- </ul>
- </td>
- </tr>
- </table>
+
+
+ <div style="width:600px;float:left;">
+ <div id="myGrid" style="width:100%;height:500px;"></div>
+ </div>
+
+ <div class="options-panel" style="width:320px;margin-left:650px;">
+ <h2>Demonstrates:</h2>
+ <ul>
+ <li>adding basic keyboard navigation and editing</li>
+ <li>custom editors and validators</li>
+ <li>auto-edit settings</li>
+ </ul>
+
+ <h2>Options:</h2>
+ <button onclick="grid.setOptions({autoEdit:true})">Auto-edit ON</button>
+ &nbsp;
+ <button onclick="grid.setOptions({autoEdit:false})">Auto-edit OFF</button>
+ </div>
<script language="JavaScript" src="../lib/firebugx.js"></script>
@@ -58,7 +63,8 @@
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:"desc", name:"Description", field:"description", width:100, editor:LongTextCellEditor},
+ {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor},
{id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor},
{id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor},
{id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor},
@@ -80,6 +86,7 @@
var d = (data[i] = {});
d["title"] = "Task " + i;
+ d["description"] = "This is a sample task description.\n It can be multiline";
d["duration"] = "5 days";
d["percentComplete"] = Math.round(Math.random() * 100);
d["start"] = "01/01/2009";
diff --git a/examples/example4-model.html b/examples/example4-model.html
index b3a02ef..691e404 100644
--- a/examples/example4-model.html
+++ b/examples/example4-model.html
@@ -232,7 +232,8 @@
selectedRowIds = [];
var rows = grid.getSelectedRows();
for (var i = 0, l = rows.length; i < l; i++) {
- selectedRowIds.push(dataView.rows[rows[i]].id);
+ var item = dataView.rows[rows[i]];
+ if (item) selectedRowIds.push(item.id);
}
};
diff --git a/slick.editors.js b/slick.editors.js
index 4adbb9d..b954fb4 100644
--- a/slick.editors.js
+++ b/slick.editors.js
@@ -1,649 +1,694 @@
+/* THESE FORMATTERS & EDITORS ARE JUST SAMPLES! */
+
(function($) {
-var SlickEditor = {
+ var SlickEditor = {
- SelectorCellFormatter : function(row, cell, value, columnDef, dataContext) {
- return (!dataContext ? "" : row);
- },
+ SelectorCellFormatter : function(row, cell, value, columnDef, dataContext) {
+ return (!dataContext ? "" : row);
+ },
- PercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {
- if (value == null || value === "")
- return "-";
- else if (value < 50)
- return "<span style='color:red;font-weight:bold;'>" + value + "%</span>";
- else
- return "<span style='color:green'>" + value + "%</span>";
- },
+ PercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {
+ if (value == null || value === "")
+ return "-";
+ else if (value < 50)
+ return "<span style='color:red;font-weight:bold;'>" + value + "%</span>";
+ else
+ return "<span style='color:green'>" + value + "%</span>";
+ },
- GraphicalPercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {
- if (value == null || value === "")
- return "";
+ GraphicalPercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {
+ if (value == null || value === "")
+ return "";
- var color;
+ var color;
- if (value < 30)
- color = "red";
- else if (value < 70)
- color = "silver";
- else
- color = "green";
+ 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>";
- },
+ return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>";
+ },
- YesNoCellFormatter : function(row, cell, value, columnDef, dataContext) {
- return value ? "Yes" : "No";
- },
+ YesNoCellFormatter : function(row, cell, value, columnDef, dataContext) {
+ return value ? "Yes" : "No";
+ },
- BoolCellFormatter : function(row, cell, value, columnDef, dataContext) {
- return value ? "<img src='../images/tick.png'>" : "";
- },
+ BoolCellFormatter : function(row, cell, value, columnDef, dataContext) {
+ return value ? "<img src='../images/tick.png'>" : "";
+ },
- TaskNameFormatter : function(row, cell, value, columnDef, dataContext) {
- // todo: html encode
- var spacer = "<span style='display:inline-block;height:1px;width:" + (2 + 15 * dataContext["indent"]) + "px'></span>";
- return spacer + " <img src='../images/expand.gif'>&nbsp;" + value;
- },
+ 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'>&nbsp;" + value;
+ },
- ResourcesFormatter : function(row, cell, value, columnDef, dataContext) {
- var resources = dataContext["resources"];
+ ResourcesFormatter : function(row, cell, value, columnDef, dataContext) {
+ var resources = dataContext["resources"];
- if (!resources || resources.length == 0)
- return "";
+ 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(", ");
- },
+ if (columnDef.width < 50)
+ return (resources.length > 1 ? "<center><img src='../images/user_identity_plus.gif' " : "<center><img src='../images/user_identity.gif' ") +
+ " title='" + resources.join(", ") + "'></center>";
+ else
+ return resources.join(", ");
+ },
- StarFormatter : function(row, cell, value, columnDef, dataContext) {
- return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : "";
- },
+ StarFormatter : function(row, cell, value, columnDef, dataContext) {
+ return (value) ? "<img src='../images/bullet_star.png' align='absmiddle'>" : "";
+ },
- TextCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
+ TextCellEditor : function(args) {
+ var $input;
+ var defaultValue = args.value;
+ var scope = this;
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
+ this.init = function() {
+ $input = $("<INPUT type=text class='editor-text' />");
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
- $input.appendTo($container);
+ $input.appendTo(args.container);
- $input.bind("keydown.nav", function(e) {
- if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
- e.stopImmediatePropagation();
- }
- });
-
- $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;
- }
+ $input.bind("keydown.nav", function(e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ });
- return {
- valid: true,
- msg: null
+ $input.focus().select();
};
- };
- this.init();
- },
+ this.destroy = function() {
+ $input.remove();
+ };
- IntegerCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
+ this.focus = function() {
+ $input.focus();
+ };
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
+ this.setValue = function(value) {
+ $input.val(value);
+ defaultValue = value;
+ };
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
+ this.getValue = function() {
+ return $input.val();
+ };
- $input.bind("keydown.nav", function(e) {
- if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
- e.stopImmediatePropagation();
+ this.isValueChanged = function() {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
+
+ this.validate = function() {
+ if (args.column.validator) {
+ var validationResults = args.column.validator(scope.getValue());
+ if (!validationResults.valid)
+ return validationResults;
}
- });
- $input.appendTo($container);
- $input.focus().select();
- };
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+ this.init();
+ },
- this.destroy = function() {
- $input.remove();
- };
+ IntegerCellEditor : function(args) {
+ var $input;
+ var defaultValue = args.value;
+ var scope = this;
- this.focus = function() {
- $input.focus();
- };
+ this.init = function() {
+ $input = $("<INPUT type=text class='editor-text' />");
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- };
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
- this.getValue = function() {
- var val = $.trim($input.val());
- return (val == "") ? 0 : parseInt($input.val(), 10);
- };
+ $input.bind("keydown.nav", function(e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ });
- this.isValueChanged = function() {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
- };
+ $input.appendTo(args.container);
+ $input.focus().select();
+ };
- this.validate = function() {
- if (isNaN($input.val()))
- return {
- valid: false,
- msg: "Please enter a valid integer"
- };
+ this.destroy = function() {
+ $input.remove();
+ };
+
+ this.focus = function() {
+ $input.focus();
+ };
- return {
- valid: true,
- msg: null
+ this.setValue = function(value) {
+ $input.val(value);
+ defaultValue = value;
};
- };
- this.init();
- },
+ this.getValue = function() {
+ var val = $.trim($input.val());
+ return (val == "") ? 0 : parseInt($input.val(), 10);
+ };
- DateCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
+ this.isValueChanged = function() {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
+ this.validate = function() {
+ if (isNaN($input.val()))
+ return {
+ valid: false,
+ msg: "Please enter a valid integer"
+ };
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
+ return {
+ valid: true,
+ msg: null
+ };
+ };
- $input.appendTo($container);
- $input.focus().select();
- $input.datepicker({
- showOn: "button",
- buttonImageOnly: true,
- buttonImage: "../images/calendar.gif"
- });
- $input.width($input.width() - 18);
- };
+ this.init();
+ },
+ DateCellEditor : function(args) {
+ var $input;
+ var defaultValue = args.value;
+ var scope = this;
+ var calendarOpen = false;
- this.destroy = function() {
- $input.datepicker("hide");
- $input.datepicker("destroy");
- $input.remove();
- };
+ this.init = function() {
+ $input = $("<INPUT type=text class='editor-text' />");
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
- this.focus = function() {
- $input.focus();
- };
+ $input.appendTo(args.container);
+ $input.focus().select();
+ $input.datepicker({
+ showOn: "button",
+ buttonImageOnly: true,
+ buttonImage: "../images/calendar.gif",
+ beforeShow: function() { calendarOpen = true },
+ onClose: function() { calendarOpen = false }
+ });
+ $input.width($input.width() - 18);
+ };
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- };
+ this.destroy = function() {
+ $.datepicker.dpDiv.stop(true,true);
+ $input.datepicker("hide");
+ $input.datepicker("destroy");
+ $input.remove();
+ };
- this.getValue = function() {
- return $input.val();
- };
+ this.show = function() {
+ if (calendarOpen) {
+ $.datepicker.dpDiv.stop(true,true).show();
+ }
+ };
- this.isValueChanged = function() {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
- };
+ this.hide = function() {
+ if (calendarOpen) {
+ $.datepicker.dpDiv.stop(true,true).hide();
+ }
+ };
- this.validate = function() {
- return {
- valid: true,
- msg: null
+ this.position = function(position) {
+ if (!calendarOpen) return;
+ $.datepicker.dpDiv
+ .css("top", position.top + 30)
+ .css("left", position.left);
};
- };
- this.init();
- },
+ this.focus = function() {
+ $input.focus();
+ };
- YesNoSelectCellEditor : function($container, columnDef, value, dataContext) {
- var $select;
- var defaultValue = value;
- var scope = this;
+ this.setValue = function(value) {
+ $input.val(value);
+ defaultValue = value;
+ };
- this.init = function() {
- $select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>");
+ this.getValue = function() {
+ return $input.val();
+ };
- if (defaultValue)
- $select.val('yes');
- else
- $select.val('no');
+ this.isValueChanged = function() {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
- $select.appendTo($container);
+ this.validate = function() {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
- $select.focus();
- };
+ this.init();
+ },
+ YesNoSelectCellEditor : function(args) {
+ var $select;
+ var defaultValue = args.value;
+ var scope = this;
- this.destroy = function() {
- $select.remove();
- };
+ 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');
- this.focus = function() {
- $select.focus();
- };
+ $select.appendTo(args.container);
- this.setValue = function(value) {
- $select.val(value);
- defaultValue = value;
- };
+ $select.focus();
+ };
- this.getValue = function() {
- return ($select.val() == 'yes');
- };
+ this.destroy = function() {
+ $select.remove();
+ };
- this.isValueChanged = function() {
- return ($select.val() != defaultValue);
- };
+ this.focus = function() {
+ $select.focus();
+ };
- this.validate = function() {
- return {
- valid: true,
- msg: null
+ this.setValue = function(value) {
+ $select.val(value);
+ defaultValue = value;
};
- };
- this.init();
- },
+ this.getValue = function() {
+ return ($select.val() == 'yes');
+ };
- YesNoCheckboxCellEditor : function($container, columnDef, value, dataContext) {
- var $select;
- var defaultValue = value;
- var scope = this;
+ this.isValueChanged = function() {
+ return ($select.val() != defaultValue);
+ };
- this.init = function() {
- $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>");
+ this.validate = function() {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
- if (defaultValue)
- $select.attr("checked", "checked");
+ this.init();
+ },
- $select.appendTo($container);
- $select.focus();
- };
+ YesNoCheckboxCellEditor : function(args) {
+ var $select;
+ var defaultValue = args.value;
+ var scope = this;
+ this.init = function() {
+ $select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>");
- this.destroy = function() {
- $select.remove();
- };
+ if (defaultValue)
+ $select.attr("checked", "checked");
+ $select.appendTo(args.container);
+ $select.focus();
+ };
- this.focus = function() {
- $select.focus();
- };
- this.setValue = function(value) {
- if (value)
- $select.attr("checked", "checked");
- else
- $select.removeAttr("checked");
+ this.destroy = function() {
+ $select.remove();
+ };
- defaultValue = value;
- };
- this.getValue = function() {
- return $select.attr("checked");
- };
+ this.focus = function() {
+ $select.focus();
+ };
- this.isValueChanged = function() {
- return (scope.getValue() != defaultValue);
- };
+ this.setValue = function(value) {
+ if (value)
+ $select.attr("checked", "checked");
+ else
+ $select.removeAttr("checked");
- this.validate = function() {
- return {
- valid: true,
- msg: null
+ defaultValue = value;
};
- };
- this.init();
- },
+ this.getValue = function() {
+ return $select.attr("checked");
+ };
- PercentCompleteCellEditor : function($container, columnDef, value, dataContext) {
- var $input, $picker;
- var defaultValue = value;
- var scope = this;
+ this.isValueChanged = function() {
+ return (scope.getValue() != defaultValue);
+ };
- this.init = function() {
- $input = $("<INPUT type=text class='editor-percentcomplete' />");
+ this.validate = function() {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
+ this.init();
+ },
- $input.width($container.innerWidth() - 25);
- $input.appendTo($container);
+ PercentCompleteCellEditor : function(args) {
+ var $input, $picker;
+ var defaultValue = args.value;
+ var scope = this;
- $picker = $("<div class='editor-percentcomplete-picker' />").appendTo($container);
+ this.init = function() {
+ $input = $("<INPUT type=text class='editor-percentcomplete' />");
- $picker.append("<div class='editor-percentcomplete-helper'><div class='editor-percentcomplete-wrapper'><div class='editor-percentcomplete-slider' /><div class='editor-percentcomplete-buttons' /></div></div>");
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
- $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.width($(args.container).innerWidth() - 25);
+ $input.appendTo(args.container);
- $input.focus().select();
+ $picker = $("<div class='editor-percentcomplete-picker' />").appendTo(args.container);
- $picker.find(".editor-percentcomplete-slider").slider({
- orientation: "vertical",
- range: "min",
- value: defaultValue,
- slide: function(event, ui) {
- $input.val(ui.value)
- }
- });
+ $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 button").bind("click", function(e) {
- $input.val($(this).attr("val"));
- $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val"));
- })
- };
+ $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();
- this.destroy = function() {
- $input.remove();
- $picker.remove();
- };
+ $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.focus = function() {
- $input.focus();
- };
- this.setValue = function(value) {
- $input.val(value);
- defaultValue = value;
- };
+ this.destroy = function() {
+ $input.remove();
+ $picker.remove();
+ };
- 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.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"
+ };
- this.validate = function() {
- if (isNaN($input.val()))
return {
- valid: false,
- msg: "Please enter a valid positive number"
+ valid: true,
+ msg: null
};
+ };
+
+ this.init();
+ },
+
+ TaskNameCellEditor : function(args) {
+ var $input;
+ var defaultValue = args.value;
+ var scope = this;
+
+ this.init = function() {
+ $input = $("<INPUT type=text class='editor-text' />");
- return {
- valid: true,
- msg: null
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
+
+ $input.bind("keydown.nav", function(e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ });
+ $input.appendTo(args.container);
+ $input.focus().select();
};
- };
- this.init();
- },
+ this.destroy = function() {
+ $input.remove();
+ };
- TaskNameCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
+ this.focus = function() {
+ $input.focus();
+ };
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
+ this.setValue = function(value) {
+ $input.val(value);
+ defaultValue = value;
+ };
- if (value != null)
- {
- $input[0].defaultValue = value;
- $input.val(defaultValue);
- }
+ this.getValue = function() {
+ return $input.val();
+ };
- $input.bind("keydown.nav", function(e) {
- if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
- e.stopImmediatePropagation();
+ 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;
}
- });
- $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() == "")
+ if ($input.val() == "")
+ return {
+ valid: false,
+ msg: "This field cannot be empty"
+ };
+
return {
- valid: false,
- msg: "This field cannot be empty"
+ valid: true,
+ msg: null
};
-
- return {
- valid: true,
- msg: null
};
- };
-
- this.init();
- },
- ResourcesCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = [];
- var scope = this;
+ this.init();
+ },
- this.init = function() {
- $input = $("<INPUT type=text class='editor-text' />");
+ StarCellEditor : function(args) {
+ var $input;
+ var defaultValue = args.value;
+ var scope = this;
- var resources = dataContext ? dataContext["resources"] : null;
+ function toggle(e) {
+ if (e.type == "keydown" && e.which != 32) return;
- defaultValue = resources ? resources.concat() : [];
+ if ($input.css("opacity") == "1")
+ $input.css("opacity", 0.5);
+ else
+ $input.css("opacity", 1);
- if (resources != null)
- {
- $input[0].defaultValue = defaultValue.join(", ");
- $input.val(defaultValue.join(", "));
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
}
- $input.bind("keydown.nav", function(e) {
- if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
- e.stopImmediatePropagation();
- }
- });
- $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;
- }
+ this.init = function() {
+ $input = $("<IMG src='../images/bullet_star.png' align=absmiddle tabIndex=0 title='Click or press Space to toggle' />");
- // todo: implement
+ if (defaultValue)
+ $input.css("opacity", 1);
+ else
+ $input.css("opacity", 0.5);
- return {
- valid: true,
- msg: null
+ $input.bind("click keydown", toggle);
+
+ $input.appendTo(args.container);
+ $input.focus();
};
- };
- this.init();
- },
+ this.destroy = function() {
+ $input.unbind("click keydown", toggle);
+ $input.remove();
+ };
- StarCellEditor : function($container, columnDef, value, dataContext) {
- var $input;
- var defaultValue = value;
- var scope = this;
+ this.focus = function() {
+ $input.focus();
+ };
- function toggle(e) {
- if (e.type == "keydown" && e.which != 32) return;
+ this.setValue = function(value) {
+ defaultValue = value;
- if ($input.css("opacity") == "1")
- $input.css("opacity", 0.5);
- else
- $input.css("opacity", 1);
+ if (defaultValue)
+ $input.css("opacity", 1);
+ else
+ $input.css("opacity", 0.2);
+ };
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
+ this.getValue = function() {
+ return $input.css("opacity") == "1";
+ };
- this.init = function() {
- $input = $("<IMG src='../images/bullet_star.png' align=absmiddle tabIndex=0 title='Click or press Space to toggle' />");
+ this.isValueChanged = function() {
+ return defaultValue != scope.getValue();
+ };
- if (defaultValue)
- $input.css("opacity", 1);
- else
- $input.css("opacity", 0.5);
+ this.validate = function() {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
- $input.bind("click keydown", toggle);
+ this.init();
+ },
- $input.appendTo($container);
- $input.focus();
- };
+ /*
+ * An example of a "detached" editor.
+ * The UI is added onto document BODY and .position(), .show() and .hide() are implemented.
+ * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
+ */
+ LongTextCellEditor : function (args) {
+ var $input, $wrapper;
+ var defaultValue = args.value;
+ var scope = this;
- this.destroy = function() {
- $input.unbind("click keydown", toggle);
- $input.remove();
- };
+ this.init = function() {
+ var $container = $("body");
- this.focus = function() {
- $input.focus();
- };
+ $wrapper = $("<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>")
+ .appendTo($container);
- this.setValue = function(value) {
- defaultValue = value;
+ $input = $("<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>")
+ .appendTo($wrapper);
- if (defaultValue)
- $input.css("opacity", 1);
- else
- $input.css("opacity", 0.2);
- };
+ $("<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>")
+ .appendTo($wrapper);
+
+ $wrapper.find("button:first").bind("click", this.save);
+ $wrapper.find("button:last").bind("click", this.cancel);
+ $input.bind("keydown", this.handleKeyDown);
+
+ if (args.value != null) {
+ $input[0].defaultValue = args.value;
+ $input.val(defaultValue);
+ }
+
+ scope.position(args.position);
+ $input.focus().select();
+ };
+
+ this.handleKeyDown = function(e) {
+ if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
+ scope.save();
+ }
+ else if (e.which == $.ui.keyCode.ESCAPE) {
+ e.preventDefault();
+ scope.cancel();
+ }
+ else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
+ e.preventDefault();
+ grid.navigatePrev();
+ }
+ else if (e.which == $.ui.keyCode.TAB) {
+ e.preventDefault();
+ grid.navigateNext();
+ }
+ };
+
+ this.save = function() {
+ args.commitChanges();
+ };
+
+ this.cancel = function() {
+ $input.val(defaultValue);
+ args.cancelChanges();
+ };
- this.getValue = function() {
- return $input.css("opacity") == "1";
- };
+ this.hide = function() {
+ $wrapper.hide();
+ };
+
+ this.show = function() {
+ $wrapper.show();
+ };
+
+ this.position = function(position) {
+ $wrapper
+ .css("top", position.top - 5)
+ .css("left", position.left - 5)
+ };
+
+ this.destroy = function() {
+ $wrapper.remove();
+ };
+
+ this.focus = function() {
+ $input.focus();
+ };
- this.isValueChanged = function() {
- return (defaultValue == true) != scope.getValue();
- };
+ this.setValue = function(value) {
+ $input.val(value);
+ defaultValue = value;
+ };
+
+ this.getValue = function() {
+ return $input.val();
+ };
- this.validate = function() {
- return {
- valid: true,
- msg: null
+ this.isValueChanged = function() {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
- };
- this.init();
- }
- };
+ this.validate = function() {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ };
- $.extend(window, SlickEditor);
+ $.extend(window, SlickEditor);
})(jQuery);
diff --git a/slick.grid.js b/slick.grid.js
index a20cf39..42ac051 100644
--- a/slick.grid.js
+++ b/slick.grid.js
@@ -429,6 +429,7 @@ if (!jQuery.fn.drag) {
resizeAndRender();
+ bindAncestorScrollEvents();
$viewport.bind("scroll", handleScroll);
$container.bind("resize", resizeAndRender);
$canvas.bind("keydown", handleKeyDown);
@@ -439,6 +440,20 @@ if (!jQuery.fn.drag) {
$headerScroller.bind("contextmenu", handleHeaderContextMenu);
}
+ // TODO: this is static. need to handle page mutation.
+ function bindAncestorScrollEvents() {
+ var elem = $canvas[0];
+ while ((elem = elem.parentNode) != document.body) {
+ // bind to scroll containers only
+ if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight)
+ $(elem).bind("scroll.slickgrid", repositionCurrentCellEditor);
+ }
+ }
+
+ function unbindAncestorScrollEvents() {
+ $canvas.parents().unbind("scroll.slickgrid");
+ }
+
function createColumnHeaders() {
var i;
@@ -837,6 +852,7 @@ if (!jQuery.fn.drag) {
if (self.onBeforeDestroy) { self.onBeforeDestroy(); }
if ($headers.sortable) { $headers.sortable("destroy"); }
+ unbindAncestorScrollEvents();
$container.unbind("resize", resizeCanvas);
removeCssRules();
@@ -1087,6 +1103,9 @@ if (!jQuery.fn.drag) {
}
function removeAllRows() {
+ if (currentEditor) {
+ makeSelectedCellNormal();
+ }
$canvas[0].innerHTML = "";
rowsCache= {};
postProcessedRows = {};
@@ -1111,7 +1130,7 @@ if (!jQuery.fn.drag) {
var nodes = [];
for (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";
+ makeSelectedCellNormal();
}
if (rowsCache[rows[i]]) {
@@ -1391,55 +1410,47 @@ if (!jQuery.fn.drag) {
if (!options.editorLock.isActive()) {
return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event)
}
- options.editorLock.cancelCurrentEdit();
- if (currentCellNode) {
- currentCellNode.focus();
- }
+ cancelEditAndSetFocus();
break;
case 9: // tab
- gotoDir(0, (e.shiftKey) ? -1 : 1, true);
+ if (e.shiftKey)
+ navigatePrev();
+ else
+ navigateNext();
break;
case 37: // left
- gotoDir(0, -1, false);
+ navigatePrev();
break;
case 39: // right
- gotoDir(0, 1, false);
+ navigateRight();
break;
case 38: // up
- gotoDir(-1, 0, false);
+ navigateUp();
break;
case 40: // down
- gotoDir(1, 0, false);
+ navigateDown();
break;
case 13: // enter
- if (options.autoEdit) {
- gotoDir(1, 0, false);
- }
- else if (options.editable) {
+ if (options.editable) {
if (currentEditor) {
// adding new row
if (currentRow === data.length) {
- gotoDir(1, 0, false);
+ navigateDown();
}
else {
- // if the commit fails, it would do so due to a validation error
- // if so, do not steal the focus from the editor
- if (options.editorLock.commitCurrentEdit()) {
- currentCellNode.focus();
- }
+ commitEditAndSetFocus();
}
} else {
if (options.editorLock.commitCurrentEdit()) {
makeSelectedCellEditable();
}
}
-
}
break;
@@ -1593,7 +1604,6 @@ if (!jQuery.fn.drag) {
}
}
- // TODO: PERF: throttle event
function handleHover(e) {
if (!options.enableAutoTooltips) return;
var $cell = $(e.target).closest(".slick-cell",$canvas);
@@ -1623,6 +1633,7 @@ if (!jQuery.fn.drag) {
//////////////////////////////////////////////////////////////////////////////////////////////
// Cell switching
+
function resetCurrentCell() {
setSelectedCell(null,false,false);
}
@@ -1716,14 +1727,16 @@ if (!jQuery.fn.drag) {
self.onBeforeCellEditorDestroy(currentEditor);
}
currentEditor.destroy();
- $(currentCellNode).removeClass("editable invalid");
+ currentEditor = null;
- if (gridDataGetItem(currentRow)) {
- currentCellNode.innerHTML = columns[currentCell].formatter(currentRow, currentCell, gridDataGetItem(currentRow)[columns[currentCell].field], columns[currentCell], gridDataGetItem(currentRow));
- invalidatePostProcessingResults(currentRow);
- }
+ if (currentCellNode) {
+ $(currentCellNode).removeClass("editable invalid");
- currentEditor = null;
+ if (gridDataGetItem(currentRow)) {
+ currentCellNode.innerHTML = columns[currentCell].formatter(currentRow, currentCell, gridDataGetItem(currentRow)[columns[currentCell].field], columns[currentCell], gridDataGetItem(currentRow));
+ invalidatePostProcessingResults(currentRow);
+ }
+ }
// 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
@@ -1745,8 +1758,6 @@ if (!jQuery.fn.drag) {
return;
}
-
-
if (self.onBeforeEditCell && self.onBeforeEditCell(currentRow,currentCell,gridDataGetItem(currentRow)) === false) {
currentCellNode.focus();
return;
@@ -1764,13 +1775,81 @@ if (!jQuery.fn.drag) {
currentCellNode.innerHTML = "";
- currentEditor = new columns[currentCell].editor($(currentCellNode), columns[currentCell], value, gridDataGetItem(currentRow), function() {
- // if the commit fails, it would do so due to a validation error
- // if so, do not steal the focus from the editor
- if (options.editorLock.commitCurrentEdit()) {
- currentCellNode.focus();
- }
+ var columnDef = columns[currentCell];
+
+ currentEditor = new columnDef.editor({
+ grid: self,
+ gridPosition: absBox($container[0]),
+ position: absBox(currentCellNode),
+ container: currentCellNode,
+ column: columnDef,
+ value: value,
+ item: gridDataGetItem(currentRow),
+ commitChanges: commitEditAndSetFocus,
+ cancelChanges: cancelEditAndSetFocus
});
+
+ if (currentEditor.position)
+ repositionCurrentCellEditor();
+ }
+
+ function commitEditAndSetFocus() {
+ // if the commit fails, it would do so due to a validation error
+ // if so, do not steal the focus from the editor
+ if (options.editorLock.commitCurrentEdit()) {
+ currentCellNode.focus();
+ }
+
+ if (options.autoEdit) {
+ navigateDown();
+ }
+ }
+
+ function cancelEditAndSetFocus() {
+ if (options.editorLock.cancelCurrentEdit()) {
+ currentCellNode.focus();
+ }
+ }
+
+ // TODO: may be some issues with floats. investigate.
+ function absBox(elem) {
+ var box = {top:elem.offsetTop, left:elem.offsetLeft, bottom:0, right:0, width:$(elem).outerWidth(), height:$(elem).outerHeight(), visible:true};
+ box.bottom = box.top + box.height;
+ box.right = box.left + box.width;
+
+ // walk up the tree
+ var offsetParent = elem.offsetParent;
+ while ((elem = elem.parentNode) != document.body) {
+ box.visible = box.visible
+ && !(box.bottom <= elem.scrollTop || box.top >= elem.offsetTop + elem.scrollTop + elem.clientHeight)
+ && !(box.right <= elem.scrollLeft || box.left >= elem.offsetLeft + elem.scrollLeft + elem.clientWidth);
+
+ box.left -= elem.scrollLeft;
+ box.top -= elem.scrollTop;
+
+ if (elem === offsetParent) {
+ box.left += elem.offsetLeft;
+ box.top += elem.offsetTop;
+ offsetParent = elem.offsetParent;
+ }
+
+ box.bottom = box.top + box.height;
+ box.right = box.left + box.width;
+ }
+
+ return box;
+ }
+
+ function repositionCurrentCellEditor() {
+ if (!currentEditor || !currentCellNode || !currentEditor.position) return;
+ var cellBox = absBox(currentCellNode);
+ if (currentEditor.show && currentEditor.hide) {
+ if (!cellBox.visible)
+ currentEditor.hide();
+ else
+ currentEditor.show();
+ }
+ currentEditor.position(cellBox);
}
function getCellEditor() {
@@ -1875,6 +1954,29 @@ if (!jQuery.fn.drag) {
}
}
+ function navigateUp() {
+ gotoDir(-1, 0, false);
+ }
+
+ function navigateDown() {
+ gotoDir(1, 0, false);
+ }
+
+ function navigateLeft() {
+ gotoDir(0, -1, false);
+ }
+
+ function navigateRight() {
+ gotoDir(0, 1, false);
+ }
+
+ function navigatePrev() {
+ gotoDir(0, -1, true);
+ }
+
+ function navigateNext() {
+ gotoDir(0, 1, true);
+ }
//////////////////////////////////////////////////////////////////////////////////////////////
// IEditor implementation for the editor lock
@@ -2038,6 +2140,12 @@ if (!jQuery.fn.drag) {
"getCurrentCell": getCurrentCell,
"getCurrentCellNode": getCurrentCellNode,
"resetCurrentCell": resetCurrentCell,
+ "navigatePrev": navigatePrev,
+ "navigateNext": navigateNext,
+ "navigateUp": navigateUp,
+ "navigateDown": navigateDown,
+ "navigateLeft": navigateLeft,
+ "navigateRight": navigateRight,
"gotoCell": gotoCell,
"editCurrentCell": makeSelectedCellEditable,
"getCellEditor": getCellEditor,