/* ********************************************************
** KEYTRAP.JS - JS Key-Detection Library
** =====================================
** This file contains code to trap (detect) user keystrokes
** -- both single (F1, Enter) and combination (Ctrl+F1, 
** Shift+Enter) -- and to execute a custom function upon 
** keystroke detection.
**
** To load the KeyTrap library into an HTML document, code:
** <script src="keytrap.js" language="JavaScript"></script>
**
** Note that KeyTrap's functionality changes to an extent
** depending on the browser/version/platform in which it is 
** run. For example, NS4 cannot detect key combinations or
** arrow keys, you cannot assign an action to MSIE's F1 key 
** because it is "hard-wired" to open the Help window, etc. 
** Most of these exceptions are commented in the code.
** 
** For a delightful exegesis on the whys and wherefores of 
** KeyTrap.js, see its accompanying ScriptHead column at:
** www.zdnet.com/devhead/stories/articles/0,4413,2170375,00.html
**
** Author      Ver  Date      Comments
** ======      ===  ====      ========
** Rick Scott  1.0  12/15/00  Grand debut 
** Luan Dang   1.1  11/14/01  changed for Tivo Webremote
**
** Copyright 2000, Rick Scott, all rights reserved.
******************************************************** */


/* ********************************************************
** Key()
** =====
** vee begin mit zee lovely Key object constructor function.
** you use Key() to create Key objects, one for each key
** you want to detect. Here's the syntax:
**
** var keyName = new Key(ns4code, ns5code, ns6code, 
**                       ie4code, ie5code, ie6code, 
**                       action [, modkeys]);
**   keyName - name of Key object
**   ns4code->ie6code - numeric keycodes of this key in
**     ns4->ie6 browsers (as documented in the Key object
**     templates file, keyobjs.txt)
**   action - name of the method (function) that you want 
**     to execute when this Key object is detected 
**   modkeys (optional) - a string containing the names of
**     the modifier keys that must be depressed for this 
**     Key object to be detected: "shift", "ctrl", "alt",
**     "shift+ctrl", "shift+alt", etc.
** 
** for examples of new Key() calls, see the next section.
******************************************************** */

function Key(ns4code, ns5code, ns6code, 
             ie4code, ie5code, ie6code, action, modkeys) { 

  this.ns4code = ns4code;  // ns4 keycode value
  this.ns5code = ns5code;  // ns5 keycode value
  this.ns6code = ns6code;  // ns6 keycode value
  this.ie4code = ie4code;  // ie4 keycode value
  this.ie5code = ie5code;  // ie5 keycode value
  this.ie5code = ie6code;  // ie6 keycode value

  this.action = eval(action);  // method called on detection

  // if the optional modkeys argument is not present in the call
  // to new Key(), this.modkeys is set to "", meaning that no mod 
  // keys must be held down for this Key object to be detected

  if (arguments.length == 8) 
    this.modkeys = modkeys;
  else 
    this.modkeys = "";

}


/* *******************************************************************
** the user keys/key-combinations you define here will be detected 
** (and acted upon) in *each* page within which you load this file.
** for example, if you define an F2 Key object here that jumps to your 
** homepage, pressing F2 will perform this jump in every page within
** which you've loaded this file. for this reason, you should define
** your global (site-wide) keys/key-combinations here.
**
** to define local keys/key-combinations that will be detected 
** only in specific pages, create Key objects in SCRIPT blocks within 
** those pages. for example, if you want Enter to submit a form on one 
** specific page in your site, you'd create an appropriate Enter Key 
** object in a SCRIPT block within that page, not here!
** 
** tip - the easiest way to create Key objects:
** 1. copy/paste the desired Key obj templates from keyobjs.txt into
**    either this file or a specific HTML file (as explained above).
** 2. for each Key obj template, customize the final two Key() args,
**    action and the optional modkeys (see the syntax statement above).
** 3. for each Key obj template, create your desired action function.
** 
** for examples, see below.
******************************************************************* */

var Keys = new Array();  // store Key objects in Keys[] array
var Count = 0;           // index into Keys[] 

// as explained above, the following global keys/key-combinations 
// will be detected on *each* page into which you load this file; these
// specific keys/key-combos are deployed on the KeyTrap Demo Site pages

// these keys activate the reverse function on Tivo
// Left Arrow key - cannot be trapped in NS4!
Keys[Count++] = new Key(-1, 37, 37, 37, 37, 37, "reverse");
// [ key
Keys[Count++] = new Key(91, 219, 219, 219, 219, 219, "reverse");
function reverse() { 
   location = "reverse";
}

// these keys activate the forward function on Tivo
// Right Arrow key - cannot be trapped in NS4!
Keys[Count++] = new Key(-1, 39, 39, 39, 39, 39, "forward");
// ] key
Keys[Count++] = new Key(93, 221, 221, 221, 221, 221, "forward");
function forward() { 
   location = "forward";
}

// these keys activate the play function on Tivo
// Up Arrow key - cannot be trapped in NS4!
Keys[Count++] = new Key(-1, 38, 38, 38, 38, 38, "play");
// Enter key
Keys[Count++] = new Key(13, 13, 13, 13, 13, 13, "play");
function play() { 
   location = "play";
}

// these keys activate the pause function on Tivo
// space key
Keys[Count++] = new Key(32, 32, 32, 32, 32, 32, "pause");
// 'p'
Keys[Count++] = new Key(112, 80, 80, 80, 80, 80, "pause");
function pause() { 
   location = "pause";
}

// these keys activate the replay function on Tivo
// '/'
Keys[Count++] = new Key(47, 191, 191, 191, 191, 191, "replay");
// 'backspace'  - cannot be trapped in IE 5.50.4522 SP1
Keys[Count++] = new Key(8, 8, 8, 8, 8, 8, "replay");
function replay() { 
   location = "replay";
}

// these keys activate the pause function on Tivo
// Down Arrow key - cannot be trapped in NS4!
Keys[Count++] = new Key(-1, 40, 40, 40, 40, 40, "catchup");
// 'A' key  (All map to upper case)
Keys[Count++] = new Key(97, 65, 65, 65, 65, 65, "catchup");
function catchup() { 
   location = "catchup";
}


/* ***************************************************************
** from here to the end resides all the sexy code responsible for 
** trapping the keys and executing the actions associated with 
** global/local Key objs.
**
** one must be quite specific about browser types/versions, 
** since keycode mappings differ subtly among types/versions.
*************************************************************** */

var Ns4 = false; var Ns5 = false; var Ns6 = false; var Ns4up = false;
var Ie4 = false; var Ie5 = false; var Ie6 = false; var Ie4up = false;

// note the workaround to get Ns6
if (navigator.appName.indexOf("Netscape") != -1) {
  if (navigator.userAgent.indexOf("Netscape6") != -1) Ns6 = true;
  else if (parseInt(navigator.appVersion) >= 5) Ns5 = true;
  else if (parseInt(navigator.appVersion) >= 4) Ns4 = true;
  if (Ns4 || Ns5 || Ns6) Ns4up = true;
}
// and the workarounds to get Ie5 and Ie6
else if (navigator.appName.indexOf("Explorer") != -1) {
  if (navigator.userAgent.indexOf("MSIE 6") != -1) Ie6 = true;
  if (navigator.userAgent.indexOf("MSIE 5") != -1) Ie5 = true;
  else if (parseInt(navigator.appVersion) >= 4) Ie4 = true;
  if (Ie4 || Ie5 || Ie6) Ie4up = true;
}

// modifier-key processing globals
var Shift = false, Ctrl = false, Alt = false;
var SHIFT_KEYCODE = 16, CTRL_KEYCODE = 17, ALT_KEYCODE = 18;


/* ************************************************************
** keyDown()
** =========
** the keyDown() function does all the user key-trapping and 
** action-execution work. the evt argument is automatically 
** passed to keyDown when an onKeyDown event fires. you use it
** to get the depressed key's keycode in NS4+ browsers.
** ********************************************************* */

function keyDown(evt) { 
  // begin by getting the keycode of the depressed key and all
  // currently depressed modifier keys (Shift, Ctrl, Alt)
  var keycode;
  if (Ns4up) { 
    keycode = evt.which;
    // Netscape Shift/Ctrl/Alt processing only in 5+ browsers
    if (keycode == SHIFT_KEYCODE && (Ns5 || Ns6)) Shift = true;
    if (keycode == CTRL_KEYCODE && (Ns5 || Ns6)) Ctrl = true;
    if (keycode == ALT_KEYCODE && (Ns5 || Ns6)) Alt = true;
  }
  else if (Ie4up) {
    keycode = event.keyCode;
    Shift = event.shiftKey;
    Ctrl = event.ctrlKey;
    Alt = event.altKey;
  }

  // execute the action associated with keycode and all the 
  // currently depressed modified keys (Shift, Ctrl, Alt)
  for (var i=0; i<Keys.length; i++) { 
    if ((Ns4 && (keycode == Keys[i].ns4code)) || 
        (Ns5 && (keycode == Keys[i].ns5code)) || 
        (Ns6 && (keycode == Keys[i].ns6code)) || 
        (Ie4 && (keycode == Keys[i].ie4code)) || 
        (Ie5 && (keycode == Keys[i].ie5code)) || 
        (Ie6 && (keycode == Keys[i].ie6code))) { 
      if ((Shift && (Keys[i].modkeys.toLowerCase().indexOf("shift") != -1) || 
          !Shift && (Keys[i].modkeys.toLowerCase().indexOf("shift") == -1)) && 
          (Ctrl && (Keys[i].modkeys.toLowerCase().indexOf("ctrl") != -1) || 
          !Ctrl && (Keys[i].modkeys.toLowerCase().indexOf("ctrl") == -1)) && 
          (Alt && (Keys[i].modkeys.toLowerCase().indexOf("alt") != -1) || 
          !Alt && (Keys[i].modkeys.toLowerCase().indexOf("alt") == -1))) { 
        Keys[i].action();
        return;
      } 
    }
  } 
}


/* *************************************************************
** these final two lines make onKeyDown events (user keystrokes)
** call the above keyDown() function in NS and MSIE browsers.
** ********************************************************** */

document.onkeydown = keyDown;
if (Ns4up) document.captureEvents(Event.KEYDOWN);

