/*! KeyTable 2.3.1 * ©2009-2017 SpryMedia Ltd - datatables.net/license */ /** * @summary KeyTable * @description Spreadsheet like keyboard navigation for DataTables * @version 2.3.1 * @file dataTables.keyTable.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2009-2017 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ (function( factory ){ if ( typeof define === 'function' && define.amd ) { // AMD define( ['jquery', 'datatables.net'], function ( $ ) { return factory( $, window, document ); } ); } else if ( typeof exports === 'object' ) { // CommonJS module.exports = function (root, $) { if ( ! root ) { root = window; } if ( ! $ || ! $.fn.dataTable ) { $ = require('datatables.net')(root, $).$; } return factory( $, root, root.document ); }; } else { // Browser factory( jQuery, window, document ); } }(function( $, window, document, undefined ) { 'use strict'; var DataTable = $.fn.dataTable; var KeyTable = function ( dt, opts ) { // Sanity check that we are using DataTables 1.10 or newer if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) { throw 'KeyTable requires DataTables 1.10.8 or newer'; } // User and defaults configuration object this.c = $.extend( true, {}, DataTable.defaults.keyTable, KeyTable.defaults, opts ); // Internal settings this.s = { /** @type {DataTable.Api} DataTables' API instance */ dt: new DataTable.Api( dt ), enable: true, /** @type {bool} Flag for if a draw is triggered by focus */ focusDraw: false, /** @type {bool} Flag to indicate when waiting for a draw to happen. * Will ignore key presses at this point */ waitingForDraw: false, /** @type {object} Information about the last cell that was focused */ lastFocus: null }; // DOM items this.dom = { }; // Check if row reorder has already been initialised on this table var settings = this.s.dt.settings()[0]; var exisiting = settings.keytable; if ( exisiting ) { return exisiting; } settings.keytable = this; this._constructor(); }; $.extend( KeyTable.prototype, { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * API methods for DataTables API interface */ /** * Blur the table's cell focus */ blur: function () { this._blur(); }, /** * Enable cell focus for the table * * @param {string} state Can be `true`, `false` or `-string navigation-only` */ enable: function ( state ) { this.s.enable = state; }, /** * Focus on a cell * @param {integer} row Row index * @param {integer} column Column index */ focus: function ( row, column ) { this._focus( this.s.dt.cell( row, column ) ); }, /** * Is the cell focused * @param {object} cell Cell index to check * @returns {boolean} true if focused, false otherwise */ focused: function ( cell ) { var lastFocus = this.s.lastFocus; if ( ! lastFocus ) { return false; } var lastIdx = this.s.lastFocus.cell.index(); return cell.row === lastIdx.row && cell.column === lastIdx.column; }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constructor */ /** * Initialise the KeyTable instance * * @private */ _constructor: function () { this._tabInput(); var that = this; var dt = this.s.dt; var table = $( dt.table().node() ); // Need to be able to calculate the cell positions relative to the table if ( table.css('position') === 'static' ) { table.css( 'position', 'relative' ); } // Click to focus $( dt.table().body() ).on( 'click.keyTable', 'th, td', function (e) { if ( that.s.enable === false ) { return; } var cell = dt.cell( this ); if ( ! cell.any() ) { return; } that._focus( cell, null, false, e ); } ); // Key events $( document ).on( 'keydown.keyTable', function (e) { that._key( e ); } ); // Click blur if ( this.c.blurable ) { $( document ).on( 'mousedown.keyTable', function ( e ) { // Click on the search input will blur focus if ( $(e.target).parents( '.dataTables_filter' ).length ) { that._blur(); } // If the click was inside the DataTables container, don't blur if ( $(e.target).parents().filter( dt.table().container() ).length ) { return; } // Don't blur in Editor form if ( $(e.target).parents('div.DTE').length ) { return; } // Or an Editor date input if ( $(e.target).parents('div.editor-datetime').length ) { return; } //If the click was inside the fixed columns container, don't blur if ( $(e.target).parents().filter('.DTFC_Cloned').length ) { return; } that._blur(); } ); } if ( this.c.editor ) { var editor = this.c.editor; // Need to disable KeyTable when the main editor is shown editor.on( 'open.keyTableMain', function (e, mode, action) { if ( mode !== 'inline' && that.s.enable ) { that.enable( false ); editor.one( 'close.keyTable', function () { that.enable( true ); } ); } } ); if ( this.c.editOnFocus ) { dt.on( 'key-focus.keyTable key-refocus.keyTable', function ( e, dt, cell, orig ) { that._editor( null, orig ); } ); } // Activate Editor when a key is pressed (will be ignored, if // already active). dt.on( 'key.keyTable', function ( e, dt, key, cell, orig ) { that._editor( key, orig ); } ); } // Stave saving if ( dt.settings()[0].oFeatures.bStateSave ) { dt.on( 'stateSaveParams.keyTable', function (e, s, d) { d.keyTable = that.s.lastFocus ? that.s.lastFocus.cell.index() : null; } ); } // Redraw - retain focus on the current cell dt.on( 'draw.keyTable', function (e) { if ( that.s.focusDraw ) { return; } var lastFocus = that.s.lastFocus; if ( lastFocus && lastFocus.node && $(lastFocus.node).closest('body') === document.body ) { var relative = that.s.lastFocus.relative; var info = dt.page.info(); var row = relative.row + info.start; if ( info.recordsDisplay === 0 ) { return; } // Reverse if needed if ( row >= info.recordsDisplay ) { row = info.recordsDisplay - 1; } that._focus( row, relative.column, true, e ); } } ); dt.on( 'destroy.keyTable', function () { dt.off( '.keyTable' ); $( dt.table().body() ).off( 'click.keyTable', 'th, td' ); $( document.body ) .off( 'keydown.keyTable' ) .off( 'click.keyTable' ); } ); // Initial focus comes from state or options var state = dt.state.loaded(); if ( state && state.keyTable ) { // Wait until init is done dt.one( 'init', function () { var cell = dt.cell( state.keyTable ); // Ensure that the saved cell still exists if ( cell.any() ) { cell.focus(); } } ); } else if ( this.c.focus ) { dt.cell( this.c.focus ).focus(); } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private methods */ /** * Blur the control * * @private */ _blur: function () { if ( ! this.s.enable || ! this.s.lastFocus ) { return; } var cell = this.s.lastFocus.cell; $( cell.node() ).removeClass( this.c.className ); this.s.lastFocus = null; this._updateFixedColumns(cell.index().column); this._emitEvent( 'key-blur', [ this.s.dt, cell ] ); }, /** * Copy text from the focused cell to clipboard * * @private */ _clipboardCopy: function () { var dt = this.s.dt; // If there is a cell focused, and there is no other text selected // allow the focused cell's text to be copied to clipboard if ( this.s.lastFocus && window.getSelection && !window.getSelection().toString() ) { var cell = this.s.lastFocus.cell; var text = cell.render('display'); var hiddenDiv = $('
') .css( { height: 1, width: 1, overflow: 'hidden', position: 'fixed', top: 0, left: 0 } ); var textarea = $('