diff --git a/modules/backend/widgets/Table.php b/modules/backend/widgets/Table.php index ed6f30c92..b86e7d0f8 100644 --- a/modules/backend/widgets/Table.php +++ b/modules/backend/widgets/Table.php @@ -63,6 +63,7 @@ class Table extends WidgetBase { $this->addCss('css/table.css', 'core'); $this->addJs('js/table.js', 'core'); + $this->addJs('js/table.helper.navigation.js', 'core'); $this->addJs('js/table.datasource.base.js', 'core'); $this->addJs('js/table.datasource.client.js', 'core'); $this->addJs('js/table.processor.base.js', 'core'); diff --git a/modules/backend/widgets/table/assets/js/table.helper.navigation.js b/modules/backend/widgets/table/assets/js/table.helper.navigation.js new file mode 100644 index 000000000..36d22502a --- /dev/null +++ b/modules/backend/widgets/table/assets/js/table.helper.navigation.js @@ -0,0 +1,181 @@ +/* + * Navigation helper for the table widget. + * Implements the keyboard navigation within the current page + * and pagination. + */ ++function ($) { "use strict"; + + // NAMESPACE CHECK + // ============================ + + if ($.oc.table === undefined) + throw new Error("The $.oc.table namespace is not defined. Make sure that the table.js script is loaded."); + + if ($.oc.table.helper === undefined) + $.oc.table.helper = {} + + // NAVIGATION CLASS DEFINITION + // ============================ + + var Navigation = function(tableObj) { + // Reference to the table object + this.tableObj = tableObj + + // Event handlers + this.keydownHandler = this.onKeydown.bind(this) + + this.init() + }; + + Navigation.prototype.init = function() { + this.registerHandlers() + } + + Navigation.prototype.dispose = function() { + // Unregister event handlers + this.unregisterHandlers() + + // Remove the reference to the table object + this.tableObj = null + } + + Navigation.prototype.registerHandlers = function() { + this.tableObj.el.addEventListener('keydown', this.keydownHandler) + } + + Navigation.prototype.unregisterHandlers = function() { + this.tableObj.el.removeEventListener('keydown', this.keydownHandler); + this.keydownHandler = null + } + + // KEYBOARD NAVIGATION + // ============================ + + Navigation.prototype.navigateDown = function(ev) { + if (!this.tableObj.activeCell) + return + + if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'down')) + return + + var row = this.tableObj.activeCell.parentNode, + newRow = !ev.shiftKey ? + row.nextElementSibling : + row.parentNode.children[row.parentNode.children.length - 1] + + if (!newRow) + return + + var cell = newRow.children[this.tableObj.activeCell.cellIndex] + + if (cell) + this.tableObj.focusCell(cell) + } + + Navigation.prototype.navigateUp = function(ev) { + if (!this.tableObj.activeCell) + return + + if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'up')) + return + + var row = this.tableObj.activeCell.parentNode, + newRow = !ev.shiftKey ? + row.previousElementSibling : + row.parentNode.children[0] + + if (!newRow) + return + + var cell = newRow.children[this.tableObj.activeCell.cellIndex] + + if (cell) + this.tableObj.focusCell(cell) + } + + Navigation.prototype.navigateLeft = function(ev) { + if (!this.tableObj.activeCell) + return + + if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'left')) + return + + var row = this.tableObj.activeCell.parentNode, + newIndex = !ev.shiftKey ? + this.tableObj.activeCell.cellIndex-1 : + 0 + + var cell = row.children[newIndex] + + if (cell) + this.tableObj.focusCell(cell) + } + + Navigation.prototype.navigateRight = function(ev) { + if (!this.tableObj.activeCell) + return + + if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'right')) + return + + var row = this.tableObj.activeCell.parentNode, + newIndex = !ev.shiftKey ? + this.tableObj.activeCell.cellIndex+1 : + row.children.length-1 + + var cell = row.children[newIndex] + + if (cell) + this.tableObj.focusCell(cell) + } + + Navigation.prototype.navigateNext = function(ev) { + if (!this.tableObj.activeCell) + return + + if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'tab')) + return + + var row = this.tableObj.activeCell.parentNode, + cellCount = row.children.length, + cellIndex = this.tableObj.activeCell.cellIndex + + if (!ev.shiftKey) { + if (cellIndex < cellCount-1) + this.tableObj.focusCell(row.children[cellIndex+1]) + else { + if (row.nextElementSibling) + this.tableObj.focusCell(row.nextElementSibling.children[0]) + } + } else { + if (cellIndex > 0) + this.tableObj.focusCell(row.children[cellIndex-1]) + else { + if (row.previousElementSibling) + this.tableObj.focusCell(row.previousElementSibling.children[cellCount-1]) + } + } + + this.tableObj.stopEvent(ev) + } + + // EVENT HANDLERS + // ============================ + + Navigation.prototype.onKeydown = function(ev) { + // Handle keyboard navigation events. + + if (ev.keyCode == 40) + return this.navigateDown(ev) + else if (ev.keyCode == 38) + return this.navigateUp(ev) + else if (ev.keyCode == 37) + return this.navigateLeft(ev) + if (ev.keyCode == 39) + return this.navigateRight(ev) + if (ev.keyCode == 9) + return this.navigateNext(ev) + } + + $.oc.table.helper.navigation = Navigation; +}(window.jQuery); \ No newline at end of file diff --git a/modules/backend/widgets/table/assets/js/table.js b/modules/backend/widgets/table/assets/js/table.js index 67b14bb34..5d85f77a9 100644 --- a/modules/backend/widgets/table/assets/js/table.js +++ b/modules/backend/widgets/table/assets/js/table.js @@ -52,14 +52,16 @@ // Event handlers this.clickHandler = this.onClick.bind(this) - this.keydownHandler = this.onKeydown.bind(this) + + // Navigation helper + this.navigation = null // // Initialization // this.init() - }; + } // INTERNAL METHODS // ============================ @@ -71,6 +73,9 @@ // Create cell processors this.initCellProcessors() + // Initialize helpers + this.navigation = new $.oc.table.helper.navigation(this) + // Create header and data tables this.buildTables() @@ -109,12 +114,12 @@ Table.prototype.registerHandlers = function() { this.el.addEventListener('click', this.clickHandler) - this.el.addEventListener('keydown', this.keydownHandler) } Table.prototype.unregisterHandlers = function() { this.el.removeEventListener('click', this.clickHandler); - this.el.removeEventListener('keydown', this.keydownHandler); + + this.clickHandler = null } Table.prototype.initCellProcessors = function() { @@ -293,114 +298,6 @@ cellElement.parentNode.setAttribute('data-dirty', 1) } - Table.prototype.navigateDown = function(ev) { - if (!this.activeCell) - return - - if (this.activeCellProcessor && !this.activeCellProcessor.keyNavigationAllowed(ev, 'down')) - return - - var row = this.activeCell.parentNode, - newRow = !ev.shiftKey ? - row.nextElementSibling : - row.parentNode.children[row.parentNode.children.length - 1] - - if (!newRow) - return - - var cell = newRow.children[this.activeCell.cellIndex] - - if (cell) - this.focusCell(cell) - } - - Table.prototype.navigateUp = function(ev) { - if (!this.activeCell) - return - - if (this.activeCellProcessor && !this.activeCellProcessor.keyNavigationAllowed(ev, 'up')) - return - - var row = this.activeCell.parentNode, - newRow = !ev.shiftKey ? - row.previousElementSibling : - row.parentNode.children[0] - - if (!newRow) - return - - var cell = newRow.children[this.activeCell.cellIndex] - - if (cell) - this.focusCell(cell) - } - - Table.prototype.navigateLeft = function(ev) { - if (!this.activeCell) - return - - if (this.activeCellProcessor && !this.activeCellProcessor.keyNavigationAllowed(ev, 'left')) - return - - var row = this.activeCell.parentNode, - newIndex = !ev.shiftKey ? - this.activeCell.cellIndex-1 : - 0 - - var cell = row.children[newIndex] - - if (cell) - this.focusCell(cell) - } - - Table.prototype.navigateRight = function(ev) { - if (!this.activeCell) - return - - if (this.activeCellProcessor && !this.activeCellProcessor.keyNavigationAllowed(ev, 'right')) - return - - var row = this.activeCell.parentNode, - newIndex = !ev.shiftKey ? - this.activeCell.cellIndex+1 : - row.children.length-1 - - var cell = row.children[newIndex] - - if (cell) - this.focusCell(cell) - } - - Table.prototype.navigateNext = function(ev) { - if (!this.activeCell) - return - - if (this.activeCellProcessor && !this.activeCellProcessor.keyNavigationAllowed(ev, 'tab')) - return - - var row = this.activeCell.parentNode, - cellCount = row.children.length, - cellIndex = this.activeCell.cellIndex - - if (!ev.shiftKey) { - if (cellIndex < cellCount-1) - this.focusCell(row.children[cellIndex+1]) - else { - if (row.nextElementSibling) - this.focusCell(row.nextElementSibling.children[0]) - } - } else { - if (cellIndex > 0) - this.focusCell(row.children[cellIndex-1]) - else { - if (row.previousElementSibling) - this.focusCell(row.previousElementSibling.children[cellCount-1]) - } - } - - this.stopEvent(ev) - } - // EVENT HANDLERS // ============================ @@ -416,24 +313,6 @@ this.focusCell(target) } - Table.prototype.onKeydown = function(ev) { - // Handle keyboard navigation events. - // Cell processor editors should stop the keydown event - // bubbling if they handle the up/down/left/right - // keys by themselves. - - if (ev.keyCode == 40) - return this.navigateDown(ev) - else if (ev.keyCode == 38) - return this.navigateUp(ev) - else if (ev.keyCode == 37) - return this.navigateLeft(ev) - if (ev.keyCode == 39) - return this.navigateRight(ev) - if (ev.keyCode == 9) - return this.navigateNext(ev) - } - // PUBLIC METHODS // ============================ @@ -451,6 +330,10 @@ // Dispose cell processors this.disposeCellProcessors() + // Dispose helpers and remove references + this.navigation.dispose() + this.navigation = null + // Delete the reference to the control HTML element. // The script doesn't remove any DOM elements themselves. // If it's needed it should be done by the outer script, diff --git a/modules/backend/widgets/table/assets/js/table.processor.string.js b/modules/backend/widgets/table/assets/js/table.processor.string.js index 439aec9af..566243838 100644 --- a/modules/backend/widgets/table/assets/js/table.processor.string.js +++ b/modules/backend/widgets/table/assets/js/table.processor.string.js @@ -157,15 +157,19 @@ if (document.selection) { var range = input.createTextRange() - range.collapse(true) - range.moveStart("character", position) - range.moveEnd("character", 0) - range.select() + setTimeout(function(){ + range.collapse(true) + range.moveStart("character", position) + range.moveEnd("character", 0) + range.select() + }, 0) } if (input.selectionStart !== undefined) { - input.selectionStart = position - input.selectionEnd = position + setTimeout(function(){ + input.selectionStart = position + input.selectionEnd = position + }, 0) } return 0 diff --git a/modules/backend/widgets/table/partials/_table.htm b/modules/backend/widgets/table/partials/_table.htm index c2090b166..35a5a5d1f 100644 --- a/modules/backend/widgets/table/partials/_table.htm +++ b/modules/backend/widgets/table/partials/_table.htm @@ -302,4 +302,4 @@ ?> -
\ No newline at end of file +
\ No newline at end of file