mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Improvements to the Query Results grid:
- Improve the UI - Allow copy/paste from sets of rows, columns or arbitrary blocks of cells Patch by Matt, Shruti, Joao and Sarah @ Pivotal Fixes #2476
This commit is contained in:
@@ -45,7 +45,7 @@ class TestColumnPropertiesSql(SQLTemplateTestBase):
|
||||
|
||||
self.assertEqual('some_column', first_row['name'])
|
||||
self.assertEqual('character varying', first_row['cltype'])
|
||||
self.assertEqual(2, len(fetch_result))
|
||||
self.assertEqual(3, len(fetch_result))
|
||||
|
||||
@staticmethod
|
||||
def get_template_file(version, filename):
|
||||
|
||||
@@ -69,7 +69,7 @@ class ConnectsToServerFeatureTest(BaseFeatureTest):
|
||||
self.page.find_by_xpath("//button[contains(.,'Save')]").click()
|
||||
|
||||
def _tables_node_expandable(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item('acceptance_test_db')
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
|
||||
@@ -11,6 +11,7 @@ import pyperclip
|
||||
import time
|
||||
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from regression.python_test_utils import test_utils
|
||||
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||
@@ -21,9 +22,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
Tests various ways to copy data from the query results grid.
|
||||
"""
|
||||
|
||||
|
||||
scenarios = [
|
||||
("Test Copying Query Results", dict())
|
||||
("Copy rows, column using button and keyboard shortcut", dict())
|
||||
]
|
||||
|
||||
def before(self):
|
||||
@@ -50,29 +50,118 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest):
|
||||
self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
|
||||
self.page.find_by_id("btn-flash").click()
|
||||
|
||||
time.sleep(5)
|
||||
self._copies_rows()
|
||||
self._copies_columns()
|
||||
self._copies_row_using_keyboard_shortcut()
|
||||
self._copies_column_using_keyboard_shortcut()
|
||||
self._copies_rectangular_selection()
|
||||
self._shift_resizes_rectangular_selection()
|
||||
self._shift_resizes_column_selection()
|
||||
self._mouseup_outside_grid_still_makes_a_selection()
|
||||
|
||||
def _copies_rows(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
time.sleep(5)
|
||||
self.page.find_by_xpath("//*[contains(@class, 'sr')]/*[1]/input[@type='checkbox']").click()
|
||||
self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
|
||||
self.assertEqual("'Some-Name','6'",
|
||||
self.assertEqual("'Some-Name','6','some info'",
|
||||
pyperclip.paste())
|
||||
|
||||
def _copies_columns(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]/input").click()
|
||||
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
|
||||
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||
|
||||
self.assertEqual(
|
||||
"""'Some-Name'
|
||||
'Some-Other-Name'""",
|
||||
'Some-Other-Name'
|
||||
'Yet-Another-Name'""",
|
||||
pyperclip.paste())
|
||||
|
||||
def _copies_row_using_keyboard_shortcut(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual("'Some-Name','6','some info'",
|
||||
pyperclip.paste())
|
||||
|
||||
def _copies_column_using_keyboard_shortcut(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
|
||||
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual(
|
||||
"""'Some-Name'
|
||||
'Some-Other-Name'
|
||||
'Yet-Another-Name'""",
|
||||
pyperclip.paste())
|
||||
|
||||
def _copies_rectangular_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
top_left_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., 'Some-Other-Name')]")
|
||||
bottom_right_cell = self.page.find_by_xpath("//div[contains(@class, 'slick-cell') and contains(., '14')]")
|
||||
|
||||
ActionChains(self.page.driver).click_and_hold(top_left_cell).move_to_element(bottom_right_cell) \
|
||||
.release(bottom_right_cell).perform()
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual("""'Some-Other-Name','22'
|
||||
'Yet-Another-Name','14'""", pyperclip.paste())
|
||||
|
||||
def _shift_resizes_rectangular_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
top_left_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., 'Some-Other-Name')]")
|
||||
initial_bottom_right_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., '14')]")
|
||||
ActionChains(self.page.driver).click_and_hold(top_left_cell).move_to_element(initial_bottom_right_cell) \
|
||||
.release(initial_bottom_right_cell).perform()
|
||||
|
||||
ActionChains(self.page.driver).key_down(Keys.SHIFT).send_keys(Keys.ARROW_RIGHT).key_up(Keys.SHIFT).perform()
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual("""'Some-Other-Name','22','some other info'
|
||||
'Yet-Another-Name','14','cool info'""", pyperclip.paste())
|
||||
|
||||
def _shift_resizes_column_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'value')]").click()
|
||||
ActionChains(self.page.driver).key_down(Keys.SHIFT).send_keys(Keys.ARROW_LEFT) \
|
||||
.key_up(Keys.SHIFT).perform()
|
||||
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual(
|
||||
"""'Some-Name','6'
|
||||
'Some-Other-Name','22'
|
||||
'Yet-Another-Name','14'""",
|
||||
pyperclip.paste())
|
||||
|
||||
def _mouseup_outside_grid_still_makes_a_selection(self):
|
||||
pyperclip.copy("old clipboard contents")
|
||||
|
||||
bottom_right_cell = self.page.find_by_xpath(
|
||||
"//div[contains(@class, 'slick-cell') and contains(., 'cool info')]")
|
||||
|
||||
load_button = self.page.find_by_xpath("//button[@id='btn-load-file']")
|
||||
ActionChains(self.page.driver).click_and_hold(bottom_right_cell) \
|
||||
.move_to_element(load_button) \
|
||||
.release(load_button) \
|
||||
.perform()
|
||||
|
||||
ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
|
||||
self.assertEqual("'cool info'", pyperclip.paste())
|
||||
|
||||
def after(self):
|
||||
self.page.close_query_tool()
|
||||
self.page.remove_server(self.server)
|
||||
|
||||
@@ -33,7 +33,7 @@ class TableDdlFeatureTest(BaseFeatureTest):
|
||||
def runTest(self):
|
||||
test_utils.create_table(self.server, "acceptance_test_db", "test_table")
|
||||
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item('acceptance_test_db')
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
|
||||
@@ -236,8 +236,6 @@ CREATE TABLE public.defaults
|
||||
CheckForViewDataTest._get_cell_xpath("r1", "3")
|
||||
).click()
|
||||
|
||||
# for debugging
|
||||
print(row1_cell2_xpath)
|
||||
self._compare_cell_value(row1_cell2_xpath, "[default]")
|
||||
# reset cell value to previous one
|
||||
self._update_cell(row1_cell2_xpath, ["1", "", "int"])
|
||||
|
||||
@@ -88,7 +88,7 @@ class CheckForXssFeatureTest(BaseFeatureTest):
|
||||
self.page.find_by_xpath("//button[contains(.,'Save')]").click()
|
||||
|
||||
def _tables_node_expandable(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item('acceptance_test_db')
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
|
||||
@@ -58,7 +58,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
||||
self.page.find_by_xpath("//button[contains(.,'Save')]").click()
|
||||
|
||||
def _function_node_expandable(self):
|
||||
self.page.toggle_open_tree_item(self.server['name'])
|
||||
self.page.toggle_open_server(self.server['name'])
|
||||
self.page.toggle_open_tree_item('Databases')
|
||||
self.page.toggle_open_tree_item('postgres')
|
||||
self.page.toggle_open_tree_item('Schemas')
|
||||
|
||||
BIN
web/pgadmin/static/img/select-all-icon.png
Normal file
BIN
web/pgadmin/static/img/select-all-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 132 B |
191
web/pgadmin/static/js/selection/active_cell_capture.js
Normal file
191
web/pgadmin/static/js/selection/active_cell_capture.js
Normal file
@@ -0,0 +1,191 @@
|
||||
define([
|
||||
'jquery',
|
||||
'sources/selection/range_selection_helper'
|
||||
], function ($, RangeSelectionHelper) {
|
||||
|
||||
var ActiveCellCapture = function () {
|
||||
var KEY_RIGHT = 39;
|
||||
var KEY_LEFT = 37;
|
||||
var KEY_UP = 38;
|
||||
var KEY_DOWN = 40;
|
||||
|
||||
var bypassDefaultActiveCellRangeChange = false;
|
||||
var isColumnsResized = false;
|
||||
var isMouseInHeader = false;
|
||||
var grid;
|
||||
|
||||
var init = function (slickGrid) {
|
||||
grid = slickGrid;
|
||||
grid.onDragEnd.subscribe(onDragEndHandler);
|
||||
grid.onHeaderClick.subscribe(onHeaderClickHandler);
|
||||
grid.onClick.subscribe(onClickHandler);
|
||||
grid.onActiveCellChanged.subscribe(onActiveCellChangedHandler);
|
||||
grid.onKeyDown.subscribe(onKeyDownHandler);
|
||||
grid.onHeaderMouseEnter.subscribe(onHeaderMouseEnterHandler);
|
||||
grid.onHeaderMouseLeave.subscribe(onHeaderMouseLeaveHandler);
|
||||
grid.onColumnsResized.subscribe(onColumnsResizedHandler);
|
||||
};
|
||||
|
||||
var destroy = function () {
|
||||
grid.onDragEnd.unsubscribe(onDragEndHandler);
|
||||
grid.onHeaderClick.unsubscribe(onHeaderClickHandler);
|
||||
grid.onActiveCellChanged.unsubscribe(onActiveCellChangedHandler);
|
||||
grid.onKeyDown.unsubscribe(onKeyDownHandler);
|
||||
grid.onHeaderMouseEnter.unsubscribe(onHeaderMouseEnterHandler);
|
||||
grid.onHeaderMouseLeave.unsubscribe(onHeaderMouseLeaveHandler);
|
||||
grid.onColumnsResized.unsubscribe(onColumnsResizedHandler);
|
||||
};
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
});
|
||||
|
||||
function onDragEndHandler(event, dragData) {
|
||||
bypassDefaultActiveCellRangeChange = true;
|
||||
grid.setActiveCell(dragData.range.start.row, dragData.range.start.cell);
|
||||
}
|
||||
|
||||
function onHeaderClickHandler(event, args) {
|
||||
if (isColumnsResizedAndCurrentlyInHeader()) {
|
||||
isColumnsResized = false;
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
bypassDefaultActiveCellRangeChange = true;
|
||||
|
||||
var clickedColumn = args.column.pos + 1;
|
||||
if (isClickingLastClickedHeader(0, clickedColumn)) {
|
||||
if (isSingleRangeSelected()) {
|
||||
grid.resetActiveCell();
|
||||
} else {
|
||||
grid.setActiveCell(0, retrievePreviousSelectedRange().fromCell);
|
||||
}
|
||||
} else if (!isClickingInSelectedColumn(clickedColumn)) {
|
||||
grid.setActiveCell(0, clickedColumn);
|
||||
}
|
||||
}
|
||||
|
||||
function isEditableNewRow(row) {
|
||||
return row >= grid.getDataLength();
|
||||
}
|
||||
|
||||
function onHeaderMouseLeaveHandler() {
|
||||
isMouseInHeader = false;
|
||||
}
|
||||
|
||||
function onHeaderMouseEnterHandler() {
|
||||
isMouseInHeader = true;
|
||||
isColumnsResized = false;
|
||||
}
|
||||
|
||||
function onColumnsResizedHandler() {
|
||||
isColumnsResized = true;
|
||||
}
|
||||
|
||||
function onClickHandler(event, args) {
|
||||
if (isRowHeader(args.cell)) {
|
||||
bypassDefaultActiveCellRangeChange = true;
|
||||
var rowClicked = args.row;
|
||||
|
||||
if (isEditableNewRow(rowClicked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isClickingLastClickedHeader(rowClicked, 1)) {
|
||||
if (isSingleRangeSelected()) {
|
||||
grid.resetActiveCell();
|
||||
} else {
|
||||
grid.setActiveCell(retrievePreviousSelectedRange().fromRow, 1);
|
||||
}
|
||||
} else if (!isClickingInSelectedRow(rowClicked)) {
|
||||
grid.setActiveCell(rowClicked, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onActiveCellChangedHandler(event, args) {
|
||||
if (bypassDefaultActiveCellRangeChange) {
|
||||
bypassDefaultActiveCellRangeChange = false;
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyDownHandler(event) {
|
||||
if (hasActiveCell() && isShiftArrowKey(event)) {
|
||||
selectOnlyRangeOfActiveCell();
|
||||
}
|
||||
}
|
||||
|
||||
function isColumnsResizedAndCurrentlyInHeader() {
|
||||
return isMouseInHeader && isColumnsResized;
|
||||
}
|
||||
|
||||
function isClickingLastClickedHeader(clickedRow, clickedColumn) {
|
||||
return hasActiveCell() && grid.getActiveCell().row === clickedRow && grid.getActiveCell().cell === clickedColumn;
|
||||
}
|
||||
|
||||
function isClickingInSelectedColumn(clickedColumn) {
|
||||
var column = RangeSelectionHelper.rangeForColumn(grid, clickedColumn);
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
var ranges = cellSelectionModel.getSelectedRanges();
|
||||
return RangeSelectionHelper.isRangeSelected(ranges, column);
|
||||
}
|
||||
|
||||
function isRowHeader(cellClicked) {
|
||||
return grid.getColumns()[cellClicked].id === 'row-header-column';
|
||||
}
|
||||
|
||||
function isClickingInSelectedRow(rowClicked) {
|
||||
var row = RangeSelectionHelper.rangeForRow(grid, rowClicked);
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
var ranges = cellSelectionModel.getSelectedRanges();
|
||||
return RangeSelectionHelper.isRangeSelected(ranges, row);
|
||||
}
|
||||
|
||||
function isSingleRangeSelected() {
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
var ranges = cellSelectionModel.getSelectedRanges();
|
||||
return ranges.length === 1;
|
||||
}
|
||||
|
||||
function retrievePreviousSelectedRange() {
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
var ranges = cellSelectionModel.getSelectedRanges();
|
||||
return ranges[ranges.length - 2];
|
||||
}
|
||||
|
||||
function isArrowKey(event) {
|
||||
return event.which === KEY_RIGHT
|
||||
|| event.which === KEY_UP
|
||||
|| event.which === KEY_LEFT
|
||||
|| event.which === KEY_DOWN;
|
||||
}
|
||||
|
||||
function isModifiedByShiftOnly(event) {
|
||||
return event.shiftKey
|
||||
&& !event.ctrlKey
|
||||
&& !event.altKey;
|
||||
}
|
||||
|
||||
function isShiftArrowKey(event) {
|
||||
return isModifiedByShiftOnly(event) && isArrowKey(event);
|
||||
}
|
||||
|
||||
function hasActiveCell() {
|
||||
return !!grid.getActiveCell();
|
||||
}
|
||||
|
||||
function selectOnlyRangeOfActiveCell() {
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
var ranges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
if (ranges.length > 1) {
|
||||
cellSelectionModel.setSelectedRanges([ranges.pop()]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return ActiveCellCapture;
|
||||
});
|
||||
@@ -1,34 +1,45 @@
|
||||
define(['jquery', 'sources/selection/range_selection_helper', 'slickgrid'], function ($, rangeSelectionHelper) {
|
||||
define([
|
||||
'jquery',
|
||||
'sources/selection/range_selection_helper',
|
||||
'slickgrid'
|
||||
], function ($, RangeSelectionHelper) {
|
||||
var ColumnSelector = function () {
|
||||
var Slick = window.Slick;
|
||||
var gridEventBus = new Slick.EventHandler();
|
||||
|
||||
var init = function (grid) {
|
||||
grid.onHeaderClick.subscribe(function (event, eventArgument) {
|
||||
var column = eventArgument.column;
|
||||
|
||||
if (column.selectable !== false) {
|
||||
|
||||
if (!clickedCheckbox(event)) {
|
||||
var $checkbox = $("[data-id='checkbox-" + column.id + "']");
|
||||
toggleCheckbox($checkbox);
|
||||
}
|
||||
|
||||
updateRanges(grid, column.id);
|
||||
}
|
||||
}
|
||||
);
|
||||
gridEventBus.subscribe(grid.onHeaderClick, handleHeaderClick.bind(null, grid));
|
||||
grid.getSelectionModel().onSelectedRangesChanged
|
||||
.subscribe(handleSelectedRangesChanged.bind(null, grid));
|
||||
};
|
||||
|
||||
var handleSelectedRangesChanged = function (grid, event, ranges) {
|
||||
$('[data-cell-type="column-header-row"] input:checked')
|
||||
.each(function (index, checkbox) {
|
||||
var $checkbox = $(checkbox);
|
||||
var columnIndex = grid.getColumnIndex($checkbox.data('column-id'));
|
||||
var isStillSelected = rangeSelectionHelper.isRangeSelected(ranges, rangeSelectionHelper.rangeForColumn(grid, columnIndex));
|
||||
if (!isStillSelected) {
|
||||
toggleCheckbox($checkbox);
|
||||
}
|
||||
});
|
||||
var handleHeaderClick = function (grid, event, args) {
|
||||
var columnDefinition = args.column;
|
||||
|
||||
grid.focus();
|
||||
|
||||
if (isColumnSelectable(columnDefinition)) {
|
||||
var $columnHeader = $(event.target);
|
||||
if (hasClickedChildOfColumnHeader(event)) {
|
||||
$columnHeader = $(event.target).parents(".slick-header-column");
|
||||
}
|
||||
$columnHeader.toggleClass('selected');
|
||||
|
||||
updateRanges(grid, columnDefinition.id);
|
||||
}
|
||||
};
|
||||
|
||||
var handleSelectedRangesChanged = function (grid, event, selectedRanges) {
|
||||
$('.slick-header-column').each(function (index, columnHeader) {
|
||||
var $spanHeaderColumn = $(columnHeader).find('[data-cell-type="column-header-row"]');
|
||||
var columnIndex = grid.getColumnIndex($spanHeaderColumn.data('column-id'));
|
||||
|
||||
if (isColumnSelected(grid, selectedRanges, columnIndex)) {
|
||||
$(columnHeader).addClass('selected');
|
||||
} else {
|
||||
$(columnHeader).removeClass('selected');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var updateRanges = function (grid, columnId) {
|
||||
@@ -37,13 +48,13 @@ define(['jquery', 'sources/selection/range_selection_helper', 'slickgrid'], func
|
||||
|
||||
var columnIndex = grid.getColumnIndex(columnId);
|
||||
|
||||
var columnRange = rangeSelectionHelper.rangeForColumn(grid, columnIndex);
|
||||
var columnRange = RangeSelectionHelper.rangeForColumn(grid, columnIndex);
|
||||
var newRanges;
|
||||
if (rangeSelectionHelper.isRangeSelected(ranges, columnRange)) {
|
||||
newRanges = rangeSelectionHelper.removeRange(ranges, columnRange);
|
||||
if (RangeSelectionHelper.isRangeSelected(ranges, columnRange)) {
|
||||
newRanges = RangeSelectionHelper.removeRange(ranges, columnRange);
|
||||
} else {
|
||||
if (rangeSelectionHelper.areAllRangesColumns(ranges, grid)) {
|
||||
newRanges = rangeSelectionHelper.addRange(ranges, columnRange);
|
||||
if (RangeSelectionHelper.areAllRangesSingleColumns(ranges, grid)) {
|
||||
newRanges = RangeSelectionHelper.addRange(ranges, columnRange);
|
||||
} else {
|
||||
newRanges = [columnRange];
|
||||
}
|
||||
@@ -51,28 +62,38 @@ define(['jquery', 'sources/selection/range_selection_helper', 'slickgrid'], func
|
||||
selectionModel.setSelectedRanges(newRanges);
|
||||
};
|
||||
|
||||
var clickedCheckbox = function (e) {
|
||||
return e.target.type == "checkbox"
|
||||
var hasClickedChildOfColumnHeader = function (event) {
|
||||
return !$(event.target).hasClass("slick-header-column");
|
||||
};
|
||||
|
||||
var toggleCheckbox = function (checkbox) {
|
||||
if (checkbox.prop("checked")) {
|
||||
checkbox.prop("checked", false)
|
||||
} else {
|
||||
checkbox.prop("checked", true)
|
||||
}
|
||||
var isColumnSelectable = function (columnDefinition) {
|
||||
return columnDefinition.selectable !== false;
|
||||
};
|
||||
|
||||
var getColumnDefinitionsWithCheckboxes = function (columnDefinitions) {
|
||||
var isColumnSelected = function (grid, selectedRanges, columnIndex) {
|
||||
var allRangesAreRows = RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges);
|
||||
return isAnyCellSelectedInColumn(grid, selectedRanges, columnIndex) && !allRangesAreRows;
|
||||
};
|
||||
|
||||
var isAnyCellSelectedInColumn = function (grid, selectedRanges, columnIndex) {
|
||||
var isStillSelected = RangeSelectionHelper.isRangeEntirelyWithinSelectedRanges(selectedRanges,
|
||||
RangeSelectionHelper.rangeForColumn(grid, columnIndex));
|
||||
var cellSelectedInColumn = RangeSelectionHelper.isAnyCellOfColumnSelected(selectedRanges, columnIndex);
|
||||
|
||||
return isStillSelected || cellSelectedInColumn;
|
||||
};
|
||||
|
||||
var getColumnDefinitions = function (columnDefinitions) {
|
||||
return _.map(columnDefinitions, function (columnDefinition) {
|
||||
if (columnDefinition.selectable !== false) {
|
||||
if (isColumnSelectable(columnDefinition)) {
|
||||
var name =
|
||||
"<span data-cell-type='column-header-row' " +
|
||||
" data-test='output-column-header'>" +
|
||||
" <input data-id='checkbox-" + columnDefinition.id + "' " +
|
||||
" data-column-id='" + columnDefinition.id + "' " +
|
||||
" type='checkbox'/>" +
|
||||
" <span class='column-description'>" + columnDefinition.name + "</span>" +
|
||||
" data-test='output-column-header'" +
|
||||
" data-column-id='" + columnDefinition.id + "'>" +
|
||||
" <span class='column-description'>" +
|
||||
" <span class='column-name'>" + columnDefinition.display_name + "</span>" +
|
||||
" <span class='column-type'>" + columnDefinition.column_type + "</span>" +
|
||||
" </span>" +
|
||||
"</span>";
|
||||
return _.extend(columnDefinition, {
|
||||
name: name
|
||||
@@ -85,7 +106,7 @@ define(['jquery', 'sources/selection/range_selection_helper', 'slickgrid'], func
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"getColumnDefinitionsWithCheckboxes": getColumnDefinitionsWithCheckboxes
|
||||
"getColumnDefinitions": getColumnDefinitions
|
||||
});
|
||||
};
|
||||
return ColumnSelector;
|
||||
|
||||
@@ -3,8 +3,9 @@ define([
|
||||
'underscore',
|
||||
'sources/selection/clipboard',
|
||||
'sources/selection/range_selection_helper',
|
||||
'sources/selection/range_boundary_navigator'],
|
||||
function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
|
||||
'sources/selection/range_boundary_navigator'
|
||||
],
|
||||
function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
|
||||
var copyData = function () {
|
||||
var self = this;
|
||||
|
||||
@@ -14,8 +15,7 @@ define([
|
||||
var data = grid.getData();
|
||||
var rows = grid.getSelectedRows();
|
||||
|
||||
|
||||
if (allTheRangesAreFullRows(selectedRanges, columnDefinitions)) {
|
||||
if (RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges)) {
|
||||
self.copied_rows = rows.map(function (rowIndex) {
|
||||
return data[rowIndex];
|
||||
});
|
||||
@@ -24,7 +24,6 @@ define([
|
||||
self.copied_rows = [];
|
||||
setPasteRowButtonEnablement(self.can_edit, false);
|
||||
}
|
||||
|
||||
var csvText = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, selectedRanges);
|
||||
if (csvText) {
|
||||
clipboard.copyTextToClipboard(csvText);
|
||||
@@ -45,8 +44,8 @@ define([
|
||||
if(RangeSelectionHelper.isFirstColumnData(columnDefinitions)) {
|
||||
return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
|
||||
}
|
||||
return _.isEqual(_.union.apply(null, colRangeBounds), [1, columnDefinitions.length - 1]);
|
||||
return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
|
||||
};
|
||||
|
||||
return copyData;
|
||||
return copyData
|
||||
});
|
||||
@@ -1,5 +1,8 @@
|
||||
define(['jquery', 'sources/selection/column_selector', 'sources/selection/row_selector'],
|
||||
function ($, ColumnSelector, RowSelector) {
|
||||
define(['jquery',
|
||||
'sources/selection/column_selector',
|
||||
'sources/selection/row_selector',
|
||||
'sources/selection/range_selection_helper'],
|
||||
function ($, ColumnSelector, RowSelector, RangeSelectionHelper) {
|
||||
var Slick = window.Slick;
|
||||
|
||||
var GridSelector = function (columnDefinitions) {
|
||||
@@ -20,58 +23,44 @@ define(['jquery', 'sources/selection/column_selector', 'sources/selection/row_se
|
||||
grid.registerPlugin(columnSelector);
|
||||
};
|
||||
|
||||
var getColumnDefinitionsWithCheckboxes = function (columnDefinitions) {
|
||||
columnDefinitions = columnSelector.getColumnDefinitionsWithCheckboxes(columnDefinitions);
|
||||
columnDefinitions = rowSelector.getColumnDefinitionsWithCheckboxes(columnDefinitions);
|
||||
var getColumnDefinitions = function (columnDefinitions) {
|
||||
columnDefinitions = columnSelector.getColumnDefinitions(columnDefinitions);
|
||||
columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
|
||||
|
||||
columnDefinitions[0].selectAllOnClick = true;
|
||||
columnDefinitions[0].name = '<input type="checkbox" data-id="checkbox-select-all" ' +
|
||||
'title="Select/Deselect All"/>' + columnDefinitions[0].name;
|
||||
columnDefinitions[0].name = '<span data-id="select-all" ' +
|
||||
'title="Select/Deselect All">' +
|
||||
'<br>' +
|
||||
columnDefinitions[0].name +
|
||||
'<img class="select-all-icon" src="/static/img/select-all-icon.png">' +
|
||||
'</span>';
|
||||
return columnDefinitions;
|
||||
};
|
||||
|
||||
function handleSelectedRangesChanged(grid) {
|
||||
$("[data-id='checkbox-select-all']").prop("checked", isEntireGridSelected(grid));
|
||||
}
|
||||
|
||||
function isEntireGridSelected(grid) {
|
||||
var selectionModel = grid.getSelectionModel();
|
||||
var selectedRanges = selectionModel.getSelectedRanges();
|
||||
return selectedRanges.length == 1 && isSameRange(selectedRanges[0], getRangeOfWholeGrid(grid));
|
||||
}
|
||||
|
||||
function toggleSelectAll(grid) {
|
||||
if (isEntireGridSelected(grid)) {
|
||||
deselect(grid);
|
||||
if(RangeSelectionHelper.isEntireGridSelected(grid)) {
|
||||
$("[data-id='select-all']").addClass("selected");
|
||||
} else {
|
||||
selectAll(grid)
|
||||
$("[data-id='select-all']").removeClass("selected");
|
||||
}
|
||||
}
|
||||
|
||||
var isSameRange = function (range, otherRange) {
|
||||
return range.fromCell == otherRange.fromCell && range.toCell == otherRange.toCell &&
|
||||
range.fromRow == otherRange.fromRow && range.toRow == otherRange.toRow;
|
||||
};
|
||||
|
||||
function getRangeOfWholeGrid(grid) {
|
||||
return new Slick.Range(0, 1, grid.getDataLength() - 1, grid.getColumns().length - 1);
|
||||
function toggleSelectAll(grid) {
|
||||
if (RangeSelectionHelper.isEntireGridSelected(grid)) {
|
||||
selectNone(grid);
|
||||
} else {
|
||||
RangeSelectionHelper.selectAll(grid);
|
||||
}
|
||||
}
|
||||
|
||||
function deselect(grid) {
|
||||
function selectNone(grid) {
|
||||
var selectionModel = grid.getSelectionModel();
|
||||
selectionModel.setSelectedRanges([]);
|
||||
}
|
||||
|
||||
function selectAll(grid) {
|
||||
var range = getRangeOfWholeGrid(grid);
|
||||
var selectionModel = grid.getSelectionModel();
|
||||
|
||||
selectionModel.setSelectedRanges([range]);
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"getColumnDefinitionsWithCheckboxes": getColumnDefinitionsWithCheckboxes
|
||||
"getColumnDefinitions": getColumnDefinitions
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
define(['sources/selection/range_selection_helper'], function (RangeSelectionHelper) {
|
||||
define(['sources/selection/range_selection_helper'],
|
||||
function (RangeSelectionHelper) {
|
||||
return {
|
||||
getUnion: function (allRanges) {
|
||||
if (_.isEmpty(allRanges)) {
|
||||
@@ -77,6 +78,10 @@ define(['sources/selection/range_selection_helper'], function (RangeSelectionHel
|
||||
removeFirstColumn: function (colRangeBounds) {
|
||||
var unionedColRanges = this.getUnion(colRangeBounds);
|
||||
|
||||
if(unionedColRanges.length == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var firstSubrangeStartsAt0 = function () {
|
||||
return unionedColRanges[0][0] == 0;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,26 @@ define(['slickgrid'], function () {
|
||||
var isRangeSelected = function (selectedRanges, range) {
|
||||
return _.any(selectedRanges, function (selectedRange) {
|
||||
return isSameRange(selectedRange, range)
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
var isAnyCellOfColumnSelected = function (selectedRanges, column) {
|
||||
return _.any(selectedRanges, function (selectedRange) {
|
||||
return selectedRange.fromCell <= column && selectedRange.toCell >= column
|
||||
});
|
||||
};
|
||||
|
||||
var isAnyCellOfRowSelected = function (selectedRanges, row) {
|
||||
return _.any(selectedRanges, function (selectedRange) {
|
||||
return selectedRange.fromRow <= row && selectedRange.toRow >= row
|
||||
});
|
||||
};
|
||||
|
||||
var isRangeEntirelyWithinSelectedRanges = function (selectedRanges, range) {
|
||||
return _.any(selectedRanges, function (selectedRange) {
|
||||
return selectedRange.fromCell <= range.fromCell && selectedRange.toCell >= range.toCell &&
|
||||
selectedRange.fromRow <= range.fromRow && selectedRange.toRow >= range.toRow;
|
||||
});
|
||||
};
|
||||
|
||||
var removeRange = function (selectedRanges, range) {
|
||||
@@ -23,29 +42,25 @@ define(['slickgrid'], function () {
|
||||
return ranges;
|
||||
};
|
||||
|
||||
var areAllRangesRows = function (ranges, grid) {
|
||||
var areAllRangesSingleRows = function (ranges, grid) {
|
||||
return _.every(ranges, function (range) {
|
||||
return range.fromRow == range.toRow &&
|
||||
range.fromCell == 1 && range.toCell == grid.getColumns().length - 1
|
||||
return range.fromRow == range.toRow && rangeHasCompleteRows(grid, range);
|
||||
})
|
||||
};
|
||||
|
||||
var areAllRangesColumns = function (ranges, grid) {
|
||||
return _.every(ranges, function (range) {
|
||||
return range.fromCell == range.toCell &&
|
||||
range.fromRow == 0 && range.toRow == grid.getDataLength() - 1
|
||||
})
|
||||
var areAllRangesSingleColumns = function (ranges, grid) {
|
||||
return _.every(ranges, isRangeAColumn.bind(this, grid))
|
||||
};
|
||||
|
||||
var rangeForRow = function (grid, rowId) {
|
||||
var columnDefinitions = grid.getColumns();
|
||||
if(isFirstColumnData(columnDefinitions)) {
|
||||
if (isFirstColumnData(columnDefinitions)) {
|
||||
return new Slick.Range(rowId, 0, rowId, grid.getColumns().length - 1);
|
||||
}
|
||||
return new Slick.Range(rowId, 1, rowId, grid.getColumns().length - 1);
|
||||
};
|
||||
|
||||
function rangeForColumn(grid, columnIndex) {
|
||||
var rangeForColumn = function (grid, columnIndex) {
|
||||
return new Slick.Range(0, columnIndex, grid.getDataLength() - 1, columnIndex)
|
||||
};
|
||||
|
||||
@@ -63,16 +78,85 @@ define(['slickgrid'], function () {
|
||||
return !_.isUndefined(columnDefinitions[0].pos);
|
||||
};
|
||||
|
||||
var areAllRangesCompleteColumns = function (grid, ranges) {
|
||||
return _.every(ranges, function (range) {
|
||||
return rangeHasCompleteColumns(grid, range);
|
||||
})
|
||||
};
|
||||
|
||||
var areAllRangesCompleteRows = function (grid, ranges) {
|
||||
return _.every(ranges, function (range) {
|
||||
return rangeHasCompleteRows(grid, range);
|
||||
})
|
||||
};
|
||||
|
||||
var getIndexesOfCompleteRows = function (grid, ranges) {
|
||||
var indexArray = [];
|
||||
ranges.forEach(function (range) {
|
||||
if (rangeHasCompleteRows(grid, range))
|
||||
indexArray = _.union(indexArray, _.range(range.fromRow, range.toRow + 1));
|
||||
});
|
||||
|
||||
return indexArray;
|
||||
};
|
||||
|
||||
var isRangeAColumn = function (grid, range) {
|
||||
return range.fromCell == range.toCell &&
|
||||
range.fromRow == 0 && range.toRow == grid.getDataLength() - 1;
|
||||
};
|
||||
|
||||
var rangeHasCompleteColumns = function (grid, range) {
|
||||
return range.fromRow === 0 && range.toRow === grid.getDataLength() - 1;
|
||||
};
|
||||
|
||||
var rangeHasCompleteRows = function (grid, range) {
|
||||
return range.fromCell === getFirstDataColumnIndex(grid) &&
|
||||
range.toCell === getLastDataColumnIndex(grid);
|
||||
};
|
||||
|
||||
function getFirstDataColumnIndex(grid) {
|
||||
return _.findIndex(grid.getColumns(), function (columnDefinition) {
|
||||
var pos = columnDefinition.pos;
|
||||
|
||||
return !_.isUndefined(pos) && isSelectable(columnDefinition);
|
||||
});
|
||||
}
|
||||
|
||||
function getLastDataColumnIndex(grid) {
|
||||
return _.findLastIndex(grid.getColumns(), isSelectable);
|
||||
}
|
||||
|
||||
function isSelectable(columnDefinition) {
|
||||
return (_.isUndefined(columnDefinition.selectable) || columnDefinition.selectable === true);
|
||||
}
|
||||
|
||||
function selectAll(grid) {
|
||||
var range = getRangeOfWholeGrid(grid);
|
||||
var selectionModel = grid.getSelectionModel();
|
||||
|
||||
selectionModel.setSelectedRanges([range]);
|
||||
}
|
||||
|
||||
return {
|
||||
addRange: addRange,
|
||||
removeRange: removeRange,
|
||||
isRangeSelected: isRangeSelected,
|
||||
areAllRangesRows: areAllRangesRows,
|
||||
areAllRangesColumns: areAllRangesColumns,
|
||||
areAllRangesSingleRows: areAllRangesSingleRows,
|
||||
areAllRangesSingleColumns: areAllRangesSingleColumns,
|
||||
areAllRangesCompleteRows: areAllRangesCompleteRows,
|
||||
areAllRangesCompleteColumns: areAllRangesCompleteColumns,
|
||||
rangeForRow: rangeForRow,
|
||||
rangeForColumn: rangeForColumn,
|
||||
isEntireGridSelected: isEntireGridSelected,
|
||||
getRangeOfWholeGrid: getRangeOfWholeGrid,
|
||||
isFirstColumnData: isFirstColumnData
|
||||
isFirstColumnData: isFirstColumnData,
|
||||
getIndexesOfCompleteRows: getIndexesOfCompleteRows,
|
||||
selectAll: selectAll,
|
||||
isRangeAColumn: isRangeAColumn,
|
||||
rangeHasCompleteColumns: rangeHasCompleteColumns,
|
||||
rangeHasCompleteRows: rangeHasCompleteRows,
|
||||
isAnyCellOfColumnSelected: isAnyCellOfColumnSelected,
|
||||
isRangeEntirelyWithinSelectedRanges: isRangeEntirelyWithinSelectedRanges,
|
||||
isAnyCellOfRowSelected: isAnyCellOfRowSelected,
|
||||
}
|
||||
});
|
||||
@@ -1,85 +1,99 @@
|
||||
define(['jquery', 'sources/selection/range_selection_helper', 'slickgrid'], function ($, rangeSelectionHelper) {
|
||||
define([
|
||||
'jquery',
|
||||
'sources/selection/range_selection_helper',
|
||||
'slickgrid'
|
||||
], function ($, RangeSelectionHelper) {
|
||||
var RowSelector = function () {
|
||||
var Slick = window.Slick;
|
||||
|
||||
var gridEventBus = new Slick.EventHandler();
|
||||
|
||||
var init = function (grid) {
|
||||
grid.getSelectionModel()
|
||||
.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged.bind(null, grid));
|
||||
grid.getSelectionModel().onSelectedRangesChanged
|
||||
.subscribe(handleSelectedRangesChanged.bind(null, grid));
|
||||
gridEventBus
|
||||
.subscribe(grid.onClick, handleClick.bind(null, grid))
|
||||
.subscribe(grid.onClick, handleClick.bind(null, grid));
|
||||
};
|
||||
|
||||
var handleClick = function (grid, event, args) {
|
||||
if (grid.getColumns()[args.cell].id === 'row-header-column') {
|
||||
if (event.target.type != "checkbox") {
|
||||
var checkbox = $(event.target).find('input[type="checkbox"]');
|
||||
toggleCheckbox($(checkbox));
|
||||
var $rowHeaderSpan = $(event.target);
|
||||
|
||||
if ($rowHeaderSpan.data('cell-type') != "row-header-selector") {
|
||||
$rowHeaderSpan = $(event.target).find('[data-cell-type="row-header-selector"]');
|
||||
}
|
||||
|
||||
$rowHeaderSpan.parent().toggleClass('selected');
|
||||
updateRanges(grid, args.row);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var handleSelectedRangesChanged = function (grid, event, ranges) {
|
||||
$('[data-cell-type="row-header-checkbox"]:checked')
|
||||
.each(function (index, checkbox) {
|
||||
var $checkbox = $(checkbox);
|
||||
var row = parseInt($checkbox.data('row'));
|
||||
var isStillSelected = rangeSelectionHelper.isRangeSelected(ranges,
|
||||
rangeSelectionHelper.rangeForRow(grid, row));
|
||||
if (!isStillSelected) {
|
||||
toggleCheckbox($checkbox);
|
||||
}
|
||||
});
|
||||
}
|
||||
var handleSelectedRangesChanged = function (grid, event, selectedRanges) {
|
||||
$('[data-cell-type="row-header-selector"]').each(function (index, rowHeaderSpan) {
|
||||
var $rowHeaderSpan = $(rowHeaderSpan);
|
||||
var row = parseInt($rowHeaderSpan.data('row'));
|
||||
|
||||
if (isRowSelected(grid, selectedRanges, row)) {
|
||||
$rowHeaderSpan.parent().addClass('selected');
|
||||
} else {
|
||||
$rowHeaderSpan.parent().removeClass('selected');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var updateRanges = function (grid, rowId) {
|
||||
var selectionModel = grid.getSelectionModel();
|
||||
var ranges = selectionModel.getSelectedRanges();
|
||||
|
||||
var rowRange = rangeSelectionHelper.rangeForRow(grid, rowId);
|
||||
var rowRange = RangeSelectionHelper.rangeForRow(grid, rowId);
|
||||
|
||||
var newRanges;
|
||||
if (rangeSelectionHelper.isRangeSelected(ranges, rowRange)) {
|
||||
newRanges = rangeSelectionHelper.removeRange(ranges, rowRange);
|
||||
if (RangeSelectionHelper.isRangeSelected(ranges, rowRange)) {
|
||||
newRanges = RangeSelectionHelper.removeRange(ranges, rowRange);
|
||||
} else {
|
||||
if (rangeSelectionHelper.areAllRangesRows(ranges, grid)) {
|
||||
newRanges = rangeSelectionHelper.addRange(ranges, rowRange);
|
||||
if (RangeSelectionHelper.areAllRangesSingleRows(ranges, grid)) {
|
||||
newRanges = RangeSelectionHelper.addRange(ranges, rowRange);
|
||||
} else {
|
||||
newRanges = [rowRange];
|
||||
}
|
||||
}
|
||||
selectionModel.setSelectedRanges(newRanges);
|
||||
}
|
||||
|
||||
var toggleCheckbox = function (checkbox) {
|
||||
if (checkbox.prop("checked")) {
|
||||
checkbox.prop("checked", false)
|
||||
} else {
|
||||
checkbox.prop("checked", true)
|
||||
}
|
||||
};
|
||||
|
||||
var getColumnDefinitionsWithCheckboxes = function (columnDefinitions) {
|
||||
var isAnyCellSelectedInRow = function (grid, selectedRanges, row) {
|
||||
var isStillSelected = RangeSelectionHelper.isRangeEntirelyWithinSelectedRanges(selectedRanges,
|
||||
RangeSelectionHelper.rangeForRow(grid, row));
|
||||
var cellSelectedInRow = RangeSelectionHelper.isAnyCellOfRowSelected(selectedRanges, row);
|
||||
|
||||
return isStillSelected || cellSelectedInRow;
|
||||
};
|
||||
|
||||
var isRowSelected = function (grid, selectedRanges, row) {
|
||||
var allRangesAreColumns = RangeSelectionHelper.areAllRangesCompleteColumns(grid, selectedRanges);
|
||||
return isAnyCellSelectedInRow(grid, selectedRanges, row) && !allRangesAreColumns;
|
||||
};
|
||||
|
||||
var getColumnDefinitions = function (columnDefinitions) {
|
||||
columnDefinitions.unshift({
|
||||
id: 'row-header-column',
|
||||
name: '',
|
||||
selectable: false,
|
||||
focusable: false,
|
||||
formatter: function (rowIndex) {
|
||||
return '<input type="checkbox" ' +
|
||||
return '<span ' +
|
||||
'data-row="' + rowIndex + '" ' +
|
||||
'data-cell-type="row-header-checkbox"/>'
|
||||
}
|
||||
'data-cell-type="row-header-selector"/>'
|
||||
},
|
||||
width: 30
|
||||
});
|
||||
return columnDefinitions;
|
||||
};
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"getColumnDefinitionsWithCheckboxes": getColumnDefinitionsWithCheckboxes
|
||||
"getColumnDefinitions": getColumnDefinitions
|
||||
});
|
||||
};
|
||||
|
||||
return RowSelector;
|
||||
});
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'underscore'
|
||||
'underscore',
|
||||
'sources/selection/range_selection_helper'
|
||||
],
|
||||
function ($, _) {
|
||||
function ($, _, RangeSelectionHelper) {
|
||||
function disableButton(selector) {
|
||||
$(selector).prop('disabled', true);
|
||||
}
|
||||
@@ -85,21 +86,25 @@ define(
|
||||
disableButton('#btn-delete-row');
|
||||
disableButton('#btn-copy-row');
|
||||
|
||||
if (!_.has(this.selection, 'getSelectedRows')) {
|
||||
setStagedRows({});
|
||||
return;
|
||||
function areAllSelectionsEntireRows() {
|
||||
return RangeSelectionHelper.areAllRangesCompleteRows(self.grid,
|
||||
self.selection.getSelectedRanges())
|
||||
}
|
||||
|
||||
var selectedRows = this.selection.getSelectedRows();
|
||||
var selectedRanges = this.selection.getSelectedRanges();
|
||||
|
||||
if (selectedRows.length > 0) {
|
||||
if (selectedRanges.length > 0) {
|
||||
enableButton('#btn-copy-row');
|
||||
}
|
||||
|
||||
if (areAllSelectionsEntireRows()) {
|
||||
var selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges())
|
||||
var stagedRows = getPrimaryKeysForSelectedRows(self, selectedRows);
|
||||
setStagedRows(stagedRows);
|
||||
if (_.isEmpty(stagedRows)) {
|
||||
this.selection.setSelectedRows([]);
|
||||
}
|
||||
|
||||
enableButton('#btn-copy-row');
|
||||
if (isEditMode()) {
|
||||
enableButton('#btn-delete-row');
|
||||
}
|
||||
@@ -110,5 +115,3 @@ define(
|
||||
return setStagedRows;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
228
web/pgadmin/static/js/selection/xcell_selection_model.js
Normal file
228
web/pgadmin/static/js/selection/xcell_selection_model.js
Normal file
@@ -0,0 +1,228 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'sources/selection/range_selection_helper',
|
||||
'sources/slickgrid/pgslick.cellrangeselector',
|
||||
|
||||
'slickgrid'
|
||||
], function ($, _, RangeSelectionHelper, PGCellRangeSelector) {
|
||||
var XCellSelectionModel = function (options) {
|
||||
|
||||
var KEY_ARROW_RIGHT = 39;
|
||||
var KEY_ARROW_LEFT = 37;
|
||||
var KEY_ARROW_UP = 38;
|
||||
var KEY_ARROW_DOWN = 40;
|
||||
|
||||
var _grid;
|
||||
var _canvas;
|
||||
var _ranges = [];
|
||||
var _self = this;
|
||||
var _selector = new PGCellRangeSelector({
|
||||
"selectionCss": {
|
||||
"border": "2px solid black"
|
||||
}
|
||||
});
|
||||
var _options;
|
||||
var _defaults = {
|
||||
selectActiveCell: true
|
||||
};
|
||||
|
||||
|
||||
function init(grid) {
|
||||
_options = $.extend(true, {}, _defaults, options);
|
||||
_grid = grid;
|
||||
_canvas = _grid.getCanvasNode();
|
||||
_grid.onActiveCellChanged.subscribe(handleActiveCellChange);
|
||||
_grid.onKeyDown.subscribe(handleKeyDown);
|
||||
grid.registerPlugin(_selector);
|
||||
_selector.onCellRangeSelected.subscribe(handleCellRangeSelected);
|
||||
_selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);
|
||||
$(window.parent).mouseup(handleWindowMouseUp);
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
_grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);
|
||||
_grid.onKeyDown.unsubscribe(handleKeyDown);
|
||||
_selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);
|
||||
_selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);
|
||||
_grid.unregisterPlugin(_selector);
|
||||
$(window.parent).off('mouseup', handleWindowMouseUp);
|
||||
}
|
||||
|
||||
function removeInvalidRanges(ranges) {
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var r = ranges[i];
|
||||
if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) {
|
||||
result.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function setSelectedRanges(ranges) {
|
||||
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
|
||||
if ((!_ranges || _ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
|
||||
|
||||
_ranges = removeInvalidRanges(ranges);
|
||||
_self.onSelectedRangesChanged.notify(_ranges);
|
||||
}
|
||||
|
||||
function getSelectedRanges() {
|
||||
return _ranges;
|
||||
}
|
||||
|
||||
function setSelectedRows(rows) {
|
||||
_ranges = [];
|
||||
|
||||
for(var i = 0 ; i < rows.length ; i++) {
|
||||
_ranges.push(RangeSelectionHelper.rangeForRow(_grid, rows[i]));
|
||||
}
|
||||
}
|
||||
|
||||
function handleBeforeCellRangeSelected(e, args) {
|
||||
if (_grid.getEditorLock().isActive()) {
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCellRangeSelected(e, args) {
|
||||
setSelectedRanges([args.range]);
|
||||
}
|
||||
|
||||
function handleActiveCellChange(e, args) {
|
||||
if (_options.selectActiveCell && args.row != null && args.cell != null) {
|
||||
setSelectedRanges([new Slick.Range(args.row, args.cell)]);
|
||||
}
|
||||
}
|
||||
|
||||
function arrowKeyPressed(event) {
|
||||
return event.which == KEY_ARROW_RIGHT
|
||||
|| event.which == KEY_ARROW_LEFT
|
||||
|| event.which == KEY_ARROW_UP
|
||||
|| event.which == KEY_ARROW_DOWN;
|
||||
}
|
||||
|
||||
function shiftArrowKeyPressed(event) {
|
||||
return event.shiftKey && !event.ctrlKey && !event.altKey &&
|
||||
(arrowKeyPressed(event));
|
||||
}
|
||||
|
||||
function needUpdateRange(newRange) {
|
||||
return removeInvalidRanges([newRange]).length;
|
||||
}
|
||||
|
||||
function handleKeyDown(e) {
|
||||
var ranges;
|
||||
var lastSelectedRange;
|
||||
var anchorActiveCell = _grid.getActiveCell();
|
||||
|
||||
function isKey(key) { return e.which === key; }
|
||||
|
||||
function getKeycode() { return e.which; }
|
||||
|
||||
function shouldScrollToBottommostRow() { return anchorActiveCell.row === newSelectedRange.fromRow; }
|
||||
|
||||
function shouldScrollToRightmostColumn() { return anchorActiveCell.cell === newSelectedRange.fromCell; }
|
||||
|
||||
function getMobileCellFromRange(range, activeCell) {
|
||||
var mobileCell = {};
|
||||
|
||||
mobileCell.row = range.fromRow === activeCell.row ? range.toRow : range.fromRow;
|
||||
mobileCell.cell = range.fromCell === activeCell.cell ? range.toCell : range.fromCell;
|
||||
|
||||
return mobileCell;
|
||||
}
|
||||
|
||||
function getNewRange(rangeCorner, oppositeCorner) {
|
||||
var newFromCell = rangeCorner.cell <= oppositeCorner.cell ? rangeCorner.cell : oppositeCorner.cell;
|
||||
var newToCell = rangeCorner.cell <= oppositeCorner.cell ? oppositeCorner.cell : rangeCorner.cell;
|
||||
|
||||
var newFromRow = rangeCorner.row <= oppositeCorner.row ? rangeCorner.row : oppositeCorner.row;
|
||||
var newToRow = rangeCorner.row <= oppositeCorner.row ? oppositeCorner.row : rangeCorner.row;
|
||||
|
||||
return new Slick.Range(
|
||||
newFromRow,
|
||||
newFromCell,
|
||||
newToRow,
|
||||
newToCell
|
||||
);
|
||||
}
|
||||
|
||||
if (anchorActiveCell && shiftArrowKeyPressed(e)) {
|
||||
ranges = getSelectedRanges();
|
||||
if (!ranges.length) {
|
||||
ranges.push(new Slick.Range(anchorActiveCell.row, anchorActiveCell.cell));
|
||||
}
|
||||
|
||||
// keyboard can work with last range only
|
||||
lastSelectedRange = ranges.pop();
|
||||
|
||||
// can't handle selection out of active cell
|
||||
if (!lastSelectedRange.contains(anchorActiveCell.row, anchorActiveCell.cell)) {
|
||||
lastSelectedRange = new Slick.Range(anchorActiveCell.row, anchorActiveCell.cell);
|
||||
}
|
||||
|
||||
var mobileCell = getMobileCellFromRange(lastSelectedRange, anchorActiveCell);
|
||||
|
||||
switch (getKeycode()) {
|
||||
case KEY_ARROW_LEFT:
|
||||
mobileCell.cell -= 1;
|
||||
break;
|
||||
case KEY_ARROW_RIGHT:
|
||||
mobileCell.cell += 1;
|
||||
break;
|
||||
case KEY_ARROW_UP:
|
||||
mobileCell.row -= 1;
|
||||
break;
|
||||
case KEY_ARROW_DOWN:
|
||||
mobileCell.row += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
var newSelectedRange = getNewRange(anchorActiveCell, mobileCell);
|
||||
|
||||
if (needUpdateRange(newSelectedRange)) {
|
||||
var rowToView = shouldScrollToBottommostRow() ? newSelectedRange.toRow : newSelectedRange.fromRow;
|
||||
var columnToView = shouldScrollToRightmostColumn() ? newSelectedRange.toCell : newSelectedRange.fromCell;
|
||||
|
||||
if (isKey(KEY_ARROW_RIGHT) || isKey(KEY_ARROW_LEFT)) {
|
||||
_grid.scrollColumnIntoView(columnToView);
|
||||
} else if (isKey(KEY_ARROW_UP) || isKey(KEY_ARROW_DOWN)) {
|
||||
_grid.scrollRowIntoView(rowToView);
|
||||
}
|
||||
ranges.push(newSelectedRange);
|
||||
} else {
|
||||
ranges.push(lastSelectedRange);
|
||||
}
|
||||
|
||||
setSelectedRanges(ranges);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
function handleWindowMouseUp(event) {
|
||||
var selectedRange = _selector.getCurrentRange();
|
||||
if (!_.isUndefined(selectedRange)) {
|
||||
_grid.onDragEnd.notify({range: selectedRange});
|
||||
}
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"getSelectedRanges": getSelectedRanges,
|
||||
"setSelectedRanges": setSelectedRanges,
|
||||
"setSelectedRows": setSelectedRows,
|
||||
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
|
||||
"onSelectedRangesChanged": new Slick.Event()
|
||||
});
|
||||
};
|
||||
return XCellSelectionModel;
|
||||
});
|
||||
18
web/pgadmin/static/js/slickgrid/cell_selector.js
Normal file
18
web/pgadmin/static/js/slickgrid/cell_selector.js
Normal file
@@ -0,0 +1,18 @@
|
||||
define(["slickgrid"], function () {
|
||||
var Slick = window.Slick;
|
||||
|
||||
return function () {
|
||||
this.init = function (grid) {
|
||||
grid.onActiveCellChanged.subscribe(function (event, slickEvent) {
|
||||
grid.getSelectionModel().setSelectedRanges([
|
||||
new Slick.Range(
|
||||
slickEvent.row,
|
||||
slickEvent.cell,
|
||||
slickEvent.row,
|
||||
slickEvent.cell
|
||||
)
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
define([
|
||||
'sources/selection/copy_data',
|
||||
'sources/selection/range_selection_helper'
|
||||
],
|
||||
function (copyData, RangeSelectionHelper) {
|
||||
return function handleQueryOutputKeyboardEvent(event, args) {
|
||||
var KEY_C = 67;
|
||||
var KEY_A = 65;
|
||||
var modifiedKey = event.keyCode;
|
||||
var isModifierDown = event.ctrlKey || event.metaKey;
|
||||
this.slickgrid = args.grid;
|
||||
|
||||
if (isModifierDown && modifiedKey == KEY_C) {
|
||||
copyData.apply(this);
|
||||
}
|
||||
|
||||
if (isModifierDown && modifiedKey == KEY_A) {
|
||||
RangeSelectionHelper.selectAll(this.slickgrid);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
define([
|
||||
'jquery',
|
||||
|
||||
'slickgrid',
|
||||
], function ($) {
|
||||
/***
|
||||
* Displays an overlay on top of a given cell range.
|
||||
*
|
||||
* TODO:
|
||||
* Currently, it blocks mouse events to DOM nodes behind it.
|
||||
* Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding.
|
||||
* Could also construct the borders separately using 4 individual DIVs.
|
||||
*
|
||||
* @param {Grid} grid
|
||||
* @param {Object} options
|
||||
*/
|
||||
var PGCellRangeDecorator = function (grid, options) {
|
||||
var _elem;
|
||||
var _defaults = {
|
||||
selectionCssClass: 'slick-range-decorator',
|
||||
selectionCss: {
|
||||
"zIndex": "9999",
|
||||
"border": "2px dashed red"
|
||||
}
|
||||
};
|
||||
|
||||
options = $.extend(true, {}, _defaults, options);
|
||||
|
||||
|
||||
function show(range) {
|
||||
if (!_elem) {
|
||||
_elem = $("<div></div>", {css: options.selectionCss})
|
||||
.addClass(options.selectionCssClass)
|
||||
.css("position", "absolute")
|
||||
.appendTo(grid.getCanvasNode());
|
||||
}
|
||||
|
||||
var from = grid.getCellNodeBox(range.fromRow, range.fromCell);
|
||||
var to = grid.getCellNodeBox(range.toRow, range.toCell);
|
||||
|
||||
// TODO: This is the original Slickgrid code temporary fix to solve
|
||||
// pgAdmin alignment of the selection box on the bottom right corner
|
||||
// _elem.css({
|
||||
// top: from.top - 1,
|
||||
// left: from.left - 1,
|
||||
// height: to.bottom - from.top - 2,
|
||||
// width: to.right - from.left - 2
|
||||
// });
|
||||
|
||||
_elem.css({
|
||||
top: from.top - 1,
|
||||
left: from.left - 1,
|
||||
height: to.bottom - from.top + 2,
|
||||
width: to.right - from.left + 1
|
||||
});
|
||||
|
||||
return _elem;
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (_elem) {
|
||||
_elem.remove();
|
||||
_elem = null;
|
||||
}
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"show": show,
|
||||
"hide": hide
|
||||
});
|
||||
};
|
||||
return PGCellRangeDecorator;
|
||||
});
|
||||
119
web/pgadmin/static/js/slickgrid/pgslick.cellrangeselector.js
Normal file
119
web/pgadmin/static/js/slickgrid/pgslick.cellrangeselector.js
Normal file
@@ -0,0 +1,119 @@
|
||||
define([
|
||||
'jquery',
|
||||
'sources/slickgrid/pgslick.cellrangedecorator',
|
||||
|
||||
'slickgrid',
|
||||
], function ($, PGCellRangeDecorator) {
|
||||
|
||||
var PGCellRangeSelector = function (options) {
|
||||
var _grid;
|
||||
var _canvas;
|
||||
var _currentlySelectedRange;
|
||||
var _dragging;
|
||||
var _decorator;
|
||||
var _self = this;
|
||||
var _handler = new Slick.EventHandler();
|
||||
var _defaults = {
|
||||
selectionCss: {
|
||||
"border": "2px dashed blue"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function init(grid) {
|
||||
options = $.extend(true, {}, _defaults, options);
|
||||
_decorator = new PGCellRangeDecorator(grid, options);
|
||||
_grid = grid;
|
||||
_canvas = _grid.getCanvasNode();
|
||||
_handler
|
||||
.subscribe(_grid.onDragInit, handleDragInit)
|
||||
.subscribe(_grid.onDragStart, handleDragStart)
|
||||
.subscribe(_grid.onDrag, handleDrag)
|
||||
.subscribe(_grid.onDragEnd, handleDragEnd);
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
_handler.unsubscribeAll();
|
||||
}
|
||||
|
||||
function handleDragInit(e, dd) {
|
||||
// prevent the grid from cancelling drag'n'drop by default
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
function handleDragStart(e, dd) {
|
||||
var cell = _grid.getCellFromEvent(e);
|
||||
if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {
|
||||
if (_grid.canCellBeSelected(cell.row, cell.cell)) {
|
||||
_dragging = true;
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
if (!_dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
_grid.focus();
|
||||
|
||||
var start = _grid.getCellFromPoint(
|
||||
dd.startX - $(_canvas).offset().left,
|
||||
dd.startY - $(_canvas).offset().top);
|
||||
|
||||
dd.range = {start: start, end: {}};
|
||||
_currentlySelectedRange = dd.range;
|
||||
return _decorator.show(new Slick.Range(start.row, start.cell));
|
||||
}
|
||||
|
||||
function handleDrag(e, dd) {
|
||||
if (!_dragging) {
|
||||
return;
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
var end = _grid.getCellFromPoint(
|
||||
e.pageX - $(_canvas).offset().left,
|
||||
e.pageY - $(_canvas).offset().top);
|
||||
|
||||
if (!_grid.canCellBeSelected(end.row, end.cell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dd.range.end = end;
|
||||
_currentlySelectedRange = dd.range;
|
||||
_decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
|
||||
}
|
||||
|
||||
function handleDragEnd(e, dd) {
|
||||
if (!_dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dragging = false;
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
_decorator.hide();
|
||||
_self.onCellRangeSelected.notify({
|
||||
range: new Slick.Range(
|
||||
dd.range.start.row,
|
||||
dd.range.start.cell,
|
||||
dd.range.end.row,
|
||||
dd.range.end.cell
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentRange() {
|
||||
return _currentlySelectedRange;
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
"getCurrentRange": getCurrentRange,
|
||||
|
||||
"onBeforeCellRangeSelected": new Slick.Event(),
|
||||
"onCellRangeSelected": new Slick.Event()
|
||||
});
|
||||
};
|
||||
return PGCellRangeSelector;
|
||||
});
|
||||
9
web/pgadmin/static/vendor/slickgrid/README
vendored
9
web/pgadmin/static/vendor/slickgrid/README
vendored
@@ -1,9 +0,0 @@
|
||||
WARNING!!
|
||||
|
||||
The following changes have been made to SlickGrid. These must be re-applied if updating the code from upstream!
|
||||
|
||||
- The CSS class 'slick-row' has been renamed to 'sr'
|
||||
|
||||
- The CSS class 'slick-cell' has been renamed to 'sc'
|
||||
|
||||
The intent of these changes is to minimise memory usage by the grid, by saving a few bytes per row/cell.
|
||||
9
web/pgadmin/static/vendor/slickgrid/README.md
vendored
Normal file
9
web/pgadmin/static/vendor/slickgrid/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
## This is the 6pac SlickGrid repo
|
||||
|
||||
This is the acknowledged most active non-customised fork of SlickGrid.
|
||||
|
||||
It aims to be a viable alternative master repo, building on the legacy of the mleibman/SlickGrid master branch, keeping libraries up to date and applying small, safe core patches and enhancements without turning into a personalised build.
|
||||
|
||||
Check out the [examples](https://github.com/6pac/SlickGrid/wiki/Examples) for examples demonstrating new features and use cases, such as dynamic grid creation and editors with third party controls.
|
||||
|
||||
Also check out the [wiki](https://github.com/6pac/SlickGrid/wiki) for news and documentation.
|
||||
@@ -12,12 +12,12 @@
|
||||
grid.onColumnsReordered.subscribe(updateColumnOrder);
|
||||
options = $.extend({}, defaults, options);
|
||||
|
||||
$menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body);
|
||||
$menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;overflow-y:scroll;' />").appendTo(document.body);
|
||||
|
||||
$menu.bind("mouseleave", function (e) {
|
||||
$menu.on("mouseleave", function (e) {
|
||||
$(this).fadeOut(options.fadeSpeed)
|
||||
});
|
||||
$menu.bind("click", updateColumn);
|
||||
$menu.on("click", updateColumn);
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
}
|
||||
|
||||
$("<label />")
|
||||
.text(columns[i].name)
|
||||
.html(columns[i].name)
|
||||
.prepend($input)
|
||||
.appendTo($li);
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
$menu
|
||||
.css("top", e.pageY - 10)
|
||||
.css("left", e.pageX - 10)
|
||||
.css("max-height", $(window).height() - e.pageY -10)
|
||||
.fadeIn(options.fadeSpeed);
|
||||
}
|
||||
|
||||
|
||||
BIN
web/pgadmin/static/vendor/slickgrid/images/CheckboxN.png
vendored
Normal file
BIN
web/pgadmin/static/vendor/slickgrid/images/CheckboxN.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 257 B |
BIN
web/pgadmin/static/vendor/slickgrid/images/CheckboxY.png
vendored
Normal file
BIN
web/pgadmin/static/vendor/slickgrid/images/CheckboxY.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 B |
@@ -46,10 +46,12 @@
|
||||
if (e.which == 86 && (e.ctrlKey || e.metaKey)) {
|
||||
if (_copiedRanges) {
|
||||
e.preventDefault();
|
||||
clearCopySelection();
|
||||
ranges = _grid.getSelectionModel().getSelectedRanges();
|
||||
_self.onPasteCells.notify({from: _copiedRanges, to: ranges});
|
||||
_copiedRanges = null;
|
||||
if (!_grid.getOptions().preserveCopiedSelectionOnPaste) {
|
||||
clearCopySelection();
|
||||
_copiedRanges = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
449
web/pgadmin/static/vendor/slickgrid/plugins/slick.cellexternalcopymanager.js
vendored
Normal file
449
web/pgadmin/static/vendor/slickgrid/plugins/slick.cellexternalcopymanager.js
vendored
Normal file
@@ -0,0 +1,449 @@
|
||||
(function ($) {
|
||||
// register namespace
|
||||
$.extend(true, window, {
|
||||
"Slick": {
|
||||
"CellExternalCopyManager": CellExternalCopyManager
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function CellExternalCopyManager(options) {
|
||||
/*
|
||||
This manager enables users to copy/paste data from/to an external Spreadsheet application
|
||||
such as MS-Excel® or OpenOffice-Spreadsheet.
|
||||
|
||||
Since it is not possible to access directly the clipboard in javascript, the plugin uses
|
||||
a trick to do it's job. After detecting the keystroke, we dynamically create a textarea
|
||||
where the browser copies/pastes the serialized data.
|
||||
|
||||
options:
|
||||
copiedCellStyle : sets the css className used for copied cells. default : "copied"
|
||||
copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : "copy-manager"
|
||||
dataItemColumnValueExtractor : option to specify a custom column value extractor function
|
||||
dataItemColumnValueSetter : option to specify a custom column value setter function
|
||||
clipboardCommandHandler : option to specify a custom handler for paste actions
|
||||
includeHeaderWhenCopying : set to true and the plugin will take the name property from each column (which is usually what appears in your header) and put that as the first row of the text that's copied to the clipboard
|
||||
bodyElement: option to specify a custom DOM element which to will be added the hidden textbox. It's useful if the grid is inside a modal dialog.
|
||||
onCopyInit: optional handler to run when copy action initializes
|
||||
onCopySuccess: optional handler to run when copy action is complete
|
||||
newRowCreator: function to add rows to table if paste overflows bottom of table
|
||||
readOnlyMode: suppresses paste
|
||||
*/
|
||||
var _grid;
|
||||
var _self = this;
|
||||
var _copiedRanges;
|
||||
var _options = options || {};
|
||||
var _copiedCellStyleLayerKey = _options.copiedCellStyleLayerKey || "copy-manager";
|
||||
var _copiedCellStyle = _options.copiedCellStyle || "copied";
|
||||
var _clearCopyTI = 0;
|
||||
var _bodyElement = _options.bodyElement || document.body;
|
||||
var _onCopyInit = _options.onCopyInit || null;
|
||||
var _onCopySuccess = _options.onCopySuccess || null;
|
||||
|
||||
var keyCodes = {
|
||||
'C': 67,
|
||||
'V': 86,
|
||||
'ESC': 27,
|
||||
'INSERT': 45
|
||||
};
|
||||
|
||||
function init(grid) {
|
||||
_grid = grid;
|
||||
_grid.onKeyDown.subscribe(handleKeyDown);
|
||||
|
||||
// we need a cell selection model
|
||||
var cellSelectionModel = grid.getSelectionModel();
|
||||
if (!cellSelectionModel){
|
||||
throw new Error("Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())");
|
||||
}
|
||||
// we give focus on the grid when a selection is done on it.
|
||||
// without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work
|
||||
cellSelectionModel.onSelectedRangesChanged.subscribe(function(e, args){
|
||||
_grid.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
_grid.onKeyDown.unsubscribe(handleKeyDown);
|
||||
}
|
||||
|
||||
function getDataItemValueForColumn(item, columnDef) {
|
||||
if (_options.dataItemColumnValueExtractor) {
|
||||
var dataItemColumnValueExtractorValue = _options.dataItemColumnValueExtractor(item, columnDef);
|
||||
|
||||
if (dataItemColumnValueExtractorValue)
|
||||
return dataItemColumnValueExtractorValue;
|
||||
}
|
||||
|
||||
var retVal = '';
|
||||
|
||||
// if a custom getter is not defined, we call serializeValue of the editor to serialize
|
||||
if (columnDef.editor){
|
||||
var editorArgs = {
|
||||
'container':$("<p>"), // a dummy container
|
||||
'column':columnDef,
|
||||
'position':{'top':0, 'left':0}, // a dummy position required by some editors
|
||||
'grid':_grid
|
||||
};
|
||||
var editor = new columnDef.editor(editorArgs);
|
||||
editor.loadValue(item);
|
||||
retVal = editor.serializeValue();
|
||||
editor.destroy();
|
||||
}
|
||||
else {
|
||||
retVal = item[columnDef.field];
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
function setDataItemValueForColumn(item, columnDef, value) {
|
||||
if (_options.dataItemColumnValueSetter) {
|
||||
return _options.dataItemColumnValueSetter(item, columnDef, value);
|
||||
}
|
||||
|
||||
// if a custom setter is not defined, we call applyValue of the editor to unserialize
|
||||
if (columnDef.editor){
|
||||
var editorArgs = {
|
||||
'container':$("body"), // a dummy container
|
||||
'column':columnDef,
|
||||
'position':{'top':0, 'left':0}, // a dummy position required by some editors
|
||||
'grid':_grid
|
||||
};
|
||||
var editor = new columnDef.editor(editorArgs);
|
||||
editor.loadValue(item);
|
||||
editor.applyValue(item, value);
|
||||
editor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _createTextBox(innerText){
|
||||
var ta = document.createElement('textarea');
|
||||
ta.style.position = 'absolute';
|
||||
ta.style.left = '-1000px';
|
||||
ta.style.top = document.body.scrollTop + 'px';
|
||||
ta.value = innerText;
|
||||
_bodyElement.appendChild(ta);
|
||||
ta.select();
|
||||
|
||||
return ta;
|
||||
}
|
||||
|
||||
function _decodeTabularData(_grid, ta){
|
||||
var columns = _grid.getColumns();
|
||||
var clipText = ta.value;
|
||||
var clipRows = clipText.split(/[\n\f\r]/);
|
||||
// trim trailing CR if present
|
||||
if (clipRows[clipRows.length - 1]=="") { clipRows.pop(); }
|
||||
|
||||
var clippedRange = [];
|
||||
var j = 0;
|
||||
|
||||
_bodyElement.removeChild(ta);
|
||||
for (var i=0; i<clipRows.length; i++) {
|
||||
if (clipRows[i]!="")
|
||||
clippedRange[j++] = clipRows[i].split("\t");
|
||||
else
|
||||
clippedRange[i] = [""];
|
||||
}
|
||||
var selectedCell = _grid.getActiveCell();
|
||||
var ranges = _grid.getSelectionModel().getSelectedRanges();
|
||||
var selectedRange = ranges && ranges.length ? ranges[0] : null; // pick only one selection
|
||||
var activeRow = null;
|
||||
var activeCell = null;
|
||||
|
||||
if (selectedRange){
|
||||
activeRow = selectedRange.fromRow;
|
||||
activeCell = selectedRange.fromCell;
|
||||
} else if (selectedCell){
|
||||
activeRow = selectedCell.row;
|
||||
activeCell = selectedCell.cell;
|
||||
} else {
|
||||
// we don't know where to paste
|
||||
return;
|
||||
}
|
||||
|
||||
var oneCellToMultiple = false;
|
||||
var destH = clippedRange.length;
|
||||
var destW = clippedRange.length ? clippedRange[0].length : 0;
|
||||
if (clippedRange.length == 1 && clippedRange[0].length == 1 && selectedRange){
|
||||
oneCellToMultiple = true;
|
||||
destH = selectedRange.toRow - selectedRange.fromRow +1;
|
||||
destW = selectedRange.toCell - selectedRange.fromCell +1;
|
||||
}
|
||||
var availableRows = _grid.getData().length - activeRow;
|
||||
var addRows = 0;
|
||||
if(availableRows < destH)
|
||||
{
|
||||
var d = _grid.getData();
|
||||
for(addRows = 1; addRows <= destH - availableRows; addRows++)
|
||||
d.push({});
|
||||
_grid.setData(d);
|
||||
_grid.render();
|
||||
}
|
||||
|
||||
var overflowsBottomOfGrid = activeRow + destH > _grid.getDataLength();
|
||||
|
||||
if (_options.newRowCreator && overflowsBottomOfGrid) {
|
||||
|
||||
var newRowsNeeded = activeRow + destH - _grid.getDataLength();
|
||||
|
||||
_options.newRowCreator(newRowsNeeded);
|
||||
|
||||
}
|
||||
|
||||
var clipCommand = {
|
||||
|
||||
isClipboardCommand: true,
|
||||
clippedRange: clippedRange,
|
||||
oldValues: [],
|
||||
cellExternalCopyManager: _self,
|
||||
_options: _options,
|
||||
setDataItemValueForColumn: setDataItemValueForColumn,
|
||||
markCopySelection: markCopySelection,
|
||||
oneCellToMultiple: oneCellToMultiple,
|
||||
activeRow: activeRow,
|
||||
activeCell: activeCell,
|
||||
destH: destH,
|
||||
destW: destW,
|
||||
maxDestY: _grid.getDataLength(),
|
||||
maxDestX: _grid.getColumns().length,
|
||||
h: 0,
|
||||
w: 0,
|
||||
|
||||
execute: function() {
|
||||
this.h=0;
|
||||
for (var y = 0; y < this.destH; y++){
|
||||
this.oldValues[y] = [];
|
||||
this.w=0;
|
||||
this.h++;
|
||||
for (var x = 0; x < this.destW; x++){
|
||||
this.w++;
|
||||
var desty = activeRow + y;
|
||||
var destx = activeCell + x;
|
||||
|
||||
if (desty < this.maxDestY && destx < this.maxDestX ) {
|
||||
var nd = _grid.getCellNode(desty, destx);
|
||||
var dt = _grid.getDataItem(desty);
|
||||
this.oldValues[y][x] = dt[columns[destx]['field']];
|
||||
if (oneCellToMultiple)
|
||||
this.setDataItemValueForColumn(dt, columns[destx], clippedRange[0][0]);
|
||||
else
|
||||
this.setDataItemValueForColumn(dt, columns[destx], clippedRange[y] ? clippedRange[y][x] : '');
|
||||
_grid.updateCell(desty, destx);
|
||||
_grid.onCellChange.notify({
|
||||
row: desty,
|
||||
cell: destx,
|
||||
item: dt,
|
||||
grid: _grid
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bRange = {
|
||||
'fromCell': activeCell,
|
||||
'fromRow': activeRow,
|
||||
'toCell': activeCell+this.w-1,
|
||||
'toRow': activeRow+this.h-1
|
||||
}
|
||||
|
||||
this.markCopySelection([bRange]);
|
||||
_grid.getSelectionModel().setSelectedRanges([bRange]);
|
||||
this.cellExternalCopyManager.onPasteCells.notify({ranges: [bRange]});
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
for (var y = 0; y < this.destH; y++){
|
||||
for (var x = 0; x < this.destW; x++){
|
||||
var desty = activeRow + y;
|
||||
var destx = activeCell + x;
|
||||
|
||||
if (desty < this.maxDestY && destx < this.maxDestX ) {
|
||||
var nd = _grid.getCellNode(desty, destx);
|
||||
var dt = _grid.getDataItem(desty);
|
||||
if (oneCellToMultiple)
|
||||
this.setDataItemValueForColumn(dt, columns[destx], this.oldValues[0][0]);
|
||||
else
|
||||
this.setDataItemValueForColumn(dt, columns[destx], this.oldValues[y][x]);
|
||||
_grid.updateCell(desty, destx);
|
||||
_grid.onCellChange.notify({
|
||||
row: desty,
|
||||
cell: destx,
|
||||
item: dt,
|
||||
grid: _grid
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bRange = {
|
||||
'fromCell': activeCell,
|
||||
'fromRow': activeRow,
|
||||
'toCell': activeCell+this.w-1,
|
||||
'toRow': activeRow+this.h-1
|
||||
}
|
||||
|
||||
this.markCopySelection([bRange]);
|
||||
_grid.getSelectionModel().setSelectedRanges([bRange]);
|
||||
this.cellExternalCopyManager.onPasteCells.notify({ranges: [bRange]});
|
||||
|
||||
if(addRows > 1){
|
||||
var d = _grid.getData();
|
||||
for(; addRows > 1; addRows--)
|
||||
d.splice(d.length - 1, 1);
|
||||
_grid.setData(d);
|
||||
_grid.render();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(_options.clipboardCommandHandler) {
|
||||
_options.clipboardCommandHandler(clipCommand);
|
||||
}
|
||||
else {
|
||||
clipCommand.execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleKeyDown(e, args) {
|
||||
var ranges;
|
||||
if (!_grid.getEditorLock().isActive() || _grid.getOptions().autoEdit) {
|
||||
if (e.which == keyCodes.ESC) {
|
||||
if (_copiedRanges) {
|
||||
e.preventDefault();
|
||||
clearCopySelection();
|
||||
_self.onCopyCancelled.notify({ranges: _copiedRanges});
|
||||
_copiedRanges = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ((e.which === keyCodes.C || e.which === keyCodes.INSERT) && (e.ctrlKey || e.metaKey) && !e.shiftKey) { // CTRL+C or CTRL+INS
|
||||
if (_onCopyInit) {
|
||||
_onCopyInit.call();
|
||||
}
|
||||
ranges = _grid.getSelectionModel().getSelectedRanges();
|
||||
if (ranges.length != 0) {
|
||||
_copiedRanges = ranges;
|
||||
markCopySelection(ranges);
|
||||
_self.onCopyCells.notify({ranges: ranges});
|
||||
|
||||
var columns = _grid.getColumns();
|
||||
var clipText = "";
|
||||
|
||||
for (var rg = 0; rg < ranges.length; rg++){
|
||||
var range = ranges[rg];
|
||||
var clipTextRows = [];
|
||||
for (var i=range.fromRow; i< range.toRow+1 ; i++){
|
||||
var clipTextCells = [];
|
||||
var dt = _grid.getDataItem(i);
|
||||
|
||||
if (clipTextRows == "" && _options.includeHeaderWhenCopying) {
|
||||
var clipTextHeaders = [];
|
||||
for (var j = range.fromCell; j < range.toCell + 1 ; j++) {
|
||||
if (columns[j].name.length > 0)
|
||||
clipTextHeaders.push(columns[j].name);
|
||||
}
|
||||
clipTextRows.push(clipTextHeaders.join("\t"));
|
||||
}
|
||||
|
||||
for (var j=range.fromCell; j< range.toCell+1 ; j++){
|
||||
clipTextCells.push(getDataItemValueForColumn(dt, columns[j]));
|
||||
}
|
||||
clipTextRows.push(clipTextCells.join("\t"));
|
||||
}
|
||||
clipText += clipTextRows.join("\r\n") + "\r\n";
|
||||
}
|
||||
|
||||
if(window.clipboardData) {
|
||||
window.clipboardData.setData("Text", clipText);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
var focusEl = document.activeElement;
|
||||
|
||||
var ta = _createTextBox(clipText);
|
||||
|
||||
ta.focus();
|
||||
|
||||
setTimeout(function(){
|
||||
_bodyElement.removeChild(ta);
|
||||
// restore focus
|
||||
if (focusEl)
|
||||
focusEl.focus();
|
||||
else
|
||||
console.log("Not element to restore focus to after copy?");
|
||||
|
||||
}, 100);
|
||||
|
||||
if (_onCopySuccess) {
|
||||
var rowCount = 0;
|
||||
// If it's cell selection, use the toRow/fromRow fields
|
||||
if (ranges.length === 1) {
|
||||
rowCount = (ranges[0].toRow + 1) - ranges[0].fromRow;
|
||||
}
|
||||
else {
|
||||
rowCount = ranges.length;
|
||||
}
|
||||
_onCopySuccess.call(this, rowCount);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_options.readOnlyMode && (
|
||||
(e.which === keyCodes.V && (e.ctrlKey || e.metaKey) && !e.shiftKey)
|
||||
|| (e.which === keyCodes.INSERT && e.shiftKey && !e.ctrlKey)
|
||||
)) { // CTRL+V or Shift+INS
|
||||
var ta = _createTextBox('');
|
||||
|
||||
setTimeout(function(){
|
||||
_decodeTabularData(_grid, ta);
|
||||
}, 100);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function markCopySelection(ranges) {
|
||||
clearCopySelection();
|
||||
|
||||
var columns = _grid.getColumns();
|
||||
var hash = {};
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
|
||||
hash[j] = {};
|
||||
for (var k = ranges[i].fromCell; k <= ranges[i].toCell && k<columns.length; k++) {
|
||||
hash[j][columns[k].id] = _copiedCellStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
_grid.setCellCssStyles(_copiedCellStyleLayerKey, hash);
|
||||
clearTimeout(_clearCopyTI);
|
||||
_clearCopyTI = setTimeout(function(){
|
||||
_self.clearCopySelection();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function clearCopySelection() {
|
||||
_grid.removeCellCssStyles(_copiedCellStyleLayerKey);
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
"clearCopySelection": clearCopySelection,
|
||||
"handleKeyDown":handleKeyDown,
|
||||
|
||||
"onCopyCells": new Slick.Event(),
|
||||
"onCopyCancelled": new Slick.Event(),
|
||||
"onPasteCells": new Slick.Event()
|
||||
});
|
||||
}
|
||||
})(jQuery);
|
||||
@@ -10,6 +10,7 @@
|
||||
function CellRangeSelector(options) {
|
||||
var _grid;
|
||||
var _canvas;
|
||||
var _currentlySelectedRange;
|
||||
var _dragging;
|
||||
var _decorator;
|
||||
var _self = this;
|
||||
@@ -61,7 +62,7 @@
|
||||
dd.startY - $(_canvas).offset().top);
|
||||
|
||||
dd.range = {start: start, end: {}};
|
||||
|
||||
_currentlySelectedRange = dd.range;
|
||||
return _decorator.show(new Slick.Range(start.row, start.cell));
|
||||
}
|
||||
|
||||
@@ -80,6 +81,7 @@
|
||||
}
|
||||
|
||||
dd.range.end = end;
|
||||
_currentlySelectedRange = dd.range;
|
||||
_decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
|
||||
}
|
||||
|
||||
@@ -102,9 +104,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentRange() {
|
||||
return _currentlySelectedRange;
|
||||
}
|
||||
|
||||
$.extend(this, {
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
"getCurrentRange": getCurrentRange,
|
||||
|
||||
"onBeforeCellRangeSelected": new Slick.Event(),
|
||||
"onCellRangeSelected": new Slick.Event()
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
|
||||
function setSelectedRanges(ranges) {
|
||||
// simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged
|
||||
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
|
||||
if ((!_ranges || _ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
|
||||
|
||||
_ranges = removeInvalidRanges(ranges);
|
||||
@@ -75,6 +75,7 @@
|
||||
}
|
||||
|
||||
function handleCellRangeSelected(e, args) {
|
||||
_grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
|
||||
setSelectedRanges([args.range]);
|
||||
}
|
||||
|
||||
@@ -94,8 +95,9 @@
|
||||
*/
|
||||
var ranges, last;
|
||||
var active = _grid.getActiveCell();
|
||||
var metaKey = e.ctrlKey || e.metaKey;
|
||||
|
||||
if ( active && e.shiftKey && !e.ctrlKey && !e.altKey &&
|
||||
if ( active && e.shiftKey && !metaKey && !e.altKey &&
|
||||
(e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) {
|
||||
|
||||
ranges = getSelectedRanges();
|
||||
|
||||
@@ -122,11 +122,11 @@
|
||||
}
|
||||
|
||||
if (button.handler) {
|
||||
btn.bind("click", button.handler);
|
||||
btn.on("click", button.handler);
|
||||
}
|
||||
|
||||
btn
|
||||
.bind("click", handleButtonClick)
|
||||
.on("click", handleButtonClick)
|
||||
.appendTo(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,13 +100,13 @@
|
||||
_grid.setColumns(_grid.getColumns());
|
||||
|
||||
// Hide the menu on outside click.
|
||||
$(document.body).bind("mousedown", handleBodyMouseDown);
|
||||
$(document.body).on("mousedown", handleBodyMouseDown);
|
||||
}
|
||||
|
||||
|
||||
function destroy() {
|
||||
_handler.unsubscribeAll();
|
||||
$(document.body).unbind("mousedown", handleBodyMouseDown);
|
||||
$(document.body).off("mousedown", handleBodyMouseDown);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
}
|
||||
|
||||
$el
|
||||
.bind("click", showMenu)
|
||||
.on("click", showMenu)
|
||||
.appendTo(args.node);
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@
|
||||
.data("command", item.command || '')
|
||||
.data("column", columnDef)
|
||||
.data("item", item)
|
||||
.bind("click", handleMenuItemClick)
|
||||
.on("click", handleMenuItemClick)
|
||||
.appendTo($menu);
|
||||
|
||||
if (item.disabled) {
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
|
||||
function setSelectedRanges(ranges) {
|
||||
// simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged
|
||||
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
|
||||
if ((!_ranges || _ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
|
||||
_ranges = ranges;
|
||||
_self.onSelectedRangesChanged.notify(_ranges);
|
||||
@@ -121,8 +121,8 @@
|
||||
|
||||
if (active >= 0 && active < _grid.getDataLength()) {
|
||||
_grid.scrollRowIntoView(active);
|
||||
_ranges = rowsToRanges(getRowsRange(top, bottom));
|
||||
setSelectedRanges(_ranges);
|
||||
var tempRanges = rowsToRanges(getRowsRange(top, bottom));
|
||||
setSelectedRanges(tempRanges);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
@@ -166,8 +166,8 @@
|
||||
_grid.setActiveCell(cell.row, cell.cell);
|
||||
}
|
||||
|
||||
_ranges = rowsToRanges(selection);
|
||||
setSelectedRanges(_ranges);
|
||||
var tempRanges = rowsToRanges(selection);
|
||||
setSelectedRanges(tempRanges);
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -29,23 +29,23 @@ classes should alter those!
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sr.ui-state-active {
|
||||
.slick-row.ui-state-active {
|
||||
background: #F5F7D7;
|
||||
}
|
||||
|
||||
.sr {
|
||||
.slick-row {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 0px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.sr.selected {
|
||||
.slick-row.selected {
|
||||
z-index: 10;
|
||||
background: #DFE8F6;
|
||||
}
|
||||
|
||||
.sc {
|
||||
.slick-cell {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
@@ -73,11 +73,11 @@ classes should alter those!
|
||||
background: white;
|
||||
}
|
||||
|
||||
.sc.selected {
|
||||
.slick-cell.selected {
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
.sc.active {
|
||||
.slick-cell.active {
|
||||
border-color: gray;
|
||||
border-style: solid;
|
||||
}
|
||||
@@ -86,20 +86,20 @@ classes should alter those!
|
||||
background: silver !important;
|
||||
}
|
||||
|
||||
.sr.odd {
|
||||
.slick-row.odd {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.sr.ui-state-active {
|
||||
.slick-row.ui-state-active {
|
||||
background: #F5F7D7;
|
||||
}
|
||||
|
||||
.sr.loading {
|
||||
.slick-row.loading {
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity = 50);
|
||||
}
|
||||
|
||||
.sc.invalid {
|
||||
.slick-cell.invalid {
|
||||
border-color: red;
|
||||
-moz-animation-duration: 0.2s;
|
||||
-webkit-animation-duration: 0.2s;
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
RIGHT: 39,
|
||||
TAB: 9,
|
||||
UP: 38
|
||||
}
|
||||
},
|
||||
"preClickClassName" : "slick-edit-preclick"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -423,7 +424,7 @@
|
||||
|
||||
/***
|
||||
* Sets the specified edit controller as the active edit controller (acquire edit lock).
|
||||
* If another edit controller is already active, and exception will be thrown.
|
||||
* If another edit controller is already active, and exception will be throw new Error(.
|
||||
* @method activate
|
||||
* @param editController {EditController} edit controller acquiring the lock
|
||||
*/
|
||||
@@ -432,26 +433,26 @@
|
||||
return;
|
||||
}
|
||||
if (activeEditController !== null) {
|
||||
throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController";
|
||||
throw new Error("SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController");
|
||||
}
|
||||
if (!editController.commitCurrentEdit) {
|
||||
throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";
|
||||
throw new Error("SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()");
|
||||
}
|
||||
if (!editController.cancelCurrentEdit) {
|
||||
throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";
|
||||
throw new Error("SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()");
|
||||
}
|
||||
activeEditController = editController;
|
||||
};
|
||||
|
||||
/***
|
||||
* Unsets the specified edit controller as the active edit controller (release edit lock).
|
||||
* If the specified edit controller is not the active one, an exception will be thrown.
|
||||
* If the specified edit controller is not the active one, an exception will be throw new Error(.
|
||||
* @method deactivate
|
||||
* @param editController {EditController} edit controller releasing the lock
|
||||
*/
|
||||
this.deactivate = function (editController) {
|
||||
if (activeEditController !== editController) {
|
||||
throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one";
|
||||
throw new Error("SlickGrid.EditorLock.deactivate: specified editController is not the currently active one");
|
||||
}
|
||||
activeEditController = null;
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
for (var i = startingIndex, l = items.length; i < l; i++) {
|
||||
id = items[i][idProperty];
|
||||
if (id === undefined) {
|
||||
throw "Each data element must implement a unique 'id' property";
|
||||
throw new Error("Each data element must implement a unique 'id' property");
|
||||
}
|
||||
idxById[id] = i;
|
||||
}
|
||||
@@ -118,7 +118,7 @@
|
||||
for (var i = 0, l = items.length; i < l; i++) {
|
||||
id = items[i][idProperty];
|
||||
if (id === undefined || idxById[id] !== i) {
|
||||
throw "Each data element must implement a unique 'id' property";
|
||||
throw new Error("Each data element must implement a unique 'id' property");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getFilteredItems(){
|
||||
return filteredItems;
|
||||
}
|
||||
|
||||
|
||||
function getFilter(){
|
||||
return filter;
|
||||
}
|
||||
|
||||
function setFilter(filterFn) {
|
||||
filter = filterFn;
|
||||
if (options.inlineFilters) {
|
||||
@@ -330,7 +339,7 @@
|
||||
|
||||
function updateItem(id, item) {
|
||||
if (idxById[id] === undefined || id !== item[idProperty]) {
|
||||
throw "Invalid or non-matching id";
|
||||
throw new Error("Invalid or non-matching id");
|
||||
}
|
||||
items[idxById[id]] = item;
|
||||
if (!updated) {
|
||||
@@ -355,7 +364,7 @@
|
||||
function deleteItem(id) {
|
||||
var idx = idxById[id];
|
||||
if (idx === undefined) {
|
||||
throw "Invalid id";
|
||||
throw new Error("Invalid id");
|
||||
}
|
||||
delete idxById[id];
|
||||
items.splice(idx, 1);
|
||||
@@ -762,14 +771,17 @@
|
||||
// get the current page
|
||||
var paged;
|
||||
if (pagesize) {
|
||||
if (filteredItems.length < pagenum * pagesize) {
|
||||
pagenum = Math.floor(filteredItems.length / pagesize);
|
||||
if (filteredItems.length <= pagenum * pagesize) {
|
||||
if (filteredItems.length === 0) {
|
||||
pagenum = 0;
|
||||
} else {
|
||||
pagenum = Math.floor((filteredItems.length - 1) / pagesize);
|
||||
}
|
||||
}
|
||||
paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
|
||||
} else {
|
||||
paged = filteredItems;
|
||||
}
|
||||
|
||||
return {totalRows: filteredItems.length, rows: paged};
|
||||
}
|
||||
|
||||
@@ -980,6 +992,10 @@
|
||||
if (key != args.key) { return; }
|
||||
if (args.hash) {
|
||||
storeCellCssStyles(args.hash);
|
||||
} else {
|
||||
grid.onCellCssStylesChanged.unsubscribe(styleChanged);
|
||||
self.onRowsChanged.unsubscribe(update);
|
||||
self.onRowCountChanged.unsubscribe(update);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -997,6 +1013,8 @@
|
||||
"getItems": getItems,
|
||||
"setItems": setItems,
|
||||
"setFilter": setFilter,
|
||||
"getFilter": getFilter,
|
||||
"getFilteredItems": getFilteredItems,
|
||||
"sort": sort,
|
||||
"fastSort": fastSort,
|
||||
"reSort": reSort,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"Editors": {
|
||||
"Text": TextEditor,
|
||||
"Integer": IntegerEditor,
|
||||
"Float": FloatEditor,
|
||||
"Float": FloatEditor,
|
||||
"Date": DateEditor,
|
||||
"YesNoSelect": YesNoSelectEditor,
|
||||
"Checkbox": CheckboxEditor,
|
||||
@@ -29,7 +29,7 @@
|
||||
this.init = function () {
|
||||
$input = $("<INPUT type=text class='editor-text' />")
|
||||
.appendTo(args.container)
|
||||
.bind("keydown.nav", function (e) {
|
||||
.on("keydown.nav", function (e) {
|
||||
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
@@ -98,7 +98,7 @@
|
||||
this.init = function () {
|
||||
$input = $("<INPUT type=text class='editor-text' />");
|
||||
|
||||
$input.bind("keydown.nav", function (e) {
|
||||
$input.on("keydown.nav", function (e) {
|
||||
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
@@ -167,7 +167,7 @@
|
||||
this.init = function () {
|
||||
$input = $("<INPUT type=text class='editor-text' />");
|
||||
|
||||
$input.bind("keydown.nav", function (e) {
|
||||
$input.on("keydown.nav", function (e) {
|
||||
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
@@ -185,24 +185,24 @@
|
||||
$input.focus();
|
||||
};
|
||||
|
||||
function getDecimalPlaces() {
|
||||
// returns the number of fixed decimal places or null
|
||||
var rtn = args.column.editorFixedDecimalPlaces;
|
||||
if (typeof rtn == 'undefined') {
|
||||
rtn = FloatEditor.DefaultDecimalPlaces;
|
||||
}
|
||||
return (!rtn && rtn!==0 ? null : rtn);
|
||||
}
|
||||
function getDecimalPlaces() {
|
||||
// returns the number of fixed decimal places or null
|
||||
var rtn = args.column.editorFixedDecimalPlaces;
|
||||
if (typeof rtn == 'undefined') {
|
||||
rtn = FloatEditor.DefaultDecimalPlaces;
|
||||
}
|
||||
return (!rtn && rtn!==0 ? null : rtn);
|
||||
}
|
||||
|
||||
this.loadValue = function (item) {
|
||||
defaultValue = item[args.column.field];
|
||||
|
||||
var decPlaces = getDecimalPlaces();
|
||||
if (decPlaces !== null
|
||||
&& (defaultValue || defaultValue===0)
|
||||
&& defaultValue.toFixed) {
|
||||
defaultValue = defaultValue.toFixed(decPlaces);
|
||||
}
|
||||
var decPlaces = getDecimalPlaces();
|
||||
if (decPlaces !== null
|
||||
&& (defaultValue || defaultValue===0)
|
||||
&& defaultValue.toFixed) {
|
||||
defaultValue = defaultValue.toFixed(decPlaces);
|
||||
}
|
||||
|
||||
$input.val(defaultValue);
|
||||
$input[0].defaultValue = defaultValue;
|
||||
@@ -210,14 +210,14 @@
|
||||
};
|
||||
|
||||
this.serializeValue = function () {
|
||||
var rtn = parseFloat($input.val()) || 0;
|
||||
var rtn = parseFloat($input.val()) || 0;
|
||||
|
||||
var decPlaces = getDecimalPlaces();
|
||||
if (decPlaces !== null
|
||||
&& (rtn || rtn===0)
|
||||
&& rtn.toFixed) {
|
||||
rtn = parseFloat(rtn.toFixed(decPlaces));
|
||||
}
|
||||
var decPlaces = getDecimalPlaces();
|
||||
if (decPlaces !== null
|
||||
&& (rtn || rtn===0)
|
||||
&& rtn.toFixed) {
|
||||
rtn = parseFloat(rtn.toFixed(decPlaces));
|
||||
}
|
||||
|
||||
return rtn;
|
||||
};
|
||||
@@ -269,8 +269,7 @@
|
||||
$input.datepicker({
|
||||
showOn: "button",
|
||||
buttonImageOnly: true,
|
||||
buttonImage: "../images/calendar.gif",
|
||||
beforeShow: function () {
|
||||
beforeShow: function () {
|
||||
calendarOpen = true
|
||||
},
|
||||
onClose: function () {
|
||||
@@ -422,6 +421,10 @@
|
||||
}
|
||||
};
|
||||
|
||||
this.preClick = function () {
|
||||
$select.prop('checked', !$select.prop('checked'));
|
||||
};
|
||||
|
||||
this.serializeValue = function () {
|
||||
return $select.prop('checked');
|
||||
};
|
||||
@@ -470,7 +473,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
$picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) {
|
||||
$picker.find(".editor-percentcomplete-buttons button").on("click", function (e) {
|
||||
$input.val($(this).attr("val"));
|
||||
$picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val"));
|
||||
})
|
||||
@@ -535,15 +538,15 @@
|
||||
$wrapper = $("<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>")
|
||||
.appendTo($container);
|
||||
|
||||
$input = $("<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>")
|
||||
$input = $("<TEXTAREA hidefocus rows=5 style='background:white;width:250px;height:80px;border:0;outline:0'>")
|
||||
.appendTo($wrapper);
|
||||
|
||||
$("<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>")
|
||||
.appendTo($wrapper);
|
||||
|
||||
$wrapper.find("button:first").bind("click", this.save);
|
||||
$wrapper.find("button:last").bind("click", this.cancel);
|
||||
$input.bind("keydown", this.handleKeyDown);
|
||||
$wrapper.find("button:first").on("click", this.save);
|
||||
$wrapper.find("button:last").on("click", this.cancel);
|
||||
$input.on("keydown", this.handleKeyDown);
|
||||
|
||||
scope.position(args.position);
|
||||
$input.focus().select();
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
"PercentComplete": PercentCompleteFormatter,
|
||||
"PercentCompleteBar": PercentCompleteBarFormatter,
|
||||
"YesNo": YesNoFormatter,
|
||||
"Checkmark": CheckmarkFormatter
|
||||
"Checkmark": CheckmarkFormatter,
|
||||
"Checkbox": CheckboxFormatter
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -53,6 +55,10 @@
|
||||
return value ? "Yes" : "No";
|
||||
}
|
||||
|
||||
function CheckboxFormatter(row, cell, value, columnDef, dataContext) {
|
||||
return '<img class="slick-edit-preclick" src="../images/' + (value ? "CheckboxY" : "CheckboxN") + '.png">';
|
||||
}
|
||||
|
||||
function CheckmarkFormatter(row, cell, value, columnDef, dataContext) {
|
||||
return value ? "<img src='../images/tick.png'>" : "";
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ No built-in (selected, editable, highlight, flashing, invalid, loading, :focus)
|
||||
classes should alter those!
|
||||
*/
|
||||
|
||||
.slick-header.ui-state-default, .slick-headerrow.ui-state-default {
|
||||
.slick-header.ui-state-default, .slick-headerrow.ui-state-default, .slick-footerrow.ui-state-default {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-left: 0px !important;
|
||||
}
|
||||
|
||||
.slick-header-columns, .slick-headerrow-columns {
|
||||
.slick-header-columns, .slick-headerrow-columns, .slick-footerrow-columns {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
@@ -21,7 +21,7 @@ classes should alter those!
|
||||
.slick-header-column.ui-state-default {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
/*box-sizing: content-box !important; */
|
||||
/*box-sizing: content-box !important; use this for Firefox! */
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
@@ -36,7 +36,7 @@ classes should alter those!
|
||||
float: left;
|
||||
}
|
||||
|
||||
.slick-headerrow-column.ui-state-default {
|
||||
.slick-headerrow-column.ui-state-default, .slick-footerrow-column.ui-state-default {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,21 @@ classes should alter those!
|
||||
float: left;
|
||||
}
|
||||
|
||||
.slick-sort-indicator-numbered {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 5px;
|
||||
margin-left: 4px;
|
||||
margin-top: 0;
|
||||
padding-left: 1px;
|
||||
line-height: 20px;
|
||||
float: left;
|
||||
font-family: Arial;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
color: #6190CD;
|
||||
}
|
||||
|
||||
.slick-sort-indicator-desc {
|
||||
background: url(images/sort-desc.gif);
|
||||
}
|
||||
@@ -81,13 +96,13 @@ classes should alter those!
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.sr.ui-widget-content, .sr.ui-state-active {
|
||||
.slick-row.ui-widget-content, .slick-row.ui-state-active {
|
||||
position: absolute;
|
||||
border: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sc, .slick-headerrow-column {
|
||||
.slick-cell, .slick-headerrow-column , .slick-footerrow-column{
|
||||
position: absolute;
|
||||
border: 1px solid transparent;
|
||||
border-right: 1px dotted silver;
|
||||
@@ -102,6 +117,12 @@ classes should alter those!
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
}
|
||||
.slick-cell, .slick-headerrow-column{
|
||||
border-bottom-color: silver;
|
||||
}
|
||||
.slick-footerrow-column {
|
||||
border-top-color: silver;
|
||||
}
|
||||
|
||||
.slick-group {
|
||||
}
|
||||
@@ -110,7 +131,7 @@ classes should alter those!
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sc.highlighted {
|
||||
.slick-cell.highlighted {
|
||||
background: lightskyblue;
|
||||
background: rgba(0, 0, 255, 0.2);
|
||||
-webkit-transition: all 0.5s;
|
||||
@@ -119,11 +140,11 @@ classes should alter those!
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.sc.flashing {
|
||||
.slick-cell.flashing {
|
||||
border: 1px solid red !important;
|
||||
}
|
||||
|
||||
.sc.editable {
|
||||
.slick-cell.editable {
|
||||
z-index: 11;
|
||||
overflow: visible;
|
||||
background: white;
|
||||
@@ -131,7 +152,7 @@ classes should alter those!
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.sc:focus {
|
||||
.slick-cell:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
534
web/pgadmin/static/vendor/slickgrid/slick.grid.js
vendored
534
web/pgadmin/static/vendor/slickgrid/slick.grid.js
vendored
File diff suppressed because it is too large
Load Diff
158
web/pgadmin/static/vendor/slickgrid/slick.groupitemmetadataprovider.js
vendored
Normal file
158
web/pgadmin/static/vendor/slickgrid/slick.groupitemmetadataprovider.js
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
(function ($) {
|
||||
$.extend(true, window, {
|
||||
Slick: {
|
||||
Data: {
|
||||
GroupItemMetadataProvider: GroupItemMetadataProvider
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/***
|
||||
* Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView.
|
||||
* This metadata overrides the default behavior and formatting of those rows so that they appear and function
|
||||
* correctly when processed by the grid.
|
||||
*
|
||||
* This class also acts as a grid plugin providing event handlers to expand & collapse groups.
|
||||
* If "grid.registerPlugin(...)" is not called, expand & collapse will not work.
|
||||
*
|
||||
* @class GroupItemMetadataProvider
|
||||
* @module Data
|
||||
* @namespace Slick.Data
|
||||
* @constructor
|
||||
* @param options
|
||||
*/
|
||||
function GroupItemMetadataProvider(options) {
|
||||
var _grid;
|
||||
var _defaults = {
|
||||
groupCssClass: "slick-group",
|
||||
groupTitleCssClass: "slick-group-title",
|
||||
totalsCssClass: "slick-group-totals",
|
||||
groupFocusable: true,
|
||||
totalsFocusable: false,
|
||||
toggleCssClass: "slick-group-toggle",
|
||||
toggleExpandedCssClass: "expanded",
|
||||
toggleCollapsedCssClass: "collapsed",
|
||||
enableExpandCollapse: true,
|
||||
groupFormatter: defaultGroupCellFormatter,
|
||||
totalsFormatter: defaultTotalsCellFormatter
|
||||
};
|
||||
|
||||
options = $.extend(true, {}, _defaults, options);
|
||||
|
||||
|
||||
function defaultGroupCellFormatter(row, cell, value, columnDef, item) {
|
||||
if (!options.enableExpandCollapse) {
|
||||
return item.title;
|
||||
}
|
||||
|
||||
var indentation = item.level * 15 + "px";
|
||||
|
||||
return "<span class='" + options.toggleCssClass + " " +
|
||||
(item.collapsed ? options.toggleCollapsedCssClass : options.toggleExpandedCssClass) +
|
||||
"' style='margin-left:" + indentation +"'>" +
|
||||
"</span>" +
|
||||
"<span class='" + options.groupTitleCssClass + "' level='" + item.level + "'>" +
|
||||
item.title +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
function defaultTotalsCellFormatter(row, cell, value, columnDef, item) {
|
||||
return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || "";
|
||||
}
|
||||
|
||||
|
||||
function init(grid) {
|
||||
_grid = grid;
|
||||
_grid.onClick.subscribe(handleGridClick);
|
||||
_grid.onKeyDown.subscribe(handleGridKeyDown);
|
||||
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
if (_grid) {
|
||||
_grid.onClick.unsubscribe(handleGridClick);
|
||||
_grid.onKeyDown.unsubscribe(handleGridKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
function handleGridClick(e, args) {
|
||||
var item = this.getDataItem(args.row);
|
||||
if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) {
|
||||
var range = _grid.getRenderedRange();
|
||||
this.getData().setRefreshHints({
|
||||
ignoreDiffsBefore: range.top,
|
||||
ignoreDiffsAfter: range.bottom + 1
|
||||
});
|
||||
|
||||
if (item.collapsed) {
|
||||
this.getData().expandGroup(item.groupingKey);
|
||||
} else {
|
||||
this.getData().collapseGroup(item.groupingKey);
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add -/+ handling
|
||||
function handleGridKeyDown(e, args) {
|
||||
if (options.enableExpandCollapse && (e.which == $.ui.keyCode.SPACE)) {
|
||||
var activeCell = this.getActiveCell();
|
||||
if (activeCell) {
|
||||
var item = this.getDataItem(activeCell.row);
|
||||
if (item && item instanceof Slick.Group) {
|
||||
var range = _grid.getRenderedRange();
|
||||
this.getData().setRefreshHints({
|
||||
ignoreDiffsBefore: range.top,
|
||||
ignoreDiffsAfter: range.bottom + 1
|
||||
});
|
||||
|
||||
if (item.collapsed) {
|
||||
this.getData().expandGroup(item.groupingKey);
|
||||
} else {
|
||||
this.getData().collapseGroup(item.groupingKey);
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getGroupRowMetadata(item) {
|
||||
return {
|
||||
selectable: false,
|
||||
focusable: options.groupFocusable,
|
||||
cssClasses: options.groupCssClass,
|
||||
columns: {
|
||||
0: {
|
||||
colspan: "*",
|
||||
formatter: options.groupFormatter,
|
||||
editor: null
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getTotalsRowMetadata(item) {
|
||||
return {
|
||||
selectable: false,
|
||||
focusable: options.totalsFocusable,
|
||||
cssClasses: options.totalsCssClass,
|
||||
formatter: options.totalsFormatter,
|
||||
editor: null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
"init": init,
|
||||
"destroy": destroy,
|
||||
"getGroupRowMetadata": getGroupRowMetadata,
|
||||
"getTotalsRowMetadata": getTotalsRowMetadata
|
||||
};
|
||||
}
|
||||
})(jQuery);
|
||||
206
web/pgadmin/static/vendor/slickgrid/slick.remotemodel-yahoo.js
vendored
Normal file
206
web/pgadmin/static/vendor/slickgrid/slick.remotemodel-yahoo.js
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
(function ($) {
|
||||
/***
|
||||
* A sample AJAX data store implementation.
|
||||
* Right now, it's hooked up to load Hackernews stories, but can
|
||||
* easily be extended to support any JSONP-compatible backend that accepts paging parameters.
|
||||
*/
|
||||
function RemoteModel() {
|
||||
// private
|
||||
var PAGESIZE = 10;
|
||||
var data = {length: 0};
|
||||
var h_request = null;
|
||||
var req = null; // ajax request
|
||||
|
||||
// events
|
||||
var onDataLoading = new Slick.Event();
|
||||
var onDataLoaded = new Slick.Event();
|
||||
|
||||
|
||||
function init() {
|
||||
}
|
||||
|
||||
|
||||
function isDataLoaded(from, to) {
|
||||
for (var i = from; i <= to; i++) {
|
||||
if (data[i] == undefined || data[i] == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function clear() {
|
||||
for (var key in data) {
|
||||
delete data[key];
|
||||
}
|
||||
data.length = 0;
|
||||
}
|
||||
|
||||
function ensureData(from, to) {
|
||||
if (req) {
|
||||
req.abort();
|
||||
for (var i = req.fromPage; i <= req.toPage; i++) {
|
||||
data[i * PAGESIZE] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (from < 0) {
|
||||
from = 0;
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
to = Math.min(to, data.length - 1);
|
||||
}
|
||||
|
||||
var fromPage = Math.floor(from / PAGESIZE);
|
||||
var toPage = Math.floor(to / PAGESIZE);
|
||||
|
||||
while (data[fromPage * PAGESIZE] !== undefined && fromPage < toPage)
|
||||
fromPage++;
|
||||
|
||||
while (data[toPage * PAGESIZE] !== undefined && fromPage < toPage)
|
||||
toPage--;
|
||||
|
||||
if (fromPage > toPage || ((fromPage == toPage) && data[fromPage * PAGESIZE] !== undefined)) {
|
||||
// TODO: look-ahead
|
||||
onDataLoaded.notify({from: from, to: to});
|
||||
return;
|
||||
}
|
||||
|
||||
var recStart = (fromPage * PAGESIZE);
|
||||
var recCount = (((toPage - fromPage) * PAGESIZE) + PAGESIZE);
|
||||
|
||||
var url = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20rss"
|
||||
+ "(" + recStart + "%2C" + recCount + ")"
|
||||
+ "%20where%20url%3D%22http%3A%2F%2Frss.news.yahoo.com%2Frss%2Ftopstories%22"
|
||||
+ "&format=json";
|
||||
|
||||
if (h_request != null) {
|
||||
clearTimeout(h_request);
|
||||
}
|
||||
|
||||
h_request = setTimeout(function () {
|
||||
for (var i = fromPage; i <= toPage; i++)
|
||||
data[i * PAGESIZE] = null; // null indicates a 'requested but not available yet'
|
||||
|
||||
onDataLoading.notify({from: from, to: to});
|
||||
|
||||
req = $.jsonp({
|
||||
url: url,
|
||||
callbackParameter: "callback",
|
||||
cache: true,
|
||||
success: function (json, textStatus, xOptions) {
|
||||
onSuccess(json, recStart)
|
||||
},
|
||||
error: function () {
|
||||
onError(fromPage, toPage)
|
||||
}
|
||||
});
|
||||
|
||||
req.fromPage = fromPage;
|
||||
req.toPage = toPage;
|
||||
}, 50);
|
||||
}
|
||||
|
||||
|
||||
function onError(fromPage, toPage) {
|
||||
alert("error loading pages " + fromPage + " to " + toPage);
|
||||
}
|
||||
|
||||
// SAMPLE DATA
|
||||
// {
|
||||
// "query": {
|
||||
// "count": 40,
|
||||
// "created": "2015-03-03T00:34:00Z",
|
||||
// "lang": "en-US",
|
||||
// "results": {
|
||||
// "item": [
|
||||
// {
|
||||
// "title": "Netanyahu assails Iran deal, touts US-Israel ties",
|
||||
// "description": "<p><a href=\"http://news.yahoo.com/netanyahu-us-officials-face-off-iran-133539021--politics.html\"><img src=\"http://l2.yimg.com/bt/api/res/1.2/4eoBxbJStrbGAKbmBYOJfg--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9NzU7dz0xMzA-/http://media.zenfs.com/en_us/News/ap_webfeeds/2f3a20c2d46d9f096f0f6a706700d430.jpg\" width=\"130\" height=\"86\" alt=\"Israeli Prime Minister Benjamin Netanyahu addresses the 2015 American Israel Public Affairs Committee (AIPAC) Policy Conference in Washington, Monday, March 2, 2015. (AP Photo/Cliff Owen)\" align=\"left\" title=\"Israeli Prime Minister Benjamin Netanyahu addresses the 2015 American Israel Public Affairs Committee (AIPAC) Policy Conference in Washington, Monday, March 2, 2015. (AP Photo/Cliff Owen)\" border=\"0\" /></a>WASHINGTON (AP) — Seeking to lower tensions, Benjamin Netanyahu and U.S. officials cast their dispute over Iran as a family squabble on Monday, even as the Israeli leader claimed President Barack Obama did not — and could not — fully understand his nation's vital security concerns.</p><br clear=\"all\"/>",
|
||||
// "link": "http://news.yahoo.com/netanyahu-us-officials-face-off-iran-133539021--politics.html",
|
||||
// "pubDate": "Mon, 02 Mar 2015 19:17:36 -0500",
|
||||
// "source": {
|
||||
// "url": "http://www.ap.org/",
|
||||
// "content": "Associated Press"
|
||||
// },
|
||||
// "guid": {
|
||||
// "isPermaLink": "false",
|
||||
// "content": "netanyahu-us-officials-face-off-iran-133539021--politics"
|
||||
// },
|
||||
// "content": {
|
||||
// "height": "86",
|
||||
// "type": "image/jpeg",
|
||||
// "url": "http://l2.yimg.com/bt/api/res/1.2/4eoBxbJStrbGAKbmBYOJfg--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9NzU7dz0xMzA-/http://media.zenfs.com/en_us/News/ap_webfeeds/2f3a20c2d46d9f096f0f6a706700d430.jpg",
|
||||
// "width": "130"
|
||||
// },
|
||||
// "text": {
|
||||
// "type": "html",
|
||||
// "content": "<p><a href=\"http://news.yahoo.com/netanyahu-us-officials-face-off-iran-133539021--politics.html\"><img src=\"http://l2.yimg.com/bt/api/res/1.2/4eoBxbJStrbGAKbmBYOJfg--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9NzU7dz0xMzA-/http://media.zenfs.com/en_us/News/ap_webfeeds/2f3a20c2d46d9f096f0f6a706700d430.jpg\" width=\"130\" height=\"86\" alt=\"Israeli Prime Minister Benjamin Netanyahu addresses the 2015 American Israel Public Affairs Committee (AIPAC) Policy Conference in Washington, Monday, March 2, 2015. (AP Photo/Cliff Owen)\" align=\"left\" title=\"Israeli Prime Minister Benjamin Netanyahu addresses the 2015 American Israel Public Affairs Committee (AIPAC) Policy Conference in Washington, Monday, March 2, 2015. (AP Photo/Cliff Owen)\" border=\"0\" /></a>WASHINGTON (AP) — Seeking to lower tensions, Benjamin Netanyahu and U.S. officials cast their dispute over Iran as a family squabble on Monday, even as the Israeli leader claimed President Barack Obama did not — and could not — fully understand his nation's vital security concerns.</p><br clear=\"all\"/>"
|
||||
// },
|
||||
// "credit": {
|
||||
// "role": "publishing company"
|
||||
// }
|
||||
// },
|
||||
// {... },
|
||||
// {... },
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function onSuccess(json, recStart) {
|
||||
var recEnd = recStart;
|
||||
if (json.query.count > 0) {
|
||||
var results = json.query.results.item;
|
||||
recEnd = recStart + results.length;
|
||||
data.length = 100;// Math.min(parseInt(results.length),1000); // limitation of the API
|
||||
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var item = results[i];
|
||||
|
||||
item.pubDate = new Date(item.pubDate);
|
||||
|
||||
data[recStart + i] = { index: recStart + i };
|
||||
data[recStart + i].pubDate = item.pubDate;
|
||||
data[recStart + i].title = item.title;
|
||||
data[recStart + i].url = item.link;
|
||||
data[recStart + i].text = item.description;
|
||||
}
|
||||
}
|
||||
req = null;
|
||||
|
||||
onDataLoaded.notify({from: recStart, to: recEnd});
|
||||
}
|
||||
|
||||
|
||||
function reloadData(from, to) {
|
||||
for (var i = from; i <= to; i++)
|
||||
delete data[i];
|
||||
|
||||
ensureData(from, to);
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
return {
|
||||
// properties
|
||||
"data": data,
|
||||
|
||||
// methods
|
||||
"clear": clear,
|
||||
"isDataLoaded": isDataLoaded,
|
||||
"ensureData": ensureData,
|
||||
"reloadData": reloadData,
|
||||
|
||||
// events
|
||||
"onDataLoading": onDataLoading,
|
||||
"onDataLoaded": onDataLoaded
|
||||
};
|
||||
}
|
||||
|
||||
// Slick.Data.RemoteModel
|
||||
$.extend(true, window, { Slick: { Data: { RemoteModel: RemoteModel }}});
|
||||
})(jQuery);
|
||||
169
web/pgadmin/static/vendor/slickgrid/slick.remotemodel.js
vendored
Normal file
169
web/pgadmin/static/vendor/slickgrid/slick.remotemodel.js
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
(function ($) {
|
||||
/***
|
||||
* A sample AJAX data store implementation.
|
||||
* Right now, it's hooked up to load search results from Octopart, but can
|
||||
* easily be extended to support any JSONP-compatible backend that accepts paging parameters.
|
||||
*/
|
||||
function RemoteModel() {
|
||||
// private
|
||||
var PAGESIZE = 50;
|
||||
var data = {length: 0};
|
||||
var searchstr = "";
|
||||
var sortcol = null;
|
||||
var sortdir = 1;
|
||||
var h_request = null;
|
||||
var req = null; // ajax request
|
||||
|
||||
// events
|
||||
var onDataLoading = new Slick.Event();
|
||||
var onDataLoaded = new Slick.Event();
|
||||
|
||||
|
||||
function init() {
|
||||
}
|
||||
|
||||
|
||||
function isDataLoaded(from, to) {
|
||||
for (var i = from; i <= to; i++) {
|
||||
if (data[i] == undefined || data[i] == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function clear() {
|
||||
for (var key in data) {
|
||||
delete data[key];
|
||||
}
|
||||
data.length = 0;
|
||||
}
|
||||
|
||||
|
||||
function ensureData(from, to) {
|
||||
if (req) {
|
||||
req.abort();
|
||||
for (var i = req.fromPage; i <= req.toPage; i++)
|
||||
data[i * PAGESIZE] = undefined;
|
||||
}
|
||||
|
||||
if (from < 0) {
|
||||
from = 0;
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
to = Math.min(to, data.length - 1);
|
||||
}
|
||||
|
||||
var fromPage = Math.floor(from / PAGESIZE);
|
||||
var toPage = Math.floor(to / PAGESIZE);
|
||||
|
||||
while (data[fromPage * PAGESIZE] !== undefined && fromPage < toPage)
|
||||
fromPage++;
|
||||
|
||||
while (data[toPage * PAGESIZE] !== undefined && fromPage < toPage)
|
||||
toPage--;
|
||||
|
||||
if (fromPage > toPage || ((fromPage == toPage) && data[fromPage * PAGESIZE] !== undefined)) {
|
||||
// TODO: look-ahead
|
||||
onDataLoaded.notify({from: from, to: to});
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "http://octopart.com/api/v3/parts/search?apikey=68b25f31&include[]=short_description&show[]=uid&show[]=manufacturer&show[]=mpn&show[]=brand&show[]=octopart_url&show[]=short_description&q=" + searchstr + "&start=" + (fromPage * PAGESIZE) + "&limit=" + (((toPage - fromPage) * PAGESIZE) + PAGESIZE);
|
||||
|
||||
if (sortcol != null) {
|
||||
url += ("&sortby=" + sortcol + ((sortdir > 0) ? "+asc" : "+desc"));
|
||||
}
|
||||
|
||||
if (h_request != null) {
|
||||
clearTimeout(h_request);
|
||||
}
|
||||
|
||||
h_request = setTimeout(function () {
|
||||
for (var i = fromPage; i <= toPage; i++)
|
||||
data[i * PAGESIZE] = null; // null indicates a 'requested but not available yet'
|
||||
|
||||
onDataLoading.notify({from: from, to: to});
|
||||
|
||||
req = $.jsonp({
|
||||
url: url,
|
||||
callbackParameter: "callback",
|
||||
cache: true,
|
||||
success: onSuccess,
|
||||
error: function () {
|
||||
onError(fromPage, toPage)
|
||||
}
|
||||
});
|
||||
req.fromPage = fromPage;
|
||||
req.toPage = toPage;
|
||||
}, 50);
|
||||
}
|
||||
|
||||
|
||||
function onError(fromPage, toPage) {
|
||||
alert("error loading pages " + fromPage + " to " + toPage);
|
||||
}
|
||||
|
||||
function onSuccess(resp) {
|
||||
var from = resp.request.start, to = from + resp.results.length;
|
||||
data.length = Math.min(parseInt(resp.hits),1000); // limitation of the API
|
||||
|
||||
for (var i = 0; i < resp.results.length; i++) {
|
||||
var item = resp.results[i].item;
|
||||
|
||||
data[from + i] = item;
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
// Slick.Data.RemoteModel
|
||||
$.extend(true, window, { Slick: { Data: { RemoteModel: RemoteModel }}});
|
||||
})(jQuery);
|
||||
@@ -260,7 +260,7 @@ li {
|
||||
}
|
||||
|
||||
.slick-header-column.ui-state-default {
|
||||
height: 40px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
#datagrid .grid-header label {
|
||||
@@ -279,7 +279,7 @@ li {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.sc.cell-move-handle {
|
||||
.slick-cell.cell-move-handle {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
border-right: solid gray;
|
||||
@@ -291,15 +291,15 @@ li {
|
||||
background: #b6b9bd;
|
||||
}
|
||||
|
||||
.sr.selected .cell-move-handle {
|
||||
.slick-row.selected .cell-move-handle {
|
||||
background: #D5DC8D;
|
||||
}
|
||||
|
||||
.sr .cell-actions {
|
||||
.slick-row .cell-actions {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.sr.complete {
|
||||
.slick-row.complete {
|
||||
background-color: #DFD;
|
||||
color: #555;
|
||||
}
|
||||
@@ -332,7 +332,7 @@ li {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.sr.selected .cell-selection {
|
||||
.slick-row.selected .cell-selection {
|
||||
background-color: transparent; /* show default selected row background */
|
||||
}
|
||||
|
||||
@@ -350,9 +350,23 @@ li {
|
||||
|
||||
#datagrid .slick-header .slick-header-column.ui-state-default {
|
||||
color: #222222;
|
||||
padding: 4px 0 4px 6px;
|
||||
background-color: #e8e8e8;
|
||||
border-bottom: 1px solid black;
|
||||
padding: 4px 0 3px 6px;
|
||||
background-color: #E8E8E8;
|
||||
border-bottom: 1px solid silver !important;
|
||||
}
|
||||
|
||||
#datagrid .slick-header .slick-header-column.selected {
|
||||
background-color: #2C76B4;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
#datagrid .slick-header .slick-header-column .column-name {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.column-description {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.column-description {
|
||||
@@ -400,9 +414,11 @@ input.editor-checkbox:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Override selected row color */
|
||||
.sc.selected {
|
||||
background-color: #DEE8F1 !important;
|
||||
/* Remove active cell border */
|
||||
.slick-cell.active {
|
||||
border: 1px solid transparent;
|
||||
border-right: 1px dotted silver;
|
||||
border-bottom-color: silver;
|
||||
}
|
||||
|
||||
/* To highlight all newly inserted rows */
|
||||
@@ -430,9 +446,18 @@ input.editor-checkbox:focus {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* Override selected row color */
|
||||
#datagrid .slick-row .slick-cell.selected {
|
||||
background-color: #DEE8F1;
|
||||
}
|
||||
|
||||
/* color the first column */
|
||||
.sr .sc:first-child {
|
||||
background-color: #e8e8e8;
|
||||
#datagrid .slick-row .slick-cell.l0.r0 {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
#datagrid .slick-row .slick-cell.l0.r0.selected {
|
||||
background-color: #2C76B4;
|
||||
}
|
||||
|
||||
#datagrid div.slick-header.ui-state-default {
|
||||
@@ -453,6 +478,7 @@ input.editor-checkbox:focus {
|
||||
-ms-box-sizing: content-box;
|
||||
}
|
||||
|
||||
.sr.ui-widget-content {
|
||||
border-top: 1px solid silver;
|
||||
.select-all-icon {
|
||||
margin-left: 9px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
@@ -1,32 +1,40 @@
|
||||
define([
|
||||
'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'alertify',
|
||||
'pgadmin', 'backbone', 'backgrid', 'codemirror', 'pgadmin.misc.explain',
|
||||
'sources/selection/grid_selector', 'sources/selection/clipboard',
|
||||
'sources/selection/copy_data',
|
||||
'sources/selection/set_staged_rows', 'sources/sqleditor_utils',
|
||||
'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
|
||||
'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
|
||||
'codemirror/addon/selection/active-line', 'codemirror/addon/fold/foldcode',
|
||||
'codemirror/addon/fold/foldgutter', 'codemirror/addon/hint/show-hint',
|
||||
'codemirror/addon/hint/sql-hint', 'pgadmin.file_manager',
|
||||
'pgadmin-sqlfoldcode',
|
||||
'codemirror/addon/scroll/simplescrollbars',
|
||||
'codemirror/addon/dialog/dialog',
|
||||
'codemirror/addon/search/search',
|
||||
'codemirror/addon/search/searchcursor',
|
||||
'codemirror/addon/search/jump-to-line',
|
||||
'backgrid.sizeable.columns', 'slickgrid/slick.formatters',
|
||||
'slick.pgadmin.formatters', 'slickgrid/slick.editors',
|
||||
'slick.pgadmin.editors', 'slickgrid/plugins/slick.autotooltips',
|
||||
'slickgrid/plugins/slick.cellrangedecorator',
|
||||
'slickgrid/plugins/slick.cellrangeselector',
|
||||
'slickgrid/plugins/slick.cellselectionmodel',
|
||||
'slickgrid/plugins/slick.cellcopymanager',
|
||||
'slickgrid/plugins/slick.rowselectionmodel',
|
||||
'slickgrid/slick.grid'
|
||||
'jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
|
||||
'backbone', 'backgrid', 'codemirror', 'pgadmin.misc.explain',
|
||||
'sources/selection/grid_selector',
|
||||
'sources/selection/active_cell_capture',
|
||||
'sources/selection/clipboard',
|
||||
'sources/selection/copy_data',
|
||||
'sources/selection/range_selection_helper',
|
||||
'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
|
||||
'sources/selection/xcell_selection_model',
|
||||
'sources/selection/set_staged_rows',
|
||||
'sources/gettext', 'sources/sqleditor_utils',
|
||||
|
||||
'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
|
||||
'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
|
||||
'codemirror/addon/selection/active-line', 'codemirror/addon/fold/foldcode',
|
||||
'codemirror/addon/fold/foldgutter', 'codemirror/addon/hint/show-hint',
|
||||
'codemirror/addon/hint/sql-hint', 'pgadmin.file_manager',
|
||||
'pgadmin-sqlfoldcode',
|
||||
'codemirror/addon/scroll/simplescrollbars',
|
||||
'codemirror/addon/dialog/dialog',
|
||||
'codemirror/addon/search/search',
|
||||
'codemirror/addon/search/searchcursor',
|
||||
'codemirror/addon/search/jump-to-line',
|
||||
'backgrid.sizeable.columns', 'slickgrid/slick.formatters',
|
||||
'slick.pgadmin.formatters', 'slickgrid/slick.editors',
|
||||
'slick.pgadmin.editors', 'slickgrid/plugins/slick.autotooltips',
|
||||
'slickgrid/plugins/slick.cellrangedecorator',
|
||||
'slickgrid/plugins/slick.cellrangeselector',
|
||||
'slickgrid/plugins/slick.cellselectionmodel',
|
||||
'slickgrid/plugins/slick.cellcopymanager',
|
||||
'slickgrid/plugins/slick.rowselectionmodel',
|
||||
'slickgrid/slick.grid'
|
||||
], function(
|
||||
gettext, $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror,
|
||||
pgExplain, GridSelector, clipboard, copyData, setStagedRows, SqlEditorUtils
|
||||
$, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, GridSelector,
|
||||
ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
|
||||
XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils
|
||||
) {
|
||||
/* Return back, this has been called more than once */
|
||||
if (pgAdmin.SqlEditor)
|
||||
@@ -543,6 +551,8 @@ define([
|
||||
pos: c.pos,
|
||||
field: c.name,
|
||||
name: c.label,
|
||||
display_name: c.display_name,
|
||||
column_type: c.column_type,
|
||||
not_null: c.not_null,
|
||||
has_default_val: c.has_default_val
|
||||
};
|
||||
@@ -583,7 +593,7 @@ define([
|
||||
});
|
||||
|
||||
var gridSelector = new GridSelector();
|
||||
grid_columns = gridSelector.getColumnDefinitionsWithCheckboxes(grid_columns);
|
||||
grid_columns = gridSelector.getColumnDefinitions(grid_columns);
|
||||
|
||||
var grid_options = {
|
||||
editable: true,
|
||||
@@ -637,7 +647,8 @@ define([
|
||||
|
||||
var grid = new Slick.Grid($data_grid, collection, grid_columns, grid_options);
|
||||
grid.registerPlugin( new Slick.AutoTooltips({ enableForHeaderCells: false }) );
|
||||
grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow: false}));
|
||||
grid.registerPlugin(new ActiveCellCapture());
|
||||
grid.setSelectionModel(new XCellSelectionModel());
|
||||
grid.registerPlugin(gridSelector);
|
||||
|
||||
var editor_data = {
|
||||
@@ -698,41 +709,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
// Listener function for COPY/PASTE operation on grid
|
||||
grid.onKeyDown.subscribe(function (e, args) {
|
||||
var c = e.keyCode,
|
||||
ctrlDown = e.ctrlKey||e.metaKey; // Mac support
|
||||
|
||||
// (ctrlDown && c==67) return false // c
|
||||
// (ctrlDown && c==86) return false // v
|
||||
// (ctrlDown && c==88) return false // x
|
||||
|
||||
|
||||
if (!ctrlDown && !(c==67 || c==86 || c==88)) {
|
||||
return; // Not a copy paste opration
|
||||
}
|
||||
|
||||
var grid = args.grid, column_info, column_values, value,
|
||||
cell = args.cell, row = args.row;
|
||||
|
||||
// Copy operation (Only when if there is no row selected)
|
||||
// When user press `Ctrl + c` on selected cell
|
||||
if(ctrlDown && c==67) {
|
||||
// May be single cell is selected
|
||||
column_info = grid.getColumns()[cell]
|
||||
// Fetch current row data from grid
|
||||
column_values = grid.getDataItem(row, cell)
|
||||
// Get the value from cell
|
||||
value = column_values[column_info.pos] || '';
|
||||
// Copy this value to Clipboard
|
||||
if(value)
|
||||
clipboard.copyTextToClipboard(value);
|
||||
// Go to cell again
|
||||
grid.gotoCell(row, cell, false);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
grid.onKeyDown.subscribe(handleQueryOutputKeyboardEvent);
|
||||
|
||||
// Listener function which will be called when user updates existing rows
|
||||
grid.onCellChange.subscribe(function (e, args) {
|
||||
@@ -2100,6 +2077,8 @@ define([
|
||||
'cell': col_cell,
|
||||
'can_edit': self.can_edit,
|
||||
'type': type,
|
||||
'display_name': c.display_name,
|
||||
'column_type': col_type,
|
||||
'not_null': c.not_null,
|
||||
'has_default_val': c.has_default_val
|
||||
};
|
||||
|
||||
@@ -19,16 +19,27 @@ from pgadmin.utils.route import BaseTestGenerator
|
||||
|
||||
class TestVersionedTemplateLoader(BaseTestGenerator):
|
||||
scenarios = [
|
||||
("Test versioned template loader", dict())
|
||||
("Render a template when called", dict(scenario=1)),
|
||||
("Render a version 9.1 template when it is present", dict(scenario=2)),
|
||||
("Render a version 9.2 template when request for a higher version", dict(scenario=3)),
|
||||
("Render default version when version 9.0 was requested and only 9.1 and 9.2 are present", dict(scenario=4)),
|
||||
("Raise error when version is smaller than available templates", dict(scenario=5))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.loader = VersionedTemplateLoader(FakeApp())
|
||||
|
||||
def runTest(self):
|
||||
self.test_get_source_returns_a_template()
|
||||
self.test_get_source_when_the_version_is_9_1_returns_9_1_template()
|
||||
self.test_get_source_when_the_version_is_9_3_and_there_are_templates_for_9_2_and_9_1_returns_9_2_template()
|
||||
if self.scenario == 1:
|
||||
self.test_get_source_returns_a_template()
|
||||
if self.scenario == 2:
|
||||
self.test_get_source_when_the_version_is_9_1_returns_9_1_template()
|
||||
if self.scenario == 3:
|
||||
self.test_get_source_when_the_version_is_9_3_and_there_are_templates_for_9_2_and_9_1_returns_9_2_template()
|
||||
if self.scenario == 4:
|
||||
self.test_get_source_when_version_is_9_0_and_there_are_templates_for_9_1_and_9_2_returns_default_template()
|
||||
if self.scenario == 5:
|
||||
self.test_raise_not_found_exception_when_postgres_version_less_than_all_available_sql_templates()
|
||||
|
||||
def test_get_source_returns_a_template(self):
|
||||
expected_content = "Some SQL" \
|
||||
@@ -57,10 +68,12 @@ class TestVersionedTemplateLoader(BaseTestGenerator):
|
||||
self.assertEqual("Some 9.2 SQL", str(content).replace("\r",""))
|
||||
self.assertIn(sql_path, filename)
|
||||
|
||||
def test_get_source_when_the_version_is_9_0_and_there_are_templates_for_9_1_and_9_2_returns_default_template(self):
|
||||
def test_get_source_when_version_is_9_0_and_there_are_templates_for_9_1_and_9_2_returns_default_template(self):
|
||||
# For cross platform we join the SQL path (This solves the slashes issue)
|
||||
sql_path = os.path.join("some_feature", "sql", "default", "some_action_with_default.sql")
|
||||
content, filename, up_to_dateness = self.loader.get_source(None, "some_feature/sql/#90000#/some_action_with_default.sql")
|
||||
content, filename, up_to_dateness = self.loader.get_source(
|
||||
None,
|
||||
"some_feature/sql/#90000#/some_action_with_default.sql")
|
||||
|
||||
self.assertEqual("Some default SQL", str(content).replace("\r",""))
|
||||
self.assertIn(sql_path, filename)
|
||||
|
||||
@@ -89,6 +89,31 @@ class PgadminPage:
|
||||
def toggle_open_tree_item(self, tree_item_text):
|
||||
self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "']/../*[@class='aciTreeButton']").click()
|
||||
|
||||
def toggle_open_server(self, tree_item_text):
|
||||
def check_for_password_dialog_or_tree_open(driver):
|
||||
try:
|
||||
dialog = driver.find_element_by_id("frmPassword")
|
||||
except WebDriverException:
|
||||
dialog = None
|
||||
|
||||
try:
|
||||
database_node = driver.find_element_by_xpath("//*[@id='tree']//*[.='Databases']/../*[@class='aciTreeButton']")
|
||||
except WebDriverException:
|
||||
database_node = None
|
||||
|
||||
return dialog is not None or database_node is not None
|
||||
|
||||
self.toggle_open_tree_item(tree_item_text)
|
||||
self._wait_for("Waiting for password dialog or tree to open", check_for_password_dialog_or_tree_open)
|
||||
|
||||
try:
|
||||
self.driver.find_element_by_id("frmPassword")
|
||||
# Enter password here if needed
|
||||
self.click_modal_ok()
|
||||
except WebDriverException:
|
||||
return
|
||||
|
||||
|
||||
def find_by_xpath(self, xpath):
|
||||
return self.wait_for_element(lambda driver: driver.find_element_by_xpath(xpath))
|
||||
|
||||
|
||||
342
web/regression/javascript/selection/active_cell_capture_spec.js
Normal file
342
web/regression/javascript/selection/active_cell_capture_spec.js
Normal file
@@ -0,0 +1,342 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
define([
|
||||
'sources/selection/active_cell_capture',
|
||||
'sources/selection/range_selection_helper',
|
||||
], function (ActiveCellCapture, RangeSelectionHelper) {
|
||||
describe('ActiveCellCapture', function () {
|
||||
var grid, activeCellPlugin, getSelectedRangesSpy, setSelectedRangesSpy;
|
||||
var onHeaderClickFunction;
|
||||
var onClickFunction;
|
||||
var onColumnsResizedFunction;
|
||||
var onHeaderMouseEnterFunction;
|
||||
var onHeaderMouseLeaveFunction;
|
||||
|
||||
beforeEach(function () {
|
||||
getSelectedRangesSpy = jasmine.createSpy('getSelectedRangesSpy');
|
||||
setSelectedRangesSpy = jasmine.createSpy('setSelectedRangesSpy');
|
||||
grid = {
|
||||
getSelectionModel: function () {
|
||||
return {
|
||||
getSelectedRanges: getSelectedRangesSpy,
|
||||
setSelectedRanges: setSelectedRangesSpy,
|
||||
}
|
||||
},
|
||||
|
||||
getColumns: function () {
|
||||
return [
|
||||
{id: 'row-header-column'},
|
||||
{id: 'column-1'},
|
||||
{id: 'column-2'}
|
||||
]
|
||||
},
|
||||
|
||||
onDragEnd: jasmine.createSpyObj('onDragEnd', ['subscribe']),
|
||||
onActiveCellChanged: jasmine.createSpyObj('onActiveCellChanged', ['subscribe']),
|
||||
onHeaderClick: jasmine.createSpy('onHeaderClick'),
|
||||
onClick: jasmine.createSpy('onClick'),
|
||||
onKeyDown: jasmine.createSpyObj('onKeyDown', ['subscribe']),
|
||||
onColumnsResized: jasmine.createSpyObj('onColumnsResized', ['subscribe']),
|
||||
onHeaderMouseEnter: jasmine.createSpyObj('onHeaderMouseEnter', ['subscribe']),
|
||||
onHeaderMouseLeave: jasmine.createSpyObj('onHeaderMouseLeave', ['subscribe']),
|
||||
|
||||
getDataLength: function () { return 10 },
|
||||
|
||||
setActiveCell: jasmine.createSpy('setActiveCell'),
|
||||
getActiveCell: jasmine.createSpy('getActiveCell'),
|
||||
resetActiveCell: jasmine.createSpy('resetActiveCell'),
|
||||
};
|
||||
activeCellPlugin = new ActiveCellCapture();
|
||||
|
||||
grid.onHeaderClick.subscribe =
|
||||
jasmine.createSpy('subscribe').and.callFake(function (callback) {
|
||||
onHeaderClickFunction = callback.bind(activeCellPlugin);
|
||||
});
|
||||
|
||||
grid.onClick.subscribe =
|
||||
jasmine.createSpy('subscribe').and.callFake(function (callback) {
|
||||
onClickFunction = callback.bind(activeCellPlugin);
|
||||
});
|
||||
|
||||
grid.onColumnsResized.subscribe =
|
||||
jasmine.createSpy('subscribe').and.callFake(function (callback) {
|
||||
onColumnsResizedFunction = callback.bind(activeCellPlugin);
|
||||
});
|
||||
|
||||
grid.onHeaderMouseEnter.subscribe =
|
||||
jasmine.createSpy('subscribe').and.callFake(function (callback) {
|
||||
onHeaderMouseEnterFunction = callback.bind(activeCellPlugin);
|
||||
});
|
||||
|
||||
grid.onHeaderMouseLeave.subscribe =
|
||||
jasmine.createSpy('subscribe').and.callFake(function (callback) {
|
||||
onHeaderMouseLeaveFunction = callback.bind(activeCellPlugin);
|
||||
});
|
||||
|
||||
activeCellPlugin.init(grid);
|
||||
});
|
||||
|
||||
describe('onHeaderClickHandler', function () {
|
||||
describe('when no ranges are selected', function () {
|
||||
beforeEach(function () {
|
||||
getSelectedRangesSpy.and.returnValue([]);
|
||||
onHeaderClickFunction({}, {column: {pos: 1}});
|
||||
grid.getActiveCell.and.returnValue(null);
|
||||
});
|
||||
|
||||
it('should set the active cell', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(0, 2);
|
||||
});
|
||||
|
||||
it('should not change the selected ranges', function () {
|
||||
expect(setSelectedRangesSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when only one column is already selected', function () {
|
||||
beforeEach(function () {
|
||||
getSelectedRangesSpy.and.returnValue([RangeSelectionHelper.rangeForColumn(grid, 2)]);
|
||||
grid.getActiveCell.and.returnValue({cell: 2, row: 0});
|
||||
});
|
||||
|
||||
describe('when a different column is clicked', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderClickFunction({}, {column: {pos: 4}})
|
||||
});
|
||||
|
||||
it('should set the active cell to the newly clicked columns top cell', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(0, 5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the same column is clicked', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderClickFunction({}, {column: {pos: 1}});
|
||||
});
|
||||
|
||||
it('should reset the active cell', function () {
|
||||
expect(grid.resetActiveCell).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when mouse is inside the header row', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderMouseEnterFunction({}, {});
|
||||
});
|
||||
|
||||
describe('when user finishes resizing the selected column', function () {
|
||||
var eventSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
eventSpy = jasmine.createSpyObj('event', ['stopPropagation']);
|
||||
onColumnsResizedFunction({}, {grid: grid});
|
||||
onHeaderClickFunction(eventSpy, {column: {pos: 1}});
|
||||
});
|
||||
|
||||
it('should not deselect the current selected column', function () {
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
expect(grid.resetActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should prevent further event propagation', function () {
|
||||
expect(eventSpy.stopPropagation).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when the user clicks the resized column header', function () {
|
||||
beforeEach(function () {
|
||||
eventSpy.stopPropagation.calls.reset();
|
||||
onHeaderClickFunction(eventSpy, {column: {pos: 2}});
|
||||
});
|
||||
|
||||
it('should change the active cell', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(0, 3);
|
||||
expect(grid.resetActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow further event propagation', function () {
|
||||
expect(eventSpy.stopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when mouse is outside the header row', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderMouseEnterFunction({}, {});
|
||||
onHeaderMouseLeaveFunction({}, {});
|
||||
});
|
||||
|
||||
describe('when user finishes resizing the selected column', function () {
|
||||
beforeEach(function () {
|
||||
onColumnsResizedFunction({}, {grid: grid});
|
||||
});
|
||||
|
||||
it('should not deselect the current selected column', function () {
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
expect(grid.resetActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when the user clicks another column header', function () {
|
||||
var eventSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
eventSpy = jasmine.createSpyObj('event', ['stopPropagation']);
|
||||
onHeaderMouseEnterFunction({}, {});
|
||||
onHeaderClickFunction(eventSpy, {column: {pos: 3}});
|
||||
});
|
||||
|
||||
it('should change the active cell', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(0, 4);
|
||||
expect(grid.resetActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow further event propagation', function () {
|
||||
expect(eventSpy.stopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when three non-consecutive columns are selected', function () {
|
||||
beforeEach(function () {
|
||||
getSelectedRangesSpy.and.returnValue([
|
||||
RangeSelectionHelper.rangeForColumn(grid, 10),
|
||||
RangeSelectionHelper.rangeForColumn(grid, 6),
|
||||
RangeSelectionHelper.rangeForColumn(grid, 22),
|
||||
]);
|
||||
grid.getActiveCell.and.returnValue({cell: 22, row: 0});
|
||||
});
|
||||
|
||||
describe('when the third column is clicked (thereby deselecting it)', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderClickFunction({}, {column: {pos: 21}})
|
||||
});
|
||||
|
||||
it('should set the active cell to the second column', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(0, 6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the second column is clicked (thereby deselecting it)', function () {
|
||||
beforeEach(function () {
|
||||
onHeaderClickFunction({}, {column: {pos: 5}})
|
||||
});
|
||||
|
||||
it('should not set the active cell', function () {
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClick', function () {
|
||||
describe('when the target is a random cell in the grid', function () {
|
||||
it('should not change the active cell', function () {
|
||||
onClickFunction({}, {row: 1, cell: 2});
|
||||
grid.getActiveCell.and.returnValue(null);
|
||||
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the target is the row header', function () {
|
||||
describe('when no rows are selected', function () {
|
||||
beforeEach(function () {
|
||||
getSelectedRangesSpy.and.returnValue([]);
|
||||
onClickFunction({}, {row: 1, cell: 0});
|
||||
grid.getActiveCell.and.returnValue(null);
|
||||
});
|
||||
|
||||
it('changes the active cell', function () {
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(1, 1);
|
||||
});
|
||||
|
||||
it('should not change the selected ranges', function () {
|
||||
expect(setSelectedRangesSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is one cell selected', function () {
|
||||
beforeEach(function () {
|
||||
grid.getActiveCell.and.returnValue({row: 4, cell: 5});
|
||||
getSelectedRangesSpy.and.returnValue([
|
||||
new Slick.Range(4, 5)
|
||||
]);
|
||||
});
|
||||
|
||||
it('sets the active cell', function () {
|
||||
onClickFunction({}, {row: 4, cell: 0});
|
||||
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(4, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is one row selected', function () {
|
||||
beforeEach(function () {
|
||||
grid.getActiveCell.and.returnValue({row: 3, cell: 1});
|
||||
getSelectedRangesSpy.and.returnValue([
|
||||
RangeSelectionHelper.rangeForRow(grid, 3)
|
||||
]);
|
||||
});
|
||||
|
||||
it('resets the selected row', function () {
|
||||
onClickFunction({}, {row: 3, cell: 0});
|
||||
|
||||
expect(grid.resetActiveCell).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when we select a different row', function () {
|
||||
it('should change the active cell', function () {
|
||||
onClickFunction({}, {row: 9, cell: 0});
|
||||
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(9, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are 2 rows selected', function () {
|
||||
beforeEach(function () {
|
||||
grid.getActiveCell.and.returnValue({row: 3, cell: 1});
|
||||
getSelectedRangesSpy.and.returnValue([
|
||||
RangeSelectionHelper.rangeForRow(grid, 5),
|
||||
RangeSelectionHelper.rangeForRow(grid, 3)
|
||||
]);
|
||||
});
|
||||
|
||||
describe('when the last selected row is clicked again', function () {
|
||||
it('should change the active cell to the first selected row', function () {
|
||||
onClickFunction({}, {row: 3, cell: 0});
|
||||
|
||||
expect(grid.setActiveCell).toHaveBeenCalledWith(5, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the first selected row is clicked again', function () {
|
||||
it('should not change the active cell', function () {
|
||||
onClickFunction({}, {row: 5, cell: 0});
|
||||
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and the editable new row', function () {
|
||||
beforeEach(function () {
|
||||
onClickFunction({}, {row: 10, cell: 0})
|
||||
});
|
||||
it('does not select the row', function () {
|
||||
expect(grid.setActiveCell).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,85 +1,120 @@
|
||||
define(
|
||||
["jquery",
|
||||
"underscore",
|
||||
"slickgrid/slick.grid",
|
||||
"sources/selection/column_selector",
|
||||
"slickgrid/slick.rowselectionmodel",
|
||||
"slickgrid"
|
||||
"sources/selection/active_cell_capture",
|
||||
"sources/selection/grid_selector",
|
||||
'sources/selection/xcell_selection_model',
|
||||
|
||||
"slickgrid",
|
||||
'sources/slickgrid/pgslick.cellrangedecorator',
|
||||
'sources/slickgrid/pgslick.cellrangeselector',
|
||||
"slickgrid/slick.grid",
|
||||
],
|
||||
function ($, _, SlickGrid, ColumnSelector, RowSelectionModel, Slick) {
|
||||
function ($, _, ColumnSelector, ActiveCellCapture, GridSelector, XCellSelectionModel) {
|
||||
var KEY_RIGHT = 39;
|
||||
var KEY_LEFT = 37;
|
||||
var KEY_UP = 38;
|
||||
var KEY_DOWN = 40;
|
||||
|
||||
var Slick = window.Slick;
|
||||
var SlickGrid = Slick.Grid;
|
||||
|
||||
describe("ColumnSelector", function () {
|
||||
var container, data, columns, options;
|
||||
beforeEach(function () {
|
||||
container = $("<div></div>");
|
||||
container.height(9999);
|
||||
container.width(9999);
|
||||
|
||||
data = [{'some-column-name': 'first value', 'second column': 'second value'}];
|
||||
data = [{
|
||||
'some-column-name': 'first value',
|
||||
'second column': 'second value',
|
||||
'third column': 'nonselectable value'
|
||||
}, {
|
||||
'some-column-name': 'row 1 - first value',
|
||||
'second column': 'row 1 - second value',
|
||||
'third column': 'row 1 - nonselectable value'
|
||||
}];
|
||||
|
||||
columns = [
|
||||
{
|
||||
id: 'row-header-column',
|
||||
name: 'row header column name',
|
||||
selectable: false,
|
||||
display_name: 'row header column name',
|
||||
column_type: 'text'
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
name: 'some-column-name',
|
||||
pos: 0,
|
||||
display_name: 'some-column-name',
|
||||
column_type: 'text'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'second column',
|
||||
pos: 1,
|
||||
display_name: 'second column',
|
||||
column_type: 'json'
|
||||
},
|
||||
{
|
||||
id: 'third-column-id',
|
||||
name: 'third column',
|
||||
pos: 2,
|
||||
display_name: 'third column',
|
||||
column_type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'some-non-selectable-column',
|
||||
selectable: false
|
||||
selectable: false,
|
||||
pos: 3,
|
||||
display_name: 'some-non-selectable-column',
|
||||
column_type: 'numeric'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
describe("when a column is not selectable", function () {
|
||||
it("does not create a checkbox for selecting the column", function () {
|
||||
var checkboxColumn = {
|
||||
name: 'some-column-name-4',
|
||||
selectable: false
|
||||
};
|
||||
columns.push(checkboxColumn);
|
||||
|
||||
setupGrid(columns);
|
||||
|
||||
expect(container.find('.slick-header-columns input').length).toBe(2)
|
||||
});
|
||||
});
|
||||
|
||||
it("renders a checkbox in the column header", function () {
|
||||
setupGrid(columns);
|
||||
|
||||
expect(container.find('.slick-header-columns input').length).toBe(2)
|
||||
];
|
||||
});
|
||||
|
||||
it("displays the name of the column", function () {
|
||||
setupGrid(columns);
|
||||
|
||||
expect($(container.find('.slick-header-columns .slick-column-name')[0]).text())
|
||||
expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
|
||||
.toContain('some-column-name');
|
||||
expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
|
||||
.toContain('text');
|
||||
expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
|
||||
.toContain('second column');
|
||||
expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
|
||||
.toContain('json');
|
||||
});
|
||||
|
||||
it("preserves the other attributes of column definitions", function () {
|
||||
var columnSelector = new ColumnSelector();
|
||||
var selectableColumns = columnSelector.getColumnDefinitionsWithCheckboxes(columns);
|
||||
var selectableColumns = columnSelector.getColumnDefinitions(columns);
|
||||
|
||||
expect(selectableColumns[0].id).toBe('1');
|
||||
expect(selectableColumns[1].id).toBe('1');
|
||||
});
|
||||
|
||||
describe("selecting columns", function () {
|
||||
var grid, rowSelectionModel;
|
||||
describe("with ActiveCellCapture, CellSelectionModel, and GridSelector: selecting columns", function () {
|
||||
var grid, cellSelectionModel;
|
||||
beforeEach(function () {
|
||||
var columnSelector = new ColumnSelector();
|
||||
columns = columnSelector.getColumnDefinitionsWithCheckboxes(columns);
|
||||
columns = columnSelector.getColumnDefinitions(columns);
|
||||
data = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
|
||||
data.push({
|
||||
'some-column-name': 'some-value-' + i,
|
||||
'second column': 'second value ' + i,
|
||||
'third column': 'third value ' + i,
|
||||
'fourth column': 'fourth value ' + i,
|
||||
});
|
||||
}
|
||||
grid = new SlickGrid(container, data, columns, options);
|
||||
grid = new SlickGrid(container, data, columns);
|
||||
|
||||
rowSelectionModel = new RowSelectionModel();
|
||||
grid.setSelectionModel(rowSelectionModel);
|
||||
grid.registerPlugin(new ActiveCellCapture());
|
||||
cellSelectionModel = new XCellSelectionModel();
|
||||
grid.setSelectionModel(cellSelectionModel);
|
||||
|
||||
grid.registerPlugin(columnSelector);
|
||||
grid.invalidate();
|
||||
@@ -92,57 +127,118 @@ define(
|
||||
|
||||
describe("when the user clicks a column header", function () {
|
||||
it("selects the column", function () {
|
||||
container.find('.slick-header-column')[0].click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
container.find('.slick-header-column:contains(some-column-name)').click();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expectOnlyTheFirstColumnToBeSelected(selectedRanges);
|
||||
});
|
||||
|
||||
it("toggles a selected class to the header cell", function () {
|
||||
container.find('.slick-header-column:contains(second column)').click();
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBe(true);
|
||||
|
||||
container.find('.slick-header-column:contains(second column)').click();
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user clicks additional column headers", function () {
|
||||
describe("when the user clicks an additional column header", function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-header-column')[1].click();
|
||||
container.find('.slick-header-column:contains(some-column-name)').click();
|
||||
container.find('.slick-header-column:contains(second column)').click();
|
||||
});
|
||||
|
||||
it("selects additional columns", function () {
|
||||
container.find('.slick-header-column')[0].click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(2);
|
||||
var column1 = selectedRanges[0];
|
||||
|
||||
expect(selectedRanges.length).toEqual(2);
|
||||
expect(column1.fromCell).toBe(1);
|
||||
expect(column1.toCell).toBe(1);
|
||||
|
||||
var column2 = selectedRanges[1];
|
||||
expect(column2.fromCell).toBe(2);
|
||||
expect(column2.toCell).toBe(2);
|
||||
});
|
||||
|
||||
expect(column2.fromCell).toBe(0);
|
||||
expect(column2.toCell).toBe(0);
|
||||
describe("and presses shift + right-arrow", function () {
|
||||
beforeEach(function () {
|
||||
pressShiftArrow(KEY_RIGHT);
|
||||
});
|
||||
|
||||
it("keeps the last column selected", function () {
|
||||
expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
|
||||
it("grows the selection to the right", function () {
|
||||
var selectedRange = cellSelectionModel.getSelectedRanges()[0];
|
||||
expect(selectedRange.fromCell).toBe(2);
|
||||
expect(selectedRange.toCell).toBe(3);
|
||||
expect(selectedRange.fromRow).toBe(0);
|
||||
expect(selectedRange.toRow).toBe(9);
|
||||
});
|
||||
|
||||
it("keeps selected class on columns 2 and 3", function () {
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBe(true);
|
||||
expect($(container.find('.slick-header-column:contains(third column)')).hasClass('selected'))
|
||||
.toBe(true);
|
||||
expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user deselects the last selected column header", function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-header-column:contains(second column)').click();
|
||||
});
|
||||
|
||||
describe("and presses shift + right-arrow", function () {
|
||||
it("first and second columns are selected", function () {
|
||||
pressShiftArrow(KEY_RIGHT);
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
expect(selectedRanges[0].fromCell).toBe(1);
|
||||
expect(selectedRanges[0].toCell).toBe(2);
|
||||
expect(selectedRanges[0].fromRow).toBe(0);
|
||||
expect(selectedRanges[0].toRow).toBe(9);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user clicks a column header checkbox", function () {
|
||||
describe("when the user clicks a column header description", function () {
|
||||
it("selects the column", function () {
|
||||
container.find('.slick-header-columns input')[0].click();
|
||||
container.find('.slick-header-columns span.column-description:contains(some-column-name)').click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expectOnlyTheFirstColumnToBeSelected(selectedRanges);
|
||||
});
|
||||
|
||||
it("checks the checkbox", function () {
|
||||
container.find('.slick-header-column')[1].click();
|
||||
expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeTruthy();
|
||||
it("toggles a selected class to the header cell", function () {
|
||||
container.find('.slick-header-column span.column-description:contains(second column)').click();
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBe(true);
|
||||
|
||||
container.find('.slick-header-column span.column-description:contains(second column)').click();
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a row is selected", function () {
|
||||
beforeEach(function () {
|
||||
var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
|
||||
rowSelectionModel.setSelectedRanges(selectedRanges);
|
||||
cellSelectionModel.setSelectedRanges(selectedRanges);
|
||||
});
|
||||
|
||||
it("deselects the row", function () {
|
||||
container.find('.slick-header-column')[1].click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
@@ -152,7 +248,7 @@ define(
|
||||
expect(column.toCell).toBe(1);
|
||||
expect(column.fromRow).toBe(0);
|
||||
expect(column.toRow).toBe(9);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("clicking a second time", function () {
|
||||
@@ -160,14 +256,9 @@ define(
|
||||
container.find('.slick-header-column')[1].click();
|
||||
});
|
||||
|
||||
it("unchecks checkbox", function () {
|
||||
container.find('.slick-header-column')[1].click();
|
||||
expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeFalsy();
|
||||
});
|
||||
|
||||
it("deselects the column", function () {
|
||||
container.find('.slick-header-column')[1].click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toEqual(0);
|
||||
})
|
||||
@@ -176,7 +267,7 @@ define(
|
||||
describe("when the column is not selectable", function () {
|
||||
it("does not select the column", function () {
|
||||
$(container.find('.slick-header-column:contains(some-non-selectable-column)')).click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toEqual(0);
|
||||
});
|
||||
@@ -187,49 +278,142 @@ define(
|
||||
container.find('.slick-header-column')[1].click();
|
||||
});
|
||||
|
||||
it("unchecks the checkbox", function () {
|
||||
rowSelectionModel.setSelectedRanges([]);
|
||||
it("removes selected class from header", function () {
|
||||
cellSelectionModel.setSelectedRanges([]);
|
||||
|
||||
expect($(container.find('.slick-header-columns input')[1])
|
||||
.is(':checked')).toBeFalsy();
|
||||
expect($(container.find('.slick-header-column')[1]).hasClass('selected'))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a non-column range was already selected", function () {
|
||||
beforeEach(function () {
|
||||
var selectedRanges = [new Slick.Range(0, 0, 1, 0)];
|
||||
rowSelectionModel.setSelectedRanges(selectedRanges);
|
||||
var selectedRanges = [new Slick.Range(0, 0, 2, 0)];
|
||||
cellSelectionModel.setSelectedRanges(selectedRanges);
|
||||
});
|
||||
|
||||
it("deselects the non-column range", function () {
|
||||
container.find('.slick-header-column')[0].click();
|
||||
container.find('.slick-header-column:contains(some-column-name)').click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expectOnlyTheFirstColumnToBeSelected(selectedRanges);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a column is selected', function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-header-column:contains(some-column-name)').click();
|
||||
});
|
||||
|
||||
describe('when the user click a cell on the current range', function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-cell.l1.r1')[1].click();
|
||||
});
|
||||
|
||||
it('column is deselected', function () {
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
var column = selectedRanges[0];
|
||||
|
||||
expect(column.fromCell).toBe(1);
|
||||
expect(column.toCell).toBe(1);
|
||||
expect(column.fromRow).toBe(1);
|
||||
expect(column.toRow).toBe(1);
|
||||
});
|
||||
|
||||
it('keep select class on column header', function () {
|
||||
expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
|
||||
.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user click a cell outside the current range', function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-cell.l2.r2')[2].click();
|
||||
});
|
||||
|
||||
it('column is deselected', function () {
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
var column = selectedRanges[0];
|
||||
|
||||
expect(column.fromCell).toBe(2);
|
||||
expect(column.toCell).toBe(2);
|
||||
expect(column.fromRow).toBe(2);
|
||||
expect(column.toRow).toBe(2);
|
||||
});
|
||||
|
||||
it('remove select class on "some-column-name" column header', function () {
|
||||
expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
|
||||
.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user click in a row header', function () {
|
||||
beforeEach(function () {
|
||||
var selectedRanges = [new Slick.Range(1, 1, 1, 3)];
|
||||
cellSelectionModel.setSelectedRanges(selectedRanges);
|
||||
});
|
||||
|
||||
it('column is deselected', function () {
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
var column = selectedRanges[0];
|
||||
|
||||
expect(column.fromCell).toBe(1);
|
||||
expect(column.toCell).toBe(3);
|
||||
expect(column.fromRow).toBe(1);
|
||||
expect(column.toRow).toBe(1);
|
||||
});
|
||||
|
||||
it('no column should have the class "selected"', function () {
|
||||
expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var setupGrid = function (columns) {
|
||||
function setupGrid(columns) {
|
||||
var columnSelector = new ColumnSelector();
|
||||
columns = columnSelector.getColumnDefinitionsWithCheckboxes(columns);
|
||||
columns = columnSelector.getColumnDefinitions(columns);
|
||||
var grid = new SlickGrid(container, data, columns, options);
|
||||
|
||||
var rowSelectionModel = new RowSelectionModel();
|
||||
grid.setSelectionModel(rowSelectionModel);
|
||||
var cellSelectionModel = new XCellSelectionModel();
|
||||
grid.setSelectionModel(cellSelectionModel);
|
||||
|
||||
grid.registerPlugin(columnSelector);
|
||||
grid.invalidate();
|
||||
};
|
||||
}
|
||||
|
||||
function expectOnlyTheFirstColumnToBeSelected(selectedRanges) {
|
||||
var row = selectedRanges[0];
|
||||
|
||||
expect(selectedRanges.length).toEqual(1);
|
||||
expect(row.fromCell).toBe(0);
|
||||
expect(row.toCell).toBe(0);
|
||||
expect(row.fromCell).toBe(1);
|
||||
expect(row.toCell).toBe(1);
|
||||
expect(row.fromRow).toBe(0);
|
||||
expect(row.toRow).toBe(9);
|
||||
}
|
||||
|
||||
function pressShiftArrow(keyCode) {
|
||||
var pressEvent = new $.Event("keydown");
|
||||
pressEvent.shiftKey = true;
|
||||
pressEvent.ctrlKey = false;
|
||||
pressEvent.altKey = false;
|
||||
pressEvent.which = keyCode;
|
||||
|
||||
$(container.find('.grid-canvas')).trigger(pressEvent);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,21 +1,38 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
define(
|
||||
["jquery",
|
||||
"slickgrid/slick.grid",
|
||||
"slickgrid/slick.rowselectionmodel",
|
||||
"sources/selection/xcell_selection_model",
|
||||
"sources/selection/copy_data",
|
||||
"sources/selection/clipboard",
|
||||
"sources/selection/range_selection_helper"
|
||||
],
|
||||
function ($, SlickGrid, RowSelectionModel, copyData, clipboard, RangeSelectionHelper) {
|
||||
function ($, SlickGrid, XCellSelectionModel, copyData, clipboard, RangeSelectionHelper) {
|
||||
describe('copyData', function () {
|
||||
var grid, sqlEditor;
|
||||
var grid, sqlEditor, gridContainer, buttonPasteRow;
|
||||
|
||||
beforeEach(function () {
|
||||
var data = [[1, "leopord", "12"],
|
||||
[2, "lion", "13"],
|
||||
[3, "puma", "9"]];
|
||||
|
||||
var columns = [{
|
||||
var columns = [
|
||||
{
|
||||
id: 'row-header-column',
|
||||
name: 'row header column name',
|
||||
selectable: false,
|
||||
display_name: 'row header column name',
|
||||
column_type: 'text'
|
||||
},
|
||||
{
|
||||
name: "id",
|
||||
pos: 0,
|
||||
label: "id<br> numeric",
|
||||
@@ -39,17 +56,18 @@ define(
|
||||
}
|
||||
]
|
||||
;
|
||||
var gridContainer = $("<div id='grid'></div>");
|
||||
gridContainer = $("<div id='grid'></div>");
|
||||
$("body").append(gridContainer);
|
||||
$("body").append("<button id='btn-paste-row' disabled></button>");
|
||||
buttonPasteRow = $("<button id='btn-paste-row' disabled></button>");
|
||||
$("body").append(buttonPasteRow);
|
||||
grid = new Slick.Grid("#grid", data, columns, {});
|
||||
grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow: false}));
|
||||
grid.setSelectionModel(new XCellSelectionModel());
|
||||
sqlEditor = {slickgrid: grid};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$("body").remove('#grid');
|
||||
$("body").remove('#btn-paste-row');
|
||||
gridContainer.remove();
|
||||
buttonPasteRow.remove();
|
||||
});
|
||||
|
||||
describe("when rows are selected", function () {
|
||||
@@ -83,8 +101,8 @@ define(
|
||||
|
||||
describe("when a column is selected", function () {
|
||||
beforeEach(function () {
|
||||
var firstColumn = new Slick.Range(0, 0, 2, 0);
|
||||
grid.getSelectionModel().setSelectedRanges([firstColumn])
|
||||
var firstDataColumn = RangeSelectionHelper.rangeForColumn(grid, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([firstDataColumn])
|
||||
});
|
||||
|
||||
it("copies text to the clipboard", function () {
|
||||
@@ -108,9 +126,11 @@ define(
|
||||
});
|
||||
|
||||
describe("when the user can edit the grid", function () {
|
||||
it("disables the paste row button", function () {
|
||||
beforeEach(function () {
|
||||
copyData.apply(_.extend({can_edit: true}, sqlEditor));
|
||||
});
|
||||
|
||||
it("disables the paste row button", function () {
|
||||
expect($("#btn-paste-row").prop('disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
define(["jquery",
|
||||
"underscore",
|
||||
"slickgrid/slick.grid",
|
||||
"slickgrid/slick.rowselectionmodel",
|
||||
"sources/selection/xcell_selection_model",
|
||||
"sources/selection/grid_selector"
|
||||
],
|
||||
function ($, _, SlickGrid, RowSelectionModel, GridSelector) {
|
||||
function ($, _, SlickGrid, XCellSelectionModel, GridSelector) {
|
||||
describe("GridSelector", function () {
|
||||
var container, data, columns, gridSelector, rowSelectionModel;
|
||||
var container, data, columns, gridSelector, xCellSelectionModel;
|
||||
|
||||
beforeEach(function () {
|
||||
container = $("<div></div>");
|
||||
@@ -14,13 +14,15 @@ define(["jquery",
|
||||
columns = [{
|
||||
id: '1',
|
||||
name: 'some-column-name',
|
||||
pos: 0
|
||||
}, {
|
||||
id: '2',
|
||||
name: 'second column',
|
||||
pos: 1
|
||||
}];
|
||||
|
||||
gridSelector = new GridSelector();
|
||||
columns = gridSelector.getColumnDefinitionsWithCheckboxes(columns);
|
||||
columns = gridSelector.getColumnDefinitions(columns);
|
||||
|
||||
data = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
@@ -28,8 +30,8 @@ define(["jquery",
|
||||
}
|
||||
var grid = new SlickGrid(container, data, columns);
|
||||
|
||||
rowSelectionModel = new RowSelectionModel();
|
||||
grid.setSelectionModel(rowSelectionModel);
|
||||
xCellSelectionModel = new XCellSelectionModel();
|
||||
grid.setSelectionModel(xCellSelectionModel);
|
||||
|
||||
grid.registerPlugin(gridSelector);
|
||||
grid.invalidate();
|
||||
@@ -48,11 +50,7 @@ define(["jquery",
|
||||
expect(leftmostColumn.id).toBe('row-header-column');
|
||||
});
|
||||
|
||||
it("renders checkboxes for selecting columns", function () {
|
||||
expect(container.find('[data-test="output-column-header"] input').length).toBe(2)
|
||||
});
|
||||
|
||||
it("renders a checkbox for selecting all the cells", function () {
|
||||
it("renders a button for selecting all the cells", function () {
|
||||
expect(container.find("[title='Select/Deselect All']").length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -60,7 +58,7 @@ define(["jquery",
|
||||
it("selects the whole grid", function () {
|
||||
container.find("[title='Select/Deselect All']").parent().click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = xCellSelectionModel.getSelectedRanges();
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
var selectedRange = selectedRanges[0];
|
||||
expect(selectedRange.fromCell).toBe(1);
|
||||
@@ -69,25 +67,19 @@ define(["jquery",
|
||||
expect(selectedRange.toRow).toBe(9);
|
||||
});
|
||||
|
||||
it("checks the checkbox", function () {
|
||||
it("adds selected class", function () {
|
||||
container.find("[title='Select/Deselect All']").parent().click();
|
||||
|
||||
expect($(container.find("[data-id='checkbox-select-all']")).is(':checked')).toBeTruthy();
|
||||
})
|
||||
expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the main checkbox in the corner gets selected", function () {
|
||||
it("unchecks all the columns", function () {
|
||||
container.find("[title='Select/Deselect All']").click();
|
||||
|
||||
expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeFalsy();
|
||||
expect($(container.find('.slick-header-columns input')[2]).is(':checked')).toBeFalsy();
|
||||
});
|
||||
describe("when the select all button in the corner gets selected", function () {
|
||||
|
||||
it("selects all the cells", function () {
|
||||
container.find("[title='Select/Deselect All']").click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = xCellSelectionModel.getSelectedRanges();
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
var selectedRange = selectedRanges[0];
|
||||
expect(selectedRange.fromCell).toBe(1);
|
||||
@@ -96,7 +88,7 @@ define(["jquery",
|
||||
expect(selectedRange.toRow).toBe(9);
|
||||
});
|
||||
|
||||
describe("when the main checkbox in the corner gets deselected", function () {
|
||||
describe("when the select all button in the corner gets deselected", function () {
|
||||
beforeEach(function () {
|
||||
container.find("[title='Select/Deselect All']").click();
|
||||
});
|
||||
@@ -104,7 +96,7 @@ define(["jquery",
|
||||
it("deselects all the cells", function () {
|
||||
container.find("[title='Select/Deselect All']").click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = xCellSelectionModel.getSelectedRanges();
|
||||
expect(selectedRanges.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -114,11 +106,10 @@ define(["jquery",
|
||||
container.find("[title='Select/Deselect All']").click();
|
||||
});
|
||||
|
||||
it("unchecks the main checkbox", function () {
|
||||
var ranges = [new Slick.Range(0, 0, 0, 1)];
|
||||
rowSelectionModel.setSelectedRanges(ranges);
|
||||
it("removes the selected class", function () {
|
||||
container.find("[title='Select/Deselect All']").parent().click();
|
||||
|
||||
expect($(container.find("[title='Select/Deselect All']")).is(':checked')).toBeFalsy();
|
||||
expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,7 +61,6 @@ define(['sources/selection/range_boundary_navigator'], function (rangeBoundaryNa
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("#mapDimensionBoundaryUnion", function () {
|
||||
it("returns a list of the results of the callback", function () {
|
||||
var rangeBounds = [[0, 1], [3, 3]];
|
||||
@@ -132,20 +131,27 @@ define(['sources/selection/range_boundary_navigator'], function (rangeBoundaryNa
|
||||
});
|
||||
|
||||
it("returns csv for the provided ranges", function () {
|
||||
|
||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
|
||||
|
||||
expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
|
||||
});
|
||||
|
||||
describe("when no cells are selected", function () {
|
||||
it("should return an empty string", function () {
|
||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
|
||||
|
||||
expect(csvResult).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is an extra column with checkboxes", function () {
|
||||
|
||||
beforeEach(function () {
|
||||
columnDefinitions = [{name: 'not-a-data-column'}, {name: 'id', pos: 0}, {name: 'animal', pos: 1}, {
|
||||
name: 'size',
|
||||
pos: 2
|
||||
}];
|
||||
ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)];
|
||||
|
||||
});
|
||||
|
||||
it("returns csv for the columns with data", function () {
|
||||
@@ -153,6 +159,13 @@ define(['sources/selection/range_boundary_navigator'], function (rangeBoundaryNa
|
||||
|
||||
expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
|
||||
});
|
||||
describe("when no cells are selected", function () {
|
||||
it("should return an empty string", function () {
|
||||
var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
|
||||
|
||||
expect(csvResult).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
define([
|
||||
'jquery',
|
||||
'slickgrid/slick.grid',
|
||||
'sources/selection/range_selection_helper'
|
||||
], function ($, SlickGrid, RangeSelectionHelper) {
|
||||
describe("RangeSelectionHelper utility functions", function () {
|
||||
var grid;
|
||||
beforeEach(function () {
|
||||
var container, data, columns, options;
|
||||
container = $("<div></div>");
|
||||
container.height(9999);
|
||||
|
||||
columns = [{
|
||||
id: '1',
|
||||
name: 'some-column-name',
|
||||
pos: 0
|
||||
}, {
|
||||
id: 'second-column-id',
|
||||
name: 'second column',
|
||||
pos: 1
|
||||
}];
|
||||
|
||||
data = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
|
||||
}
|
||||
|
||||
grid = new SlickGrid(container, data, columns, options);
|
||||
grid.invalidate();
|
||||
});
|
||||
|
||||
describe("#getIndexesOfCompleteRows", function () {
|
||||
describe("when selected ranges are not rows", function () {
|
||||
it("returns an empty array", function () {
|
||||
var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
|
||||
|
||||
expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
|
||||
.toEqual([]);
|
||||
});
|
||||
});
|
||||
describe("when selected range", function () {
|
||||
describe("is a single row", function () {
|
||||
it("returns an array with one index", function () {
|
||||
var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
|
||||
|
||||
expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
|
||||
.toEqual([1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("is multiple rows", function () {
|
||||
it("returns an array of each row's index", function () {
|
||||
var multipleRowRange = [
|
||||
RangeSelectionHelper.rangeForRow(grid, 0),
|
||||
RangeSelectionHelper.rangeForRow(grid, 3),
|
||||
RangeSelectionHelper.rangeForRow(grid, 2),
|
||||
];
|
||||
|
||||
var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
|
||||
indexesOfCompleteRows.sort();
|
||||
expect(indexesOfCompleteRows).toEqual([0, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("contains a multi row selection", function () {
|
||||
it("returns an array of each individual row's index", function () {
|
||||
var multipleRowRange = [
|
||||
new Slick.Range(3, 0, 5, 1)
|
||||
];
|
||||
|
||||
var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
|
||||
indexesOfCompleteRows.sort();
|
||||
expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
|
||||
});
|
||||
|
||||
describe("and also contains a selection that is not a row", function () {
|
||||
it("returns an array of only the complete rows' indexes", function () {
|
||||
var multipleRowRange = [
|
||||
new Slick.Range(8, 1, 9, 1),
|
||||
new Slick.Range(3, 0, 5, 1)
|
||||
];
|
||||
|
||||
var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
|
||||
indexesOfCompleteRows.sort();
|
||||
expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -2,26 +2,37 @@ define(
|
||||
["jquery",
|
||||
"underscore",
|
||||
"slickgrid/slick.grid",
|
||||
"sources/selection/active_cell_capture",
|
||||
"sources/selection/row_selector",
|
||||
"slickgrid/slick.rowselectionmodel",
|
||||
'sources/selection/xcell_selection_model',
|
||||
|
||||
"slickgrid",
|
||||
'sources/slickgrid/pgslick.cellrangedecorator',
|
||||
'sources/slickgrid/pgslick.cellrangeselector',
|
||||
],
|
||||
function ($, _, SlickGrid, RowSelector, RowSelectionModel, Slick) {
|
||||
function ($, _, SlickGrid, ActiveCellCapture, RowSelector, XCellSelectionModel, Slick) {
|
||||
var KEY_RIGHT = 39;
|
||||
var KEY_LEFT = 37;
|
||||
var KEY_UP = 38;
|
||||
var KEY_DOWN = 40;
|
||||
describe("RowSelector", function () {
|
||||
var container, data, columnDefinitions, grid, rowSelectionModel;
|
||||
var container, data, columnDefinitions, grid, cellSelectionModel;
|
||||
|
||||
beforeEach(function () {
|
||||
container = $("<div></div>");
|
||||
container.height(9999);
|
||||
container.width(9999);
|
||||
|
||||
columnDefinitions = [{
|
||||
id: '1',
|
||||
name: 'some-column-name',
|
||||
selectable: true
|
||||
selectable: true,
|
||||
pos: 0
|
||||
}, {
|
||||
id: '2',
|
||||
name: 'second column',
|
||||
selectable: true
|
||||
selectable: true,
|
||||
pos: 1
|
||||
}];
|
||||
|
||||
var rowSelector = new RowSelector();
|
||||
@@ -29,11 +40,13 @@ define(
|
||||
for (var i = 0; i < 10; i++) {
|
||||
data.push(['some-value-' + i, 'second value ' + i]);
|
||||
}
|
||||
columnDefinitions = rowSelector.getColumnDefinitionsWithCheckboxes(columnDefinitions);
|
||||
columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
|
||||
grid = new SlickGrid(container, data, columnDefinitions);
|
||||
|
||||
rowSelectionModel = new RowSelectionModel();
|
||||
grid.setSelectionModel(rowSelectionModel);
|
||||
grid.registerPlugin(new ActiveCellCapture());
|
||||
cellSelectionModel = new XCellSelectionModel();
|
||||
grid.setSelectionModel(cellSelectionModel);
|
||||
|
||||
grid.registerPlugin(rowSelector);
|
||||
grid.invalidate();
|
||||
|
||||
@@ -53,9 +66,9 @@ define(
|
||||
expect(leftmostColumn.selectable).toBe(false);
|
||||
});
|
||||
|
||||
it("renders a checkbox the leftmost column", function () {
|
||||
expect(container.find('.sr').length).toBe(11);
|
||||
expect(container.find('.sr .sc:first-child input[type="checkbox"]').length).toBe(10);
|
||||
it("renders a span on the leftmost column", function () {
|
||||
expect(container.find('.slick-row').length).toBe(10);
|
||||
expect(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]').length).toBe(10);
|
||||
});
|
||||
|
||||
it("preserves the other attributes of column definitions", function () {
|
||||
@@ -64,44 +77,168 @@ define(
|
||||
});
|
||||
|
||||
describe("selecting rows", function () {
|
||||
describe("when the user clicks a row header checkbox", function () {
|
||||
describe("when the user clicks a row header span", function () {
|
||||
it("selects the row", function () {
|
||||
container.find('.sr .sc:first-child input[type="checkbox"]')[0].click();
|
||||
container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[0].click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expectOnlyTheFirstRowToBeSelected(selectedRanges);
|
||||
});
|
||||
|
||||
it("checks the checkbox", function () {
|
||||
container.find('.sr .sc:first-child input[type="checkbox"]')[5].click();
|
||||
it("add selected class to parent of the span", function () {
|
||||
container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[5].click();
|
||||
|
||||
expect($(container.find('.sr .sc:first-child input[type="checkbox"]')[5])
|
||||
.is(':checked')).toBeTruthy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[5])
|
||||
.hasClass('selected')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user clicks a row header", function () {
|
||||
it("selects the row", function () {
|
||||
container.find('.sr .sc:first-child')[0].click();
|
||||
beforeEach(function () {
|
||||
container.find('.slick-row .slick-cell:first-child')[1].click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
expectOnlyTheFirstRowToBeSelected(selectedRanges);
|
||||
});
|
||||
it("selects the row", function () {
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
var row = selectedRanges[0];
|
||||
|
||||
expect(selectedRanges.length).toEqual(1);
|
||||
expect(row.fromCell).toBe(1);
|
||||
expect(row.toCell).toBe(2);
|
||||
expect(row.fromRow).toBe(1);
|
||||
expect(row.toRow).toBe(1);
|
||||
});
|
||||
|
||||
it("checks the checkbox", function () {
|
||||
container.find('.sr .sc:first-child')[7].click();
|
||||
it("add selected class to parent of the span", function () {
|
||||
|
||||
expect($(container.find('.sr .sc:first-child input[type="checkbox"]')[7])
|
||||
.is(':checked')).toBeTruthy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[1])
|
||||
.hasClass('selected')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("when the user clicks again the same row header", function () {
|
||||
it("add selected class to parent of the span", function () {
|
||||
container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1].click();
|
||||
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[1])
|
||||
.hasClass('selected')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and presses shift + down-arrow", function () {
|
||||
beforeEach(function () {
|
||||
pressShiftArrow(KEY_DOWN);
|
||||
});
|
||||
|
||||
it("keeps the last row selected", function () {
|
||||
expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
|
||||
it("grows the selection down", function () {
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
var row = selectedRanges[0];
|
||||
|
||||
expect(selectedRanges.length).toEqual(1);
|
||||
expect(row.fromCell).toBe(1);
|
||||
expect(row.toCell).toBe(2);
|
||||
expect(row.fromRow).toBe(1);
|
||||
expect(row.toRow).toBe(2);
|
||||
});
|
||||
|
||||
it("keeps selected class on rows 1 and 2", function () {
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[0])
|
||||
.hasClass('selected')).toBeFalsy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[1])
|
||||
.hasClass('selected')).toBeTruthy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[2])
|
||||
.hasClass('selected')).toBeTruthy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child ')[3])
|
||||
.hasClass('selected')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user clicks a cell on the current range', function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-cell.l1.r1')[5].click();
|
||||
});
|
||||
|
||||
it('row gets deselected', function () {
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
var newSelection = selectedRanges[0];
|
||||
|
||||
expect(newSelection.fromCell).toBe(1);
|
||||
expect(newSelection.fromRow).toBe(5);
|
||||
expect(newSelection.toCell).toBe(1);
|
||||
expect(newSelection.toRow).toBe(5);
|
||||
});
|
||||
|
||||
it('keep select class on row header', function () {
|
||||
expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
|
||||
.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user clicks a cell outside the current range', function () {
|
||||
beforeEach(function () {
|
||||
container.find('.slick-cell.l2.r2')[2].click();
|
||||
});
|
||||
|
||||
it('row gets deselected', function () {
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
|
||||
var newSelection = selectedRanges[0];
|
||||
|
||||
expect(newSelection.fromCell).toBe(2);
|
||||
expect(newSelection.fromRow).toBe(2);
|
||||
expect(newSelection.toCell).toBe(2);
|
||||
expect(newSelection.toRow).toBe(2);
|
||||
});
|
||||
|
||||
it('remove select class on "some-column-name" column header', function () {
|
||||
expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
|
||||
.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user has a column selected', function () {
|
||||
beforeEach(function () {
|
||||
var selectedRanges = [new Slick.Range(0, 1, 9, 1)];
|
||||
cellSelectionModel.setSelectedRanges(selectedRanges);
|
||||
});
|
||||
|
||||
it('no row should have the class "selected"', function () {
|
||||
expect($(container.find('.slick-cell.l0.r0')[0]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[1]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[3]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[4]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
|
||||
.toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user clicks multiple row headers", function () {
|
||||
it("selects another row", function () {
|
||||
container.find('.sr .sc:first-child')[4].click();
|
||||
container.find('.sr .sc:first-child')[0].click();
|
||||
container.find('.slick-row .slick-cell:first-child')[4].click();
|
||||
container.find('.slick-row .slick-cell:first-child')[0].click();
|
||||
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expect(selectedRanges.length).toEqual(2);
|
||||
|
||||
var row1 = selectedRanges[0];
|
||||
@@ -117,49 +254,59 @@ define(
|
||||
describe("when a column was already selected", function () {
|
||||
beforeEach(function () {
|
||||
var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
|
||||
rowSelectionModel.setSelectedRanges(selectedRanges);
|
||||
cellSelectionModel.setSelectedRanges(selectedRanges);
|
||||
});
|
||||
|
||||
it("deselects the column", function () {
|
||||
container.find('.sr .sc:first-child')[0].click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
container.find('.slick-row .slick-cell:first-child')[0].click();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expectOnlyTheFirstRowToBeSelected(selectedRanges);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the row is deselected through setSelectedRanges", function () {
|
||||
beforeEach(function () {
|
||||
container.find('.sr .sc:first-child')[4].click();
|
||||
container.find('.slick-row .slick-cell:first-child')[4].click();
|
||||
});
|
||||
|
||||
it("should uncheck the checkbox", function () {
|
||||
rowSelectionModel.setSelectedRanges([]);
|
||||
it("should remove the selected class", function () {
|
||||
cellSelectionModel.setSelectedRanges([]);
|
||||
|
||||
expect($(container.find('.sr .sc:first-child input[type="checkbox"]')[4])
|
||||
.is(':checked')).toBeFalsy();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[4])
|
||||
.hasClass('selected')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("click a second time", function () {
|
||||
beforeEach(function () {
|
||||
container.find('.sr .sc:first-child')[1].click();
|
||||
container.find('.slick-row .slick-cell:first-child')[1].click();
|
||||
});
|
||||
|
||||
it("unchecks checkbox", function () {
|
||||
container.find('.sr .sc:first-child')[1].click();
|
||||
expect($(container.find('.sr .sc:first-child input[type="checkbox"]')[1])
|
||||
.is(':checked')).toBeFalsy();
|
||||
it("removes the selected class", function () {
|
||||
container.find('.slick-row .slick-cell:first-child')[1].click();
|
||||
expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1])
|
||||
.hasClass('selected')).toBeFalsy();
|
||||
});
|
||||
|
||||
it("unselects the row", function () {
|
||||
container.find('.sr .sc:first-child')[1].click();
|
||||
var selectedRanges = rowSelectionModel.getSelectedRanges();
|
||||
container.find('.slick-row .slick-cell:first-child')[1].click();
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toEqual(0);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function pressShiftArrow(keyCode) {
|
||||
var pressEvent = new $.Event("keydown");
|
||||
pressEvent.shiftKey = true;
|
||||
pressEvent.ctrlKey = false;
|
||||
pressEvent.altKey = false;
|
||||
pressEvent.which = keyCode;
|
||||
|
||||
$(container.find('.grid-canvas')).trigger(pressEvent);
|
||||
}
|
||||
});
|
||||
|
||||
function expectOnlyTheFirstRowToBeSelected(selectedRanges) {
|
||||
|
||||
@@ -12,131 +12,137 @@ define([
|
||||
"underscore",
|
||||
"sources/selection/set_staged_rows",
|
||||
], function ($, _, SetStagedRows) {
|
||||
describe('when no full rows are selected', function () {
|
||||
var sqlEditorObj, deleteButton, copyButton;
|
||||
describe('set_staged_rows', function () {
|
||||
var sqlEditorObj, gridSpy, deleteButton, copyButton, selectionSpy;
|
||||
beforeEach(function () {
|
||||
var gridSpy = jasmine.createSpyObj('gridSpy', ['getData', 'getCellNode']);
|
||||
gridSpy = jasmine.createSpyObj('gridSpy', ['getData', 'getCellNode', 'getColumns']);
|
||||
gridSpy.getData.and.returnValue([
|
||||
{0: 'one', 1: 'two', __temp_PK: '123'},
|
||||
{0: 'three', 1: 'four', __temp_PK: '456'},
|
||||
{0: 'five', 1: 'six', __temp_PK: '789'},
|
||||
{0: 'seven', 1: 'eight', __temp_PK: '432'}
|
||||
]);
|
||||
gridSpy.getColumns.and.returnValue([
|
||||
{
|
||||
pos: 0,
|
||||
selectable: true,
|
||||
}, {
|
||||
pos: 1,
|
||||
selectable: true,
|
||||
}
|
||||
]);
|
||||
|
||||
selectionSpy = jasmine.createSpyObj('selectionSpy', ['setSelectedRows', 'getSelectedRanges']);
|
||||
|
||||
deleteButton = $('<button id="btn-delete-row"></button>');
|
||||
copyButton = $('<button id="btn-copy-row"></button>');
|
||||
|
||||
sqlEditorObj = {
|
||||
grid: gridSpy,
|
||||
editor: {
|
||||
handler: {
|
||||
data_store: {
|
||||
staged_rows: {1: [1, 2]}
|
||||
}
|
||||
staged_rows: {'456': {}}
|
||||
},
|
||||
can_edit: false
|
||||
}
|
||||
}
|
||||
},
|
||||
keys: null,
|
||||
selection: selectionSpy,
|
||||
columns: [
|
||||
{
|
||||
name: 'a pk column',
|
||||
pos: 0
|
||||
},
|
||||
{
|
||||
name: 'some column',
|
||||
pos: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
$('body').append(deleteButton);
|
||||
$('body').append(copyButton);
|
||||
deleteButton.prop('disabled', false);
|
||||
copyButton.prop('disabled', false);
|
||||
|
||||
deleteButton.prop('disabled', true);
|
||||
copyButton.prop('disabled', true);
|
||||
|
||||
selectionSpy = jasmine.createSpyObj('selectionSpy', [
|
||||
'setSelectedRows',
|
||||
'getSelectedRanges',
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
copyButton.remove();
|
||||
deleteButton.remove();
|
||||
});
|
||||
describe('when no full rows are selected', function () {
|
||||
describe('when nothing is selected', function () {
|
||||
beforeEach(function () {
|
||||
selectionSpy.getSelectedRanges.and.returnValue([]);
|
||||
sqlEditorObj.selection = selectionSpy;
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
});
|
||||
|
||||
describe('when getSelectedRows is not present in the selection model', function () {
|
||||
beforeEach(function () {
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
});
|
||||
it('should disable the delete row button', function () {
|
||||
expect($('#btn-delete-row').prop('disabled')).toBeTruthy();
|
||||
it('should disable the delete row button', function () {
|
||||
expect($('#btn-delete-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should disable the copy row button', function () {
|
||||
expect($('#btn-copy-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should clear staged rows', function () {
|
||||
expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable the copy row button', function () {
|
||||
expect($('#btn-copy-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
describe('when there is a selection', function () {
|
||||
beforeEach(function () {
|
||||
var range = {
|
||||
fromCell: 0,
|
||||
toCell: 0,
|
||||
fromRow: 1,
|
||||
toRow: 1,
|
||||
};
|
||||
|
||||
it('should clear staged rows', function () {
|
||||
expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({});
|
||||
selectionSpy.getSelectedRanges.and.returnValue([range]);
|
||||
sqlEditorObj.selection = selectionSpy;
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
});
|
||||
|
||||
it('should disable the delete row button', function () {
|
||||
expect($('#btn-delete-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should disable the copy row button', function () {
|
||||
expect($('#btn-copy-row').prop('disabled')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should clear staged rows', function () {
|
||||
expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when getSelectedRows is present in the selection model', function () {
|
||||
describe('when 2 full rows are selected', function () {
|
||||
beforeEach(function () {
|
||||
var selectionSpy = jasmine.createSpyObj('selectionSpy', ['getSelectedRows', 'setSelectedRows']);
|
||||
selectionSpy.getSelectedRows.and.returnValue([]);
|
||||
sqlEditorObj.selection = selectionSpy;
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
});
|
||||
|
||||
it('should disable the delete row button', function () {
|
||||
expect($('#btn-delete-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should disable the copy row button', function () {
|
||||
expect($('#btn-copy-row').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should clear staged rows', function () {
|
||||
expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when 2 full rows are selected', function () {
|
||||
describe('when getSelectedRows is present in the selection model', function () {
|
||||
var sqlEditorObj, gridSpy, deleteButton, copyButton;
|
||||
beforeEach(function () {
|
||||
gridSpy = jasmine.createSpyObj('gridSpy', ['getData', 'getCellNode']);
|
||||
gridSpy.getData.and.returnValue([
|
||||
{0: 'one', 1: 'two', __temp_PK: '123'},
|
||||
{0: 'three', 1: 'four', __temp_PK: '456'},
|
||||
{0: 'five', 1: 'six', __temp_PK: '789'},
|
||||
{0: 'seven', 1: 'eight', __temp_PK: '432'}
|
||||
]);
|
||||
|
||||
var selectionSpy = jasmine.createSpyObj('selectionSpy', ['getSelectedRows', 'setSelectedRows']);
|
||||
selectionSpy.getSelectedRows.and.returnValue([1, 2]);
|
||||
|
||||
deleteButton = $('<button id="btn-delete-row"></button>');
|
||||
copyButton = $('<button id="btn-copy-row"></button>');
|
||||
|
||||
sqlEditorObj = {
|
||||
grid: gridSpy,
|
||||
editor: {
|
||||
handler: {
|
||||
data_store: {
|
||||
staged_rows: {'456': {}}
|
||||
},
|
||||
can_edit: false
|
||||
}
|
||||
},
|
||||
keys: null,
|
||||
selection: selectionSpy,
|
||||
columns: [
|
||||
{
|
||||
name: 'a pk column',
|
||||
pos: 0
|
||||
},
|
||||
{
|
||||
name: 'some column',
|
||||
pos: 1
|
||||
}
|
||||
]
|
||||
var range1 = {
|
||||
fromCell: 0,
|
||||
toCell: 1,
|
||||
fromRow: 1,
|
||||
toRow: 1,
|
||||
};
|
||||
var range2 = {
|
||||
fromCell: 0,
|
||||
toCell: 1,
|
||||
fromRow: 2,
|
||||
toRow: 2,
|
||||
};
|
||||
|
||||
$('body').append(deleteButton);
|
||||
$('body').append(copyButton);
|
||||
|
||||
deleteButton.prop('disabled', true);
|
||||
copyButton.prop('disabled', true);
|
||||
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
copyButton.remove();
|
||||
deleteButton.remove();
|
||||
selectionSpy.getSelectedRanges.and.returnValue([range1, range2]);
|
||||
sqlEditorObj.selection = selectionSpy;
|
||||
});
|
||||
|
||||
describe('when table does not have primary keys', function () {
|
||||
@@ -181,7 +187,6 @@ define([
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
expect(sqlEditorObj.selection.setSelectedRows).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('selected rows missing primary key', function () {
|
||||
@@ -203,7 +208,6 @@ define([
|
||||
SetStagedRows.call(sqlEditorObj, {}, {});
|
||||
expect(sqlEditorObj.selection.setSelectedRows).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when the selected row is a new row', function () {
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
define([
|
||||
'sources/selection/xcell_selection_model',
|
||||
"slickgrid/slick.grid",
|
||||
'slickgrid',
|
||||
], function (XCellSelectionModel, SlickGrid, Slick) {
|
||||
describe('XCellSelectionModel', function () {
|
||||
var KEY_RIGHT = 39;
|
||||
var KEY_LEFT = 37;
|
||||
var KEY_UP = 38;
|
||||
var KEY_DOWN = 40;
|
||||
|
||||
var container, grid;
|
||||
var oldWindowParent = window.parent;
|
||||
|
||||
beforeEach(function () {
|
||||
window.parent = window;
|
||||
|
||||
var columns = [{
|
||||
id: 'row-header-column',
|
||||
name: 'row header column name',
|
||||
selectable: false,
|
||||
}, {
|
||||
id: '1',
|
||||
name: 'some-column-name',
|
||||
pos: 0
|
||||
}, {
|
||||
id: 'second-column-id',
|
||||
name: 'second column',
|
||||
pos: 1
|
||||
}, {
|
||||
id: 'third-column-id',
|
||||
name: 'third column',
|
||||
pos: 2
|
||||
},
|
||||
];
|
||||
|
||||
var data = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
'some-column-name': 'some-value-' + i,
|
||||
'second column': 'second value ' + i,
|
||||
'third column': 'third value ' + i,
|
||||
'fourth column': 'fourth value ' + i,
|
||||
});
|
||||
}
|
||||
container = $("<div></div>");
|
||||
container.height(9999);
|
||||
container.width(9999);
|
||||
|
||||
grid = new SlickGrid(container, data, columns);
|
||||
grid.setSelectionModel(new XCellSelectionModel());
|
||||
$("body").append(container);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
grid.destroy();
|
||||
container.remove();
|
||||
window.parent = oldWindowParent;
|
||||
});
|
||||
|
||||
describe('handleKeyDown', function () {
|
||||
describe('when we press a random key', function () {
|
||||
it('should not change the range', function () {
|
||||
var range = new Slick.Range(1, 2);
|
||||
grid.setActiveCell(1, 2);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressKey(72);
|
||||
|
||||
expect(grid.getSelectionModel().getSelectedRanges()[0]).toEqual(range);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when we press an arrow key ', function () {
|
||||
it('should select the cell to the right', function () {
|
||||
var range = new Slick.Range(1, 2);
|
||||
grid.setActiveCell(1, 2);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressKey(KEY_RIGHT);
|
||||
|
||||
expectOneSelectedRange(1, 3, 1, 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when we press shift', function () {
|
||||
describe('and we press an arrow key', function () {
|
||||
var scrollColumnIntoViewSpy, scrollRowIntoViewSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
scrollColumnIntoViewSpy = spyOn(grid, 'scrollColumnIntoView');
|
||||
scrollRowIntoViewSpy = spyOn(grid, 'scrollRowIntoView');
|
||||
});
|
||||
|
||||
describe('the right arrow', function () {
|
||||
describe('when a cell is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(1, 1);
|
||||
grid.setActiveCell(1, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
});
|
||||
|
||||
it('increases the range by one to the right', function () {
|
||||
expectOneSelectedRange(1, 1, 1, 2);
|
||||
});
|
||||
|
||||
it('should scroll the next column into view', function () {
|
||||
expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
|
||||
expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('pressing right again grows the range right', function () {
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 1, 3);
|
||||
});
|
||||
|
||||
it('then pressing left keeps the original selection', function () {
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 1, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a column is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(0, 1, 9, 1);
|
||||
grid.setActiveCell(0, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
});
|
||||
|
||||
it('increases the range by one column to the right', function () {
|
||||
expectOneSelectedRange(0, 1, 9, 2);
|
||||
});
|
||||
|
||||
it('should scroll the next column into view', function () {
|
||||
expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
|
||||
expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('the left arrow', function () {
|
||||
describe('when a cell is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(1, 3);
|
||||
grid.setActiveCell(1, 3);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
});
|
||||
|
||||
it('increases the range by one to the left', function () {
|
||||
expectOneSelectedRange(1, 2, 1, 3);
|
||||
});
|
||||
|
||||
it('should scroll previous column into view', function () {
|
||||
expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
|
||||
expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('pressing left again grows the range the left', function () {
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 1, 3);
|
||||
});
|
||||
|
||||
it('then pressing right keeps the original selection', function () {
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
|
||||
expectOneSelectedRange(1, 3, 1, 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a column is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(0, 2, 9, 2);
|
||||
grid.setActiveCell(0, 2);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
});
|
||||
|
||||
it('increases the range by one column to the left', function () {
|
||||
expectOneSelectedRange(0, 1, 9, 2);
|
||||
});
|
||||
|
||||
it('should scroll previous column into view', function () {
|
||||
expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(1);
|
||||
expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('the up arrow', function () {
|
||||
describe('when a cell is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(2, 2);
|
||||
grid.setActiveCell(2, 2);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
});
|
||||
|
||||
it('increases the range by one up', function () {
|
||||
expectOneSelectedRange(1, 2, 2, 2);
|
||||
});
|
||||
|
||||
it('should scroll the row above into view', function () {
|
||||
expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
|
||||
expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('pressing up again grows the range up', function () {
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
|
||||
expectOneSelectedRange(0, 2, 2, 2);
|
||||
});
|
||||
|
||||
it('then pressing down keeps the original selection', function () {
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
|
||||
expectOneSelectedRange(2, 2, 2, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a row is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(2, 1, 2, 3);
|
||||
grid.setActiveCell(2, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
});
|
||||
|
||||
it('increases the range by one row up', function () {
|
||||
expectOneSelectedRange(1, 1, 2, 3);
|
||||
});
|
||||
|
||||
it('should scroll the row above into view', function () {
|
||||
expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
|
||||
expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('the down arrow', function () {
|
||||
describe('when a cell is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(2, 2);
|
||||
grid.setActiveCell(2, 2);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
});
|
||||
|
||||
it('increases the range by one down', function () {
|
||||
expectOneSelectedRange(2, 2, 3, 2);
|
||||
});
|
||||
|
||||
it('should scroll the row below into view', function () {
|
||||
expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
|
||||
expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('pressing down again grows the range down', function () {
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
|
||||
expectOneSelectedRange(2, 2, 4, 2);
|
||||
});
|
||||
|
||||
it('then pressing up keeps the original selection', function () {
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
|
||||
expectOneSelectedRange(2, 2, 2, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a row is selected', function () {
|
||||
beforeEach(function () {
|
||||
var range = new Slick.Range(2, 1, 2, 3);
|
||||
grid.setActiveCell(2, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
});
|
||||
|
||||
it('increases the range by one row down', function () {
|
||||
expectOneSelectedRange(2, 1, 3, 3);
|
||||
});
|
||||
|
||||
it('should scroll the row below into view', function () {
|
||||
expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
|
||||
expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rectangular selection works', function () {
|
||||
|
||||
it('in the down-and-rightward direction', function () {
|
||||
var range = new Slick.Range(1, 1);
|
||||
grid.setActiveCell(1, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 4, 3);
|
||||
});
|
||||
|
||||
it('in the up-and-leftward direction', function () {
|
||||
var range = new Slick.Range(4, 3);
|
||||
grid.setActiveCell(4, 3);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 4, 3);
|
||||
});
|
||||
|
||||
it('in the up-and-rightward direction', function () {
|
||||
var range = new Slick.Range(4, 1);
|
||||
grid.setActiveCell(4, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_UP);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
pressShiftPlusKey(KEY_RIGHT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 4, 3);
|
||||
});
|
||||
|
||||
it('in the down-and-leftward direction', function () {
|
||||
var range = new Slick.Range(1, 3);
|
||||
grid.setActiveCell(1, 3);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_DOWN);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
|
||||
expectOneSelectedRange(1, 1, 4, 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and we are on an edge', function () {
|
||||
var range;
|
||||
|
||||
beforeEach(function () {
|
||||
range = new Slick.Range(2, 1);
|
||||
grid.setActiveCell(2, 1);
|
||||
grid.getSelectionModel().setSelectedRanges([range]);
|
||||
});
|
||||
|
||||
it('we still have the selected range before we arrowed', function () {
|
||||
pressShiftPlusKey(KEY_LEFT);
|
||||
expectOneSelectedRange(2, 1, 2, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when we drag and drop', function () {
|
||||
var dd;
|
||||
// We could not find an elegant way to calculate this value
|
||||
// after changing window size we saw this was a constant value
|
||||
var offsetLeftColumns = 100;
|
||||
|
||||
function cellTopPosition($cell, rowNumber) {
|
||||
return $(grid.getCanvasNode()).offset().top + $cell[0].scrollHeight * rowNumber;
|
||||
}
|
||||
|
||||
function cellLeftPosition(columnNumber) {
|
||||
return $(grid.getCanvasNode()).offset().left + offsetLeftColumns * columnNumber;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
var initialPosition = {cell: 3, row: 4};
|
||||
var $cell = $($('.slick-cell.l3')[initialPosition.row]);
|
||||
var event = {
|
||||
target: $cell,
|
||||
isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
|
||||
isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
|
||||
stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
|
||||
};
|
||||
|
||||
dd = {
|
||||
grid: grid,
|
||||
startX: cellLeftPosition(initialPosition.cell),
|
||||
startY: cellTopPosition($cell, initialPosition.row)
|
||||
};
|
||||
|
||||
grid.onDragStart.notify(dd, event, grid);
|
||||
});
|
||||
|
||||
describe('when the drop happens outside of the grid', function () {
|
||||
beforeEach(function () {
|
||||
var $cell = $($('.slick-cell.l1')[1]);
|
||||
var finalPosition = {cell: 1, row: 1};
|
||||
|
||||
var event = {
|
||||
target: $cell,
|
||||
isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
|
||||
isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
|
||||
stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
|
||||
|
||||
pageX: cellLeftPosition(finalPosition.cell),
|
||||
pageY: cellTopPosition($cell, finalPosition.row)
|
||||
};
|
||||
|
||||
grid.onDrag.notify(dd, event, grid);
|
||||
$(window).mouseup();
|
||||
});
|
||||
it('should call handleDragEnd from CellRangeSelector', function () {
|
||||
var newRange = grid.getSelectionModel().getSelectedRanges();
|
||||
|
||||
expect(newRange.length).toBe(1);
|
||||
|
||||
expect(newRange[0].fromCell).toBe(1);
|
||||
expect(newRange[0].toCell).toBe(3);
|
||||
expect(newRange[0].fromRow).toBe(1);
|
||||
expect(newRange[0].toRow).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when we mouse up and no drag and drop occured', function () {
|
||||
beforeEach(function () {
|
||||
grid.onDragEnd.notify = jasmine.createSpy('notify');
|
||||
grid.onDragEnd.notify.calls.reset();
|
||||
$(window).mouseup();
|
||||
});
|
||||
|
||||
it('do not notify onDragEnd', function () {
|
||||
expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSelectedRows', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
grid.getSelectionModel().setSelectedRanges(
|
||||
[new Slick.Range(1, 1, 1, 1)]
|
||||
);
|
||||
});
|
||||
|
||||
describe('when passed an empty array', function () {
|
||||
beforeEach(function () {
|
||||
grid.getSelectionModel().setSelectedRows([]);
|
||||
});
|
||||
it('clears ranges', function () {
|
||||
var newRanges = grid.getSelectionModel().getSelectedRanges();
|
||||
expect(newRanges.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets ranges corresponding to rows', function () {
|
||||
grid.getSelectionModel().setSelectedRows([0, 2]);
|
||||
|
||||
var selectedRanges = grid.getSelectionModel().getSelectedRanges();
|
||||
|
||||
expect(selectedRanges.length).toBe(2);
|
||||
expectRangeToMatch(selectedRanges[0], 0, 1, 0, 3);
|
||||
expectRangeToMatch(selectedRanges[1], 2, 1, 2, 3);
|
||||
});
|
||||
});
|
||||
|
||||
function pressKey(keyCode) {
|
||||
var pressEvent = new $.Event("keydown");
|
||||
pressEvent.which = keyCode;
|
||||
|
||||
$(container.find('.grid-canvas')).trigger(pressEvent);
|
||||
}
|
||||
|
||||
function pressShiftPlusKey(keyCode) {
|
||||
var pressEvent = new $.Event("keydown");
|
||||
pressEvent.shiftKey = true;
|
||||
pressEvent.which = keyCode;
|
||||
|
||||
$(container.find('.grid-canvas')).trigger(pressEvent);
|
||||
}
|
||||
|
||||
function expectOneSelectedRange(fromRow, fromCell, toRow, toCell) {
|
||||
var selectedRanges = grid.getSelectionModel().getSelectedRanges();
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
expectRangeToMatch(selectedRanges[0], fromRow, fromCell, toRow, toCell);
|
||||
}
|
||||
|
||||
function expectRangeToMatch(range, fromRow, fromCell, toRow, toCell) {
|
||||
expect(range.fromRow).toBe(fromRow);
|
||||
expect(range.toRow).toBe(toRow);
|
||||
expect(range.fromCell).toBe(fromCell);
|
||||
expect(range.toCell).toBe(toCell);
|
||||
}
|
||||
});
|
||||
})
|
||||
;
|
||||
77
web/regression/javascript/slickgrid/cell_selector_spec.js
Normal file
77
web/regression/javascript/slickgrid/cell_selector_spec.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
define(["jquery",
|
||||
"slickgrid/slick.grid",
|
||||
"sources/selection/xcell_selection_model",
|
||||
"sources/slickgrid/cell_selector",
|
||||
"sources/selection/range_selection_helper"
|
||||
],
|
||||
function ($, SlickGrid, XCellSelectionModel, CellSelector, RangeSelectionHelper) {
|
||||
describe("CellSelector", function () {
|
||||
var container, columns, cellSelector, data, cellSelectionModel, grid;
|
||||
beforeEach(function () {
|
||||
container = $("<div></div>");
|
||||
container.height(9999);
|
||||
container.width(9999);
|
||||
columns = [{
|
||||
name: 'some-column-name',
|
||||
}, {
|
||||
name: 'second column',
|
||||
}];
|
||||
|
||||
cellSelector = new CellSelector();
|
||||
|
||||
data = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
|
||||
}
|
||||
grid = new SlickGrid(container, data, columns);
|
||||
|
||||
cellSelectionModel = new XCellSelectionModel();
|
||||
grid.setSelectionModel(cellSelectionModel);
|
||||
|
||||
grid.registerPlugin(cellSelector);
|
||||
grid.invalidate();
|
||||
|
||||
$("body").append(container);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$("body").find(container).remove();
|
||||
});
|
||||
|
||||
describe("when the user clicks or tabs to a cell", function () {
|
||||
it("sets the selected range to that cell", function () {
|
||||
var row = 1, column = 0;
|
||||
$(container.find(".slick-row .slick-cell.l" + column)[row]).click();
|
||||
|
||||
var selectedRanges = cellSelectionModel.getSelectedRanges();
|
||||
expect(selectedRanges.length).toBe(1);
|
||||
expect(selectedRanges[0].fromCell).toBe(0);
|
||||
expect(selectedRanges[0].toCell).toBe(0);
|
||||
expect(selectedRanges[0].fromRow).toBe(1);
|
||||
expect(selectedRanges[0].toRow).toBe(1);
|
||||
});
|
||||
|
||||
it("deselects previously selected ranges", function () {
|
||||
var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
|
||||
var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
|
||||
row2Range);
|
||||
cellSelectionModel.setSelectedRanges(ranges);
|
||||
|
||||
var row = 4, column = 1;
|
||||
$(container.find(".slick-row .slick-cell.l" + column)[row]).click();
|
||||
|
||||
expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,143 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
define([
|
||||
'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
|
||||
'sources/selection/clipboard',
|
||||
'sources/selection/range_selection_helper',
|
||||
'sources/selection/xcell_selection_model',
|
||||
'slickgrid'
|
||||
],
|
||||
function (handleQueryOutputKeyboardEvent, clipboard, RangeSelectionHelper, XCellSelectionModel) {
|
||||
var Slick = window.Slick;
|
||||
|
||||
describe('#handleQueryOutputKeyboardEvent', function () {
|
||||
var event, view, grid, slickEvent;
|
||||
beforeEach(function () {
|
||||
event = {
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
which: -1,
|
||||
keyCode: -1,
|
||||
preventDefault: jasmine.createSpy('preventDefault')
|
||||
};
|
||||
|
||||
var data = [['', '0,0-cell-content', '0,1-cell-content'],
|
||||
['', '1,0-cell-content', '1,1-cell-content'],
|
||||
['', '2,0-cell-content', '2,1-cell-content']];
|
||||
var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
|
||||
pos: 2,
|
||||
name: 'secondColumn'
|
||||
}];
|
||||
grid = new Slick.Grid($('<div></div>'), data, columnDefinitions);
|
||||
grid.setSelectionModel(new XCellSelectionModel());
|
||||
|
||||
slickEvent = {
|
||||
grid: grid
|
||||
};
|
||||
|
||||
view = {};
|
||||
spyOn(clipboard, 'copyTextToClipboard');
|
||||
});
|
||||
|
||||
describe("when a range is selected", function () {
|
||||
beforeEach(function () {
|
||||
grid.getSelectionModel().setSelectedRanges([
|
||||
RangeSelectionHelper.rangeForRow(grid, 0),
|
||||
RangeSelectionHelper.rangeForRow(grid, 2),
|
||||
]);
|
||||
});
|
||||
|
||||
describe("pressing Command + C", function () {
|
||||
beforeEach(function () {
|
||||
event.metaKey = true;
|
||||
event.keyCode = 67;
|
||||
});
|
||||
|
||||
it("copies the cell content to the clipboard", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("pressing Ctrl + C", function () {
|
||||
beforeEach(function () {
|
||||
event.ctrlKey = true;
|
||||
event.keyCode = 67;
|
||||
});
|
||||
|
||||
it("copies the cell content to the clipboard", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
|
||||
});
|
||||
});
|
||||
|
||||
describe("pressing Command + A", function () {
|
||||
beforeEach(function () {
|
||||
event.metaKey = true;
|
||||
event.keyCode = 65;
|
||||
});
|
||||
|
||||
it("selects the entire grid to ranges", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
|
||||
expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pressing Ctrl + A", function () {
|
||||
beforeEach(function () {
|
||||
event.ctrlKey = true;
|
||||
event.keyCode = 65;
|
||||
});
|
||||
|
||||
it("selects the entire grid to ranges", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
|
||||
expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when no ranges are selected", function () {
|
||||
describe("pressing Command + A", function () {
|
||||
beforeEach(function () {
|
||||
event.metaKey = true;
|
||||
event.keyCode = 65;
|
||||
});
|
||||
|
||||
it("selects the entire grid to ranges", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
|
||||
expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pressing Ctrl + A", function () {
|
||||
beforeEach(function () {
|
||||
event.ctrlKey = true;
|
||||
event.keyCode = 65;
|
||||
});
|
||||
|
||||
it("selects the entire grid to ranges", function () {
|
||||
handleQueryOutputKeyboardEvent(event, slickEvent);
|
||||
|
||||
expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
|
||||
expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -36,7 +36,6 @@ require.config({
|
||||
'underscore.string': sourcesDir + 'vendor/underscore/underscore.string',
|
||||
'slickgrid': sourcesDir + 'vendor/slickgrid/slick.core',
|
||||
'slickgrid/slick.grid': sourcesDir + 'vendor/slickgrid/slick.grid',
|
||||
'slickgrid/slick.rowselectionmodel': sourcesDir + 'vendor/slickgrid/plugins/slick.rowselectionmodel',
|
||||
'translations': '/base/regression/javascript/fake_translations',
|
||||
'sources': sourcesDir + 'js',
|
||||
'browser': '/base/pgadmin/browser/static/js'
|
||||
@@ -58,11 +57,23 @@ require.config({
|
||||
],
|
||||
"exports": 'window.Slick.Grid'
|
||||
},
|
||||
"slickgrid/slick.rowselectionmodel": {
|
||||
"sources/slickgrid/pgslick.cellrangedecorator": {
|
||||
"deps": [
|
||||
"jquery"
|
||||
],
|
||||
"exports": 'window.Slick.RowSelectionModel'
|
||||
"exports": 'PGRowRangeDecorator'
|
||||
},
|
||||
"sources/slickgrid/pgslick.cellrangeselector": {
|
||||
"deps": [
|
||||
"jquery", "sources/slickgrid/pgslick.cellrangedecorator"
|
||||
],
|
||||
"exports": 'PGCellRangeSelector'
|
||||
},
|
||||
"sources/selection/xcell_selection_model": {
|
||||
"deps": [
|
||||
"jquery", "sources/slickgrid/pgslick.cellrangeselector"
|
||||
],
|
||||
"exports": 'XCellSelectionModel'
|
||||
},
|
||||
"backbone": {
|
||||
"deps": ['underscore', 'jquery'],
|
||||
|
||||
@@ -159,12 +159,14 @@ def create_table(server, db_name, table_name):
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
'''CREATE TABLE "%s" (some_column VARCHAR, value NUMERIC)''' %
|
||||
'''CREATE TABLE "%s" (some_column VARCHAR, value NUMERIC, details VARCHAR)''' %
|
||||
table_name)
|
||||
pg_cursor.execute(
|
||||
'''INSERT INTO "%s" VALUES ('Some-Name', 6)''' % table_name)
|
||||
'''INSERT INTO "%s" VALUES ('Some-Name', 6, 'some info')''' % table_name)
|
||||
pg_cursor.execute(
|
||||
'''INSERT INTO "%s" VALUES ('Some-Other-Name', 22)''' % table_name)
|
||||
'''INSERT INTO "%s" VALUES ('Some-Other-Name', 22, 'some other info')''' % table_name)
|
||||
pg_cursor.execute(
|
||||
'''INSERT INTO "%s" VALUES ('Yet-Another-Name', 14, 'cool info')''' % table_name)
|
||||
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pyperclip~=1.5.27
|
||||
selenium==3.3.1
|
||||
selenium==3.3.3
|
||||
testscenarios==0.5.0
|
||||
testtools==2.0.0
|
||||
traceback2==1.4.0
|
||||
|
||||
Reference in New Issue
Block a user