Last active
August 29, 2015 14:07
-
-
Save brihter/f987539b2ff12dc3693a to your computer and use it in GitHub Desktop.
ExtJS 3.x grid multi-cell selection model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en-US"> | |
<head> | |
<title>MultiCell Selection Model Example</title> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
<link type="text/css" href="../../resources/js/ext-3.4.1/resources/css/ext-all.css" rel="stylesheet" /> | |
<script type="text/javascript" src="../../resources/js/ext-3.4.1/adapter/ext/ext-base.js"></script> | |
<script type="text/javascript" src="../../resources/js/ext-3.4.1/ext-all-debug.js"></script> | |
<script src="../../src/js/grid/MultiCellSelectionModel.js"></script> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
Ext.onReady(function () { | |
//#region store | |
var store = new Ext.data.JsonStore({ | |
fields: [ | |
{ name: 'firstname' }, | |
{ name: 'lastname' }, | |
{ name: 'address' }, | |
{ name: 'zip' }, | |
{ name: 'post' }, | |
{ name: 'country' } | |
], | |
data: [ | |
{ "firstname": "Janez", "lastname": "Lacan", "address": "Drnovo", "zip": 6018, "post": "Ptuj", "country": "Austria" }, | |
{ "firstname": "Janez", "lastname": "Lacan", "address": "Dunajska cesta", "zip": 4865, "post": "Celje", "country": "Croatia" }, | |
{ "firstname": "Bostjan", "lastname": "Novak", "address": "Vurnikova cesta", "zip": 4239, "post": "Leskovec pri Krskem", "country": "Croatia" }, | |
{ "firstname": "Pavle", "lastname": "Rihter", "address": "Vurnikova cesta", "zip": 4461, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "Janez", "lastname": "Petrarca", "address": "Drnovo", "zip": 7473, "post": "Ptuj", "country": "Hungary" }, | |
{ "firstname": "Luka", "lastname": "Novak", "address": "Dunajska cesta", "zip": 5578, "post": "Ljubljana", "country": "Croatia" }, | |
{ "firstname": "Allen", "lastname": "Novak", "address": "Na Jami", "zip": 5516, "post": "Ptuj", "country": "Germany" }, | |
{ "firstname": "Jernej", "lastname": "Rihter", "address": "Dunajska cesta", "zip": 7024, "post": "Leskovec pri Krskem", "country": "Austria" }, | |
{ "firstname": "Luka", "lastname": "Lacan", "address": "Cesta Krskih Zrtev", "zip": 9149, "post": "Brezice", "country": "Italy" }, | |
{ "firstname": "Bostjan", "lastname": "Gartner", "address": "Vurnikova cesta", "zip": 6833, "post": "Leskovec pri Krskem", "country": "Germany" }, | |
{ "firstname": "Luka", "lastname": "Petrarca", "address": "Ameriska ulica", "zip": 2984, "post": "Koper", "country": "Hungary" }, | |
{ "firstname": "Janez", "lastname": "Lacan", "address": "Vurnikova cesta", "zip": 5452, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "Janez", "lastname": "Petrarca", "address": "Cesta Krskih Zrtev", "zip": 7449, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "Janez", "lastname": "Hostar", "address": "Na Jami", "zip": 2576, "post": "Koper", "country": "Croatia" }, | |
{ "firstname": "Jernej", "lastname": "Hostar", "address": "Vurnikova cesta", "zip": 8335, "post": "Brezice", "country": "Croatia" }, | |
{ "firstname": "Jernej", "lastname": "Petrarca", "address": "Drnovo", "zip": 2516, "post": "Radece", "country": "Croatia" }, | |
{ "firstname": "Pavle", "lastname": "Lacan", "address": "Dunajska cesta", "zip": 5000, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "Allen", "lastname": "Lacan", "address": "Vurnikova cesta", "zip": 9943, "post": "Novo mesto", "country": "Austria" }, | |
{ "firstname": "Bostjan", "lastname": "Lacan", "address": "Drnovo", "zip": 2623, "post": "Krsko", "country": "Croatia" }, | |
{ "firstname": "Janez", "lastname": "Lacan", "address": "Vurnikova cesta", "zip": 4707, "post": "Koper", "country": "Croatia" } | |
] | |
}); | |
var store2 = new Ext.data.JsonStore({ | |
fields: [ | |
{ name: 'firstname' }, | |
{ name: 'lastname' }, | |
{ name: 'address' }, | |
{ name: 'zip' }, | |
{ name: 'post' }, | |
{ name: 'country' } | |
], | |
data: [ | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 6018, "post": "Ptuj", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 4865, "post": "Celje", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4239, "post": "Leskovec pri Krskem", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4461, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 7473, "post": "Ptuj", "country": "Hungary" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 5578, "post": "Ljubljana", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Na Jami", "zip": 5516, "post": "Ptuj", "country": "Germany" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 7024, "post": "Leskovec pri Krskem", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Cesta Krskih Zrtev", "zip": 9149, "post": "Brezice", "country": "Italy" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 6833, "post": "Leskovec pri Krskem", "country": "Germany" }, | |
{ "firstname": "", "lastname": "", "address": "Ameriska ulica", "zip": 2984, "post": "Koper", "country": "Hungary" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 5452, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Cesta Krskih Zrtev", "zip": 7449, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "", "lastname": "", "address": "Na Jami", "zip": 2576, "post": "Koper", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 8335, "post": "Brezice", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 2516, "post": "Radece", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 5000, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 9943, "post": "Novo mesto", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 2623, "post": "Krsko", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4707, "post": "Koper", "country": "Croatia" } | |
] | |
}); | |
var store3 = new Ext.data.JsonStore({ | |
fields: [ | |
{ name: 'firstname' }, | |
{ name: 'lastname' }, | |
{ name: 'address' }, | |
{ name: 'zip' }, | |
{ name: 'post' }, | |
{ name: 'country' } | |
], | |
data: [ | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 6018, "post": "Ptuj", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 4865, "post": "Celje", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4239, "post": "Leskovec pri Krskem", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4461, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 7473, "post": "Ptuj", "country": "Hungary" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 5578, "post": "Ljubljana", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Na Jami", "zip": 5516, "post": "Ptuj", "country": "Germany" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 7024, "post": "Leskovec pri Krskem", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Cesta Krskih Zrtev", "zip": 9149, "post": "Brezice", "country": "Italy" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 6833, "post": "Leskovec pri Krskem", "country": "Germany" }, | |
{ "firstname": "", "lastname": "", "address": "Ameriska ulica", "zip": 2984, "post": "Koper", "country": "Hungary" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 5452, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Cesta Krskih Zrtev", "zip": 7449, "post": "Celje", "country": "Slovenia" }, | |
{ "firstname": "", "lastname": "", "address": "Na Jami", "zip": 2576, "post": "Koper", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 8335, "post": "Brezice", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 2516, "post": "Radece", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Dunajska cesta", "zip": 5000, "post": "Radece", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 9943, "post": "Novo mesto", "country": "Austria" }, | |
{ "firstname": "", "lastname": "", "address": "Drnovo", "zip": 2623, "post": "Krsko", "country": "Croatia" }, | |
{ "firstname": "", "lastname": "", "address": "Vurnikova cesta", "zip": 4707, "post": "Koper", "country": "Croatia" } | |
] | |
}); | |
//#endregion | |
var grid = new Ext.grid.GridPanel({ | |
title: 'MultiCell Selection - Source (use ctrl/shift for multiple cell selection)', | |
x: 10, | |
y: 10, | |
width: 640, | |
height: 480, | |
store: store, | |
trackMouseOver: false, | |
sm: new Ext.grid.MultiCellSelectionModel({ | |
enableDrag: true, | |
dragGroup: 'test' | |
}), | |
columns: [ | |
{ header: 'Firstname', width: 100, dataIndex: 'firstname' }, | |
{ header: 'Lastname', width: 100, dataIndex: 'lastname' }, | |
{ header: 'Address', width: 120, dataIndex: 'address' }, | |
{ header: 'ZIP', width: 50, dataIndex: 'zip' }, | |
{ header: 'Post', width: 120, dataIndex: 'post' }, | |
{ header: 'Country', width: 80, dataIndex: 'country' } | |
] | |
}); | |
var grid2 = new Ext.grid.GridPanel({ | |
title: 'MultiCell Selection - Target (drop the selected cells here)', | |
x: 660, | |
y: 10, | |
width: 640, | |
height: 480, | |
store: store2, | |
trackMouseOver: false, | |
sm: new Ext.grid.MultiCellSelectionModel({ | |
enableDrop: true, | |
dropGroup: 'test' | |
}), | |
columns: [ | |
{ header: 'Firstname', width: 100, dataIndex: 'firstname' }, | |
{ header: 'Lastname', width: 100, dataIndex: 'lastname' }, | |
{ header: 'Address', width: 120, dataIndex: 'address' }, | |
{ header: 'ZIP', width: 50, dataIndex: 'zip' }, | |
{ header: 'Post', width: 120, dataIndex: 'post' }, | |
{ header: 'Country', width: 80, dataIndex: 'country' } | |
] | |
}); | |
var grid3 = new Ext.grid.GridPanel({ | |
title: 'MultiCell Selection - different drop target (no drop)', | |
x: 660, | |
y: 500, | |
width: 640, | |
height: 480, | |
store: store3, | |
trackMouseOver: false, | |
sm: new Ext.grid.MultiCellSelectionModel({ | |
enableDrop: true, | |
dropGroup: 'diff' | |
}), | |
columns: [ | |
{ header: 'Firstname', width: 100, dataIndex: 'firstname' }, | |
{ header: 'Lastname', width: 100, dataIndex: 'lastname' }, | |
{ header: 'Address', width: 120, dataIndex: 'address' }, | |
{ header: 'ZIP', width: 50, dataIndex: 'zip' }, | |
{ header: 'Post', width: 120, dataIndex: 'post' }, | |
{ header: 'Country', width: 80, dataIndex: 'country' } | |
] | |
}); | |
var viewport = new Ext.Viewport({ | |
layout: 'absolute', | |
items: [ | |
grid, | |
grid2, | |
grid3 | |
] | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @class Ext.grid.MultiCellSelectionModel | |
* @extends Ext.grid.CellSelectionModel | |
* @cfg {Boolean} enableDrag Enable cell drag | |
* @cfg {String} dragGroup The name of the drag group, the drag group will only interact with the corresponding drop group | |
* @cfg {Boolean} enableDrop Enable cell drop | |
* @cfg {String} dropGroup The name of the drop group, the drop group will only interact with the corresponding drag group | |
*/ | |
Ext.grid.MultiCellSelectionModel = Ext.extend(Ext.grid.CellSelectionModel, { | |
constructor: function (config) { | |
var me = this; | |
Ext.applyIf(config, { | |
enableDrag: false, | |
enableDrop: false | |
}); | |
Ext.apply(me, config); | |
me.addEvents( | |
'beforecellselect', | |
'cellselect', | |
'selectionchange', | |
'rowselect', | |
'columnselect' | |
); | |
Ext.grid.MultiCellSelectionModel.superclass.constructor.call(me); | |
me.selection = []; | |
me.selectionIndex = {}; | |
}, | |
initEvents: function () { | |
var me = this; | |
Ext.grid.MultiCellSelectionModel.superclass.initEvents.call(me); | |
// drag | |
if (me.enableDrag === true) { | |
me.grid.on('render', me.handleDrag, me); | |
} | |
// drop | |
if (me.enableDrop === true) { | |
me.grid.on('render', me.handleDrop, me); | |
} | |
// remove the original mousedown listener, since we need drag&drop | |
// selection will be done on "mouseup" (cell click) | |
me.grid.un('cellmousedown', me.handleMouseDown, me); | |
me.grid.on('cellclick', me.handleMouseClick, me); | |
}, | |
handleMouseClick: function (g, row, cell, e) { | |
var me = this; | |
if (e.button !== 0 || this.isLocked()) | |
return; | |
var range = false, | |
clear = true; | |
if (e.ctrlKey == true) { | |
clear = false; | |
} | |
if (e.shiftKey) { | |
range = true; | |
} | |
me.select(row, cell, false, focus, null, range, clear); | |
}, | |
handleDrag: function (grid) { | |
var me = this, | |
ddGroup = undefined; | |
if (me.dragGroup && me.dragGroup.length > 0) { | |
ddGroup = me.dragGroup; | |
} | |
grid.dragZone = new Ext.dd.DragZone(grid.getEl(), { | |
ddGroup: ddGroup, | |
getDragData: function (e) { | |
var selection = me.getSelectedCells(); | |
if (!selection) | |
return; | |
if (selection.length === 0) | |
return; | |
var dragEl = document.createElement('div'), | |
target = e.getTarget(), | |
rowIndex = grid.view.findRowIndex(target), | |
cellIndex = grid.view.findCellIndex(target); | |
dragEl.innerHTML = String.format('{0} cells selected', selection.length); | |
return { | |
ddel: dragEl, | |
selection: selection, | |
selectionSource: { | |
x: cellIndex, | |
y: rowIndex | |
} | |
}; | |
}, | |
getRepairXY: function (e) { | |
var me = this, | |
dd = me.dragData; | |
return Ext.Element.fly(dd.selection[0].el).getXY(); | |
} | |
}); | |
}, | |
handleDrop: function (grid) { | |
var me = this, | |
ddGroup = undefined; | |
if (me.dropGroup && me.dropGroup.length > 0) { | |
ddGroup = me.dropGroup; | |
} | |
grid.dropZone = new Ext.dd.DropZone(grid.getView().scroller, { | |
ddGroup: ddGroup, | |
getTargetFromEvent: function (e) { | |
return e.getTarget('.x-grid3-cell'); | |
}, | |
onNodeEnter: function (target, dd, e, data) { | |
var i, | |
cell, | |
x, | |
y, | |
trgt; | |
var rowIndex = grid.view.findRowIndex(target), | |
cellIndex = grid.view.findCellIndex(target), | |
offsetX = cellIndex - data.selectionSource.x, | |
offsetY = rowIndex - data.selectionSource.y; | |
for (i = 0; i < data.selection.length; ++i) { | |
cell = data.selection[i].cell; | |
x = cell[1] + offsetX; | |
y = cell[0] + offsetY; | |
if (!grid.view.getRow(y)) { | |
continue; | |
} | |
trgt = grid.view.getCell(y, x); | |
if (!trgt) { | |
continue; | |
} | |
Ext.fly(trgt).addClass('x-grid3-cell-selected'); | |
} | |
}, | |
onNodeOut: function (target, dd, e, data) { | |
var i, | |
cell, | |
x, | |
y, | |
trgt; | |
var rowIndex = grid.view.findRowIndex(target), | |
cellIndex = grid.view.findCellIndex(target), | |
offsetX = cellIndex - data.selectionSource.x, | |
offsetY = rowIndex - data.selectionSource.y; | |
for (i = 0; i < data.selection.length; ++i) { | |
cell = data.selection[i].cell; | |
x = cell[1] + offsetX; | |
y = cell[0] + offsetY; | |
if (!grid.view.getRow(y)) { | |
continue; | |
} | |
trgt = grid.view.getCell(y, x); | |
if (!trgt) { | |
continue; | |
} | |
Ext.fly(trgt).removeClass('x-grid3-cell-selected'); | |
} | |
}, | |
onNodeDrop: function (target, dd, e, data) { | |
return true; | |
} | |
}); | |
}, | |
select: function (rowIndex, colIndex, preventViewNotify, preventFocus, r, range, clear) { | |
var me = this; | |
if (me.fireEvent('beforecellselect', me, rowIndex, colIndex) === false) | |
return; | |
var x, x1, minX, maxX, | |
y, y1, minY, maxY, | |
v = me.grid.getView(); | |
// clear | |
if (clear === true) { | |
me.clearSelections(); | |
} | |
if (range === true) { | |
// range | |
if (me.rangeStart == null) { | |
me.rangeStart = [rowIndex, colIndex]; | |
me.rangeStop = [rowIndex, colIndex]; | |
} else { | |
me.rangeStop = [rowIndex, colIndex]; | |
} | |
x1 = me.rangeStart[1]; | |
y1 = me.rangeStart[0]; | |
x2 = me.rangeStop[1]; | |
y2 = me.rangeStop[0]; | |
minX = (x2 < x1) ? x2 : x1; | |
maxX = (x2 < x1) ? x1 : x2; | |
minY = (y2 < y1) ? y2 : y1; | |
maxY = (y2 < y1) ? y1 : y2; | |
// select | |
for (x = minX; x <= maxX; ++x) { | |
for (y = minY; y <= maxY; ++y) { | |
if (me.isSelected(y, x)) { | |
me.removeFromSelection(y, x); | |
} else { | |
me.addToSelection(y, x); | |
if (!preventViewNotify) { | |
v.onCellSelect(y, x); | |
} | |
} | |
} | |
} | |
} | |
else { | |
// single cell | |
me.rangeStart = [rowIndex, colIndex]; | |
me.rangeStop = [rowIndex, colIndex]; | |
if (me.isSelected(rowIndex, colIndex)) { | |
me.removeFromSelection(rowIndex, colIndex); | |
} else { | |
me.addToSelection(rowIndex, colIndex); | |
if (!preventViewNotify) { | |
v.onCellSelect(rowIndex, colIndex); | |
} | |
} | |
} | |
me.fireEvent('cellselect', me, rowIndex, colIndex); | |
me.fireEvent('selectionchange', me, me.selection); | |
}, | |
/** | |
* @private | |
*/ | |
addToSelection: function (rowIndex, colIndex) { | |
var me = this, | |
r, | |
obj; | |
r = r || me.grid.store.getAt(rowIndex); | |
obj = { | |
record: r, | |
cell: [rowIndex, colIndex], | |
el: me.grid.view.getCell(rowIndex, colIndex) | |
}; | |
me.selectionIndex[colIndex + ',' + rowIndex] = obj; | |
me.selection.push(obj); | |
if (me.rangeStart === null) { | |
me.rangeStart = [rowIndex, colIndex]; | |
} else { | |
me.rangeStop = [rowIndex, colIndex]; | |
} | |
}, | |
/** | |
* @private | |
*/ | |
isSelected: function (rowIndex, colIndex) { | |
var key = colIndex + ',' + rowIndex; | |
if (!this.selectionIndex[key]) | |
return false; | |
return true; | |
}, | |
/** | |
* @private | |
*/ | |
removeFromSelection: function (rowIndex, colIndex) { | |
var me = this, | |
s = me.selection, | |
key = colIndex + ',' + rowIndex; | |
me.selectionIndex[key] = null; | |
delete me.selectionIndex[key]; | |
for (i = 0; i < s.length; ++i) { | |
if (s[i].cell[0] == rowIndex && s[i].cell[1] == colIndex) { | |
me.grid.view.onCellDeselect(s[i].cell[0], s[i].cell[1]); | |
s.splice(i, 1); | |
} | |
} | |
me.fireEvent('selectionchange', me, null); | |
}, | |
clearSelections: function (preventNotify) { | |
var me = this, | |
s = me.selection; | |
if (!s) | |
return; | |
var i; | |
if (preventNotify !== true) { | |
for (i = 0; i < s.length; ++i) { | |
me.grid.view.onCellDeselect(s[i].cell[0], s[i].cell[1]); | |
} | |
} | |
me.selection = []; | |
me.selectionIndex = {}; | |
me.fireEvent('selectionchange', me, null); | |
}, | |
handleKeyDown: Ext.emptyFn, | |
getSelectedCells: function () { | |
return this.selection; | |
}, | |
/** | |
* Selects a grid row. | |
* @param {Number} rowIndex 0-based row index. | |
*/ | |
selectRow: function (rowIndex) { | |
var i, | |
me = this; | |
if (!me.columnCount) { | |
me.columnCount = me.grid.colModel.config.length; | |
} | |
// clear | |
me.clearSelections(); | |
// select | |
for (i = 1; i <= me.columnCount; ++i) { | |
me.select(rowIndex, i, false, true, null, false, false); | |
} | |
}, | |
/** | |
* Selects a grid column. | |
* @param columnIndex 0-based column index. | |
*/ | |
selectColumn: function (columnIndex) { | |
var i, | |
me = this; | |
// make index 0-based | |
columnIndex++; | |
if (!me.rowCount) { | |
me.rowCount = me.grid.store.data.length; | |
} | |
// clear | |
me.clearSelections(); | |
// select | |
for (i = 0; i < me.rowCount; ++i) { | |
me.select(i, columnIndex, false, true, null, false, false); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment