Mercurial > repos > saskia-hiltemann > ireport
diff DataTables-1.9.4/extras/KeyTable/js/KeyTable.js @ 0:ac5f9272033b draft
first upload
author | saskia-hiltemann |
---|---|
date | Tue, 01 Jul 2014 11:42:23 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataTables-1.9.4/extras/KeyTable/js/KeyTable.js Tue Jul 01 11:42:23 2014 -0400 @@ -0,0 +1,1111 @@ +/* + * File: KeyTable.js + * Version: 1.1.7 + * CVS: $Idj$ + * Description: Keyboard navigation for HTML tables + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: Fri Mar 13 21:24:02 GMT 2009 + * Modified: $Date$ by $Author$ + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: Just a little bit of fun :-) + * Contact: www.sprymedia.co.uk/contact + * + * Copyright 2009-2011 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, available at: + * http://datatables.net/license_gpl2 + * http://datatables.net/license_bsd + */ + + +function KeyTable ( oInit ) +{ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * API parameters + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Variable: block + * Purpose: Flag whether or not KeyTable events should be processed + * Scope: KeyTable - public + */ + this.block = false; + + /* + * Variable: event + * Purpose: Container for all event application methods + * Scope: KeyTable - public + * Notes: This object contains all the public methods for adding and removing events - these + * are dynamically added later on + */ + this.event = { + "remove": {} + }; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * API methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Function: fnGetCurrentPosition + * Purpose: Get the currently focused cell's position + * Returns: array int: [ x, y ] + * Inputs: void + */ + this.fnGetCurrentPosition = function () + { + return [ _iOldX, _iOldY ]; + }; + + + /* + * Function: fnGetCurrentData + * Purpose: Get the currently focused cell's data (innerHTML) + * Returns: string: - data requested + * Inputs: void + */ + this.fnGetCurrentData = function () + { + return _nOldFocus.innerHTML; + }; + + + /* + * Function: fnGetCurrentTD + * Purpose: Get the currently focused cell + * Returns: node: - focused element + * Inputs: void + */ + this.fnGetCurrentTD = function () + { + return _nOldFocus; + }; + + + /* + * Function: fnSetPosition + * Purpose: Set the position of the focused cell + * Returns: - + * Inputs: int:x - x coordinate + * int:y - y coordinate + * Notes: Thanks to Rohan Daxini for the basis of this function + */ + this.fnSetPosition = function( x, y ) + { + if ( typeof x == 'object' && x.nodeName ) + { + _fnSetFocus( x ); + } + else + { + _fnSetFocus( _fnCellFromCoords(x, y) ); + } + }; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private parameters + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* + * Variable: _nBody + * Purpose: Body node of the table - cached for renference + * Scope: KeyTable - private + */ + var _nBody = null; + + /* + * Variable: + * Purpose: + * Scope: KeyTable - private + */ + var _nOldFocus = null; + + /* + * Variable: _iOldX and _iOldY + * Purpose: X and Y coords of the old elemet that was focused on + * Scope: KeyTable - private + */ + var _iOldX = null; + var _iOldY = null; + + /* + * Variable: _that + * Purpose: Scope saving for 'this' after a jQuery event + * Scope: KeyTable - private + */ + var _that = null; + + /* + * Variable: sFocusClass + * Purpose: Class that should be used for focusing on a cell + * Scope: KeyTable - private + */ + var _sFocusClass = "focus"; + + /* + * Variable: _bKeyCapture + * Purpose: Flag for should KeyTable capture key events or not + * Scope: KeyTable - private + */ + var _bKeyCapture = false; + + /* + * Variable: _oaoEvents + * Purpose: Event cache object, one array for each supported event for speed of searching + * Scope: KeyTable - private + */ + var _oaoEvents = { + "action": [], + "esc": [], + "focus": [], + "blur": [] + }; + + /* + * Variable: _oDatatable + * Purpose: DataTables object for if we are actually using a DataTables table + * Scope: KeyTable - private + */ + var _oDatatable = null; + + var _bForm; + var _nInput; + var _bInputFocused = false; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Key table events + */ + + /* + * Function: _fnEventAddTemplate + * Purpose: Create a function (with closure for sKey) event addition API + * Returns: function: - template function + * Inputs: string:sKey - type of event to detect + */ + function _fnEventAddTemplate( sKey ) + { + /* + * Function: - + * Purpose: API function for adding event to cache + * Returns: - + * Inputs: 1. node:x - target node to add event for + * 2. function:y - callback function to apply + * or + * 1. int:x - x coord. of target cell (can be null for live events) + * 2. int:y - y coord. of target cell (can be null for live events) + * 3. function:z - callback function to apply + * Notes: This function is (interally) overloaded (in as much as javascript allows for + * that) - the target cell can be given by either node or coords. + */ + return function ( x, y, z ) { + if ( (x===null || typeof x == "number") && + (y===null || typeof y == "number") && + typeof z == "function" ) + { + _fnEventAdd( sKey, x, y, z ); + } + else if ( typeof x == "object" && typeof y == "function" ) + { + var aCoords = _fnCoordsFromCell( x ); + _fnEventAdd( sKey, aCoords[0], aCoords[1], y ); + } + else + { + alert( "Unhandable event type was added: x" +x+ " y:" +y+ " z:" +z ); + } + }; + } + + + /* + * Function: _fnEventRemoveTemplate + * Purpose: Create a function (with closure for sKey) event removal API + * Returns: function: - template function + * Inputs: string:sKey - type of event to detect + */ + function _fnEventRemoveTemplate( sKey ) + { + /* + * Function: - + * Purpose: API function for removing event from cache + * Returns: int: - number of events removed + * Inputs: 1. node:x - target node to remove event from + * 2. function:y - callback function to apply + * or + * 1. int:x - x coord. of target cell (can be null for live events) + * 2. int:y - y coord. of target cell (can be null for live events) + * 3. function:z - callback function to remove - optional + * Notes: This function is (interally) overloaded (in as much as javascript allows for + * that) - the target cell can be given by either node or coords and the function + * to remove is optional + */ + return function ( x, y, z ) { + if ( (x===null || typeof arguments[0] == "number") && + (y===null || typeof arguments[1] == "number" ) ) + { + if ( typeof arguments[2] == "function" ) + { + _fnEventRemove( sKey, x, y, z ); + } + else + { + _fnEventRemove( sKey, x, y ); + } + } + else if ( typeof arguments[0] == "object" ) + { + var aCoords = _fnCoordsFromCell( x ); + if ( typeof arguments[1] == "function" ) + { + _fnEventRemove( sKey, aCoords[0], aCoords[1], y ); + } + else + { + _fnEventRemove( sKey, aCoords[0], aCoords[1] ); + } + } + else + { + alert( "Unhandable event type was removed: x" +x+ " y:" +y+ " z:" +z ); + } + }; + } + + /* Use the template functions to add the event API functions */ + for ( var sKey in _oaoEvents ) + { + if ( sKey ) + { + this.event[sKey] = _fnEventAddTemplate( sKey ); + this.event.remove[sKey] = _fnEventRemoveTemplate( sKey ); + } + } + + + /* + * Function: _fnEventAdd + * Purpose: Add an event to the internal cache + * Returns: - + * Inputs: string:sType - type of event to add, given by the available elements in _oaoEvents + * int:x - x-coords to add event to - can be null for "blanket" event + * int:y - y-coords to add event to - can be null for "blanket" event + * function:fn - callback function for when triggered + */ + function _fnEventAdd( sType, x, y, fn ) + { + _oaoEvents[sType].push( { + "x": x, + "y": y, + "fn": fn + } ); + } + + + /* + * Function: _fnEventRemove + * Purpose: Remove an event from the event cache + * Returns: int: - number of matching events removed + * Inputs: string:sType - type of event to look for + * node:nTarget - target table cell + * function:fn - optional - remove this function. If not given all handlers of this + * type will be removed + */ + function _fnEventRemove( sType, x, y, fn ) + { + var iCorrector = 0; + + for ( var i=0, iLen=_oaoEvents[sType].length ; i<iLen-iCorrector ; i++ ) + { + if ( typeof fn != 'undefined' ) + { + if ( _oaoEvents[sType][i-iCorrector].x == x && + _oaoEvents[sType][i-iCorrector].y == y && + _oaoEvents[sType][i-iCorrector].fn == fn ) + { + _oaoEvents[sType].splice( i-iCorrector, 1 ); + iCorrector++; + } + } + else + { + if ( _oaoEvents[sType][i-iCorrector].x == x && + _oaoEvents[sType][i-iCorrector].y == y ) + { + _oaoEvents[sType].splice( i, 1 ); + return 1; + } + } + } + return iCorrector; + } + + + /* + * Function: _fnEventFire + * Purpose: Look thought the events cache and fire off the event of interest + * Returns: int:iFired - number of events fired + * Inputs: string:sType - type of event to look for + * int:x - x coord of cell + * int:y - y coord of ell + * Notes: It might be more efficient to return after the first event has been tirggered, + * but that would mean that only one function of a particular type can be + * subscribed to a particular node. + */ + function _fnEventFire ( sType, x, y ) + { + var iFired = 0; + var aEvents = _oaoEvents[sType]; + for ( var i=0 ; i<aEvents.length ; i++ ) + { + if ( (aEvents[i].x == x && aEvents[i].y == y ) || + (aEvents[i].x === null && aEvents[i].y == y ) || + (aEvents[i].x == x && aEvents[i].y === null ) || + (aEvents[i].x === null && aEvents[i].y === null ) + ) + { + aEvents[i].fn( _fnCellFromCoords(x,y), x, y ); + iFired++; + } + } + return iFired; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Focus functions + */ + + /* + * Function: _fnSetFocus + * Purpose: Set focus on a node, and remove from an old node if needed + * Returns: - + * Inputs: node:nTarget - node we want to focus on + * bool:bAutoScroll - optional - should we scroll the view port to the display + */ + function _fnSetFocus( nTarget, bAutoScroll ) + { + /* If node already has focus, just ignore this call */ + if ( _nOldFocus == nTarget ) + { + return; + } + + if ( typeof bAutoScroll == 'undefined' ) + { + bAutoScroll = true; + } + + /* Remove old focus (with blur event if needed) */ + if ( _nOldFocus !== null ) + { + _fnRemoveFocus( _nOldFocus ); + } + + /* Add the new class to highlight the focused cell */ + jQuery(nTarget).addClass( _sFocusClass ); + jQuery(nTarget).parent().addClass( _sFocusClass ); + + /* If it's a DataTable then we need to jump the paging to the relevant page */ + var oSettings; + if ( _oDatatable ) + { + oSettings = _oDatatable.fnSettings(); + var iRow = _fnFindDtCell( nTarget )[1]; + var bKeyCaptureCache = _bKeyCapture; + + /* Page forwards */ + while ( iRow >= oSettings.fnDisplayEnd() ) + { + if ( oSettings._iDisplayLength >= 0 ) + { + /* Make sure we are not over running the display array */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart += oSettings._iDisplayLength; + } + } + else + { + oSettings._iDisplayStart = 0; + } + _oDatatable.oApi._fnCalculateEnd( oSettings ); + } + + /* Page backwards */ + while ( iRow < oSettings._iDisplayStart ) + { + oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? + oSettings._iDisplayStart - oSettings._iDisplayLength : + 0; + + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + _oDatatable.oApi._fnCalculateEnd( oSettings ); + } + + /* Re-draw the table */ + _oDatatable.oApi._fnDraw( oSettings ); + + /* Restore the key capture */ + _bKeyCapture = bKeyCaptureCache; + } + + /* Cache the information that we are interested in */ + var aNewPos = _fnCoordsFromCell( nTarget ); + _nOldFocus = nTarget; + _iOldX = aNewPos[0]; + _iOldY = aNewPos[1]; + + var iViewportHeight, iViewportWidth, iScrollTop, iScrollLeft, iHeight, iWidth, aiPos; + if ( bAutoScroll ) + { + /* Scroll the viewport such that the new cell is fully visible in the rendered window */ + iViewportHeight = document.documentElement.clientHeight; + iViewportWidth = document.documentElement.clientWidth; + iScrollTop = document.body.scrollTop || document.documentElement.scrollTop; + iScrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft; + iHeight = nTarget.offsetHeight; + iWidth = nTarget.offsetWidth; + aiPos = _fnGetPos( nTarget ); + + /* Take account of scrolling in DataTables 1.7 - remove scrolling since that would add to + * the positioning calculation + */ + if ( _oDatatable && typeof oSettings.oScroll != 'undefined' && + (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) + { + aiPos[1] -= $(oSettings.nTable.parentNode).scrollTop(); + aiPos[0] -= $(oSettings.nTable.parentNode).scrollLeft(); + } + + /* Correct viewport positioning for vertical scrolling */ + if ( aiPos[1]+iHeight > iScrollTop+iViewportHeight ) + { + /* Displayed element if off the bottom of the viewport */ + _fnSetScrollTop( aiPos[1]+iHeight - iViewportHeight ); + } + else if ( aiPos[1] < iScrollTop ) + { + /* Displayed element if off the top of the viewport */ + _fnSetScrollTop( aiPos[1] ); + } + + /* Correct viewport positioning for horizontal scrolling */ + if ( aiPos[0]+iWidth > iScrollLeft+iViewportWidth ) + { + /* Displayed element is off the bottom of the viewport */ + _fnSetScrollLeft( aiPos[0]+iWidth - iViewportWidth ); + } + else if ( aiPos[0] < iScrollLeft ) + { + /* Displayed element if off the Left of the viewport */ + _fnSetScrollLeft( aiPos[0] ); + } + } + + /* Take account of scrolling in DataTables 1.7 */ + if ( _oDatatable && typeof oSettings.oScroll != 'undefined' && + (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) + { + var dtScrollBody = oSettings.nTable.parentNode; + iViewportHeight = dtScrollBody.clientHeight; + iViewportWidth = dtScrollBody.clientWidth; + iScrollTop = dtScrollBody.scrollTop; + iScrollLeft = dtScrollBody.scrollLeft; + iHeight = nTarget.offsetHeight; + iWidth = nTarget.offsetWidth; + + /* Correct for vertical scrolling */ + if ( nTarget.offsetTop + iHeight > iViewportHeight+iScrollTop ) + { + dtScrollBody.scrollTop = (nTarget.offsetTop + iHeight) - iViewportHeight; + } + else if ( nTarget.offsetTop < iScrollTop ) + { + dtScrollBody.scrollTop = nTarget.offsetTop; + } + + /* Correct for horizontal scrolling */ + if ( nTarget.offsetLeft + iWidth > iViewportWidth+iScrollLeft ) + { + dtScrollBody.scrollLeft = (nTarget.offsetLeft + iWidth) - iViewportWidth; + } + else if ( nTarget.offsetLeft < iScrollLeft ) + { + dtScrollBody.scrollLeft = nTarget.offsetLeft; + } + } + + /* Focused - so we want to capture the keys */ + _fnCaptureKeys(); + + /* Fire of the focus event if there is one */ + _fnEventFire( "focus", _iOldX, _iOldY ); + } + + + /* + * Function: _fnBlur + * Purpose: Blur focus from the whole table + * Returns: - + * Inputs: - + */ + function _fnBlur() + { + _fnRemoveFocus( _nOldFocus ); + _iOldX = null; + _iOldY = null; + _nOldFocus = null; + _fnReleaseKeys(); + } + + + /* + * Function: _fnRemoveFocus + * Purpose: Remove focus from a cell and fire any blur events which are attached + * Returns: - + * Inputs: node:nTarget - cell of interest + */ + function _fnRemoveFocus( nTarget ) + { + jQuery(nTarget).removeClass( _sFocusClass ); + jQuery(nTarget).parent().removeClass( _sFocusClass ); + _fnEventFire( "blur", _iOldX, _iOldY ); + } + + + /* + * Function: _fnClick + * Purpose: Focus on the element that has been clicked on by the user + * Returns: - + * Inputs: event:e - click event + */ + function _fnClick ( e ) + { + var nTarget = this; + while ( nTarget.nodeName != "TD" ) + { + nTarget = nTarget.parentNode; + } + + _fnSetFocus( nTarget ); + _fnCaptureKeys(); + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Key events + */ + + /* + * Function: _fnKey + * Purpose: Deal with a key events, be it moving the focus or return etc. + * Returns: bool: - allow browser default action + * Inputs: event:e - key event + */ + function _fnKey ( e ) + { + /* If user or system has blocked KeyTable from doing anything, just ignore this event */ + if ( _that.block || !_bKeyCapture ) + { + return true; + } + + /* If a modifier key is pressed (exapct shift), ignore the event */ + if ( e.metaKey || e.altKey || e.ctrlKey ) + { + return true; + } + var + x, y, + iTableWidth = _nBody.getElementsByTagName('tr')[0].getElementsByTagName('td').length, + iTableHeight; + + /* Get table height and width - done here so as to be dynamic (if table is updated) */ + if ( _oDatatable ) + { + /* + * Locate the current node in the DataTable overriding the old positions - the reason for + * is is that there might have been some DataTables interaction between the last focus and + * now + */ + var oSettings = _oDatatable.fnSettings(); + iTableHeight = oSettings.aiDisplay.length; + + var aDtPos = _fnFindDtCell( _nOldFocus ); + if ( aDtPos === null ) + { + /* If the table has been updated such that the focused cell can't be seen - do nothing */ + return; + } + _iOldX = aDtPos[ 0 ]; + _iOldY = aDtPos[ 1 ]; + } + else + { + iTableHeight = _nBody.getElementsByTagName('tr').length; + } + + /* Capture shift+tab to match the left arrow key */ + var iKey = (e.keyCode == 9 && e.shiftKey) ? -1 : e.keyCode; + + switch( iKey ) + { + case 13: /* return */ + e.preventDefault(); + e.stopPropagation(); + _fnEventFire( "action", _iOldX, _iOldY ); + return true; + + case 27: /* esc */ + if ( !_fnEventFire( "esc", _iOldX, _iOldY ) ) + { + /* Only lose focus if there isn't an escape handler on the cell */ + _fnBlur(); + return; + } + x = _iOldX; + y = _iOldY; + break; + + case -1: + case 37: /* left arrow */ + if ( _iOldX > 0 ) { + x = _iOldX - 1; + y = _iOldY; + } else if ( _iOldY > 0 ) { + x = iTableWidth-1; + y = _iOldY - 1; + } else { + /* at start of table */ + if ( iKey == -1 && _bForm ) + { + /* If we are in a form, return focus to the 'input' element such that tabbing will + * follow correctly in the browser + */ + _bInputFocused = true; + _nInput.focus(); + + /* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for + * focus + */ + setTimeout( function(){ _bInputFocused = false; }, 0 ); + _bKeyCapture = false; + _fnBlur(); + return true; + } + else + { + return false; + } + } + break; + + case 38: /* up arrow */ + if ( _iOldY > 0 ) { + x = _iOldX; + y = _iOldY - 1; + } else { + return false; + } + break; + + case 9: /* tab */ + case 39: /* right arrow */ + if ( _iOldX < iTableWidth-1 ) { + x = _iOldX + 1; + y = _iOldY; + } else if ( _iOldY < iTableHeight-1 ) { + x = 0; + y = _iOldY + 1; + } else { + /* at end of table */ + if ( iKey == 9 && _bForm ) + { + /* If we are in a form, return focus to the 'input' element such that tabbing will + * follow correctly in the browser + */ + _bInputFocused = true; + _nInput.focus(); + + /* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for + * focus + */ + setTimeout( function(){ _bInputFocused = false; }, 0 ); + _bKeyCapture = false; + _fnBlur(); + return true; + } + else + { + return false; + } + } + break; + + case 40: /* down arrow */ + if ( _iOldY < iTableHeight-1 ) { + x = _iOldX; + y = _iOldY + 1; + } else { + return false; + } + break; + + default: /* Nothing we are interested in */ + return true; + } + + _fnSetFocus( _fnCellFromCoords(x, y) ); + return false; + } + + + /* + * Function: _fnCaptureKeys + * Purpose: Start capturing key events for this table + * Returns: - + * Inputs: - + */ + function _fnCaptureKeys( ) + { + if ( !_bKeyCapture ) + { + _bKeyCapture = true; + } + } + + + /* + * Function: _fnReleaseKeys + * Purpose: Stop capturing key events for this table + * Returns: - + * Inputs: - + */ + function _fnReleaseKeys( ) + { + _bKeyCapture = false; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Support functions + */ + + /* + * Function: _fnCellFromCoords + * Purpose: Calulate the target TD cell from x and y coordinates + * Returns: node: - TD target + * Inputs: int:x - x coordinate + * int:y - y coordinate + */ + function _fnCellFromCoords( x, y ) + { + if ( _oDatatable ) + { + var oSettings = _oDatatable.fnSettings(); + if ( typeof oSettings.aoData[ oSettings.aiDisplay[ y ] ] != 'undefined' ) + { + return oSettings.aoData[ oSettings.aiDisplay[ y ] ].nTr.getElementsByTagName('td')[x]; + } + else + { + return null; + } + } + else + { + return jQuery('tr:eq('+y+')>td:eq('+x+')', _nBody )[0]; + } + } + + + /* + * Function: _fnCoordsFromCell + * Purpose: Calculate the x and y position in a table from a TD cell + * Returns: array[2] int: [x, y] + * Inputs: node:n - TD cell of interest + * Notes: Not actually interested in this for DataTables since it might go out of date + */ + function _fnCoordsFromCell( n ) + { + if ( _oDatatable ) + { + var oSettings = _oDatatable.fnSettings(); + return [ + jQuery('td', n.parentNode).index(n), + jQuery('tr', n.parentNode.parentNode).index(n.parentNode) + oSettings._iDisplayStart + ]; + } + else + { + return [ + jQuery('td', n.parentNode).index(n), + jQuery('tr', n.parentNode.parentNode).index(n.parentNode) + ]; + } + } + + + /* + * Function: _fnSetScrollTop + * Purpose: Set the vertical scrolling position + * Returns: - + * Inputs: int:iPos - scrolltop + * Notes: This is so nasty, but without browser detection you can't tell which you should set + * So on browsers that support both, the scroll top will be set twice. I can live with + * that :-) + */ + function _fnSetScrollTop( iPos ) + { + document.documentElement.scrollTop = iPos; + document.body.scrollTop = iPos; + } + + + /* + * Function: _fnSetScrollLeft + * Purpose: Set the horizontal scrolling position + * Returns: - + * Inputs: int:iPos - scrollleft + */ + function _fnSetScrollLeft( iPos ) + { + document.documentElement.scrollLeft = iPos; + document.body.scrollLeft = iPos; + } + + + /* + * Function: _fnGetPos + * Purpose: Get the position of an object on the rendered page + * Returns: array[2] int: [left, right] + * Inputs: node:obj - element of interest + */ + function _fnGetPos ( obj ) + { + var iLeft = 0; + var iTop = 0; + + if (obj.offsetParent) + { + iLeft = obj.offsetLeft; + iTop = obj.offsetTop; + obj = obj.offsetParent; + while (obj) + { + iLeft += obj.offsetLeft; + iTop += obj.offsetTop; + obj = obj.offsetParent; + } + } + return [iLeft,iTop]; + } + + + /* + * Function: _fnFindDtCell + * Purpose: Get the coords. of a cell from the DataTables internal information + * Returns: array[2] int: [x, y] coords. or null if not found + * Inputs: node:nTarget - the node of interest + */ + function _fnFindDtCell( nTarget ) + { + var oSettings = _oDatatable.fnSettings(); + for ( var i=0, iLen=oSettings.aiDisplay.length ; i<iLen ; i++ ) + { + var nTr = oSettings.aoData[ oSettings.aiDisplay[i] ].nTr; + var nTds = nTr.getElementsByTagName('td'); + for ( var j=0, jLen=nTds.length ; j<jLen ; j++ ) + { + if ( nTds[j] == nTarget ) + { + return [ j, i ]; + } + } + } + return null; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Initialisation + */ + + /* + * Function: _fnInit + * Purpose: Initialise the KeyTable + * Returns: - + * Inputs: object:oInit - optional - Initalisation object with the following parameters: + * array[2] int:focus - x and y coordinates of the initial target + * or + * node:focus - the node to set initial focus on + * node:table - the table to use, if not given, first table with class 'KeyTable' will be used + * string:focusClass - focusing class to give to table elements + * object:that - focus + * bool:initScroll - scroll the view port on load, default true + * int:tabIndex - the tab index to give the hidden input element + */ + function _fnInit( oInit, that ) + { + /* Save scope */ + _that = that; + + /* Capture undefined initialisation and apply the defaults */ + if ( typeof oInit == 'undefined' ) { + oInit = {}; + } + + if ( typeof oInit.focus == 'undefined' ) { + oInit.focus = [0,0]; + } + + if ( typeof oInit.table == 'undefined' ) { + oInit.table = jQuery('table.KeyTable')[0]; + } else { + $(oInit.table).addClass('KeyTable'); + } + + if ( typeof oInit.focusClass != 'undefined' ) { + _sFocusClass = oInit.focusClass; + } + + if ( typeof oInit.datatable != 'undefined' ) { + _oDatatable = oInit.datatable; + } + + if ( typeof oInit.initScroll == 'undefined' ) { + oInit.initScroll = true; + } + + if ( typeof oInit.form == 'undefined' ) { + oInit.form = false; + } + _bForm = oInit.form; + + /* Cache the tbody node of interest */ + _nBody = oInit.table.getElementsByTagName('tbody')[0]; + + /* If the table is inside a form, then we need a hidden input box which can be used by the + * browser to catch the browser tabbing for our table + */ + if ( _bForm ) + { + var nDiv = document.createElement('div'); + _nInput = document.createElement('input'); + nDiv.style.height = "1px"; /* Opera requires a little something */ + nDiv.style.width = "0px"; + nDiv.style.overflow = "hidden"; + if ( typeof oInit.tabIndex != 'undefined' ) + { + _nInput.tabIndex = oInit.tabIndex; + } + nDiv.appendChild(_nInput); + oInit.table.parentNode.insertBefore( nDiv, oInit.table.nextSibling ); + + jQuery(_nInput).focus( function () { + /* See if we want to 'tab into' the table or out */ + if ( !_bInputFocused ) + { + _bKeyCapture = true; + _bInputFocused = false; + if ( typeof oInit.focus.nodeName != "undefined" ) + { + _fnSetFocus( oInit.focus, oInit.initScroll ); + } + else + { + _fnSetFocus( _fnCellFromCoords( oInit.focus[0], oInit.focus[1]), oInit.initScroll ); + } + + /* Need to interup the thread for this to work */ + setTimeout( function() { _nInput.blur(); }, 0 ); + } + } ); + _bKeyCapture = false; + } + else + { + /* Set the initial focus on the table */ + if ( typeof oInit.focus.nodeName != "undefined" ) + { + _fnSetFocus( oInit.focus, oInit.initScroll ); + } + else + { + _fnSetFocus( _fnCellFromCoords( oInit.focus[0], oInit.focus[1]), oInit.initScroll ); + } + _fnCaptureKeys(); + } + + /* + * Add event listeners + * Well - I hate myself for doing this, but it would appear that key events in browsers are + * a complete mess, particulay when you consider arrow keys, which of course are one of the + * main areas of interest here. So basically for arrow keys, there is no keypress event in + * Safari and IE, while there is in Firefox and Opera. But Firefox and Opera don't repeat the + * keydown event for an arrow key. OUCH. See the following two articles for more: + * http://www.quirksmode.org/dom/events/keys.html + * https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html + * http://unixpapa.com/js/key.html + * PPK considers the IE / Safari method correct (good enough for me!) so we (urgh) detect + * Mozilla and Opera and apply keypress for them, while everything else gets keydown. If + * Mozilla or Opera change their implemention in future, this will need to be updated... + * although at the time of writing (14th March 2009) Minefield still uses the 3.0 behaviour. + */ + if ( jQuery.browser.mozilla || jQuery.browser.opera ) + { + jQuery(document).bind( "keypress", _fnKey ); + } + else + { + jQuery(document).bind( "keydown", _fnKey ); + } + + if ( _oDatatable ) + { + jQuery('tbody td', _oDatatable.fnSettings().nTable).live( 'click', _fnClick ); + } + else + { + jQuery('td', _nBody).live( 'click', _fnClick ); + } + + /* Loose table focus when click outside the table */ + jQuery(document).click( function(e) { + var nTarget = e.target; + var bTableClick = false; + while ( nTarget ) + { + if ( nTarget == oInit.table ) + { + bTableClick = true; + break; + } + nTarget = nTarget.parentNode; + } + if ( !bTableClick ) + { + _fnBlur(); + } + } ); + } + + /* Initialise our new object */ + _fnInit( oInit, this ); +}