/**
 * Allows cursor to bounce easily between input fields in a form. Cursor
 * automatically advances to the next field when current field reaches
 * maximum length. Left and right arrow keys, and backspace key, operate
 * across field boundaries.
 * 
 * To set up a chain, call InputChain.configure(element) for each <input>
 * element in the chain, specify their neighbor elements in fields
 * prevInput and/or nextInput, and optionally provide a maxlength attribute.
 * For example, a 3-input chain representing a phone number in HTML:
 * 
 *         <form name="phone">
 *           ( <input type="text" name="areaCode" maxlength="3"> )
 *           <input type="text" name="prefix" maxlength="3">
 *           - <input type="text" name="line" maxlength="4">
 *         </form>
 * 
 * ... could be configured as an InputChain with the lines of javascript setup
 * shown in InputChain.setupPhoneNumber() . Or, in the case of a phone number,
 * you can just call that method:
 * 
 *         var areaCode = document.forms["phone"].elements["areaCode"];
 *         var prefix = document.forms["phone"].elements["prefix"];
 *         var line = document.forms["phone"].elements["line"];
 *         InputChain.setupPhoneNumber(areaCode, prefix, line);
 */
var InputChain = new Object();
InputChain.BACKSPACE = window.KeyEvent ? window.KeyEvent.DOM_VK_BACK_SPACE :  8;
InputChain.LEFT      = window.KeyEvent ? window.KeyEvent.DOM_VK_LEFT       : 37;
InputChain.RIGHT     = window.KeyEvent ? window.KeyEvent.DOM_VK_RIGHT      : 39;

InputChain.configure = function(inputElement) {
// this is no longer an error, so make sure you include maxlength if you want cursor to auto-advance.
//        if ( ! inputElement.maxLength || inputElement.maxLength == -1) {
//            throw "Error on " + inputElement.name + ": maxLength attribute must be set on input elements used in InputChain.";
//        }
        inputElement.onkeydown = InputChain.checkForFocus;
        inputElement.onkeyup = InputChain.checkForCompletion;
}

InputChain.setupPhoneNumber = function(areaCodeInput, prefixInput, lineInput) {
    areaCodeInput.nextInput = prefixInput;
    areaCodeInput.maxLength = 3;
    prefixInput.prevInput = areaCodeInput;
    prefixInput.nextInput = lineInput;
    prefixInput.maxLength = 3;
    lineInput.prevInput = prefixInput;
    lineInput.maxLength = 4;
    InputChain.configure(areaCodeInput);
    InputChain.configure(prefixInput);
    InputChain.configure(lineInput);
    window.onunload_stack.push(function() {
      InputChain.destroyPhoneNumber(areaCodeInput, prefixInput, lineInput);
    });
}

InputChain.destroyPhoneNumber = function(areaCodeInput, prefixInput, lineInput) {
  areaCodeInput.nextInput = null;
  areaCodeInput.prevInput = null;
  prefixInput.nextInput = null;
  prefixInput.prevInput = null;
  lineInput.nextInput = null;
  lineInput.prevInput = null;
}

//For these two we just hope the browser retains the field's previous cursor state.
InputChain.placeCursorAtStart = function(inputElement) {
    inputElement.focus();
}
InputChain.placeCursorAtEnd = function(inputElement) {
    inputElement.focus();
    if (inputElement.createTextRange) {
        // this moves cursor to the end! ie hack.
        inputElement.createTextRange().text += "";
    }
}

// These two functions work for Mozilla and IE. They are fairly conservative with
// their "true" results, so that unsupported browsers will return false.
// The functions can return true even if text is selected; possibly REVISIT that.
// thanks to Rafi B. on
// http://the-stickman.com/web-development/javascript/finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
// for the IE hack.
InputChain.cursorAtStart = function(elem) {
    if (typeof elem.selectionStart != "undefined") {
        return (elem.selectionStart == 0 && elem.selectionEnd == 0);
    } else if (document.selection) {
        return (
            document.selection.createRange().moveStart("character", -1) == 0
            && document.selection.createRange().text == ""
        );
    } else {
        return false;
    }
}

InputChain.cursorAtEnd = function(elem) {
    if (typeof elem.selectionEnd != "undefined") {
        return (elem.selectionEnd == elem.value.length && elem.selectionStart == elem.value.length);
    } else if (document.selection) {
        return (
            document.selection.createRange().moveEnd("character", 1) == 0
            && document.selection.createRange().text == ""
        );
    } else {
        return false;
    }
}

InputChain.checkForFocus = function(e) {
    if (!e) { e = event; } // due to nasty Mozilla/Microsoft API disagreement
    var key = window.event ? e.keyCode : e.which;
    if (key == InputChain.BACKSPACE) {
        if (InputChain.cursorAtStart(this) && this.prevInput) {
            InputChain.placeCursorAtEnd(this.prevInput);
        }
    } else if (key == InputChain.LEFT) {
        if (InputChain.cursorAtStart(this) && this.prevInput) {
            InputChain.placeCursorAtEnd(this.prevInput);
        }
    } else if (key == InputChain.RIGHT) {
        if (InputChain.cursorAtEnd(this) && this.nextInput) {
            InputChain.placeCursorAtStart(this.nextInput);
        }
    }
    else {
        this.lastLength = this.value.length;
    }
    return true;
}

InputChain.checkForCompletion = function(e) {
    if (this.value.length >= this.maxLength && this.lastLength < this.maxLength && this.nextInput) {
        InputChain.placeCursorAtStart(this.nextInput);
    }
    this.lastLength = this.value.length;
    return true;
}
