/* -*- javascript -*-
     Copyright 2006 TJM Enterprises, Inc..
     All Rights Reserved
     System        : TEST_JS_JS :
     Object Name   : $RCS_FILE$
     Revision      : $REVISION$
     Date          : Thu Jul 13 18:12:15 2006
     Created By    : Bradford McKesson, TJM Enterprises, Inc.
     Created       : Thu Jul 13 18:12:15 2006

     Last Modified : <030310.0724>
     ID            : $Id: js.etf,v 2.5 2004/03/25 21:58:00 jon Exp $
     Source        : $Source: /export/cvs/cvsroot/me/macros/js.etf,v $
     Description
     Notes
     $Log: js.etf,v $
     Revision 2.5  2004/03/25 21:58:00  jon
     Final release from steve

*/
/* From http://developer.mozilla.org/en/docs/keyCode
  For keypress event in Mozilla/FF only:
   Character keyCodes stored in event.charCode
   Non-Character keyCodes stored in event.keyCode
   keydown and keyup events always store key code in event.keyCode

   charCode accounts for whether shift is pressed, accurately reports
   the character intended.

  keyCode in IE == charCode in FF for keys with symbols

  In FF for function keypress; keyCode will be set to a printable char value
  112(F1)-123(F12) and charCode will NOT be set. IE uses same values

   In IE, Function keys do not give keypress but letter keys do
   In IE for keydown, letter keys report as capital, so the Fx values
   do not conflict with any letters!

  For keydown:
   keyCode on keydown does not account for modifier keys,
   seems to return uppercase char value in FF.
   returns uppercase char value in IE.
  For keydown:
   Generally, nonprintable keys seem to map to ASCII codes that
   would require shift to generate normally (except for letters which are
   reported capital onkeydown and lowercase onkeypress).

  On Linux: ctrl-Fx will not work, these sequences are owned
   by X-Windows/KDE/Linux kernel

  Usage:
   bbkeymap_init() // creates window.keymap, window.key
   window.keymap.register_key(key.SYM, element, event_handler, handler_arguments)
 *
*/
var ns4 = document.layers;
var ie4 = document.all;
var nn6 = document.getElementById && !document.all;


function bbkeymap_init(inWindow) {
    var w = null;
    if (inWindow) {
        w = inWindow;
    } else {
        w = window;
    }
    var t = new bbkeymap();
    t.initialize();

   // var fa = w.document.getElementsByTagName('input');
   // t.register_key(key.F4, fa[0], info, {a:1});

}

// create key table entry
function keyc(inCode, inEvent, inDesc, inMod) {
    var k = {
        code:inCode,
        ie_event:inEvent,
        ff_event:inEvent,
        desc:inDesc
    };
    if (inMod && ((inMod == 'alt') || (inMod == 'ctrl'))) {
        k.mod = inMod;
    }
    return k;
}

// create key table entry for keys where
// FF detection requires a different JS event than IE detection
function keyf(inCode, inEvent, inFFEvent, inDesc) {
    var k = keyc(inCode, inEvent, inDesc);
    k.ff_event = inFFEvent;
    return k;
}

// Table of known keys
var key = new Object;

// In IE for keydown, letter keys report as capital (64-90), so these values
// do not conflict with any letters!
key.F1   = keyc(112, 'keydown', 'x-browser help,');
key.F2	 = keyc(113, 'keydown', '+ Available');
key.F3	 = keyc(114, 'keydown', 'FF search');
key.F4	 = keyc(115, 'keydown', 'IE focus address bar. IE: cannot detect bc no event generated.');
key.F5   = keyc(116, 'keydown', 'x-browser reload');
key.F10  = keyc(121, 'keydown', '+ Available');
key.F11  = keyc(122, 'keydown', 'FF toggle fullscreen mode');
key.F12  = keyc(123, 'keydown', '+ Available');

key.Au   = keyc(38,  'keydown', 'Up arrow (ASCII 28) FF: no keypress');
key.Ad   = keyc(40,  'keydown', 'Down arrow FF: no keypress');
key.Al   = keyc(37,  'keydown', 'Left arrow');
key.Ar   = keyc(39,  'keydown', 'Right arrow');

key.Esc  = keyc(27,  'keydown', ' Available(!)');
key.Enter = keyc(13, 'keydown', 'always. Cannot distinguish from keypad enter.');
key.Tab  = keyc(9,   'keydown', 'x-browser change focus');

key.Shift = keyc(16, 'keydown', ' shift (keydown only) FF keydown: shiftKey flag not set');
key.Ctrl  = keyc(17, 'keydown', ' control (keydown only) FF keydown: ctrlKey flag not set');

// alt keycode = unmodified keycode + 1000
key.alt_D = keyc(1068, 'keydown', ' alt D FF focus address bar and select address ');
key.alt_I = keyc(1073, 'keydown', ' alt I Available'); // request status information dialog
key.alt_L = keyc(1076, 'keydown', ' alt L Available'); // request ship pt select
key.alt_M = keyc(1077, 'keydown', ' alt M');
key.alt_N = keyc(1078, 'keydown', ' alt N Available'); // request barcode scan mode (alt_N alledged to do 'new browser window' but that is ctrl-N here)
key.alt_O = keyc(1079, 'keydown', ' alt O Available'); // unused
key.alt_P = keyc(1080, 'keydown', ' alt P Available'); // unused
key.alt_Q = keyc(1081, 'keydown', ' alt Q Available'); // unused
key.alt_R = keyc(1082, 'keydown', ' alt R Available'); // unused
// alt_S FF does something with history, not sure what
// alt_T FF opens new tab
// alt_B FF bookmark current url

// Charlie Fox in browser -- website key events
// https://bugzilla.mozilla.org/show_bug.cgi?id=349716
// also, FF and IE support an attribute accesskey='k' where
//  pressing shift-alt-k does something intelligent with the element
// no JS required.
key.Numlock = keyc(144, 'keydown', '');

// with numlock on,
key.KP8  = keyc(104, 'keydown', 'keydown only, 56 printable in keypress');


// Enable easy conversion from event.keyCode to a key
// code_to_key[event.keyCode] is same as values above.
var code_to_key = new Object();
for (j in key) {
    key[j].sym = j; // bit of reflection.
    var m = key[j].code;
    code_to_key[m] = key[j];
}

// return string uniquely representing this keystroke
// modifier keys are assigned different codes from unmodified keys
// alt-L has code 1076 where normal lowercase L is 76
function event_to_keysym(inCode, inEventType, inShift, inCtrl, inAlt) {
    if (code_to_key[inCode]) {
        return code_to_key[inCode].sym;
    } else {
        error(" No key for code ("+inCode+')');
    }
    return null;
}


// General means of associating a function with a key or key combination.
// to be called when the key is 'pressed'.
// Distinguish modifier keys (ctrl-j is different from 'j' is different 'shift-j'

// Dispatch keyboard events
function bbkeymap(){
	// return;
	this.key_events = {keydown:1, keypress:1, keyup:1};
    this.initialized = false;
    this.keys = new Object();

    // Public
    // convert a browser keyboard event into a key record in key object
    this.key_event_to_key = function(e) {

        var k;
        var ff_printable = true;

        if (!e.keyCode && e.charCode) {
            k = e.charCode; // FF
            ff_printable = true;
        } else if (e.keyCode) {
            k = e.keyCode;
            ff_printable = false;
        } else if(e.which) {
            k = e.which;    // FF/NS
            ff_printable = false;
        }

        var shift_down = e.shiftKey?e.shiftKey:false;
        var ctrl_down = e.ctrlKey?e.ctrlKey:false;
        var alt_down = e.altKey?e.altKey:false;
        var meta_down = e.metaKey?e.metaKey:false;

        info('key event: ' + e.type + '; keycode: ' + k +
             '; printable: '+ ff_printable +
             (shift_down?' shift ':'') +
             (ctrl_down?' ctrl ':'') +
             (meta_down?' meta ':'') +
             (alt_down?' alt ':'') );

        if (alt_down) {
            k = k + 1000;
        }
        if (ctrl_down) {
            k = k + 10000;
        }
        if (code_to_key[k]) {
            // known key
            return  code_to_key[k];
        } else {
            return undefined;
        }
    } // key_event_to_key

    // Private
    // keydown/keypress/keyup event handler
    // Interpret Browser event obj to find what key was really pressed,
    // call key_handler.
    // Called in context of DOM object
    this._bbkeystroke = function(inE) {
        var e = inE;
        if( !inE ){
            e = window.event;
        }


        var k = window.keymap.key_event_to_key(e);
        if (k) {
            var kn = k.sym;
            if (ie4 && (e.type == k.ie_event)) {
                // right event for this key in IE
                if (keymap.keys[kn]) { // key has registered handler.
                    window.dispatcher.serialized.add(keymap.keys[kn], e);
                    // return keymap.keys[kn].invoke(e);
                }
            }

            if (nn6 && (e.type == k.ff_event)) {
                // right event for this key in FF/Gecko
                if (keymap.keys[kn]) { // key has registered handler.
                    window.dispatcher.serialized.add(keymap.keys[kn], e);
                    //  return keymap.keys[kn].invoke(e);
                }
            }
        }
    } // _bbkeystroke

    // Private
    // disable browser default key operation
    // and any further processing
    this._cancel_key = function(e) {
        if (e.type in keymap.key_events) {
            e.keyCode = 0;
            e.returnValue = false;
            e.cancelBubble = true;

            return false;
        } else {
            warn('call cancel_key with a non-key event '+e.type);
            return true;
        }
    }

    // API
    // inKeySym is value from keys.something.
    // inElement is any DOM object supporting events.
    // inHandler is function to call when inKey occurs.
    //    inHandler will be called in context of inElement
    // inArgs is data to pass to inHandler.
    this.register_key = function(inKeySym, inElement, inHandler, inArgs, inLabel) {
        if (this.initialized) {
            // a handler needs (key, event, modifier_mask)
            // some keys detectable only on keydown,
            // a few (lowercase letters) only on keypress
            var k = inKeySym;
            if (typeof(inKeySym) == 'object') {
                k = inKeySym.code;
            }
            var kn = event_to_keysym(k, '', false, false, false);

            var label = kn;
            if (inLabel) {
                label = inLabel;
            }

            this.keys[kn] = new window.dispatcher.sequential_handler('keystroke', inElement, inHandler, inArgs, label);
            // this.keys[kn] = new this.handler('keystroke', inElement, inHandler, inArgs);
        }
    }

    // API
    this.unregister_key = function(inKeySym, inElement) {
        if (this.initialized) {
            var k = inKeySym;
            if (typeof(inKeySym) == 'object') {
                k = inKeySym.code;
            }
            var kn = event_to_keysym(k, '', false, false, false);
            this.keys[kn] = undefined;
            delete this.keys[kn];

        }
    }

    // debugging
    this.dump_known_keys = function() {
        debug('dumping known keys');
        for(var k in key) {
            debug('key ' + k + ' = '+ to_string(key[k]));
            if (this.keys[k]) {
                debug('key ' + k + ' has handler '+to_string_shallow(this.keys[k]));
            }
        }
    }

    // Public static
    // main
    this.initialize = function() {
        if (!this.initialized) {
            window.document.onkeypress = this._bbkeystroke;
            window.document.onkeydown = this._bbkeystroke;
            window.document.onkeyup = this._bbkeystroke;

            if (window.keymap) {
                delete window.keymap;
            }

            // make keymap name available in any context
            window.keymap = this;
            this.keymap = this;
            this.initialized = true;

        } else {
            warn('double initialization in bbkeymap');
        }

    }


}
