// 

// --------------------------------------------------------------------------
// ... lib   some general stuff
// --------------------------------------------------------------------------

Function.prototype.method = function( name, func ) {
    if ( ! this.prototype[ name ] ) {
        this.prototype[ name ] = func;
    }
};

Number.method( 'integer', function( ) {
    return Math[ this < 0 ? 'ceiling' : 'floor' ]( this );
} );

String.method( 'trim', function( ) {
    return this.replace( /^\s+|\s+$/g, '' );
} );

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function purge( d ) {
    var i, a  = d.attributes;
    if ( a ) {
        for ( i = 0; i < a.length; i += 1 ) {
            var n = a[ i ].name;
            if ( typeof d[ n ] === 'function' ) {
                d[ n ] = null;
            }
        }
    }
    var c = d.childNodes;
    if ( c ) {
        for ( i = 0; i < c.length; i += 1 ) {
            purge( d.childNodes[ i ] );
        }
    }
}

function purge_node( d ) {
    var i, c = d.childNodes;
    if ( c ) {
        for ( i = 0; i < c.length; i += 1 ) {
            purge_node( d.childNodes[ i ] );
        }
    }
    if ( d.onmouseover ) { d.onmouseover = null; }
    if ( d.onmouseout  ) { d.onmouseout  = null; }
    if ( d.onclick     ) { d.onclick     = null; }

}

function set_handler_for( el, evt, hd ) {
    var o = el[ evt ];
    if ( typeof o == 'function' ) {
        el[ evt ] = function( v ) { hd( v ); o( v ); };
    }
    else {
        el[ evt ] = hd;
    }
}

/*
(function() { // Use Object Detection to detect IE6
    try{
        if ( document.uniqueID && document.compatMode &&
                !window.XMLHttpRequest && document.execCommand ) {
            document.execCommand("BackgroundImageCache", false, true);
        }
    }
    catch( oh ){}
})();
*/

// --------------------------------------------------------------------------
// ... end of lib.js
// --------------------------------------------------------------------------



// --------------------------------------------------------------------------
//
//  cbKS     -   input selection on key events
//
// --------------------------------------------------------------------------
//
//   Synopsis:
//      ...
//      var kk;
//      var locs = [
//          { tlc: 'FRA', loc_name: 'Frankfurt' },
//          { tlc: 'PMI', loc_name: 'Mallorca' },
//          ...
//      ];
//      // locs should be an array of objects mith properties loc_name and tlc
//      function is_onload( ) {
//
//          ... ( lots of very clever stuff )
//          kk = new KS( "myInputId", locs, true );
//      }
//
// --------------------------------------------------------------------------
//  rg  09. 05. 2008
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
//  ... the KS key selector
//      Params:
//      input id    id of field to be monitored
//      objs        objects to be selected - need the property -loc_name-
//                  and ( maybe later ) perhaps TLCs ???
//      callback    function to be invoked. Will be called after selection
//                  with array of matching objects
//      debug       provides debug alerts, if set to true
// --------------------------------------------------------------------------

function KS ( input_el, locsSet, panel, locBox, debug ) {
    var _KS    = this;

    _KS.debug  = !!debug;
    _KS.alert  = function( txt ) { if ( _KS.debug ) { alert( txt ); } };
    _KS.f  = { el: input_el };

    // key control
    _KS.f.el.kc = {
        locsSet: locsSet,
        panel: panel,
        box: locBox
    };

    // add EventListeners to the original fields
    function _KU( ev ) {
        var e = new xEvent( ev );
        xStopPropagation( e );
        // xPreventDefault( e );
        // e.target.kc.box.partnerBox.panel.hide( );
        KS.ku_Add_c( e );
    }

    function _KD( ev ) {
        var e = new xEvent( ev );
        xStopPropagation( e );

        // xPreventDefault( e );
        // e.target.kc.box.partnerBox.panel.hide( );
        KS.kd_Add_c( e );
    }

    _KS.registerEvents =
        function( ) {
            set_handler_for( _KS.f.el, 'onkeyup',   _KU );
            set_handler_for( _KS.f.el, 'onkeydown', _KD );
        };

    // remove EventListeners - to be called on unload
    _KS.unregisterEvents =
        function( ) {
            _KS.f.el.kc = null;
            _KS.f.el.onkeyup   = null;
            _KS.f.el.onkeydown = null;
        };

    _KS.registerEvents( );

    this.unload = function( ) {
        // bj_alert( "KS unload" );
        _KS.unregisterEvents( );
    };

    return _KS;

}

KS.kd_Add_c = function ( e ) {
    var el = e.target;
    var c  = e.keyCode;

    if ( c === KS.KEY.tab ) {
        /* not on stage */
        if ( el.kc.locsSet.matches.length == 1 ) {
            el.kc.box.selectLoc( el.kc.locsSet.matches[ 0 ] );
        }
        if ( el.kc.box.ap_tlc_node.value === "" ) {
            el.value = "";
        }
        /* not on stage */
        el.kc.panel.hide( );
    }

};

KS.ku_Add_c = function ( e ) {
    var el = e.target;
    var c  = e.keyCode;
    var l;

    if (      c === KS.KEY.tab ) {

        el.select( );                       // JS core function
    }
    else if ( c === KS.KEY.enter ) {

        if ( el.kc.panel.panelmode === "list" ) {
            l = el.kc.locsSet.getSelectedMatch( );
            // var l = locsSet.getSelectedMatch( );
            el.kc.box.selectLoc( l );
        }
        if ( el.kc.panel.panelmode === "table" ) {
            l = el.kc.locsSet.getSelectedLoc( );
            // var l = locsSet.getSelectedLoc( );
            el.kc.box.selectLoc( l );
        }
    }
    else if ( c === KS.KEY.left ) {

        if ( el.kc.panel.panelmode === "list" ) {
            el.kc.locsSet.setPreviousMatch( );
            // locsSet.setPreviousMatch( );
        }
        if ( el.kc.panel.panelmode === "table" ) {
            el.kc.locsSet.setPreviousLoc( );
            // locsSet.setPreviousLoc( );
        }
    }
    else if ( c === KS.KEY.up ) {

        if ( el.kc.panel.panelmode === "list" ) {
            el.kc.locsSet.setPreviousMatch( );
            // locsSet.setPreviousMatch( );
        }
        if ( el.kc.panel.panelmode === "table" ) {
            el.kc.locsSet.setPreviousVerticalLoc( );
            // el.kc.locsSet.setPreviousLoc( );
            // locsSet.setPreviousLoc( );
        }
    }

    else if ( c === KS.KEY.right ) {

        if ( el.kc.panel.panelmode === "list" ) {
            el.kc.locsSet.setNextMatch( );
            // locsSet.setNextMatch( );
        }
        if ( el.kc.panel.panelmode === "table" ) {
            el.kc.locsSet.setNextLoc( );
            // locsSet.setNextLoc( );
        }
    }

    else if ( c === KS.KEY.down ) {

        if ( el.kc.panel.panelmode === "list" ) {
            el.kc.locsSet.setNextMatch( );
            // locsSet.setNextMatch( );
        }
        if ( el.kc.panel.panelmode === "table" ) {
            el.kc.locsSet.setNextVerticalLoc( );
            // el.kc.locsSet.setNextLoc( );
            // locsSet.setNextLoc( );
        }
    }


    else if ( c === KS.KEY.del || c === KS.KEY.esc ) {

        el.kc.box.selectLoc( null );
    }
    else if ( c === KS.KEY.back && el.value === '' ) {

        el.kc.box.selectLoc( null );
    }
    else {

        KS.kk_Find_kc( el );
    }

};

KS.kk_Find_kc = function ( id ) {
    var e = xGetElementById( id );

    if ( e.value === '' ) {
        e.kc.panel.showTable( );
        return;
    }

    e.kc.locsSet.buildMatches( e.value );

};

KS.ts = 500;        // total time in milli seeconds
KS.ss = 100;        // rescan time in milli seconds
KS.debug = 0;

KS.KEY = {
    back:   8,
    tab:    9,
    enter:  13,
    left:   37,
    up:     38,
    right:  39,
    down:   40,
    del:    46,
    esc:    27
};


// --------------------------------------------------------------------------
// ... the End
// --------------------------------------------------------------------------


// ----------------------------------------------------------------------------
//  LocsSet
//      set of Locs - actLocs, allLocs, ...
// ----------------------------------------------------------------------------

function LocsSet( parent, locs ) {
    this.box         = parent;          // parent box
    this.locs        = locs;            // table locs
    this.names       = [];              // loc names
    this.loc_names   = [];              // loc names + tlcs
    this.locsByName  = {};              // loc name => Loc obj
    this.locsByTLC   = {};              // loc tlc  => Loc obj
    this.matches     = [];              // list locs
    this.list_index  = 0;               // index into list locs
    this.table_index = 0;               // index into table locs
    this.anz         = { "AP": 0, "RG": 0 };

    if ( locs ) {
        this.setLocs( locs );
    }

    return this;
}

/* ----------------------------------------------------------------------------
 * ...  LocsSet methods for populating with locs and indexing
 * ------------------------------------------------------------------------- */

LocsSet.method( "setLocs", function( locs ) {
    var names      = [];
    var loc_names  = [];
    var locsByName = {};
    var hasTLC     = {};
    var sel        = this.box.get_selectedLoc( );
    var pre        = this.box.get_preselectedLoc( );
    var pre_index  = -1;
    var sel_index  = -1;
    var anz = { 'AP': 0, 'RG': 0 };

    if ( locs ) {
        for ( var i = 0; i < locs.length; i += 1 ) {
            names.push( locs[ i ].name );
            loc_names.push( locs[ i ].loc_name );
            locsByName[ locs[ i ].name ] = locs[ i ];
            hasTLC[ locs[ i ].tlc ] = true;
            anz[ locs[ i ].type ] += 1;

            if ( sel === locs[ i ] ) { sel_index = i; }
            if ( pre === locs[ i ] ) { pre_index = i; }
        }
    }
    // this.table_index = pre_index >= 0 ? pre_index : sel_index;
    this.table_index = sel_index >= 0 ? sel_index : pre_index;
    if ( this.table_index < 0 ) {
        this.table_index = 0;
    }
    this.locs        = locs;
    this.names       = names;
    this.loc_names   = loc_names;
    this.anz.AP      = anz.AP;
    this.anz.RG      = anz.RG;
    this.locsByName  = locsByName;
    this.hasTLC      = hasTLC;

} );

LocsSet.method( "getFirstLoc", function( ) {
    return this.locs[ 0 ] || null;
} );

LocsSet.method( "setIndices", function( loc ) {
    this.setTableIndex( loc );
    // this.setListIndex( loc );
} );

LocsSet.method( "includesTLC", function( tlc ) {
    return this.hasTLC[ tlc ] || false;
} );


/* ----------------------------------------------------------------------------
 * ...  LocsSet methods for List Selection with arrow keys
 * ------------------------------------------------------------------------- */

LocsSet.method( "setListIndex", function( loc ) {
    this.list_index = 0;
    for ( var i = 0; i < this.locs.length; i = i + 1 ) {
        if ( this.matches[ i ] === loc ) {
            this.list_index = i;
        }
    }
} );

LocsSet.method( "buildMatches", function( str ) {

    function toIgnoreCase( s ) {
        var sS = '',
            i;
        for ( i = 0; i < s.length; i++ ) {
            var c = s.substr( i, 1 );
            sS += '[' + c.toUpperCase( ) + c.toLowerCase( ) + ']';
        }
        return sS;
    }

    function ucFirst( s ) {
        var first = s.substr( 0, 1 );
        var rest  = s.substr( 1, s.length - 1 );
        return first.toUpperCase( ) + toIgnoreCase( rest );
    }

    var val = ucFirst( str );
    var re  = new RegExp( '\\b' + val );

    var matches = [];

    var sel        = this.box.get_selectedLoc( );
    var pre        = this.box.get_preselectedLoc( );
    var pre_index  = 0;
    var sel_index  = 0;

    for ( var i = 0; i < this.names.length; i++ ) {
        var Ergebnis = this.loc_names[ i ].match( re );
        if ( Ergebnis ) {
            var newLoc = this.locsByName[ this.names[ i ] ];

            if ( sel === newLoc ) { sel_index = matches.length; }
            if ( pre === newLoc ) { pre_index = matches.length; }

            matches.push( newLoc );
        }
    }
    this.matches    = matches;
    this.list_index = pre_index ? pre_index : sel_index;

    this.box.presentMatches( this.matches, this.list_index );

} );

LocsSet.method( "setNextMatch", function( ) {
    if ( ++this.list_index >= this.matches.length ) {
        this.list_index = 0;
    }
    this.box.preselectLoc( this.matches[ this.list_index ] );
} );

LocsSet.method( "setPreviousMatch", function( ) {
    if ( --this.list_index < 0 ) {
        this.list_index = this.matches.length - 1;
    }
    this.box.preselectLoc( this.matches[ this.list_index ] );
} );

LocsSet.method( "getSelectedMatch", function( ) {
    return ( this.matches[ this.list_index ] );
} );


/* ----------------------------------------------------------------------------
 * ...  LocsSet methods for Table Selection with arrow keys
 * ------------------------------------------------------------------------- */

LocsSet.method( "setTableIndex", function( loc ) {
    var ti = 0;
    for ( var i = 0; i < this.locs.length; i = i + 1 ) {
        if ( this.locs[ i ] === loc ) {
            ti = i;
        }
    }
    this.table_index = ti;
} );

LocsSet.method( "setNextLoc", function( ) {
    if ( ++this.table_index >= this.locs.length ) {
        this.table_index = 0;
    }
    this.box.preselectLoc( this.locs[ this.table_index ] );
} );

LocsSet.method( "setPreviousLoc", function( ) {
    if ( --this.table_index < 0 ) {
        this.table_index = this.locs.length - 1;
    }
    this.box.preselectLoc( this.locs[ this.table_index ] );
} );

LocsSet.method( "setNextVerticalLoc", function( ) {
    var n_aps = this.anz.AP;
    var n_rgs = this.anz.RG;
    var n_all = n_aps + n_rgs;
    var empty_aps = ( 4 - n_aps % 4 ) % 4;

    if ( n_aps - 4 <= this.table_index && this.table_index < n_aps ) {
        this.table_index += n_aps % 4;
        if ( this.table_index < n_aps ) {
            this.table_index += 4;
        }
    }
    else if ( n_all - 4 <= this.table_index ) {
        this.table_index = ( this.table_index + empty_aps + 1 ) % 4;
    }
    else {
        this.table_index += 4;
    }

    if ( this.table_index >= n_all ) {
        this.table_index = 0;
    }
    this.box.preselectLoc( this.locs[ this.table_index ] );
} );

LocsSet.method( "setPreviousVerticalLoc", function( ) {
    var n_aps = this.anz.AP;
    var n_rgs = this.anz.RG;
    var n_all = n_aps + n_rgs;
    var x_aps = n_aps % 4;
    var empty_aps = ( 4 - x_aps ) % 4;
    var x_rgs = n_rgs % 4;
    var empty_rgs = ( 4 - x_rgs ) % 4;

    if ( n_aps <= this.table_index && this.table_index <= n_aps + 3 ) {
        this.table_index += empty_aps - 4;
        if ( this.table_index >= n_aps ) {
            this.table_index -= 4;
        }
    }
    else if ( 0 <= this.table_index && this.table_index <= 3 ) {
        this.table_index = n_all + empty_rgs + this.table_index - 1;
        while ( this.table_index >= n_all ) {
            this.table_index -= 4;
        }
    }
    else {
        this.table_index -= 4;
    }

    this.box.preselectLoc( this.locs[ this.table_index ] );
} );

LocsSet.method( "getSelectedLoc", function( ) {
    return ( this.locs[ this.table_index ] );
} );


/* ----------------------------------------------------------------------------
 * ...  LocsSet methods for general retrieving
 * ------------------------------------------------------------------------- */

LocsSet.method( "getLocs",       function( ) { return this.locs;  } );
LocsSet.method( "getNames",      function( ) { return this.names; } );
LocsSet.method( "getLocsByName", function( ) { return this.locsByName; } );


// ----------------------------------------------------------------------------
// ... LocsSet ... the End
// ----------------------------------------------------------------------------


// ---------------------------------------------------------------------------
//  the Loc object
// ---------------------------------------------------------------------------


function Loc( seqno, tlc, name, type, area, ow_o, ow_d, rt_o, rt_d, from,
          until, parent, hex, brand ) {
    this.seqno    = seqno;
    this.tlc      = tlc;
    this.name     = name;
    this.type     = type;
    this.area     = area;
    this.ow_o     = ow_o;
    this.ow_d     = ow_d;
    this.rt_o     = rt_o;
    this.rt_d     = rt_d;
    this.from     = from;
    this.until    = until;
    this.parent   = parent;
    this.min      = from;
    this.max      = until;
    this.str      = "";
    this.hex      = hex;
    this.brand    = brand;

    var loc_name  = ( type === 'AP' ) ? name + ' ' + tlc : name;
    this.loc_name = loc_name;


    //-------------------------------------------------------------------------
    //  private date converters
    //-------------------------------------------------------------------------



    function h_getFormD8( my, day ) {   // converts form data into "YYYYMMDD"
        var m  = String( my % 12  + 1);
        while ( m.length < 2 ) { m = "0" + m; }
        var y  = String( (my - my % 12) / 12 + 1900 );
        while ( y.length < 4 ) { y = "0" + y; }
        var d = String( day );
        while ( d.length < 2 ) { d = "0" + d; }

        return y + m + d;
    }

    function h_DateToStr( d ) {         // converts JS Date into "YYYYMMDD"
        var yyyy = String(d.getFullYear());
        while (yyyy.length < 4) { yyyy = "0" + yyyy; }
        var mm = String(d.getMonth() + 1);
        while (mm.length < 2) { mm = "0" + mm; }
        var dd = String(d.getDate());
        while (dd.length < 2) { dd = "0" + dd; }
        return yyyy + mm + dd;
    }

    //-------------------------------------------------------------------------
    // explode hex compressed to '0'/'1' binary data
    //-------------------------------------------------------------------------

    function hex_to_bin( hstr ) {
        var b = "",
            i;
        for ( i = 0; i < hstr.length; i++ ) {
            b += Loc.HB[ hstr.charAt( i ) ];
        }
        return b;
    }

    if ( this.hex && this.hex.length ) {
        this.str  = hex_to_bin( this.hex );
    }

    //-----------------------------------------------------------------------------

    this.boxLocNodes = {
        orig: { table: null, list: null },
        dest: { table: null, list: null }
    };

    this.node_attrs = {
        index:    seqno,
        tlc:      tlc,
        loc_name: loc_name,
        type:     type,
        selected: false,
        active:   false
    };

    // this.locNode = this.cr_node( );

    return this;
}


/* ----------------------------------------------------------------------------
 * ...  Loc - pushing DOM elements  into live - the event handler setters
 * ------------------------------------------------------------------------- */

Loc.method( 'repr', function( ) {   // representation is language dependant!!!
    function c_date( d ) {              // turns 'YYYYMMDD' into "DD.MM.YYYY'
        return ( d.substr( 6, 2) + "." + d.substr( 4, 2) + "." + d.substr( 0, 4 ) );
    }
    return  this.type === "AP" ?
            "Die Fluege von/nach " + this.name + " sind zur Zeit buchbar " +
            " vom " + c_date( this.from ) +
            " bis zum " + c_date( this.until ) + "." :
            "Die Fluege aus der / in die Region " + this.name + " sind zur Zeit buchbar " +
            " vom " + c_date( this.from ) +
            " bis zum " + c_date( this.until ) + ".";
} );

Loc.method( 'unload',  function( ) {
    var k1, k2, node;
    for ( k1 in this.boxLocNodes ) {
        for ( k2 in this.boxLocNodes[ k1 ] ) {
            node = this.boxLocNodes[ k1 ][ k2 ];
            if ( node ) {

                Loc.clean_node( node, [ "loc" ] );

                if ( node && node.parentNode ) {
                    //// TEST
                    var rs = node.parentNode.removeChild( node );
                    if ( rs !== node ) {
                        alert( 'Problem removing node for loc ' + this.tlc );
                    }
                }
                node = null;
            }
        }
    }

} );

Loc.method( 'set_mouse_handlers', function( el ) {
    var that = this;

    var factory = function( el ) {
        var node = el;
        return {
            _mouseover: function( ) {
                    that.node_attrs.active = true;
                    xAddClass( node, 'active' );
                },
            _mouseout:  function( ) {
                    that.node_attrs.active = false;
                    xRemoveClass( node, 'active' );
                }
        };
    };
    var hd = factory( el );

    el.onmouseover = hd._mouseover;
    el.onmouseout  = hd._mouseout;

} );


Loc.method( 'set_click_handler',  function( el, box ) {
    var that = this;
    var factory = function( el, box ) {
        var loc_node = el;
        var the_box = box;
        return {
            _click: function( ) {
                    // alert( 'onclick handler invoked!' );
                    if ( the_box && the_box.selectLocByIndex ) {
                        var ind = loc_node.loc.index;
                        the_box.selectLocByIndex( ind );
                    }
                }
        };
    };
    var hd = factory( el, box );

    set_handler_for( el, 'onclick', hd._click );

} );


/* ----------------------------------------------------------------------------
 * ...  Loc DOM node creation
 * ------------------------------------------------------------------------- */

Loc.method( 'create_box_node', function( box, panelname ) {
    // var new_node = this.cr_node( );

    var new_node = document.createElement( "P" );
    xAddClass( new_node, "location" );
    var tx = document.createTextNode( this.name );
    new_node.appendChild( tx );

    new_node.loc = this.node_attrs;

    // add mouse event listener
    this.set_mouse_handlers( new_node );

    // add click event listener
    this.set_click_handler( new_node, box );

    this.boxLocNodes[ box.o_or_d ][ panelname ] = new_node;

    return new_node;
} );

Loc.method( 'get_box_node', function( box, panelname ) {
    if ( this.boxLocNodes[ box.o_or_d ][ panelname ] ) {
        Loc.clean_node( this.boxLocNodes[ box.o_or_d ][ panelname ] );
    }
    return this.create_box_node( box, panelname );
} );


Loc.method( 'scroll_into_view', function( box ) {
    this.boxLocNodes[ box ].table.scrollIntoView( false );
} );


/* ----------------------------------------------------------------------------
 * ...  Loc visibility control
 * ------------------------------------------------------------------------- */

Loc.method( 'addClass', function( c, box ) {
    // xAddClass( this.locNode, c, box );
    xAddClass( this.boxLocNodes[ box ].list, c );
    xAddClass( this.boxLocNodes[ box ].table, c );
} );

/*
Loc.method( 'hide', function( ) { this.addClass( 'hidden' );} );
*/

Loc.method( 'removeClass', function( c, box ) {
    // xRemoveClass( this.locNode, c );
    xRemoveClass( this.boxLocNodes[ box ].list, c );
    xRemoveClass( this.boxLocNodes[ box ].table, c );
} );
Loc.method( 'show', function( ) { this.removeClass( 'hidden' );} );


/* ----------------------------------------------------------------------------
 * ...  Loc representation methods
 * ------------------------------------------------------------------------- */

Loc.method( 'toString', function( ) {
    return this.name + " [ " + this.tlc + " ] ( " + this.type + " )";
} );

Loc.HB = {
    0: "0000", 1: "0001", 2: "0010", 3: "0011", 4: "0100",
    5: "0101", 6: "0110", 7: "0111", 8: "1000", 9: "1001",
    a: "1010", b: "1011", c: "1100", d: "1101", e: "1110", f: "1111",
    A: "1010", B: "1011", C: "1100", D: "1101", E: "1110", F: "1111"
};

Loc.DBG  = false;

Loc.clean_node = function( el, props ) {
    if ( !el ) {
        return;
    }

    var events = [ "mouseover", "mouseout", "click" ],
        event, i;

    for ( i = 0; i < events.length; i += 1 ) {
        event = "on" + events[ i ];
        // if ( el[ event ] && typeof el[ event ] === 'function' ) {
        if ( el[ event ] ) {
            el[ event ] = null;
        }
    }

    if ( el.loc ) {
        el.loc = null;
    }

    //// TEST
    return;
    /*
    if ( props && props.length && el.hasOwnProperty ) {
        for( i = 0; i < props.length; i += 1 ) {
            if ( el.hasOwnProperty( props[ i ] ) ) {
        	el[ props[ i ] ] = null;
            }
        }
    }
    */
};


/* ----------------------------------------------------------------------------
 * ...  the end
 * ------------------------------------------------------------------------- */





function xRemoveChildNodes( node ) {
    for ( var i = node.childNodes.length; i > 0; i-- ) {
        var dNode = node.childNodes[ i - 1 ];
        node.removeChild( dNode );
    }
    ////TEST
    Loc.clean_node( node );
}


function xRemovePanelNodes( node ) {
    var a = node.attributes,
        c = node.childNodes,
        i, n,
        events = [ 'click', 'mouseover', 'mouseout' ];


    if ( a ) {
        for ( i = 0; i < a.length; i += 1 ) {
            n = a[ i ].name;
            if ( typeof node[ n ] === 'function' ) {
                node[ n ] = null;
            }
        }
    }

/*
    for ( i = 0; i < events.length; i += 1 ) {
        if ( node[ events[ i ] ] && typeof node[ events[ i ] ] === 'function' ) {
            node[ events[ i ] ] = null;
        }
    }
*/
    if ( c ) {
        for ( i = 0; i < c.length; i += 1 ) {
            n = c[ i ];
            xRemovePanelNodes( n );
            node.removeChild( n );
            n = null;
        }
    }

}


/* ----------------------------------------------------------------------------
 * ...  LocPanel
 * ------------------------------------------------------------------------- */
function LocPanel( ap_name_node, parent, panel ) {
    var myLP = this;
    var myBox = parent;
    this.parent = parent;
    this.input_node = ap_name_node;
    // var hdr_txt = 'Bitte wÃ¤hlen Sie ein Land, einen Ort oder eine Region';
    var hdr_txt = parent.o_or_d == 'orig' ? "Bitte wählen Sie ein Land, eine Region oder einen Ort für Ihren Abflughafen aus." :
                  parent.o_or_d == 'dest' ? "Bitte wählen Sie ein Land, eine Region oder einen Ort für Ihren Zielflughafen aus." : "";


    this.header_txt = panel && panel.header_txt ? panel.header_txt : hdr_txt;

    this.ofs_x      = panel && panel.offset_x ? panel.offset_x : 0;
    this.ofs_y      = panel && panel.offset_y ? panel.offset_y : 0;

    var max_list_height   = panel && panel.max_list_height ?
                            panel.max_list_height : LocPanel.listHeight;
    this.max_list_height  = max_list_height;

    var max_table_height  = panel && panel.max_table_height ?
                            panel.max_table_height : LocPanel.tableHeight;
    this.max_table_height = max_table_height;

    if ( panel  && panel.hide_ids ) {
        this.hide_ids   = panel.hide_ids;
        this.hide_ids.on_table_view = this.hide_ids.on_table_view || [];
        this.hide_ids.on_list_view  = this.hide_ids.on_list_view  || [];

    }
    else {
        this.hide_ids   = {};
        this.hide_ids.on_table_view =  [];
        this.hide_ids.on_list_view  =  [];
    }

    this.panelmode = null;

    this.hide = function hide( ) {
        myLP.hideTable( );
        myLP.hideList( );
    };

    function showTable( ) {
        myBox.partnerBox.panel.hide( );
        myLP.hideList( );
        // xHideAllOf( myLP.hide_ids.on_table_view );
        myLP.hideCoveredNodesFor( 'table' );
        xRemoveClass( myLP.Table.main, 'hidden' );
        myLP.Table.main.style.zIndex = LocPanel.nextZ++;

        var max_body_height = myLP.max_table_height - xHeight( myLP.Table.head );

        if ( xHeight( myLP.Table.body ) > max_body_height ) {
            xHeight( myLP.Table.panel, max_body_height );
        }
        else {
            xHeight( myLP.Table.panel, xHeight( myLP.Table.body ) + 6 );
        }

        myLP.panelmode = 'table';

    }
    this.showTable = showTable;

    this.Table = this.create_Panel( 'table', true );
    var x_off = xWidth(  this.input_node ) + 2,
        y_off = xHeight( this.input_node ) + 2;
    xMoveTo( this.Table.main, xPageX( this.input_node ) + x_off + this.ofs_x,
                              xPageY( this.input_node ) + y_off + this.ofs_y );

    this.List  = this.create_Panel( 'list', false );
    xMoveTo( this.List.main, xPageX( this.input_node ) + x_off + this.ofs_x,
                             xPageY( this.input_node ) + y_off + this.ofs_y );


    this.panelCols = 4;

    var factory = function( el, box ) {
        var node = el;
        return {
            _click:  function( ) {
                    // bj_alert( "set_locMode_handlers ... click" );
                    node.select( );
                    showTable( );
                },
            _focus: function( ) {
                    if( !box.selectedLoc  ) {
                        showTable( );
                    }
                },
            _unload: function( ) {
                    // bj_alert( "set_locMode_handlers ... unload" );
                    node.onclick = null;
                    node.onfocus = null;
                }
        };
    };

    var hd = factory( this.input_node, parent );
    set_handler_for( this.input_node, 'onclick', hd._click );
    set_handler_for( this.input_node, 'onfocus', hd._focus );

    this.unload = function( ) {
        // this.hide( );
        this.input_node.onclick = null;
        this.input_node.onfocus = null;
        this.input_node.onkeyup = null;

        ////TEST
        purge_node( this.Table.main );
        purge_node( this.List.main );

        // purge( this.Table.main );
        // purge( this.List.main );

    };

}

LocPanel.method( "create_Panel", function( pref, fullTable ) {
    var c       = pref || 'panel',
        cv      = c + 'Canvas hidden',
        c_h     = c + 'Header',
        c_ht    = c + 'HeaderText',
        c_hc    = c + 'HeaderClose',
        c_p     = c + 'Panel',
        c_pb    = c + 'Body',
        c_pbh   = c + 'BodyHeader',
        c_pbd   = c + 'BodyData',
        c_pbdt  = c + 'BodyDataTable',
        c_cls   = c + 'Close',
        c_txt   = c + 'Text';

    var that    = this;

    var cr_el = function( tag, cls ) {
        var el = document.createElement( tag.toUpperCase( ) );
        if ( cls ) {
            xAddClass( el, cls );
        }
        return el;
    };

    var cr_p = function( cls, txt ) {
        var p = cr_el( "P", cls );
        if ( txt ) {
            p.appendChild( document.createTextNode( txt ) );
        }
        return p;
    };
    var cr_span = function( cls, txt ) {
        var p = cr_el( "SPAN", cls );
        if ( txt ) {
            p.appendChild( document.createTextNode( txt ) );
        }
        return p;
    };

    var cr_ph = function( cls, txt ) {
        var p = cr_el( "DIV", cls );
        if ( txt ) {
            p.appendChild( document.createTextNode( txt ) );
        }
        return p;
    };


    var factory = function( ) {
        return {
            _click: function( ) {
                    if ( that.panelmode === 'list'  ) { that.hideList( );  }
                    if ( that.panelmode === 'table' ) { that.hideTable( ); }
                }
        };
    };


    var C       = cr_el( 'div', cv ),       // canvas
        CH      = cr_el( 'div', c_h ),      // canvas header
        CHT     = cr_el( 'div', c_ht ),     // canvas header text
        CHC     = cr_el( 'div', c_hc ),     // canvas header close
        CP      = cr_el( 'div', c_p ),      // canvas panel
        CPB     = cr_el( 'div', c_pb ),     // canvas panel body
        CPB_rg  = cr_el( 'div', c_pbd ),    // canvas panel body regions
        CPB_ap  = cr_el( 'div', c_pbd );    // canvas panel body airports


    xAddClass( C, 'hidden' );
    set_handler_for( C, "onclick", function( ) {
        C.style.zIndex = LocPanel.nextZ++; } );
    // xAddEventListener( C, 'click', function( ) {
    //    C.style.zIndex = LocPanel.nextZ++; }, false );

    C.appendChild( CH );
    C.appendChild( CP);
    CP.appendChild( CPB );


    if ( fullTable ) {

        CH.appendChild( CHT );
        CHT.appendChild( cr_span( c_txt, unescape( this.header_txt ) ) );

        CH.appendChild( CHC );
        var pCP = cr_span( c_cls, "Fenster schließen" );
        CHC.appendChild( pCP );
        CHC.onclick = factory( )._click;

        CPB.appendChild( cr_ph( c_pbh, "Flughäfen" ) );
        CPB.appendChild( CPB_ap );

        CPB.appendChild( cr_el( 'HR' ) );

        CPB.appendChild( cr_ph( c_pbh, "Länder/Regionen" ) );
        CPB.appendChild( CPB_rg );

    }
    else {
        CH.appendChild( cr_p( c_txt, this.header_txt ) );
    }

    document.body.appendChild( C );

    var Panel = {
        main:  C,
        panel: CP,
        head:  CH,
        body:  CPB,
        rgs:   CPB_rg,
        aps:   CPB_ap
    };
    return Panel;

} );


LocPanel.method( "update_locs", function( loc_nodes, cols ) {

    this.allLocs = loc_nodes;

    this.rgsByName = {};        // regions representation hashmap
    this.apsByName = {};        // airport <p> hashmap
    this.rgNames   = [];        // region names array
    this.apNames   = [];        // airport names array

/* ----------------------------------------------------------------------------
 * ...  region names into rgNames, airport into apNames
 * ------------------------------------------------------------------------- */

    var i;
    for ( i = 0; i < loc_nodes.length; i += 1 ) {
        if ( loc_nodes[i].loc.type.toUpperCase( ) == 'RG' ) {
            this.rgsByName[ loc_nodes[ i ].loc.loc_name ] = loc_nodes[ i ];
            this.rgNames.push( loc_nodes[ i ].loc.loc_name );
        }
        else {
            this.apsByName[ loc_nodes[ i ].loc.loc_name ] = loc_nodes[ i ];
            this.apNames.push( loc_nodes[ i ].loc.loc_name );
        }
    }


/* ----------------------------------------------------------------------------
 * ...  sorted regions go into rgs
 * ------------------------------------------------------------------------- */

    var rgs = [];
    var sorted_rgs = this.rgNames.sort();
    for(  i = 0; i < sorted_rgs.length; i += 1 ) {
        rgs.push( this.rgsByName[ sorted_rgs[ i ] ] );
    }
    this.rgs = rgs;


/* ----------------------------------------------------------------------------
 * ... sort  --- airports go into aps.
 *      Prepend with DOM objects
 *      1. empty pace keeper DOM object
 *      2. initial capital letter
 * ------------------------------------------------------------------------- */

    var aps = [];

    var sorted_aps = this.apNames.sort();

    for ( i = 0; i < sorted_aps.length; i += 1 ) {
        var loc_name = sorted_aps[ i ];
        aps.push( this.apsByName[ loc_name ] );
    }

/*
    var seen = {};
    for(  var i = 0; i < sorted_aps.length; i += 1) {
        var loc_name = sorted_aps[ i ];
        var c = loc_name.charAt( 0 ).toUpperCase( );
        if ( !seen[ c ] ) {
            seen[ c ] = true;

            if ( i > 0 ) {          // no space keeper at beginning
                var p = document.createElement('P');
                var tx = document.createTextNode( '   ' );
                p.appendChild( tx );
                aps.push( p );
            }

            var p = document.createElement( 'P' );
            var tx = document.createTextNode( c );
            p.appendChild( tx );
            xAddClass( p, 'initiale' );
            aps.push( p );
        }
        aps.push( this.apsByName[ loc_name ] );
    }
*/

    this.aps = aps;
} );


LocPanel.method( "paintList", function paintList( arr ) {
    var node = this.List.body;

    ////TEST
    // xRemovePanelNodes( node );
    xRemoveChildNodes( node );

    for ( var i = 0; i < arr.length; i++) {
        var new_node = arr[ i ].get_box_node( this.parent, "list" );
        node.appendChild( new_node );
    }

} );


LocPanel.method( "paint_vertical", function( ) {    // not currently used
    function paintTable( arr_in, cols, node ) {
        function transpone_array( arr, cols, dbg ) {

            var lines = Math.ceil( arr.length / cols );
            var new_arr = [];
            var i;

            for ( i = 0; i < lines * cols; i += 1 ) {
                var p = document.createElement( 'P' );
                // p.innerHTML = '';
                new_arr[ i ] = p;
            }

            for ( i = 0; i < arr.length; i++ ) {
                var new_col  = Math.floor( i / lines );
                var new_line = i % lines;
                new_arr[ new_line * cols  + new_col ] =  arr[ i ];
            }

            return new_arr;
        }
        var arr = transpone_array( arr_in, cols );

        // TODO --- general xPurge in cb.js
        // if ( node ) purge( node );
        // TODO

        if ( node.childNodes ) {

            var ps = xGetElementsByClassName( "location", node );
            for ( var i = 0; i < ps.length; i += 1 ) {
                Loc.clean_node( ps[ i ], [ "loc" ] );
            }

            ////TEST
            // xRemovePanelNodes( node );
            xRemoveChildNodes( node );

        }

        var tb = document.createElement( 'TABLE' );
        var ty = document.createElement( 'TBODY' );

        i = 0;
        while ( i < arr.length ) {
            var tr = document.createElement( 'TR' );
            for ( var j = 0; j < cols; j ++) {
                var td = document.createElement( 'TD' );
                td.appendChild( arr[ i  ] );
                tr.appendChild( td );
                i++;
            }
            ty.appendChild( tr );
        }
        tb.appendChild( ty );
        node.appendChild( tb );
    }
    paintTable( this.rgs, this.panelCols, this.Table.rgs );
    paintTable( this.aps, this.panelCols, this.Table.aps );

    if ( this.input_node.kc ) {
        this.paintList( this.input_node.kc.locsSet.locs );
    }

} );

LocPanel.method( "paint", function( ) {
    function paintTable( arr, cols, node ) {
        var i, j, td, tr, tr_count,
            tb = document.createElement( 'TABLE' ),
            ty = document.createElement( 'TBODY' );

        if ( node && node.childNodes ) {
            var ps = xGetElementsByClassName( "location", node );
            for ( i = 0; i < ps.length; i++ ) {
                Loc.clean_node( ps[ i ], [ "loc" ] );
            }

            ////TEST
            // xRemovePanelNodes( node );
            xRemoveChildNodes( node );
        }

        node.appendChild( tb );
        tb.appendChild( ty );

        i = 0;
        tr_count = 0;
        while ( i < arr.length ) {
            tr = document.createElement( 'TR' );
            ty.appendChild( tr );
            tr_count += 1;
            xAddClass( tr, tr_count % 2 ? "lbp_odd" : "lbp_even" );
            for ( j = 0; j < cols && i < arr.length; j += 1 ) {
                td = document.createElement( 'TD' );
                tr.appendChild( td );
                td.appendChild( arr[ i ] );

                i++;
            }
        }
    }

    paintTable( this.rgs, this.panelCols, this.Table.rgs );
    paintTable( this.aps, this.panelCols, this.Table.aps );

    if ( this.input_node.kc ) {
        this.paintList( this.input_node.kc.locsSet.locs );
    }

} );


LocPanel.method( "hideTable", function( ) {
    // xShowAllOf( this.hide_ids.on_table_view );
    this.showCoveredNodesFor( 'table' );
    xAddClass( this.Table.main, 'hidden' );
    if ( this.panelmode === 'table' ) { this.panelmode = null; }
} );


LocPanel.method( "hideList", function( ) {
    // alert( " ... in myLP.hideList" );

    // xShowAllOf( this.hide_ids.on_list_view );
    this.showCoveredNodesFor( 'list' );
    xAddClass( this.List.main, 'hidden' );
    if ( this.panelmode === 'list' ) { this.panelmode = null; }
} );

LocPanel.method( "showList", function( ) {
    // alert( " ... in myLP.showList" );
    this.parent.partnerBox.panel.hide( );
    this.hideTable( );

    //xHideAllOf( this.hide_ids.on_list_view );
    this.hideCoveredNodesFor( 'list' );
    xRemoveClass( this.List.main, 'hidden' );
    this.List.main.style.zIndex = LocPanel.nextZ++;

    var max_body_height = this.max_list_height - xHeight( this.List.head );

    if ( xHeight( this.List.body ) > max_body_height ) {
        xHeight( this.List.panel, max_body_height );
    }
    else {
        xHeight( this.List.panel, xHeight( this.List.body ) );
    }

    this.panelmode = 'list';

} );

LocPanel.method( "hideCoveredNodesFor", function( list_or_table ) {
    if ( window.addEventListener ) {
        return;
    }
    var nodes = list_or_table === 'table' ?
                this.hide_ids.on_table_view : this.hide_ids.on_list_view;
    if ( !nodes || !nodes.length ) {
        return;
    }
    for ( var i = 0; i < nodes.length; i += 1 ) {
        xAddClass( nodes[ i ], LocPanel.hiddenClass );
    }

} );

LocPanel.method( "showCoveredNodesFor", function( list_or_table ) {
    if ( window.addEventListener ) {
        return;
    }
    var nodes = list_or_table === 'table' ?
                this.hide_ids.on_table_view : this.hide_ids.on_list_view;
    if ( !nodes || !nodes.length ) {
        return;
    }
    for ( var i = 0; i < nodes.length; i += 1 ) {
        xRemoveClass( nodes[ i ], LocPanel.hiddenClass );
    }
} );

LocPanel.nextZ = 99;
LocPanel.tableHeight = 380;
LocPanel.listHeight  = 300;
LocPanel.hiddenClass = 'hidden';


// --------------------------------------------------------------------------
// ... LocPanel     ... the End
// --------------------------------------------------------------------------


// ----------------------------------------------------------------------------
//
//       LocBox         Location Box
//
//          parent      LocBoxPair object
//          o_or_d      "orig" od "dest"
//          cfg_obj     "configuration object
//
//      Synopsis:
//
//          cfg = {
//              tlc:    'FRA',              // default tlc ( optional )
//              input:  {                   // DOM input node ids
//                  ap:  'apOrigName',      //  id of name field
//                  tlc: 'apOrigTlc'        //  id of tlc field
//              },
//              delimit_locs_to: [ 'FRA', 'BRE', 'HAM', 'DUS', 'MUC' ]
//          };
//
//      origBox = new LocBox( this, "orig", cfg );
// ----------------------------------------------------------------------------

function LocBox( parent, o_or_d, cfg ) {

    var myBox    = this;

    this.parent    = parent;
    this.o_or_d    = o_or_d;            // 'orig' or 'dest'

    this.tlc       = cfg.tlc    || '';

    this.ap_name_node = xGetElementById( cfg.input.ap );
    this.ap_tlc_node  = xGetElementById( cfg.input.tlc );

    // Try to set off autocompletion
    this.ap_name_node.setAttribute( "autocomplete", "off" );

    this.inp = '';                  // temporarily save ap

    try {
        this.panel   = new LocPanel( this.ap_name_node, this, cfg.panel );
    }
    catch( e ) {
        alert( "Could not create panel for LocBox " + o_or_d + " :" + e );
        this.panel = null;
    }


    // will be filled by parent using set_partnerBox
    this.partnerBox  = "";
    this.set_partnerBox =
        function ( partner ) { this.partnerBox = partner; };


    this.selectedLoc    = null;
    this.preselectedLoc = null;

    this.locs = this.get_all_parent_locs( cfg );

    // ourLocs  all parent locs which can serve as origin if o_or_d === 'orig'
    //                                  or as destination if o_or_d === 'dest'
    // actLocs  set of locs fitting to mode (OW/RT) and partner location
    this.ourLocs = [ ];
    this.actLocs = new LocsSet( this );

    this.fill_LocsSets( );

    var locs = this.ourLocs;
    for ( var i = 0; i < locs.length; i++ ) {
        locs[ i ].get_box_node( this, 'table' );
        locs[ i ].get_box_node( this, 'list'  );
    }


    if ( this.tlc ) {
        this.set_selectedLoc( this.parent.loc( this.tlc ) );
    }

    this.build_for = ( this.o_or_d === "orig" ) ?
        this.activate_origins_to : this.activate_destinations_from;

    this.ks = new KS( this.ap_name_node, this.actLocs, this.panel, this );

    this.unload = function( ) {
        if ( this.ks && this.ks.unload ) {
            this.ks.unload( );
        }
        if ( myBox.panel ) {
            myBox.panel.unload( );
        }
    };

    return this;
}


/* ----------------------------------------------------------------------------
 * ...  LocBox  ... the methods
 * ------------------------------------------------------------------------- */

// ----------------------------------------------------------------------------
// ...  LocBox method   get_all_parent_locs
//          inspect parent's configuration object and put all the locations
//          into an array.
//          return he sorted array
// ----------------------------------------------------------------------------

LocBox.method( "get_all_parent_locs", function ( cfg ) {
    var is_delim = cfg.delimit_locs_to && cfg.delimit_locs_to.length > 0;
    var arr      = is_delim ? cfg.delimit_locs_to : this.parent.locations;
    var unsorted = [],
        sorted   = [],
        i,
        newLoc;

    for ( i = 0; i < arr.length; i += 1 ) {
        newLoc = is_delim ? this.parent.locByTLC( arr[ i ] ) : arr[ i ];
        if ( newLoc ) {
            unsorted.push( newLoc );
        }
    }

    ////TEST
    // sorted enough for IE6
    /*@cc_on
        return unsorted;
    @*/

    var runs = 0;

    function sortLocs( unsorted_arr ) {
        function byName( a, b ) {
            runs += 1;
            return  a.type  <  b.type ? -1 :
                    a.type  >  b.type ?  1 :
                    a.name  <  b.name ? -1 :
                    a.name  >  b.name ?  1 : 0;
        }

        return unsorted_arr.sort( byName );
    }

    sorted = unsorted.length > 0 ? sortLocs( unsorted ) : sortLocs( arr );
    // sorted = unsorted;

    return sorted;

} );


// ----------------------------------------------------------------------------
// ...  LocBox method   fill_LocsSets
//          take all our locations ( this.locs ),
//          store the possibly fitting ones in this.ourLocs and
//          and fill the LocsSet for this.actLocs
// ----------------------------------------------------------------------------

LocBox.method( "fill_LocsSets", function ( ) {
    var locs    = this.locs;
    var mode    = this.parent.mode;

    var fittingLocs = [];                       // Locs objects array
    var thisLoc, i;

    for ( i = 0; i < locs.length; i++ ) {
        thisLoc = locs[ i ];                // Locs object

        if ( ( this.o_or_d === 'dest' &&
               ( thisLoc.ow_d === "D" || thisLoc.rt_d === "D" ) )   ||
             ( this.o_or_d === 'orig' &&
               ( thisLoc.ow_o === "O" || thisLoc.rt_o === "O" ) ) ) {

            fittingLocs.push( thisLoc );
        }
    }

    this.ourLocs = fittingLocs;
    this.actLocs.setLocs( fittingLocs );        // LocsSet object

    if ( ! this.actLocs.includesTLC( this.get_selectedTLC( ) ) ) {
        this.clear_selectedLoc();
    }

} );

LocBox.method( "reinitLocs", function( ) {

    // this.fill_LocsSets( );

    var tlc =  this.partnerBox.get_selectedTLC( );
    if ( tlc ) {
        this.build_for( this.parent.loc( tlc ) );
    }

    // TODO
    if ( this.panel && this.panel.panelmode === 'list' ) {
        this.panel.hideList( );
    }
    this.paint();
    // TODO
} );

LocBox.method( "build_partner_box", function ( ) {
    this.partnerBox.build_for( this.get_selectedLoc( ) );
} );


// ----------------------------------------------------------------------------
// ...  LocBox method   activate_destinations_from    --- create destinations
// ----------------------------------------------------------------------------

LocBox.method( "activate_destinations_from", function( loc ) {
    // creates destination list for an origin location

    var fittingLocs = [];                       // array of fitting Locs
    /// TEST LocsSet
    // var all_locs    = this.allLocs.getLocs( );
    var all_locs    = this.ourLocs;

    var thisLoc, i, route_index;

    for ( i = 0; i < all_locs.length; i++ ) {
        thisLoc     = all_locs[ i ];
        route_index = thisLoc.seqno;
        if ( !loc  || loc.str.substr( route_index, 1 ) === '1' ) {
            if ( this.parent.mode === 'OW' && thisLoc.ow_d === 'D' ||
                 this.parent.mode === 'RT' && thisLoc.rt_d === 'D' ) {
                fittingLocs.push( thisLoc );
            }
        }
    }

    this.actLocs.setLocs( fittingLocs );

    if ( ! this.actLocs.includesTLC( this.get_selectedTLC( ) ) ) {
        this.clear_selectedLoc();
    }

} );


// ----------------------------------------------------------------------------
// ...  LocBox method   activate_origins_to    --- create origins
// ----------------------------------------------------------------------------

LocBox.method( "activate_origins_to", function( loc ) {
    // create origins for a possibly given destination loc
    var ind = loc && loc.seqno ? loc.seqno : -1;

    var mode    = this.parent.mode;
    var actLocs = [];

    /// TEST LocsSet
    // var ourLocs = this.allLocs.getLocs( );      // array of Loc objects
    var ourLocs = this.ourLocs;                 // array of Loc objects

    for ( var i = 0; i < ourLocs.length; i++ ) {
        var thisLoc = ourLocs[ i ];
        if ( ind < 0 || thisLoc.str.substr( ind, 1 ) === "1" ) {
            if ( ( mode === "OW" && thisLoc.ow_o === "O" ) ||
                 ( mode === "RT" && thisLoc.rt_o === "O" ) ) {
                actLocs.push( thisLoc );
            }
        }
    }

    this.actLocs.setLocs( actLocs );
    if ( ! this.actLocs.includesTLC( this.get_selectedTLC( ) ) ) {
        this.clear_selectedLoc();
    }

} );


// ----------------------------------------------------------------------------
// ... LocBox method   set and get selectedLoc
// ----------------------------------------------------------------------------

LocBox.method( "selectLoc", function( loc ) {
    this.set_selectedLoc( loc );
    this.preselectLoc( loc );

    this.build_partner_box( );
    this.partnerBox.paint( );

    if ( this.actLocs ) {
        this.actLocs.setIndices( this.preselectedLoc );
    }

    if ( this.panel ) {
        this.panel.hide( );
        if ( !loc ) {
            this.panel.showTable( );
        }
    }

    if ( loc ) {
        this.ap_name_node.select( );
    }
} );

LocBox.method( "selectLocByIndex", function( loc_index ) {
    var loc = this.parent.locations[ loc_index ];
    this.selectLoc( loc );
} );

LocBox.method( "get_selectedLoc", function( ) {
    return this.selectedLoc;
} );

LocBox.method( "get_selectedTLC", function ( ) {
    return this.selectedLoc ? this.selectedLoc.tlc : '';
} );

LocBox.method( "clear_selectedLoc", function( ) {
    this.ap_name_node.value = '';
    this.ap_tlc_node.value  = '';

    if ( this.selectedLoc ) {
        this.selectedLoc.removeClass( 'selected', this.o_or_d );
    }
    this.selectedLoc = null;
} );

LocBox.method( "set_selectedLoc", function ( loc ) {
    if ( loc ) {
        this.ap_name_node.value = loc.name;
        this.ap_tlc_node.value  = loc.tlc;

        if ( this.selectedLoc ) {
            this.selectedLoc.removeClass( 'selected', this.o_or_d );
        }

        loc.addClass('selected', this.o_or_d);
        this.selectedLoc = loc;
    }
    else {
        this.clear_selectedLoc( );
    }
} );


// ----------------------------------------------------------------------------
// ... LocBox method   set and get preselectedLoc
// ----------------------------------------------------------------------------


LocBox.method( "preselectLoc", function( loc, no_scroll ) {
    if ( this.preselectedLoc ) {
        this.preselectedLoc.removeClass( 'preselected', this.o_or_d );
    }

    var myLoc = loc || this.actLocs.getFirstLoc( );

    if ( myLoc ) {
        this.preselectedLoc = myLoc;
        if ( !no_scroll ) {
            this.preselectedLoc.scroll_into_view( this.o_or_d );
        }
        this.preselectedLoc.addClass( 'preselected', this.o_or_d );
    }
} );

LocBox.method( "preselectFirstLoc", function( ) {
    this.preselectLoc( );
} );

LocBox.method( "preselectLocByIndex", function( loc_index ) {
    var loc = this.parent.locations[ loc_index ];
    this.preselectLoc( loc );
} );

LocBox.method( "get_preselectedLoc", function( ) {
    return this.preselectedLoc;
} );


// ----------------------------------------------------------------------------
// ... LocBox method   paint
// ----------------------------------------------------------------------------

LocBox.method( "paint", function( ) {

    var loc_nodes = [];                             // DOM nodes array
    var ourLocs = this.actLocs.getLocs( );          // Locs array
    for ( var i = 0; i < ourLocs.length; i++ ) {
        var p = ourLocs[ i ].get_box_node( this, "table" );
        loc_nodes.push( p );
        if ( this.get_selectedTLC( ) === ourLocs[ i ].tlc ) {
            this.set_selectedLoc( ourLocs[ i ] );
        }
    }

    if ( this.panel ) {
        this.panel.update_locs( loc_nodes );
        this.panel.paint();
    }

    var startLoc = this.get_selectedLoc( );
    this.preselectLoc( startLoc, true );
    this.actLocs.setTableIndex( startLoc );

} );


LocBox.method( "presentMatches", function( matches, index ) {

    if ( matches.length > 0 ) {
        if ( this.get_selectedLoc( ) ) {
            this.save_ap( );
            this.selectLoc( null );
            this.restore_ap( );
        }

        this.panel.paintList( matches );
        this.preselectLoc( matches[ 0 ] );
        this.panel.showList( );
    }
    else {
        this.panel.showTable( );
    }

} );


LocBox.method( "save_ap", function( )    { this.inp = this.ap_name_node.value; } );
LocBox.method( "restore_ap", function( ) { this.ap_name_node.value = this.inp; } );
LocBox.method( "get_tlc_node", function( ) { return this.ap_tlc_node; } );

/* ----------------------------------------------------------------------------
 * ...	LocBox  --- the end
 * ------------------------------------------------------------------------- */


// ---------------------------------------------------------------------------
//
//  LocBoxPair        Location Box Pair
//
//  Synopsis:
//
//      var minimal_cfg = {                 // ... these are needed
//          locations: Locations,           // Array of Loc Objects
//          mode: {
//              rt_id: 'mode_rt',           // roundtrip radio id
//              ow_id: 'mode_ow',           // oneway radio id
//              mode_name: 'mode'           // common html loc_name
//          },
//          orig: {                         // Origin
//              input:  {                   // DOM input node ids
//                  ap:  'apOrigName',      //  id of loc_name field
//                  tlc: 'apOrigTlc'        //  id of tlc field
//              },
//          dest: {                         // Destination
//              input:  {                   // DOM input node ids
//                  ap:  'apDestName',      // id of input DOM node
//                  tlc: 'apDestTlc'        // id of tlc field
//              }
//          }
//      };
//
//
//      var cfg = {
//          locations: Locations,           // Array of Loc Objects
//          mode: {
//              rt_id: 'mode_rt',           // roundtrip radio id
//              ow_id: 'mode_ow',           // oneway radio id
//              mode_name: 'mode'           // common html loc_name
//          },
//          orig: {                         // Origin
//              tlc:    'FRA',              // default tlc ( optional )
//              input:  {                   // DOM input node ids
//                  ap:  'apOrigName',      //  id of loc_name field
//                  tlc: 'apOrigTlc'        //  id of tlc field
//              },
//              delimit_locs_to: [ 'FRA', 'BRE', 'HAM', 'DUS', 'MUC' ],
//              panel: {
//                  max_list_height: 200,
//                  max_table_height: 300,
//                  header_txt: '$table_header_orig',
//                  offset_x: -100,
//                  offset_y: 0,
//                  hide_ids: {
//                      on_table_view: [],
//                      on_list_view: null
//                  }
//              }
//          },
//          dest: {                         // Destination
//              tlc:    'PMI',              // default TLC ( optional )
//              input:  {                   // DOM input node ids
//                  ap:  'apDestName',      // id of input DOM node
//                  tlc: 'apDestTlc'        // id of tlc field
//              },
//              delimit_locs_to: [ 'PMI', 'IBZ', 'ANC', 'LAS', 'MRU', 'AYT' ],
//              panel: {
//                  max_list_height: 200,
//                  max_table_height: 300,
//                  header_txt: '$table_header_dest',
//                  offset_x: 0,
//                  offset_y: 0,
//                  hide_ids: {
//                      on_table_view: [],
//                      on_list_view: []
//                  }
//              }
//          }
//      };
//
//
//      var lbp = new LocBoxPair( cfg );
//
// ---------------------------------------------------------------------------

function LocBoxPair ( cfg ) {

    var myLBP = this;
    var i;

    //  1. if cfg.static_mode defined, take cfg.static_mode.value
    //  2. else, if mode defined, inspect the dom
    //  3. else take 'RT' as default

    if ( cfg.static_mode && cfg.static_mode.value) {
        this.mode = this.static_mode = cfg.static_mode.value;
    }
    else {
        if ( cfg.mode ) {
            var mode = 'RT';                   // default: return flight
            if ( cfg.mode.ow_id ) {
                var ow_el = xGetElementById( cfg.mode.ow_id );
                if ( ow_el && ow_el.checked ) {
                    mode = 'OW';
                }
            }
            this.mode = mode;
            this.static_mode = null;
        }
        else {
            this.mode = this.static_mode = 'RT';
        }
    }

    this.locations  = cfg.locations;                // Array of Loc objects

    // Build loc index for reverse lookup
    this.locIndex   = {};
    for ( i = 0; i < this.locations.length; i++ ) {
        this.locIndex[ this.locations[ i ].tlc ] = this.locations[ i ].seqno;
    }

    // Initialize Boxes ( Origin and Destination )
    try {
        this.origBox = new LocBox( this, "orig", cfg.orig );
        this.destBox = new LocBox( this, "dest", cfg.dest );
        this.origBox.set_partnerBox( this.destBox );
        this.destBox.set_partnerBox( this.origBox );

        this.init_boxes( cfg.orig.tlc, cfg.dest.tlc );

    }
    catch( e ) {
        alert( "LBP - Could not create Location Boxes " + e );
        return null;
    }

    // Setup Loc Mode Switch
    if ( !this.static_mode ) {
        try {
            this.node_mode_ow = xGetElementById( cfg.mode.ow_id );
            this.node_mode_rt = xGetElementById( cfg.mode.rt_id );

            //// TEST only roundtrips
            this.set_locMode_handlers( this.node_mode_ow, 'OW' );
            this.set_locMode_handlers( this.node_mode_rt, 'RT' );
        }
        catch( e ) {
            alert( "LBP - Could not create Loc Mode Selection " + e );
            return null;
        }
    }

    return this;
}


/* ----------------------------------------------------------------------------
 * ...  Get Loc object by tlc or index
 * ------------------------------------------------------------------------- */

LocBoxPair.method( "unload", function ( ) {
    // Throw everything away. This is a special service for the IE,
    // which has an own understanding of garbage collection.

/*@cc_on

    var i;

    try {
        this.origBox.unload( );
    }
    catch( e ) {
        alert( "LBP --- error unloading origBox object " + e );
    }

    try {
        this.destBox.unload( );
    }
    catch( e ) {
        alert( "LBP --- error unloading destBox object " + e );
    }

    try {
        for ( i = 0; i < this.locations.length; i++ ) {
            this.locations[ i ].unload( );
        }
    }
    catch( e ) {
        alert( "LBP --- error unloading location objects " + e );
    }
@*/

} );


LocBoxPair.method( "loc", function ( tlc ) {
    return  this.locByTLC( tlc );
} );

LocBoxPair.method( "locByTLC", function ( tlc ) {
    return  tlc && isDef( this.locIndex[ tlc ] ) ?
                this.locations[ this.locIndex[ tlc ] ] : null;
} );

LocBoxPair.method( "locByIndex", function ( idx ) {
    return idx && isDef( this.locations[ idx ] ) ?
                this.locations[ idx ] : null;
} );

LocBoxPair.method( "getOrigNode", function ( ) {
    return this.origBox.get_tlc_node( );
} );

LocBoxPair.method( "getDestNode", function ( ) {
    return this.destBox.get_tlc_node( );
} );

LocBoxPair.method( "getLocMode", function ( ) {
    return this.mode;
} );

/* ----------------------------------------------------------------------------
 * ...  Check for valid location tlc
 * ------------------------------------------------------------------------- */

LocBoxPair.method( "check_tlc", function ( tlc ) {
    return( typeof( this.locIndex[ tlc  ] ) != 'undefined' ? true : false );
} );


/* ----------------------------------------------------------------------------
 * ...  Initialize boxes to passed values of destination and/or origin TLC
 *      ...  mainly delegated to the boxes themselves
 * ------------------------------------------------------------------------- */

LocBoxPair.method( "init_boxes", function( otlc, dtlc ) {
    // bj_alert( "LocBoxPair - init_boxes --- starting" );

    if ( this.check_tlc( otlc ) ) {
        this.destBox.activate_destinations_from( this.locByTLC( otlc ) );
    }
    this.destBox.paint();
    // this.destBox.preselectFirstLoc( );

    if ( this.check_tlc( dtlc ) ) {
        this.origBox.activate_origins_to( this.locByTLC( dtlc ) );
    }
    this.origBox.paint();
    // if ( !this.origBox.get_selectedLoc( ) ) {
    //     this.origBox.preselectFirstLoc( );
    // }


    // bj_alert( "LocBoxPair - init_boxes --- done" );
} );


/* ----------------------------------------------------------------------------
 * ...  Reinit box locations on locmode switch ( 'OW' / 'RT' )
 *      ...  mainly delegated to the boxes themselves
 * ------------------------------------------------------------------------- */

LocBoxPair.method( "setLocmode", function( m ) {
    this.mode = m && isStr( m ) && m.toUpperCase() === 'OW' ? 'OW' : 'RT';

    this.origBox.reinitLocs();
    this.destBox.reinitLocs();
} );


LocBoxPair.method( 'set_locMode_handlers', function( el, p ) {
    var that = this;

    var factory = function( node, para ) {
        return {
            _click:  function( ) {
                    // bj_alert( "set_locMode_handlers ... click" );
                    that.setLocmode( para );
                },
            _unload: function( ) {
                    // bj_alert( "set_locMode_handlers ... unload" );
                    purge_node( node );
                }
        };
    };
    var hd = factory( el, p );

    set_handler_for( el, "onclick",  hd._click  );
    // set_handler_for( el, "onunload", hd._unload );

} );

// --------------------------------------------------------------------------
// ... LocBoxPair   ... the End
// --------------------------------------------------------------------------


