/*
 * DD functions
 * $Id: DSDropDown.js 22267 2007-05-31 22:35:55Z jvinding $
 * $URL: https://build/subversion/DestinationSearch/ds-core-web/tags/ds-core-web-2.0.115/src/main/webapp/js/DSDropDown.js $
 */

function DSDropDownItem( elem, text, type ) {
    this.elem = elem;
    this.text = text || '';
    this.type = type || '';
    this.properties = {};
    for( var i = 3; i < arguments.length; ++i ) {
        this.setProperty( arguments[i], arguments[++i] );
    }
}
DSDropDownItem.prototype.getValue = function() {
    return this.text;
};
DSDropDownItem.prototype.setProperty = function( key, value ) {
    return( this.properties[key] = value );
};
DSDropDownItem.prototype.getProperty = function( key ) {
    return this.properties[key];
};
DSDropDownItem.prototype.isType = function( type ) {
    return( type == this.type );
};
DSDropDownItem.prototype.addEventListener = function( type, func ) {
    switch( type ) {
        case 'click':
        case 'mouseout':
        case 'mouseover':
            if( ! this.events ) {
                this.events = new LMI.Event();
            }
            if( this.events.getListeners( type ).length === 0 ) {
                var o = this;
                this.events.registerEvent( type );
                LMI.BrowserEvent.addListener( this.elem, type, function() { o.events.triggerEvent( type, null, o ); } );
            }
        break;
    }
    this.events.addListener( type, func );
};

/*
 *
 */
function DSDropDown( key, textbox ) {
    if( ! key ) { throw( 'DSDropDown: you must specify a key' ); }
    if( DSDropDown.lists[key] ) { throw( 'DSDropDown: duplicate key: "' + key + '"' ); }
    if( ! textbox ) {
        textbox = document.getElementById( key );
        if( ! textbox || textbox.id != key ) {
            throw( 'DSDropDown: unable to find textbox: "' + textbox + '"' );
        }
    }

    var bind = LMI.BrowserEvent.bind;
    this.autocomp = true;
    this.key = key;
    this.textbox = textbox;
    this.parent = textbox.parentNode;
    this.listItems = [];
    this.pos = -1;
    this.createDropDown();
    this.textbox.setAttribute( "autocomplete", "off" ); // disable browser autocomplete

    bind( this.textbox, 'click', this, DSDropDown.click );
    bind( this.textbox, 'keydown', this, DSDropDown.typing );
    bind( this.textbox, 'keypress', this, DSDropDown.typing );

    bind( this.textbox.form, 'submit', this, this.submit );

    this.events = new LMI.Event();
    this.events.registerEvent( 'typing' );
    this.events.registerEvent( 'select' );

    this.listXOffset = this.listYOffset = this.listExtraW = 0;

    DSDropDown.lists[key] = this;
    if( ! DSDropDown.inited ) {
        // Check any clicks for closing the list(s)
        LMI.BrowserEvent.addListener( document, 'click', DSDropDown.HideLists );
        DSDropDown.inited = true;
    }
}
DSDropDown.prototype.submit = function( evt ) {
    var now = new Date().getTime(),
        prev = this.lastSubmit,
        e = new LMI.BrowserEventObject( evt, window.event, this );

    if( prev && now - prev < 1000 ) {
        this.textbox.blur();
        e.preventDefault();
        e.stopPropagation();
    }
    this.lastSubmit = now;
};

DSDropDown.lists = {};
DSDropDown.inited = false;
DSDropDown.HideLists = function( evt ) {
    for( var type in DSDropDown.lists ) {
        DSDropDown.lists[type].hide();
    }
};
DSDropDown.click = function( e ) {
    this.toggleVisibility();
    e.stopPropagation();
};
DSDropDown.prototype.createDropDown = function() {
    var create = LMI.Element.create;
    if (navigator.userAgent.indexOf( "MSIE" ) >= 0) {
        this.shim = create( 'iframe', this.parent, { src:'javascript:void(0)', 'class':'DSDDList', style:'filter:alpha(opacity=0);padding:0;margin:0;' } );
    }
    this.list = create( 'div', this.parent, { 'class':'DSDDList' } );
    this.listElem = create( 'dl', this.list, { id:'dd_'+this.key } );
};
DSDropDown.prototype.getLength = function() {
    return this.listItems.length;
};
DSDropDown.prototype.setAutoComplete = function( ac ) {
    this.autocomp = ( ac ? true : false );
    return this.autocomp;
};
DSDropDown.prototype.autoComplete = function() {
    return this.autocomp;
};
DSDropDown.prototype.updateShim = function() {
    if( this.shim ) {
        this.shim.style.top = this.list.style.top;
        this.shim.style.left = this.list.style.left;
        this.shim.style.width = this.list.style.width;
        this.shim.style.display = this.list.style.display;
        this.shim.style.height = this.list.clientHeight + 'px';
    }
};
DSDropDown.prototype.positionList = function() {
    var top = this.textbox.offsetTop + this.textbox.offsetHeight;
    var left = this.textbox.offsetLeft;
    var width = this.textbox.offsetWidth - 2;

    this.list.style.top   = top   + this.listYOffset + 'px';
    this.list.style.left  = left  + this.listXOffset + 'px';
    this.list.style.width = width + this.listExtraW  + 'px';
    this.updateShim();
};
DSDropDown.prototype.setListOffset = function( x, y, w ) {
    this.listXOffset = ( typeof x != 'undefined' ? x : 0 );
    this.listYOffset = ( typeof y != 'undefined' ? y : 0 );
    this.listExtraW  = ( typeof w != 'undefined' ? w : 0 );
};
DSDropDown.prototype.positionArrow = function() {
    var top  = this.textbox.offsetTop + this.arrowYOffset;
    var left = this.textbox.offsetLeft + this.textbox.offsetWidth + this.arrowXOffset;
    this.arrow.style.top = top + 'px';
    this.arrow.style.left = left + 'px';
};
DSDropDown.prototype.addDropDownArrow = function( url, x, y ) {
    this.arrowXOffset = ( typeof x != 'undefined' ? x : 0 );
    this.arrowYOffset = ( typeof y != 'undefined' ? y : 0 );
    this.arrow = LMI.Element.create( 'img', null, { src:url, 'class':'DSDDArrow' } );
    DOMNode.appendAfter( this.arrow, this.textbox );
    LMI.BrowserEvent.bind( this.arrow, 'click', this, DSDropDown.click );
    this.positionArrow();
};
DSDropDown.getByKey = function( key ) {
    return DSDropDown.lists[key];
};
DSDropDown.getKeys = function() {
    var a = [];
    for( var i in DSDropDown.lists ) {
        a.push( i );
    }
    return a;
};

// Populate the dropdown
DSDropDown.prototype.addHeaderItem = function( text, cls, type ) {
    var d = LMI.Element.create( 'dt', this.listElem, { textValue:text } );
    LMI.StyleSheet.addClass( d, 'DSDDHeaderItem' );
    if( cls ) { LMI.StyleSheet.addClass( d, cls ); }
    var item = new DSDropDownItem( d, text, ( type ? type : text ), 'selectable', false, 'removable', false );
    this.listItems.push( item );
    return item;
};
DSDropDown.prototype.addListItem = function( elem, text, type ) {
    var d = LMI.Element.create( 'dd', this.listElem );
    d.appendChild( elem );
    var item = new DSDropDownItem( d, text, type );
    for( var i = 3; i < arguments.length; ++i ) {
        item.setProperty( arguments[i], arguments[++i] );
    }
    this.listItems.push( item );
    return item;
};
DSDropDown.prototype.addSelectablePair = function( text1, text2, type, sticky ) {
    var i = this.getLength(),
        o = this,
        create = LMI.Element.create,
        bind = LMI.BrowserEvent.bind,
        a = create( 'a', null, { textValue:text1, href:'javascript:void(0)' } );
    create( 'span', a, { textValue:text2 } );
    var item = this.addListItem( a, text1, type, 'selectable', true, 'removable', ( ! sticky ) );

    LMI.StyleSheet.editStyleValue( item.elem, 'cursor', 'pointer' );

    bind( item, 'click', this, this.useSelection );
    bind( item, 'mouseover', this, this.eventHighlight, i );
    bind( item, 'mouseout', this, this.eventDehighlight, i );
    return item;
};
DSDropDown.prototype.addSelectableItem = function( text, type, sticky ) {
    var i = this.getLength(),
        o = this,
        a = LMI.Element.create( 'a', null, { textValue:text, href:'javascript:void(0)' } ),
        item = this.addListItem( a, text, type, 'selectable', true, 'removable', ( ! sticky ) ),
        bind = LMI.BrowserEvent.bind;

    LMI.StyleSheet.editStyleValue( item.elem, 'cursor', 'pointer' );

    bind( item, 'click', this, this.useSelection );
    bind( item, 'mouseover', this, this.eventHighlight, i );
    bind( item, 'mouseout', this, this.eventDehighlight, i );
    return item;
};
DSDropDown.prototype.addLink = function( text, callback, type ) {
    var href = ( typeof callback == 'function' ? 'javascript:void(0)' : callback ),
        a = LMI.Element.create( 'a', null, { textValue:text, href:href, 'class':'link' } ),
        item = this.addListItem( a, text, type, 'selectable', false );
    if( typeof callback == 'function' ) {
        LMI.BrowserEvent.bind( item, 'click', this, callback, type );
    }
    return item;
};
DSDropDown.prototype.addRemoveAllLink = function( text, callback, type ) {
    if( typeof type == 'undefined' ) {
        type = '';
    }
    var o = this,
        item = this.addLink( text, this.removeAll, type );
    item.setProperty( 'removable', true );
    if( callback ) {
        LMI.BrowserEvent.addListener( item, 'click', callback );
    }
    return item;
};
DSDropDown.prototype.getItem = function( index ) {
    return this.listItems[index];
};

// de-populate the dropdown
DSDropDown.prototype.removeItem = function( index ) {
    var i = this.getItem( index );
    i.elem.parentNode.removeChild( i.elem );
    this.listItems.splice( index, 1 );
};
DSDropDown.prototype.isRemovable = function( index ) {
    if (arguments.length < 1) {
        index = this.pos;
    }

    if (index >= 0 && index < this.getLength()) {
        return this.getItem( index ).getProperty( 'removable' );
    }
    return false;
};
DSDropDown.prototype.removeAll = function( e, type ) {
    var i;
    for( i = 0; i < this.getLength(); ++i ) {
        while( i < this.getLength() && this.getItem( i ).isType( type ) && this.isRemovable( i ) ) {
            this.removeItem( i );
        }
    }
};

// Visual effects
DSDropDown.prototype.isShown = function() {
    return this.list.style.display == 'block';
};
DSDropDown.prototype.show = function() {
    if( this.isShown() ) {
        return;
    }
    DSDropDown.HideLists();
    this.positionList();
    this.list.style.display = 'block';
    this.updateShim();
};
DSDropDown.prototype.hide = function() {
    if( this.isShown() ) {
        this.dehighlight();
        this.list.style.display = 'none';
        this.updateShim();
    }
};
DSDropDown.prototype.toggleVisibility = function() {
    if ( this.isShown() ) {
        this.hide();
    } else {
        this.show();
    }
};

// navigation/selection
DSDropDown.prototype.isSelectable = function( index ) {
    if( arguments.length < 1 ) {
        index = this.pos;
    }
    if( index >= 0 && index < this.getLength() ) {
        return this.listItems[index].getProperty( 'selectable' );
    }
    return false;
};
DSDropDown.prototype.getValue = function( index ) {
    if( arguments.length < 1 ) {
        index = this.pos;
    }
    if( this.isSelectable( index ) ) {
        return this.listItems[index].getValue();
    }
    return '';
};
DSDropDown.prototype.useSelection = function() {
    this.setTextboxValue();
    this.hide();
};
DSDropDown.prototype.setTextboxValue = function( val ) {
    this.clearHintText();
    if( val ) {
        this.textbox.value = val;
    } else if( this.isSelectable() ) {
        this.textbox.value = this.getValue();
        this.events.triggerEvent( 'select', this.listItems[this.pos], this );
    }
};

// highlight/dehighlight change the objects position marker, and (de-)hilight the element at index
DSDropDown.prototype.highlight = function( index ) {
    if( arguments.length < 1 ) {
        index = this.pos;
    }
    if( this.isSelectable( index ) ) {
        this.pos = index;
        LMI.StyleSheet.addClass( this.listItems[index].elem, 'selected' );
    }
};
DSDropDown.prototype.eventHighlight = function( e, index ) {
    this.highlight( index );
};
DSDropDown.prototype.dehighlight = function( index ) {
    if (arguments.length < 1) {
        index = this.pos;
    }
    if( this.isSelectable( index ) ) {
        LMI.StyleSheet.removeClass( this.listItems[index].elem, 'selected' );
    }
    this.pos = -1;
};
DSDropDown.prototype.eventDehighlight = function( e, index ) {
    this.dehighlight( index );
};

// Next/Prev change which entry is selected in the drop down
DSDropDown.prototype.previous = function() {
    if( this.getLength() === 0 ) { return; }

    var index = this.pos;
    if( index < 0 ) {
        index = 0;
    } else {
        --index;
    }
    while( index > 0 && !this.isSelectable( index ) ) {
        --index;
    }
    if( this.isSelectable( index ) ) {
        this.dehighlight();
        this.pos = index;
        this.highlight();
    }
};
DSDropDown.prototype.next = function() {
    if( this.getLength() === 0 ) { return; }

    var index = this.pos;
    if( index >= this.getLength() ) {
        index = this.getLength() - 1;
    } else if( index < 0 ) {
        index = 0;
    } else {
        ++index;
    }
    while( index < this.getLength() && !this.isSelectable( index ) ) {
        ++index;
    }
    if (this.isSelectable( index ) ) {
        this.dehighlight();
        this.pos = index;
        this.highlight();
    }
};

DSDropDown.prototype.last = function() {
    this.dehighlight();
    this.pos = this.getLength();
    this.previous ();
};
DSDropDown.prototype.first = function() {
    this.dehighlight();
    this.pos = -1;
    this.next ();
};

// Typing/autocompletion code
DSDropDown.KEY_BACKSPACE = 8;
DSDropDown.KEY_TAB = 9;
DSDropDown.KEY_ENTER = 13;
DSDropDown.KEY_ESC = 27;
DSDropDown.KEY_PGUP = 33;
DSDropDown.KEY_PGDN = 34;
DSDropDown.KEY_END = 35;
DSDropDown.KEY_HOME = 36;
DSDropDown.KEY_LEFT = 37;
DSDropDown.KEY_UP = 38;
DSDropDown.KEY_RIGHT= 39;
DSDropDown.KEY_DOWN = 40;
DSDropDown.KEY_DELETE = 46;

DSDropDown.canSelect = function( tb ) {
    return( tb.createTextRange || tb.setSelectionRange );
};
DSDropDown.textboxSelect = function( tb, iStart, iEnd ) {
    var oRange;
    if (tb.createTextRange) { // IE
        oRange = tb.createTextRange();
        oRange.moveStart( "character", iStart );
        oRange.moveEnd( "character", -tb.value.length + iEnd );
        oRange.select();
    } else if( tb.setSelectionRange ) { // moz
        tb.setSelectionRange( iStart, iEnd );
    }

    tb.focus();
};

DSDropDown.getTextboxValue = function( tb ) {
    var val, oRange;
    if( document.selection ) { // IE
        oRange = document.selection.createRange();
        val = tb.value.substring( 0, tb.value.length - oRange.text.length );
    } else if( tb.setSelectionRange ) { // moz
        val =  tb.value.substring( 0, tb.selectionStart );
    } else {
        val = tb.value;
    }
    return val;
};
/* this is where it gets interesting.  they keypress event does not trap some keys (eg: arrow keys) in any IE.
   the keydown event does, but is not cancelable.  so some keys (eg keys dealing w/ navigation) are handled in the
   keydown event, others in the keypress event, then cancel all of them in the keypress event handler

   to further complicate things, the key code for 'up' is 38. the char code for '&' is also 38.
   IE also doesn't do keycode vs charcode so special handing is put in to handle that
*/
DSDropDown.getCharCode = function( e ) {
    // IE doesn't trigger the keyPress event for "special" keys (eg: arrows)
    // IE always uses keyCode
    // Safari always uses charCode but has keycodes of > 60000 for "special" keys
    // firefox sets e.charCode to 0 if it's a "special" char
    // we also need special handling for tab and enter and maybe others in safari and IE
    var k = e.getKeyCode(),
        c = e.getCharCode();
    if( e.getType() == 'keydown'
       || k > 60000
       || ( typeof c != 'undefined' && c === 0 )
       || k == DSDropDown.KEY_ENTER
       || k == DSDropDown.KEY_TAB
       || k == DSDropDown.KEY_BACKSPACE ) {
        return null; // not a character
    } else {
        return( c || k );
    }
};
DSDropDown.getKeyCode = function( e ) {
    var key = e.getKeyCode();
    if( key > 63000 ) {
        // for Safari
        switch (key) {
            case 63232: key=DSDropDown.KEY_UP; break;
            case 63273: key=DSDropDown.KEY_HOME; break;
            case 63233: key=DSDropDown.KEY_DOWN; break;
            case 63275: key=DSDropDown.KEY_END; break;
            case 63234: key=DSDropDown.KEY_LEFT; break;
            case 63276: key=DSDropDown.KEY_PGUP; break;
            case 63235: key=DSDropDown.KEY_UP; break;
            case 63277: key=DSDropDown.KEY_PGDN; break;
        }
    }
    return key;
};
DSDropDown.prototype.guess = function( inp ) {
    if( ! inp ) { inp = DSDropDown.getTextboxValue( this.textbox ); }

    var t = new RegExp( '^'+quotemeta( inp ), 'i' ),
        val;
    this.dehighlight();
    for( var i = 0; i < this.getLength(); ++i ) {
        val = this.getValue( i );
        if( this.isSelectable( i ) && t.test( val ) ) {
            this.show();
            this.highlight( i );
            break;
        }
    }
    if( DSDropDown.canSelect( this.textbox ) && this.isSelectable() ) {
        if( this.isSelectable() ) {
            this.textbox.value = inp + this.getValue().substring( inp.length );
            DSDropDown.textboxSelect( this.textbox, inp.length, this.textbox.value.length );
        }
    } else {
        // let the browser actually add the letter since the browser doesn't support selection
        // eg: safari && ie5/mac
        return true;
    }
    return false;
};
DSDropDown.typing = function( e ) {
    if( e.getAltKey() || e.getCtrlKey() ) {
        // don't interfere with browser commands
        return true;
    }

    var tbVal = DSDropDown.getTextboxValue( this.textbox ),
        c = DSDropDown.getCharCode( e ),
        k = DSDropDown.getKeyCode( e ),
        afterVal;

    if( !c && k == DSDropDown.KEY_BACKSPACE ) {
        afterVal = tbVal.substring( 0, tbVal.length-1 );
    } else if( !c && k == DSDropDown.KEY_DELETE ) {
        afterVal = tbVal;
    } else {
        afterVal = tbVal + String.fromCharCode( DSDropDown.getCharCode( e ) );
    }

    c = {
        'charCode' : c,
        'keyCode' : k,
        'beforeValue' : tbVal,
        'afterValue' : afterVal
    };

    if( ! this.events.triggerEvent( 'typing', c, this ) ) {
        return true;
    }
    if( c.charCode ) {
        if( ! this.autoComplete() ) {
            return true;
        } else if( this.guess( c.afterValue ) ) {
            return true;
        }
    } else {
        switch( c.keyCode ) {
            case DSDropDown.KEY_TAB:
                if( this.isShown() ) {
                    this.useSelection();
                }
                this.hide();
                return true;
                break;

            case DSDropDown.KEY_UP:
                if( e.getType() == 'keydown' ) {
                    this.show();
                    this.previous();
                    this.setTextboxValue();
                    return true;
                }
                break;

            case DSDropDown.KEY_DOWN:
                if( e.getType() == 'keydown' ) {
                    this.show();
                    this.next();
                    this.setTextboxValue();
                    return true;
                }
                break;

            case DSDropDown.KEY_ENTER:
                if( e.getType() == 'keydown' ) { return true; }
                if( ! this.isSelectable() || ! DSDropDown.canSelect( this.textbox ) ) {
                    /*
                     * Enter sumbits the form if there is nothing hilighted.
                     * that way they can use enter to select an item, but typing a new value
                     * and pressing enter will behave like a normal textbox
                     */
                    return true;
                } else {
                    DSDropDown.textboxSelect( this.textbox, this.textbox.value.length, this.textbox.value.length ); // unhilight
                    this.useSelection();
                    this.hide();
                }
                break;
            default:
                return true;
        }
    }

    e.preventDefault();
    return false;
};
DSDropDown.prototype.addEventListener = function( type, func ) {
    this.events.addListener( type, func );
};
DSDropDown.prototype.bindEventListener =  function( type, obj, func, arg ) {
    this.events.bind( type, obj, func, arg );
};

// Hint text is grey text that disappears when the user attempts to enter their own value
DSDropDown.prototype.setHintText = function( txt ) {
    this.hintText = txt;
    if( ! this.hintTextInited ) {
        this.hintTextInited = true;
        var o = this;
        LMI.BrowserEvent.bind( this.textbox.form, 'submit', this, this.clearHintText );
        this.showHintText();
    }
};
DSDropDown.prototype.showHintText = function() {
    if( this.hintTextInited && ( this.textbox.value === '' || this.textbox.value == this.hintText ) ) {
        LMI.StyleSheet.addClass( this.textbox, 'hintText' );
        this.textbox.value = this.hintText;
        if( this.blur ) {
            LMI.BrowserEvent.remove( this.blur );
        }
        this.focus = LMI.BrowserEvent.bind( this.textbox, 'focus', this, this.clearHintText );
    }
};
DSDropDown.prototype.clearHintText = function() {
    if( LMI.StyleSheet.isClass( this.textbox, 'hintText' ) ) {
        this.textbox.value = '';
        LMI.StyleSheet.removeClass( this.textbox, 'hintText' );
        if( this.focus ) {
            LMI.BrowserEvent.remove( this.focus );
        }
        this.blur = LMI.BrowserEvent.bind( this.textbox, 'blur', this, this.showHintText );
    }
};
