/* copied from jslint guy */
/* use this when creating a new object that inherits from another
 * eg. var newObject = Object.create(existingObject);
*/
//This is done to stop the user from clicking the "Back" button.
history.forward();
//The above is done to stop the user from clicking the "Back" button.

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
if (typeof Array.clone !== 'function') {
    Array.clone = function (o) {
        return $.extend(true, [], o);
    };
}

/*extend jQuery browser functions to return boolean for specific versions*/
jQuery.extend(jQuery.browser, {
    msie6: function () {
        return jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 6;
    },
    msie7: function () {
        return jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 7;
    },        
    msie8: function () {
        return jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 8;
    },    
    ff2: function () {
        return $.browser.version.substr(0,3) === '1.8';
    },
    mobileRexEx: /iPhone|Andriod|BlackBerry|Nokia|SymbianOS/i,
    isMobileDevice: function () {
        var agent = navigator.userAgent; 
        return navigator.userAgent.match(this.mobileRexEx) !== null; 
    },
    supportsPng: function () {
        return !jQuery.browser.msie6();
    },
    imageExtension: function () {
        return $.browser.supportsPng() ? '.png' : '.gif';
    },
    spriteCssClass: function () {
        return 'master_sprite_' + ($.browser.supportsPng() ? 'png' : 'gif');
    },
    httpsPattern: /https/ig,
    isHttps: function () {
        return this.httpsPattern.test(document.location.protocol);
    }
});

if (typeof SERKO === "undefined") {
    SERKO = {};
}

jQuery.extend(SERKO, 
    {
        autocompleter       : {
                                width : 277
                            },
        minimumTimeout      : 12,
        waitWindowShowing   : false,
        fadeDuration        : 'fast',
        slideDuration       : 'fast',
        dateFormat          : 'd MMM yyyy',
        formatDate          : function (dateString) { return SERKO.formatDateOrTime(dateString, SERKO.dateFormat); },
        timeFormat          : 'hh:mm tt',
        formatTime          : function (timeString) { return SERKO.formatDateOrTime(timeString, SERKO.timeFormat); },
        dateTimeFormat      : function () { return SERKO.dateFormat + ' ' + SERKO.timeFormat; },
        formatDateTime      : function (dateTimeString) { return SERKO.formatDateOrTime(dateTimeString, SERKO.dateTimeFormat()); },
        dayDateTimeFormat   : function () { return 'ddd ' + SERKO.dateFormat + ' ' + SERKO.timeFormat;},
        formatDayDateTime   : function (dayDateTimeString) { return SERKO.formatDateOrTime(dayDateTimeString, SERKO.dayDateTimeFormat()); },
        dayDateFormat       : function () { return 'dddd, ' + SERKO.dateFormat;},
        formatDayDate          : function (dayDateString) { return SERKO.formatDateOrTime(dayDateString, SERKO.dayDateFormat()); },
        creditCardDateFormat: 'MMMM yyyy',
        formatcreditCardDate  : function (creditCardDate) { return SERKO.formatDateOrTime(creditCardDate, SERKO.creditCardDateFormat); },
        formatDateOrTime : function (dateOrTimeString, format) { 
                                try {
                                    if ($.isUndefined(dateOrTimeString)) {
                                        return '';
                                    }
                                    if (typeof dateOrTimeString === 'object') {
                                        return dateOrTimeString.toString(format);
                                    } else {
                                        return dateOrTimeString.length === 0 ? dateOrTimeString : Date.parse(dateOrTimeString).toString(format);
                                    }
                                } catch (e) {
                                    return dateOrTimeString;
                                }
                            },
        clearFloat      : '<div class="float_clear"></div>',
        tracingEnabled  : function () { return !$.isUndefined(SERKO.Cookies.getItem('soTracing')); },
        enableTracing  : function (enable) {
            if (enable) {
                SERKO.Cookies.set(new SERKO.Cookie('soTracing', 'on'));
            } else {
                SERKO.Cookies.reset('soTracing');
            }
        },
        jumpPage        : function (url) { window.location.href = url; },
        logout          : function () { SERKO.Cookies.reset();SERKO.jumpPage('so-login.asp'); },
        key             : {
                            SHIFT   : 16,
                            CTRL    : 17,
                            ALT     : 18,
                            LEFT    : 37,
                            UP      : 38,
                            RIGHT   : 39,
                            DOWN    : 40,
                            DELETE  : 46,
                            TAB     : 9,
                            ENTER   : 13,
                            ESCAPE  : 27,
                            COMMA   : 188,
                            PAGEUP  : 33,
                            PAGEDOWN: 34,
                            BACKSPACE: 8,
                            APOSTROPHE: 222
	                    },
        // preload animated loading gif so it displays when required, rather than spending its time downloading	                    
        loadingImage    :   $(document.createElement('div')).append($(document.createElement('img')).attr({ 'id':'information_loading', 'alt':'', 'src':'SOLV2/images/status_loading_small.gif'})),
        // copy parent PageState window PageState
        copyParentPageState : function () { jQuery.extend(SERKO, { PageState : window.parent.SERKO.PageState }); },
        // copy parent pageOptions window pageOptions
        copyParentPageOptions : function () { jQuery.extend(SERKO, { pageOptions : window.parent.pageOptions }); },
        // gets the img to use for stop overs
        stopsImg        : function (stops) {
                              if (stops.length === 0 || parseInt(stops) === 0) return '';
                              var img_src = $.browser.supportsPng() 
                                    ? 'SOLV2/Images/so-qb-air-select/stops_' + stops + '.png'
                                    : 'SOLV2/Images/so-qb-air-select/stops_' + stops + '_deselected.gif';
                              return '<img alt="" src="' + img_src + '" title="This flight has ' + stops + ' Stop over(s)" class="information"/>';
                        },
        // helper method to validate all required input/selects, returns the jquery collection of requried items that are blank
        /* argument 1 is optional options 
            {
                requiredSelector : 'string',
                errorDisplayId   : 'string',
                context          : jQuery object (defaults to $(document),
                numerics         : [] array of string ids which are numberics (No zeros allowd),
                autocompleters   : [] array of ui.autocompleter variables
            }
        
        */
        validateRequireds    : function () {
                                var options = jQuery.extend({
                                                                requiredSelector : 'input.required:visible, select.required:visible, textarea.required:visible',
                                                                errorDisplayId   : '#page_error',
                                                                context          : $(document),
                                                                numeric          : [],
                                                                autocompleters   : []
                                                            }, arguments[0]);
                                
                                var pageError = $(options.errorDisplayId);
                                var requireds = $(options.requiredSelector, options.context);
                                
                                pageError.removePageError();
                                requireds.removeError();
                                
                                var blankRequireds = requireds.filter(':blank'),
                                    numericCount = 0;

                                // check required nuermics which are zero
                                $.each(options.numeric, function (index, id) {
                                    var numeric = requireds.filter(id);
                                    if (numeric.length === 0) {
                                        return true; // continue
                                    }
                                    var value = numeric.val().replace(/,/g,'');
                                    if (isNaN(value)) {
                                        blankRequireds = blankRequireds.add(numeric);
                                        numericCount += 1;
                                    }
                                });
                                
                                // add error here as autocompleters have their own addError
                                if (blankRequireds.length > 0) {
                                    blankRequireds.addError('');
                                }
                                
                                // check autocompleters
                                $.each(options.autocompleters, function (index, autocompleter) {
                                    var value = autocompleter.serkoautocompleter('getValue');
                                    if (value.id.length === 0 || $.isUndefined(value.data)) {
                                        autocompleter.serkoautocompleter('addError');
                                        blankRequireds = blankRequireds.add(autocompleter);
                                    }
                                });
                                
                                if (blankRequireds.length > 0) {
                                    pageError.showPageError((numericCount === blankRequireds.length ? 'Numeric ' : '') + 'Required');
                                }
                                
                                return blankRequireds;
                        },
                        
        // displays list of travellers
        displayTravellers : function (options) {
            options.travellersDiv.empty();
            
            if (options.travellers.length == 0 ) {
                if (options.btnSearch.is(':visible') && options.btnCreate.is(':visible')) {
                    options.travellersDiv.append('<div class="traveller_info">Click \'Search\' to add an existing Traveller.</div>')
                                         .append('<div class="traveller_info">Click \'Create\' to create a new Traveller.</div>');
                } else if (options.btnSearch.is(':visible')) {
                    options.travellersDiv.append('<div class="traveller_info">Click \'Search\' to add an existing Traveller.</div>');
                } else if (options.btnCreate.is(':visible')) {
                    options.travellersDiv.append('<div class="traveller_info">Click \'Create\' to create a new Traveller.</div>');
                } 
                return;
            }
            
            $.each(options.travellers, function(i , item){
                var row = $(document.createElement('div')).addClass('row');
                var cell1 = $(document.createElement('div')).addClass('cell');
                if (options.canCreateTraveller || options.userSelectedTravellers > 1) { 
                    cell1.append($(document.createElement('img')).addClass('link').addClass('deletebutton').attr({src: 'solv2/images/master_button_delete_enabled.gif', alt: '', title : 'Click to delete traveller'}).data('traveller', item).bind('click', options.context, options.deleteTravellerCallback));
                }
                row.append(cell1);
                
                var cell2 = $(document.createElement('div')).addClass('cell').addClass('strong information').attr('title', (item.CL_sClient_Profile_Code === null || item.CL_sClient_Profile_Code.length === 0 ? 'Created Traveller' : 'ProfileCode: ' + item.CL_sClient_Profile_Code)).text(item.PA_sPassenger_Name);
                row.append(cell2);
                row.append(SERKO.clearFloat);
                options.travellersDiv.append(row);
            });
        },
        
        getBookingTypeIcon: function (bookingType) {
            if (bookingType === SERKO.Globals.bookingType.travelAgency) {
                return '<img class="information ' + $.browser.spriteCssClass() + ' ' + SERKO.getBookingTypeIconClass(bookingType) + '" src="SOLV2/images/transparent.gif" alt="Travel Agency Booking" title="This is a Travel Agency Booking."/>';
            }
            if (bookingType === SERKO.Globals.bookingType.quickBooking) {
                return '<img class="information ' + $.browser.spriteCssClass() + ' ' + SERKO.getBookingTypeIconClass(bookingType) + '" src="SOLV2/images/transparent.gif" alt="Quick Booking" title="This is a SERKO Online Quick Booking."/>';
            }
            if (bookingType === SERKO.Globals.bookingType.customBooking) {
                return '<img class="information ' + $.browser.spriteCssClass() + ' ' + SERKO.getBookingTypeIconClass(bookingType) + '" src="SOLV2/images/transparent.gif" alt="Custom Booking" title="This is a SERKO Online Custom Booking request."/>';
            }
            if (bookingType === SERKO.Globals.bookingType.travelAgencyTakeover) {
                return '<img class="information ' + $.browser.spriteCssClass() + ' ' + SERKO.getBookingTypeIconClass(bookingType) + '" src="SOLV2/images/transparent.gif" alt="Travel Agency Takeover" title="Control of this Booking has been Taken over by the Travel Agency."/>';
            }
            return bookingType;
        },
        
        getBookingTypeIconClass: function (bookingType) {
            if (bookingType === SERKO.Globals.bookingType.travelAgency) {
                return 'bookingtype_travelagency';
            }
            if (bookingType === SERKO.Globals.bookingType.quickBooking) {
                return 'bookingtype_web';
            }
            if (bookingType === SERKO.Globals.bookingType.customBooking) {
                return 'bookingtype_custombooking';
            }
            if (bookingType === SERKO.Globals.bookingType.travelAgencyTakeover) {
                return 'bookingtype_travelagencytakeover';
            }
            return bookingType;
        },
        
        getBookingStatusIcon: function (bookingStatus, toolTip) {
            if (bookingStatus === 'Pending') {
                return '<img class="information" src="SOLV2/images/status_pending.gif" alt="Pending Authorisation" title="This Booking is Pending Authorisation. ' + toolTip + '"/>';
            }
            if (bookingStatus === 'Authorised') {
                return '<img class="information" src="SOLV2/images/status_authorised.gif" alt="Authorised" title="This Booking has been Authorised and is awaiting Ticketing. ' + toolTip + '"/>';
            }
            if (bookingStatus === 'Rejected') {
                return '<img class="information" src="SOLV2/images/status_rejected.gif" alt="Rejected" title="This Booking has been Rejected by an Authoriser. See its details for more info. ' + toolTip + '"/>';
            }
            if (bookingStatus === 'Ticketed') {
                return '<img class="information" src="SOLV2/images/status_ticketed.gif" alt="Ticketed" title="This Booking has been Ticketed. ' + toolTip + '"/>';
            }
            if (bookingStatus === 'Pending Cancellation') {
                return '<img class="information" src="SOLV2/images/status_pendingcancellation.gif" alt="Pending Cancellation" title="This Booking is Pending Cancellation. ' + toolTip + '"/>';
            }
            if (bookingStatus === 'Cancelled') {
                return '<img class="information" src="SOLV2/images/status_cancelled.gif" alt="Cancelled" title="This Booking has been Cancelled. ' + toolTip + '"/>';
            }
            return bookingStatus;
        },
        
        getContainsImage: function (contains) {
            var CONTAINS_AIR   = 1;
            var CONTAINS_CAR   = 2;
            var CONTAINS_HOTEL = 4;
            var CONTAINS_OTHER = 8;
            contains = parseInt(contains, 10);
            
            var imgs = '';
            
            if ((contains & CONTAINS_AIR) === CONTAINS_AIR) {
                imgs += '<img class="information" src="SOLV2/images/itinerary/itinerary_segment_flight.gif" alt="Flight" title="Contains at least 1 Flight segment" />';
            }
            if ((contains & CONTAINS_CAR) === CONTAINS_CAR) {
                imgs += '<img class="information" src="SOLV2/images/itinerary/itinerary_segment_car.gif" alt="Car" title="Contains at least 1 Car segment" />';
            }
            if ((contains & CONTAINS_HOTEL) === CONTAINS_HOTEL) {
                imgs += '<img class="information" src="SOLV2/images/itinerary/itinerary_segment_hotel.gif" alt="Hotel" title="Contains at least 1 Hotel segment" />';
            }       
            
            return imgs; 
        },
        
        url: function (path) {
            var parts = path.split('?');
            var url = parts[0];
            var querystring = parts.length === 1 ? '' : parts[1];
            var urlParts = url.split('/');
            var fileName = urlParts[urlParts.length - 1];
            var fileParts = fileName.split('.');
            var extension = fileParts.length === 1 ? '' : fileParts[fileParts.length - 1];
            return {
                url : url,
                file : fileName,
                name : fileParts[0],
                extension : extension,
                querystring : querystring
            };
        },
        
        loadCorporateLogo: function (img, querystring) {
            if (img.src.indexOf('blank.gif') > -1) {
                return;
            }
            var url = SERKO.url(img.src);
            switch (url.extension) {
                case 'gif':
                    img.src = 'SOLV2/images/CorporateLogos/' + url.name + '.jpg' + querystring;
                    return;
                case 'jpg':
                    img.src = 'SOLV2/images/CorporateLogos/' + url.name + '.png' + querystring;
                    return;
                default:
                img.src = 'SOLV2/images/CorporateLogos/blank.gif';
            }
        },
        
        loadAgencyLogo: function (img, querystring) {
            if (img.src.indexOf('blank.gif') > -1) {
                return;
            }
            var url = SERKO.url(img.src);
            switch (url.extension) {
                case 'gif':
                    img.src = 'SOLV2/images/AgencyLogos/' + url.name + '.jpg' + querystring;
                    return;
                case 'jpg':
                    img.src = 'SOLV2/images/AgencyLogos/' + url.name + '.png' + querystring;
                    return;
                default:
                img.src = 'SOLV2/images/AgencyLogos/blank.gif';
            }
        },
        
        // status bar
        statusBar : {
            _container: undefined,
            _showing: false,
            _setSizePosition: function () {
                var wnd = $(window),
                    height = this._container.height();
                this._container.css('top', (wnd.scrollTop() + wnd.height() - height - this._heightExtra)  + 'px');
            },
            initialise: function () {
                var self = this;
                if ($.isUndefined(self._container)) {
                    self._container = $(document.createElement('div')).addClass('statusbar').hide();
                    $(document.body).append(self._container);
                    var layout = self._container.layout();
                    self._heightExtra = layout.border.top + layout.border.bottom + layout.padding.top + layout.padding.bottom;
                }            
            },
            show: function (message, callback) {
                var self = this;
                self._showing = true;
                self.initialise();                
                self._container.empty().html(message).show();
                self._setSizePosition();
                $(window).bind('scroll.statusbar', function () {
                    self._setSizePosition();
                });
                
                if (self._showing === false) {
                    self.hide();
                }
                self._showing === false;
                
                if (!$.isUndefined(callback)) {
                    setTimeout(callback, SERKO.minimumTimeout);
                }
            },
            hide: function () {
                if (!$.isUndefined(this._container)) {
                    this._container.hide();
                    $(window).unbind('scroll.statusbar');
                }
                this._showing = false;
            }
        },
        
        mapping : {
        
            providers : {
                googleV3    : 'googleV3',
                microsoft   : 'microsoft'  // not yet coded
            },
            
            instance    : [],
            mapapi      : undefined,
            mapIconType : {
                airport             : 'airport',
                city                : 'city',
                office              : 'office',
                address             : 'address',
                hotel               : 'hotel',
                hotelBreaksPolicy   : 'hotelBreaksPolicy',
                car                 : 'car',
                carBreaksPolicy     : 'carBreaksPolicy',
                carOther            : 'carOther',
                flightN             : 'flightN',
                flightNE            : 'flightNE',
                flightE             : 'flightE',
                flightSE            : 'flightSE',
                flightS             : 'flightS',
                flightSW            : 'flightSW',
                flightW             : 'flightW',
                flightNW            : 'flightNW'
            },
            mapIcons    : [],
            
            getDefaultOptions : function () {
                return { 
                    provider : SERKO.mapping.providers.googleV3,
                    container: undefined,
                    loaded   : $.emptyFunction
                };
            },
            
            // initialise mapping for the mapping provider, loaded callback returns mapping supported for browser
            initialise: function (instanceName, options) {

                if (!instanceName) {
                    alert('Developer Error: A mapping \'instanceName\' must be supplied for the instance of the map');
                }
            
                if (!this.instance[instanceName]) {
                    this.instance[instanceName] = 
                    {
                        options: $.extend({}, this.getDefaultOptions(), options)
                    };
                }
                
                this[this.instance[instanceName].options.provider].initialise(instanceName);
            },
            
            create: function (instanceName, options) {
                if (!instanceName) {
                    alert('Developer Error: A mapping \'instanceName\' must be supplied for the instance of the map');
                }
                if (!this.instance[instanceName]) {
                    alert('Developer Error: Mapping instance \'' + instanceName + '\' does not exist');
                }
                return this[this.instance[instanceName].options.provider].create(instanceName, options);
            },
            
            destroy: function (instanceName) {

                function destroyInstance(name) {
                    if (!SERKO.mapping.instance[name]) {
                        return;
                    }
                    
                    if (SERKO.mapping.instance[name].options) {
                        SERKO.mapping[SERKO.mapping.instance[name].options.provider].destroy(name);
                    }
                    
                    SERKO.mapping.instance[name] = undefined;
                }
                
                if (instanceName) {
                    destroyInstance(instanceName);
                } else {
                    for (var name in SERKO.mapping.instance) {
                        destroyInstance(name);    
                    }
                }
                
                $(window).unbind('unload.mapping');
            },
            
            reset: function (instanceName) {

                function resetInstance(name) {
                    if (!SERKO.mapping.instance[name] || !SERKO.mapping.instance[name].options) {
                        return;
                    }
                    SERKO.mapping[SERKO.mapping.instance[name].options.provider].reset(name);
                } 
                
                if (instanceName) {
                    resetInstance(instanceName);
                } else {
                    for (var name in SERKO.mapping.instance) {
                        resetInstance(name);
                    }
                }
                
            },
            
            getStaticMap : function (providerName, options) {
                return SERKO.mapping[providerName].getStaticMap(options);
            },
            
            createPoint: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].createPoint(instanceName, options);
            },
            
            createMarker: function (instanceName, options) {

                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
            
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].createMarker(instanceName, options);
            },
            
            removeMarker: function (instanceName, options) {
            
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }

                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].removeMarker(instanceName, options);
            },
            
            createPolyline : function (instanceName, options) {
            
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
            
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].createPolyline(instanceName, options);
            },            
            
            addEvent: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].addEvent(instanceName, options);
            },            
            
            removeEvent : function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }            
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].removeEvent(instanceName, options);
            },         

            triggerEvent : function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }            
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].triggerEvent(instanceName, options);
            },       
                        
            drawCircle: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }            
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].drawCircle(instanceName, options);
            },   
            
            removeCircle: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }                        
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].removeCircle(instanceName, options);
            },   

            panToMarker: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }                                    
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].panToMarker(instanceName, options);
            },
            
            distanceInKm: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }                                                
                return SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].distanceInKm(instanceName, options);
            },
            
            panToWorldView: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].panToWorldView(instanceName, options);
            },
            
            setOptions: function (instanceName, options) {
                if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].options) {
                    return;
                }
                SERKO.mapping[SERKO.mapping.instance[instanceName].options.provider].setOptions(instanceName, options);
            },
            
            
            /* calculates a Lat/Lng square from points travelled to */
            /* this method requires the latlng.js file */
            mapSquare : function () {
                return {
                    topLeft: {
                        lat: undefined, 
                        lng: undefined  
                    },
                    topRight: {
                        lat: undefined, 
                        lng: undefined  
                    },
                    bottomLeft: {
                        lat: undefined, 
                        lng: undefined  
                    },
                    bottomRight: {
                        lat: undefined, 
                        lng: undefined  
                    },
                    addPoint: function (lat, lng) {
                        /* calculate square as 0,0 to 180,360 */
                        lat = lat + 90;
                        lng = lng + 180;
                        
                        /* bottom left  lat 0, lng 0 */
                        if ($.isUndefined(this.bottomLeft.lat) || this.bottomLeft.lat > lat) {
                            this.bottomLeft.lat = lat;
                        }                
                        if ($.isUndefined(this.bottomLeft.lng) || this.bottomLeft.lng > lng) {
                            this.bottomLeft.lng = lng;
                        }
                        
                        /* bottom right lat 0, lng 360 */
                        if ($.isUndefined(this.bottomRight.lat) || this.bottomRight.lat > lat) {
                            this.bottomRight.lat = lat;
                        }                        
                        if ($.isUndefined(this.bottomRight.lng) || this.bottomRight.lng < lng) {
                            this.bottomRight.lng = lng;
                        }

                        /* top left lat 180, lng 0 */
                        if ($.isUndefined(this.topLeft.lat) || this.topLeft.lat < lat) {
                            this.topLeft.lat = lat;
                        }                
                        if ($.isUndefined(this.topLeft.lng) || this.topLeft.lng > lng) {
                            this.topLeft.lng = lng;
                        }

                        /* top left lat 180, lng 360 */
                        if ($.isUndefined(this.topRight.lat) || this.topRight.lat < lat) {
                            this.topRight.lat = lat;
                        }                
                        if ($.isUndefined(this.topRight.lng) || this.topRight.lng < lng) {
                            this.topRight.lng = lng;
                        }                        
                    },
                    getSquareAsLatLng: function () {
                        return {
                            bottom: {
                                left: {
                                    lat: this.bottomLeft.lat - 90,
                                    lng: this.bottomLeft.lng - 180
                                },
                                right: {
                                    lat: this.bottomRight.lat - 90,
                                    lng: this.bottomRight.lng - 180
                                }
                            },                        
                            top: {
                                left: {
                                    lat: this.topLeft.lat - 90,
                                    lng: this.topLeft.lng - 180
                                },
                                right: {
                                    lat: this.topRight.lat - 90,
                                    lng: this.topRight.lng - 180
                                }
                            }
                        }
                    },
                    
                    getDiff: function () {
                        return {
                            lat: (this.topLeft.lat - this.bottomLeft.lat),
                            lng: (this.bottomRight.lng - this.bottomLeft.lng)
                        };
                    },
                    
                    getMiddle: function () {
                        var diff = this.getDiff();
                        return {
                            lat: (diff.lat === 0 ? this.topLeft.lat : this.bottomLeft.lat + (diff.lat / 2)) - 90,
                            lng: (diff.lng === 0 ? this.topLeft.lng : this.bottomLeft.lng + (diff.lng / 2)) - 180
                        };
                    },
                    
                    getSurfaceArea: function () {
                        var diff = this.getDiff();
                        return (diff.lat * diff.lng);
                    }                    
                };
            },
            
            googleV3 : {
            
                initialised: false,
                script: undefined,
                
                initialise: function (instanceName) {

                    if (this.initialised || this.script) {
                        SERKO.mapping.instance[instanceName].geocoder = new google.maps.Geocoder();
                        SERKO.mapping.instance[instanceName].options.loaded.call(this, true);
                        return;
                    }
                    
                    this.script = $('<script/>')
                            .attr({'type': 'text/javascript', src: ($.browser.isHttps() ? 'https' : 'http') + '://maps.google.com/maps/api/js?sensor=false&callback=SERKO.mapping.googleV3.loadMap'})
                            .data('instanceName', instanceName);

                    $(document).find('head:first').append(this.script);
                    $(window).bind('unload.mapping', function () {
                        SERKO.mapping.destroy();
                    });
                    
                },
                
                loadMap: function () {
                    this.initialised = true;
                    this.initialiseMapIcons();
                    var instanceName = this.script.data('instanceName');
                    SERKO.mapping.instance[instanceName].geocoder = new google.maps.Geocoder();
                    SERKO.mapping.instance[instanceName].options.loaded.call(this, true);
                },
                
                initialiseMapIcons: function () {
                
                    if (SERKO.mapping.mapIcons.length > 0) {
                        return;
                    }
                    
                    SERKO.mapping.mapIcons = [];
                    
                    var size = new google.maps.Size(28, 32),
                        origin = new google.maps.Point(0, 0),
                        ancor = new google.maps.Point(14, 32);    
                        
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.office] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_office.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.airport] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_airport.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.city] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_city.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.address] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_address.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.hotel] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_hotel.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.hotelBreaksPolicy] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_hotel_breakspolicy.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.car] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_car.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.carBreaksPolicy] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_car_breakspolicy.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.carOther] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_car_other.png", size, origin, ancor);
                    
                    // flights icons are different size and have no shadow
                    size = new google.maps.Size(19, 19);
                    anchor = new google.maps.Point(9, 9);    
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightNW] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_nw.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightNE] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_ne.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightSW] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_sw.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightSE] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_se.png", size, origin, ancor);
                    
                    // Flight N,S different again
                    size = new google.maps.Size(21, 22);
                    anchor = new google.maps.Point(10, 11);    
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightN] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_n.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightS] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_s.png", size, origin, ancor);
                    
                    // Flight E,W different again
                    size = new google.maps.Size(21, 21);
                    anchor = new google.maps.Point(10, 10);    
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightE] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_e.png", size, origin, ancor);
                    SERKO.mapping.mapIcons[SERKO.mapping.mapIconType.flightW] = new google.maps.MarkerImage("SOLV2/images/maps/map_marker_flight_w.png", size, origin, ancor);
                },
                
                // {container, infoWidth}
                create: function (instanceName, options) {
                    if (!this.initialised || !options.container) {
                        return;
                    }
                    
                    if (SERKO.mapping.instance[instanceName].map) {
                        return SERKO.mapping.instance[instanceName].map;
                    }
                    
                    var opts = $.extend({}, {center: new google.maps.LatLng(0, 0), mapTypeId: google.maps.MapTypeId.ROADMAP, zoom: 0}, options),
                        mapOptions = {
                            center: opts.center,
                            mapTypeId: opts.mapTypeId,
                            zoom: opts.zoom
                        };
                    
                    SERKO.mapping.instance[instanceName].map = new google.maps.Map(options.container, mapOptions);
                    SERKO.mapping.instance[instanceName].infowindow = new google.maps.InfoWindow({maxWidth: options.infoWidth});
                    SERKO.mapping.instance[instanceName].markers = [];
                    SERKO.mapping.instance[instanceName].overlays = [];                    
                    SERKO.mapping.instance[instanceName].listeners = [];
                    
                    return SERKO.mapping.instance[instanceName].map;
                },
                
                reset: function (instanceName) {
                    
                    if (!SERKO.mapping.instance[instanceName].map) {
                        return;
                    }
                    
                    SERKO.mapping.instance[instanceName].infowindow.close();
                    
                    $.each(SERKO.mapping.instance[instanceName].listeners, function (index, listener) {
                        google.maps.event.removeListener(listener);
                    });
                    SERKO.mapping.instance[instanceName].listeners = [];
                        
                    $.each(SERKO.mapping.instance[instanceName].markers, function (index, marker) {
                        marker.setMap(null);
                    });
                    SERKO.mapping.instance[instanceName].markers = [];
                        
                    $.each(SERKO.mapping.instance[instanceName].overlays, function (index, overlay) {
                        overlay.setMap(null);
                    });
                    
                    SERKO.mapping.instance[instanceName].overlays = [];     
                                       
                    SERKO.mapping.panToWorldView(instanceName, {});
               },
                
                destroy : function (instanceName) {
                    SERKO.mapping.googleV3.reset(instanceName);
                    SERKO.mapping.instance[instanceName].geocoder = undefined;

                    if (typeof SERKO.mapping.instance[instanceName].map === "undefined") {
                        return;
                    }

                    $(SERKO.mapping.instance[instanceName].map.getDiv()).empty();
                    SERKO.mapping.instance[instanceName].map = undefined;
                },
                
                panToWorldView: function (instanceName, options) {
                
                    if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].map) {
                        return;
                    }
                    
                    SERKO.mapping.instance[instanceName].map.setOptions({center: new google.maps.LatLng(0, 0, true), zoom: 1});
                },
                
                createPoint: function (instanceName, options) {
                    return new google.maps.LatLng(options.lat, options.lng);
                },
                
                // options {point, map, iconType, html, tooltip}
                createMarker: function (instanceName, options) {
                    
                    if (!SERKO.mapping.instance[instanceName] || !SERKO.mapping.instance[instanceName].map) {
                        return;
                    }                
                
                    var markerOptions = {
                            position: options.point,
                            map: SERKO.mapping.instance[instanceName].map,
                            title: options.tooltip,
                            flat: true,
                            icon: SERKO.mapping.mapIcons[options.iconType],
                            clickable: true,
                            draggable: false,
                            visible: true
                        },
                        marker = new google.maps.Marker(markerOptions);
                    
                    if (options.html) {
                        marker.html = options.html;
                        SERKO.mapping.instance[instanceName].listeners.push(
                            google.maps.event.addListener(marker, 'click', function () {
                                SERKO.mapping.googleV3.showInfo(instanceName, {marker: marker});
                            })
                        );
                    }
                    
                    SERKO.mapping.instance[instanceName].markers.push(marker);
                    return marker;
                },
                
                // {marker}
                removeMarker: function (instanceName, options) {
                    this.closeInfo(instanceName, {marker: options.marker});
                    options.marker.setMap(null);
                },
                
                removeCircle: function (instanceName, options) {
                    options.circle.setMap(null);
                },
                
                closeInfo: function (instanceName, options) {
                    if (!options || !options.marker || !options.marker.map) {
                        return;
                    }
                    var info = SERKO.mapping.instance[instanceName].infowindow,
                        infoPos = info.getPosition();
                        
                    if (infoPos === undefined) {
                        return;
                    }
                    
                    if (infoPos.equals(options.marker.getPosition())) {
                        info.close();
                    }
                },
                
                showInfo: function (instanceName, options) {
                    
                    var info = SERKO.mapping.instance[instanceName].infowindow,
                        dimensions = this._getIconDimensions(options.marker.getIcon());
                        
                    info.close();
                    info.setOptions(
                    {
                        content: options.marker.html,
                        position: options.marker.getPosition(),
                        pixelOffset: new google.maps.Size(0, dimensions.height, 'px', 'px')
                    });
                    info.open(options.marker.map, options.marker);
                },
                
                // options {map, points [], color, size, opacity}
                createPolyline: function (instanceName, options) {
                    var polylineOptions = {
                        path: options.points,
                        strokeColor: options.color,
                        strokeOpacity: options.opacity,
                        strokeWeight: options.size
                    };
                    
                    var polyline = new google.maps.Polyline(polylineOptions);
                    polyline.setMap(SERKO.mapping.instance[instanceName].map);
                    
                    SERKO.mapping.instance[instanceName].overlays.push(polyline);
                    
                    return polyline;
                },                
                
                
                // {object, eventName, handler}
                addEvent: function (instanceName, options) {
                    var listener = google.maps.event.addListener(options.object, options.eventName, options.handler);
                    SERKO.mapping.instance[instanceName].listeners.push(listener);
                },
                
                // {event}
                removeEvent: function (instanceName, options) {
                    google.maps.event.removeListener(options.event);
                },                
                
                // {object, eventName}
                triggerEvent: function (instanceName, options) {
                    google.maps.event.trigger(options.object, options.eventName);
                },                
                
                // {map, latLng, radius, strokeColor, strokeWeight, strokeOpacity, fillColor, fillOpacity}
                drawCircle: function (instanceName, options) {
                
                    var circle = new google.maps.Circle({
                        strokeColor: options.strokeColor,
                        strokeOpacity: options.strokeOpacity,
                        strokeWeight: options.strokeWeight,
                        fillColor: options.fillColor,
                        fillOpacity: options.fillOpacity,
                        center: options.latLng,
                        radius: options.radius * 1000 // convert km to metres
                    });
                    circle.setMap(SERKO.mapping.instance[instanceName].map);
                    
                    SERKO.mapping.instance[instanceName].overlays.push(circle);

                    return circle;
                },
                
                panToMarker: function (instanceName, marker) {
                    marker.map.panTo(marker.position);
                    google.maps.event.trigger(marker, 'click');                    
                },
                
                setOptions: function (instanceName, options) {
                    SERKO.mapping.instance[instanceName].map.setOptions(options);
                },
                
                // options {from: latlng, to: latlng}
                distanceInKm: function (instanceName, options) {
                    return LatLon.distHaversine(options.from.lat().toString().parseDeg(), options.from.lng().toString().parseDeg(), options.to.lat().toString().parseDeg(), options.to.lng().toString().parseDeg()).toString().formatAsNumber(2);
                },
                
                getStaticMap: function (options) {
                    var markers = '',
                        mapPosition = '',
                        path = '';
                        
                    if ($.isUndefined(options.markers) || options.markers.length === 0) {
                        markers = '&markers=size:mid|color:green|label:A|' + options.lat + ',' + options.lng;
                        mapPosition = 'center=' + options.lat + ',' + options.lng + '&zoom=' + options.zoom + '&';
                    } else {
                        $.each(options.markers, function (index, marker) {
                            markers += '&markers='
                                + 'size:mid|color:' + ($.isUndefined(marker.colour) ? 'green' : marker.colour) + '|'
                                + 'label:' + ($.isUndefined(marker.id) ? String.fromCharCode(97 + index) : marker.id).toUpperCase()
                                + '|' + marker.lat + ',' + marker.lng;
                        });
                        if (!$.isUndefined(options.path) && options.path.points.length > 0) {
                            path = '&path=color:0x' + options.path.colour + 'ff,weight:' + options.path.size;
                            $.each(options.path.points, function (pointIndex, point) {
                                path += '|' + point.lat + ',' + point.lng;
                            });
                        }
                        if (!$.isUndefined(options.zoom)) {
                            mapPosition = 'zoom=' + options.zoom + '&';
                        }
                    }
                    
                    return 'http://maps.google.com/maps/api/staticmap?sensor=false&' + mapPosition + 'size=' + options.width + 'x' + options.height + markers + path;
                },
                
                // googleV3 Helpers
                _getIconDimensions: function (icon) {
                    
                    for (var n in icon) {
                        if (!icon[n].width) {
                            continue;
                        }
                        return {
                            width: icon[n].width,
                            height: icon[n].height
                        };
                    }
                    
                    return {
                        width: 0,
                        height: 0
                    };
                }

            }
        },
        
        logger: {
            create: function () {
                return {
                    _items: [],
                    insert: function (label) {
                        this._items.push({label:label,time:new Date()});
                        return this;
                    },
                    reset: function () {
                        this._items = [];
                        return this;
                    },
                    toString: function () {
                        var response = '';
                        $.each(this._items, function (index, item) {
                            response += (response.length === 0 ? '' : '\n') + item.time.toString('T')  + ': ' + item.label;
                        });
                        return response;
                    }
                };
            }
        }
        
    }
);

/* light box */
    jQuery.extend(SERKO, {

        /*
        * LightBox for display modal type window or message box;
        *   LightBox.showPage(url[, options]) to load lightbox with an external page inside an iframe. 
        *       url     : is the url of the page to load
        *       options : optional options (see options below)
        *
        *   LightBox.showProcessingMessageBox(message) to show a lightbox small system processing window with bright message
        *       message : short message to show after the animated gif
        *
        *   LightBox.showMessageBox(title, message[, options]) to show a message box. Height is automatically calculated on message size
        *       title   : title of the message box
        *       message : message can be straight text or html
        *       options : optional options (see options below)
        *   
        *       options : are class of options to describe how the LightBox displays/interacts,
        *                 if anything other than the default is required then you will need to pass it through.
        *                 
        *                 defaults are;
        *
        *                   showButtons: default true,  (This will hide/show the buttons footer bar)
        *                   showClose  : false,
        *                   showPrint  : false,
        *                   showAccept : false,
        *                   showDecline: false,
        *                   showOk     : false,
        *                   showSave   : false,
        *                   showAdd    : false,
        *                   showCancel : false,
        *                   showYes    : false,
        *                   showNo     : false,
        *                   afterShow  : function () {} (this gets called once the lightbox is loaded and is visible
        *                   beforeHide    : function () {return true;}   (this is where you place a callback function, 
        *                                                  the signature is function (buttonPressed) where buttonPress = SERKO.LightBox.buttonType (string).  Must return true or false; false meaning that it will stop processing and therefore
        *                                                   will not hide the LightBox.
        *                   afterHide    : function () {}   (this is where you place a callback function, 
        *                                                  the signature is function (buttonPressed) where buttonPress = SERKO.LightBox.buttonType (string)
        *                   customButtons : Array of { isOk : true/false/undefined (captures the enter key and assigns it to this button), src : 'source of button image', buttonType : 'the key of the button that is returned during buttonpressed', width: number of pixels wide
        *                   params     : [] (This is where you can pass in an array of parameters to append to the querystring, passin as array of objects e.g. [ { name: 'flightid', value: 'ABC123' }, { name: 'Origin', value: 'AKL' } ]
        *                   context    : (place holder to store calling object if nes)
        *                   messageBoxType : string default '', css class name for message box title e.g. 'warning', 'alert' (Message Box only)
        *                   
        */

        LightBox: {
            buttonType: {
                Close   : 'Close',
                Print   : 'Print',
                Accept  : 'Accept',
                Decline : 'Decline',
                Ok      : 'Ok',
                Save    : 'Save',
                Add     : 'Add',
                Cancel  : 'Cancel',
                Yes     : 'Yes',
                No      : 'No'
            },
            showMode:   {
                page        : 'page',
                message     : 'message',
                processing  : 'processing'
            },
            container: null,
            container_content: null,
            loading: null,
            footer_buttons: null,
            footer_nobuttons: null,
            footer_center: null,
            overlay: null,
            currentShowMode: null,
            toString: function () { return 'SERKO.LightBox'; },
            context: undefined,

            initialize: function () {
                this.overlay = $(document.createElement('div')).attr({ 'id': 'lightbox_overlay' }).hide();
                this.overlay.click(function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                });
                this.overlay.dblclick(function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                });
                this.overlay.select(function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                });
                $(document.body).append(this.overlay);
                var imageExt = $.browser.supportsPng() ? 'png' : 'gif';
                this.container = $(document.createElement('div')).attr({ 'id': 'lightbox' }).hide();
                var spriteCss = $.browser.spriteCssClass();
                this.container.append(
                                                $(document.createElement('ul')).attr({ 'id': 'lightbox_header' })
                                                    .append($(document.createElement('li')).attr({ 'id': 'lightbox_header_left', 'class': 'float_left' }).addClass(spriteCss))
                                                    .append($(document.createElement('li')).attr({ 'id': 'lightbox_header_center', 'class': 'float_left' }))
                                                    .append($(document.createElement('li')).attr({ 'id': 'lightbox_header_right', 'class': 'float_left' }).addClass(spriteCss))
                                             );
                this.defaultLoadingMessage = 'Connecting...';
                this.loading = $(document.createElement('div')).attr('id','lightbox_loading').attr('style','height: 20px; background-color:white;padding-left: 10px;').append(SERKO.loadingImage.clone().append('<span class=\'small label\'>' + this.defaultLoadingMessage + '</span>'));
                this.container.append(this.loading);
                this.hideLoading();

                this.container_content = $(document.createElement('div')).attr({ 'id': 'lightbox_content' });
                this.iframe = $(document.createElement('iframe')).attr({ 'id': 'lightbox_iframe', 'name': 'lightbox_iframe', 'frameBorder': '0', src: this.iframeBlankSrc });
                this.message = $(document.createElement('div')).attr({ 'id': 'lightbox_message' }).hide();
                this.container.append(this.container_content.append(this.iframe).append(this.message));
                
                // buttons
                this.buttonClose = this.createButton(this.buttonType.Close);
                this.buttonPrint = this.createButton(this.buttonType.Print);
                this.buttonAccept = this.createButton(this.buttonType.Accept);
                this.buttonDecline = this.createButton(this.buttonType.Decline);
                this.buttonOk = this.createButton(this.buttonType.Ok);
                this.buttonSave = this.createButton(this.buttonType.Save);
                this.buttonAdd = this.createButton(this.buttonType.Add);
                this.buttonCancel = this.createButton(this.buttonType.Cancel);
                this.buttonYes = this.createButton(this.buttonType.Yes);
                this.buttonNo = this.createButton(this.buttonType.No);

                this.footer_center = $(document.createElement('li')).attr({ 'id': 'lightbox_footer_center', 'class': 'float_left' }).addClass(spriteCss)
                                        .append(this.buttonCancel).append(this.buttonClose).append(this.buttonDecline).append(this.buttonNo).append(this.buttonPrint).append(this.buttonAccept).append(this.buttonOk).append(this.buttonSave).append(this.buttonAdd).append(this.buttonYes);
                this.footer_buttons = $(document.createElement('ul')).attr({ 'id': 'lightbox_footer' })
                                            .append($(document.createElement('li')).attr({ 'id': 'lightbox_footer_left', 'class': 'float_left' }).addClass(spriteCss))
                                            .append(this.footer_center)
                                            .append($(document.createElement('li')).attr({ 'id': 'lightbox_footer_right', 'class': 'float_left' }).addClass(spriteCss))
                this.footer_nobuttons = $(document.createElement('ul')).attr({ 'id': 'lightbox_footer_nobuttons' })
                        .append($(document.createElement('li')).attr({ 'id': 'lightbox_footer_nobuttons_left' }).addClass('float_left').addClass(spriteCss))
                        .append($(document.createElement('li')).attr({ 'id': 'lightbox_footer_nobuttons_center' }).addClass('float_left'))
                        .append($(document.createElement('li')).attr({ 'id': 'lightbox_footer_nobuttons_right' }).addClass('float_right').addClass(spriteCss));
                this.container.append(this.footer_buttons);
                this.container.append(this.footer_nobuttons);
                $(document.body).append(this.container);
            },

            defaultOptions: function () {
                return {
                    showButtons     : true,                    
                    showClose       : false,
                    showPrint       : false,
                    showAccept      : false,
                    showDecline     : false,
                    showOk          : false,
                    showSave        : false,
                    showAdd         : false,
                    showCancel      : false,
                    showYes         : false,
                    showNo          : false,
                    afterShow       : function () {},
                    beforeHide      : function () { return true; },
                    afterHide       : function () { },
                    customButtons   : [],
                    params          : [],
                    context         : undefined
                };
            },

            createButton: function (buttonType, src) {
                var button = $(document.createElement('input')).attr({'type' : 'image', src: 'solv2/images/transparent.gif'}).addClass('link float_right').val(buttonType);
                
                // if not a custom button then use the standard background button image from the sprites
                if (src) {
                    button.attr('src', src);
                } else {
                    button.addClass($.browser.spriteCssClass()).addClass('lightbox_button_' + buttonType.toLowerCase());
                }
                    
                if (buttonType === this.buttonType.Print) {
                    return button.bind('click', this, function (event) {
                        event.data.print();
                    });
                }
                return button.bind('click', this, function (event) {
                    event.data.buttonClicked(buttonType);
                });
            },
            
            isVisible: function () {
                return this.container !== null && this.container.is(':visible');
            },
            
            // shows a message box with a loading animated gif, and no buttons
            showProcessingMessageBox: function (message, lightBoxOptions) {
            
                this.currentShowMode = SERKO.LightBox.showMode.processing;
                
                if (this.container === null) {
                    this.initialize();
                }
                
                this.disableFrame();
                
                // set processing box default options
                var processingDefaults = {
                    showButtons : false,
                    width       : 300
                };
                this.options = jQuery.extend({}, this.defaultOptions(), processingDefaults, lightBoxOptions);
                
                message = '<div id="lightbox_message_message"><img id="lightbox_processing_image" alt="" src="SOLV2/images/status_loading.gif" /><span id="lightbox_processing_message">' + message + '</span></div>';
                this.message.html(message).show();
                this.show();                
            },

            // show message box, can pass in custom options as second parameter, automatically resizes height
            showMessageBox: function (title, message) {
                this.currentShowMode = SERKO.LightBox.showMode.message;
                if (this.container === null) {
                    this.initialize();
                }
                this.disableFrame();
                
                // set message box default options
                var messageBoxDefaults = {
                    width: 300, // content message width
                    messageBoxType: ''
                };
                this.options = jQuery.extend({}, this.defaultOptions(), messageBoxDefaults, arguments[2]);
                
                this.message.empty();
                
                if (!$.isUndefined(title) && title.length > 0) {
                    this.message.append($(document.createElement('div')).attr('id', 'lightbox_message_title').html(title).addClass(this.options.messageBoxType));
                }
                
                var messageDiv = $(document.createElement('div')).attr('id','lightbox_message_message');
                if (!$.isUndefined(message)) {
                    if ($.isUndefined(message.each)) {
                        messageDiv.html(message);
                    } else {
                        messageDiv.append(message);
                    }
                }
                this.message.append(messageDiv).show();
                this.show();
            },

            // show lightbox with iframe link to another page, can pass in custom options as second parameter
            showPage: function (url) {
                this.currentShowMode = SERKO.LightBox.showMode.page;
                if (this.container === null) {
                    this.initialize();
                }
                this.message.html('').hide();
                
                var pageDefaults = {
                    width: 397, // _content width
                    height: 363  // _content height // set to 0 for no height
                };
                this.options = jQuery.extend({}, this.defaultOptions(), pageDefaults, arguments[1]);
                
                this.disableFrame();
                for (var i = 0; i < this.options.params.length; i++ ) {
                    url += (url.toString().indexOf('?',0) < 0) ? '?' : '&';
                    url += this.options.params[i].name + '=' + escape(this.options.params[i].value);
                }
                setTimeout(function () {
                    SERKO.LightBox.loadFrame(url);
                }, SERKO.minimumTimeout);
            },
            
            loadFrame: function (url) {
                this.iframe.bind('load', this, this.iframeLoad).attr({ src: url }).show();
                this.show();    
            },
            
            disableFrame: function () {
                this.iframe.unbind('load', this.iframeLoad).hide();
                if (this.iframeIsSet()) {
                    this.iframe.attr('src', this.iframeBlankSrc);
                }
            },
            
            iframeLoad: function (event) {
                var context = event.data;
                context.options.afterShow.call(context);
            },

            show: function () {
            
                // set context object placeholder that launched lightbox, not always used
                this.context = this.options.context;
                
                // setup 
                this.container_content.attr('class', '').addClass('showmode_' + this.currentShowMode);
                
                // hide all buttons
                this.hideButtons();
                
                // show required buttons
                if (this.options.showClose === true) this.buttonClose.show();
                if (this.options.showPrint === true) this.buttonPrint.show(); 
                if (this.options.showAccept === true) this.buttonAccept.show(); 
                if (this.options.showDecline === true) this.buttonDecline.show(); 
                if (this.options.showOk === true) this.buttonOk.show(); 
                if (this.options.showSave === true) this.buttonSave.show(); 
                if (this.options.showAdd === true) this.buttonAdd.show(); 
                if (this.options.showCancel === true) this.buttonCancel.show(); 
                if (this.options.showYes === true) this.buttonYes.show(); 
                if (this.options.showNo === true) this.buttonNo.show(); 
                
                // add custom buttons
                for (var i = 0; i < this.options.customButtons.length; i++) {
                    this.addCustomButton(this.options.customButtons[i]);
                }

                // hide callbacks
                this.beforeHide = this.options.beforeHide;
                this.afterHide = this.options.afterHide;
                
                // show buttons footer
                if (this.options.showButtons) {
                    this.footer_buttons.show();
                    this.footer_nobuttons.hide();
                } else {
                    this.footer_buttons.hide();
                    this.footer_nobuttons.show();                
                }
                
                this.setSizePosition();

                var self = this;
                
                // only call AfterShow for non iframes, the iframe AfterShow is called once the iframe has loaded
                function callAfterShow() {
                    if (self.currentShowMode === SERKO.LightBox.showMode.page) {
                        self.setHeight();
                        return;
                    }
                    SERKO.LightBox.options.afterShow.call(self);
                }
                
                // IE6 bug - hide selects for IE6
                
                if (jQuery.browser.msie6()) {
                    this.disableSelects();
                    this.overlay.show();
                    this.container.show();
                    callAfterShow();
                } else {
                    this.overlay.fadeIn(SERKO.fadeDuration, function () {
                        self.container.fadeIn(SERKO.fadeDuration, function () {
                            callAfterShow();
                        });
                    });
                }
                 
                // attach event listeners
                if (!$.browser.isMobileDevice()) {
                    $(document).bind('keydown.' + this.toString(), this, this.keyDown);
                    $(window).bind('resize.' + this.toString(), this, function (event) { 
                        event.data.setSizePosition();
                        event.data.setHeight();
                    });
                }                
                this.enable();
            },
            
            hideButtons: function () {
                this.container.find('#lightbox_footer_center input').hide().removeData('hideVisible');
            },

            hideEnabledButtons: function () {
                this.container.find('#lightbox_footer_center input:visible').each(function () {
                    var context = $(this);
                    context.data('hideVisible', true).hide();
                });
            },
            
            showEnabledButtons: function () {
                this.container.find('#lightbox_footer_center input').each(function () {
                    var context = $(this);
                    if (!$.isUndefined(context.data('hideVisible'))) {
                        context.show();
                    }
                });
            },            
            
            addCustomButton: function (customButton) {
                if (this.footer_center.find('input[value="' + customButton.buttonType + '"]').show().length > 0) {
                    return;
                }
                 
                var button = this.createButton(customButton.buttonType, customButton.src).data('customButton', customButton);
                if (!$.isUndefined(customButton.width)) {
                    button.width(customButton.width);
                }
                if ($.isUndefined(customButton.isOk) || customButton.isOk === true) {
                    this.footer_center.append(button);
                } else {
                    this.footer_center.prepend(button);
                }
            },

            showCustomButton: function (customButtonType) {
                this.footer_center.find('input[value="' + customButtonType + '"]').show();
            },
            
            hideCustomButton: function (customButtonType) {
                this.footer_center.find('input[value="' + customButtonType + '"]').hide();
            },
            
            enableButton: function (buttonType) {
                this.footer_center.find('input[value="' + buttonType + '"]').removeAttr('disabled').removeClass('lightbox_disabled').show();
            },
            
            disableSelects: function () {
                var container = this.message.get(0);
                
                $('select').each(function () {
                    if (!jQuery.contains(container, this)) {
                        $(this).addClass('lightbox_disabled');
                    }
                }); 
                
            },
            
            enableSelects: function () {
                $('select.lightbox_disabled').removeClass('lightbox_disabled');
            },
                        
			setProcessingMessage: function(message) {
				this.message.find('#lightbox_processing_message').html(message);
			},
			
            setSizePosition: function () {
                var wnd = $(window),
                    doc = $(document),
                    body = $(document.body),
                    position = {
                        top: wnd.scrollTop(),
                        left: wnd.scrollLeft()
                    },
                    dimensions = {
                        width: doc.width(false),
                        height: doc.height(true)
                    },
                    layout = this.container_content.layout(),
                    maxWidth = this.options.width + layout.border.left + layout.padding.left + layout.border.right + layout.padding.right;
                    
                this.overlay.css({ width: dimensions.width + 'px', height: dimensions.height + 'px', opacity: 0.5 });
                this.container.width(maxWidth).find('#lightbox_header, #lightbox_footer, #lightbox_footer_nobuttons').width(maxWidth);
                this.container_content.width(this.options.width);
                if (this.currentShowMode === SERKO.LightBox.showMode.page && this.options.height > 0) { 
                    this.container_content.height(this.options.height);
                } else { 
                    this.container_content.height('auto');
                }
                
                this.container.find('#lightbox_header_center, #lightbox_footer_center, #lightbox_footer_nobuttons_center').width(maxWidth - 16);
                this.setPosition();
            },
            
            setPosition: function () {
                // mobile devices will always show popups at top of screen, so position screen there after show
                var wnd = $(window),
                    doc = $(document),
                    top = $.browser.isMobileDevice() ? 20 : (doc.scrollTop() + (wnd.height() - this.container.height()) / 2);
                    
                this.container.css({
                    'left': (doc.scrollLeft() + (wnd.width() - this.container.width()) / 2) + 'px',
                    'top': top + 'px'
                });            
                
                if ($.browser.isMobileDevice()) {
                    window.scrollTo(0, 0);
                }
                
            },
            
            setMobileHeight: function () {
                if (!$.browser.isMobileDevice()) {
                    return;
                }
                this.iframe.css('height', $(this.currentFrame().document.body).height() + 'px');
            },

            setHeight: function () {            
                if ($.browser.isMobileDevice()) {
                    this.container.css('height', 'auto');
                    this.container_content.css('height', 'auto');
                    this.iframe.css('height', 'auto');
                    return;
                }
                
                var wnd = $(window);
                var wndHeight = wnd.height();
                var containerHeight = this.container.height();
                var contentHeight = this.container_content.height();

                if (wndHeight <= containerHeight) {
                    var diff = (containerHeight - wndHeight) + 10; // add 10px margin to allow window sit inside browser window
                    containerHeight = containerHeight - diff;
                    contentHeight = contentHeight - diff;
                    this.container_content.css('height', contentHeight + 'px');
                    this.container.css({
                        'top': ($(document).scrollTop() + (wndHeight - containerHeight) / 2) + 'px',
                        'height' : containerHeight + 'px'
                    });            
                }
                
                this.iframe.css({
                    'height' : contentHeight + 'px'
                });
            },

            hide: function (callBack) {
                if (this.container !== null) { 
                    if (jQuery.browser.msie6()) {
                        this.container.hide();
                    } else {
                        this.container.fadeOut(SERKO.fadeDuration);
                    }
                }
                if (this.overlay !== null) { 
                    if (jQuery.browser.msie6()) {
                        this.overlay.hide();
                        $.isFunction(callBack) ? callBack.call(this) : function () {};
                    } else {
                        this.overlay.fadeOut(SERKO.fadeDuration, $.isFunction(callBack) ? callBack : function () {});
                    }
                } 
                $(document).unbind('keydown.' + this.toString());
                $(window).unbind('resize.' + this.toString());
                
                // IE6 bug - hide selects for IE6
                if (jQuery.browser.msie6()) {
                    this.enableSelects();
                }
            },
            
            disable: function (opts) {
                var options = $.extend({message : this.defaultLoadingMessage}, opts);
                
                this.updateLoading(options.message);
                this.showLoading();
                this.footer_center.find('input:visible').attr('disabled','disabled');
                if (this.iframeIsSet()) {
                    $(this.currentFrame().document.body).find('input:image:visible:enabled, input:button:visible:enabled, input:file:visible:enabled, input:submit:visible:enabled, input:reset:visible:enabled').addClass('lightbox_temp_disabled').attr({ 'disabled' : 'disabled' });
                    /*doc.find('.serkoautocompleter').each(function () {
                        var context = $(this);
                        if (context.serkoautocompleter('isEnabled')) {
                            context.addClass('lightbox_temp_disabled').serkoautocompleter('disable');
                        }
                    });
                    */
                }
            },
            
            enable: function () {
                if (this.footer_center === null) {
                    return;
                }
                this.footer_center.find('input:visible').removeAttr('disabled');
                if (this.iframeIsSet()) {
                    $(this.currentFrame().document.body).find('.lightbox_temp_disabled').each(function () {
                        var context = $(this);
                        context.removeClass('lightbox_temp_disabled').removeAttr('disabled');
                    });
                }
                this.hideLoading();
            },
            
            iframeBlankSrc: 'solv2/images/transparent.gif',
            
            iframeIsSet: function () {
                var src = this.iframe.attr('src');
                return !$.isUndefined(src) && src.length > 0 && src.toLowerCase() !== this.iframeBlankSrc;            
            },
            
            hideLoading: function () {
                this.loading.find('div').hide();
            },
            
            showLoading: function () {
                this.loading.find('div').show();
            },
            
            updateLoading: function (message) {
                this.loading.find('span.label').html(message);
            },

            print: function (event) {
                if (!this.iframe.is(":visible")) {
                    return;
                }
                if (this.iframeIsSet()) {
                    this.currentFrame().focus();
                    this.currentFrame().printPage();
                }
            },

            currentFrame: function () {
                if ($.isUndefined(this.iframe)) {
                    return undefined;
                }
                return window.frames[this.iframe[0].name];
            },
            
            /* events */
            buttonClicked: function (buttonPressed) {
                // beforeHide callback
                if ($.isFunction(this.beforeHide)) {
                    if (!this.beforeHide.call(this, buttonPressed)) {
                        return;
                    }
                }
                this.hide(function () {
                    if ($.isFunction(SERKO.LightBox.afterHide)) {
                        SERKO.LightBox.afterHide.call(SERKO.LightBox, buttonPressed);
                    }
                });
            },
            
            keyDown: function (event) {
                if (event.data.toString() !== SERKO.LightBox.toString()) {
                    return;
                }
                
                if (event.keyCode !== SERKO.key.ENTER && event.keyCode !== SERKO.key.ESCAPE) {
                    return;
                }
                event.preventDefault();
                event.stopPropagation();
                
                var context = event.data;
                
                // enter, check for default Ok, Close, Print, Yes,
                if (event.keyCode === SERKO.key.ENTER) {
                    if (context.buttonPrint.is(":visible")) { 
                        context.print();
                        return;
                    }
                    
                    if (context.buttonOk.is(":visible")) {
                        context.buttonOk.click();
                        return;
                    }
                    if (context.buttonSave.is(":visible")) {
                        context.buttonSave.click();
                        return;
                    }
                    if (context.buttonAdd.is(":visible")) {
                        context.buttonAdd.click();
                        return;
                    }                    
                    if (context.buttonYes.is(":visible")) {
                        context.buttonYes.click();
                        return;
                    }
                    if (context.buttonAccept.is(":visible") ) {
                        context.buttonAccept.click();
                        return;
                    }
                    
                    /* check custom buttons */
                    context.footer_center.find('input:visible').each(function (index, input) {
                        var context = $(input);
                        if($.isUndefined(context.data('customButton')) || context.data('customButton').isOk === false ) return true; // continue the .each loop
                        context.click(); 
                        return false; //  break out of the .each loop
                    });
                    
                    return;
                }
                
                // esc, then close lightbox
                if (context.buttonClose.is(":visible")) { 
                    context.buttonClose.click();
                    return;
                }
                if (context.buttonCancel.is(":visible")) {
                    context.buttonCancel.click();
                    return;
                }
                if (context.buttonNo.is(":visible")) {
                    context.buttonNo.click();
                    return;
                }
                if (context.buttonDecline.is(":visible") ) {
                    context.buttonDecline.click();
                    return;
                }
                
                /* check custom buttons */
                context.footer_center.find('input:visible').each(function (index, input) {
                    var context = $(input);
                    if($.isUndefined(context.data('customButton')) || context.data('customButton').isOk === true ) return true; // continue the .each loop
                    context.click(); 
                    return false; //  break out of the .each loop
                });
                
            }
            /* end of events */

        }
    }
);

jQuery.extend(SERKO, 
    {
        DropDowns : {
                            setWaiting : function (dropDown, waitingValue) {
                                var combo = $(dropDown).get(0);
                                combo.options.length = 0;
                                combo.options[combo.options.length] = new Option(waitingValue, '');
                            },

                            setOptions : function (dropDown, valueField, displayField, ajaxUrl, ajaxParameters, callBack, options) {
                                SERKO.DropDowns.setWaiting(dropDown, 'Loading...');
                                var opts = $.extend({}, SERKO.DropDowns.setOptions.defaults, options);
                                
                                $.postJSON(ajaxUrl, ajaxParameters, function (json) {
                                    if (json.Exception.length !== 0) {
                                        $('#page_error').showPageError(json.Exception, {json: json});
                                    } else {
                                        SERKO.DropDowns.setAjaxRsOptions(dropDown, valueField, displayField, json.Items, callBack, options);
                                    }
                                });
                            },
                            
                            setAjaxRsOptions : function (dropDown, valueField, displayField, ajaxRs, callBack, options) {
                                SERKO.DropDowns.setWaiting(dropDown, 'Loading...');
                                var opts = $.extend({}, SERKO.DropDowns.setOptions.defaults, options);

                                dropDown.options.length = 0;
                                
                                if (opts.selectOptionRequired && (ajaxRs.length > 1 || opts.forceRequired)) {
                                    var option = new Option(opts.selectText, opts.selectValue);
                                    $(option).data('optionData', {});
                                    
                                    dropDown.options[dropDown.options.length] = option;
                                }
                                
                                for (var i = 0; i < ajaxRs.length; i++) {
                                    var item = ajaxRs[i];
                                    var option = new Option(SERKO.DropDowns.getDisplayFieldValue(item, displayField), item[valueField]);
                                    $(option).data('optionData', item);
                                    
                                    dropDown.options[dropDown.options.length] = option;
                                }
                                
                                $.isFunction(callBack) ? callBack(dropDown): function () {};
                            },
                            
                            /*Used to get the Display value from either the string passed in or the DisplayOption class*/
                            getDisplayFieldValue : function (item, displayField) {
                                    switch(typeof displayField) {
                                        case 'string':
                                            return item[displayField];
                                        case 'object':
                                            var displayValue = '';
                                            
                                            $.each(displayField, function (arrayCounter, arrayItem) {
                                                if(item[arrayItem.fieldName].length!=0) {
                                                    displayValue += arrayItem.startDelimiter + item[arrayItem.fieldName] + arrayItem.endDelimiter;
                                                }
                                            });
                                            
                                            return displayValue;
                                        default:
                                            return '';
                                    }
                            },
                            
                            Title : {
                                getId : function (comboId) {
                                    var selectedValue = $('#' + comboId).val();
                                    
                                    if(selectedValue.length === 0) return -1;
                                    
                                    return parseInt(selectedValue.split('||')[0]);
                                },
                                
                                getDescription : function (comboId) {
                                    var selectedValue = $('#' + comboId).val();
                                    
                                    if(selectedValue.length === 0) return '';
                                    
                                    return selectedValue.split('||')[1];
                                },
                                
                                getSex : function (comboId) {
                                    var selectedValue = $('#' + comboId).val();
                                    
                                    if(selectedValue.length === 0) return 0;
                                    
                                    return selectedValue.split('||')[2];
                                }
                            }
        }
    });
    
    jQuery.extend(SERKO.DropDowns, {        
        DisplayOption : function () {
            var setOptions = function (args) {
                return {
                    fieldName : args[0],
                    startDelimiter : args[1] === null ? '' : args[1],
                    endDelimiter : args[2] === null ? '' : args[2]
                }
            }
            
            var options = setOptions(arguments);
            
            return {
                fieldName : options.fieldName,
                startDelimiter : options.startDelimiter,
                endDelimiter : options.endDelimiter
            }
        }
    });
    
    jQuery.extend(SERKO.DropDowns.setOptions, {
        defaults : { selectOptionRequired: false, selectValue : '', selectText : '< Please Select >', forceRequired: false }
    });

jQuery.extend(SERKO,
    {
        /* static helper methods for accessing cookies */
        Cookies : {
                    /* get all cookies in the current document
                     * returns an Array of SERKO.Cookie objects
                    */ 
                    get : function () {
                            var result = [];           
                            if (document.cookie.length === 0) {
                                return result;
                            }

                            var cookies = document.cookie.split("; ");
                            for (i=0; i<cookies.length; i++) {
                                result.push(new SERKO.Cookie(cookies[i]));
                            }
                            return result;
                          },
                          
                    /* get cookie by name
                     *  returns:
                     *    undefined - equals not found
                     *    SERKO.Cookie - if found
                     */ 
                    getItem : function (cookieName) {
                                var cookies = SERKO.Cookies.get();
                                for (var i = 0; i < cookies.length; i++ ) {
                                    if (cookies[i].name === cookieName) {
                                        return cookies[i];
                                    }
                                }
                                return undefined;
                              }, 
                    
                    /* set/add cookie to the current document */
                    set : function (cookie) {
                            document.cookie = cookie.toString();
                          },
                          
                    /* reset/expire cookies - no args will reset all cookies, single string will reset a single cookie, an array of cookie names string will reset multiple*/
                    reset : function (cookieName) {
                                if ($.isUndefined(cookieName)) {
                                    var tracingEnabled = SERKO.tracingEnabled(),
                                        cookies = this.get();
                                        
                                    for (var i = 0; i < cookies.length; i++) {
                                        cookies[i].reset();
                                    }
                                    
                                    SERKO.enableTracing(tracingEnabled);
                                    
                                } else if (typeof cookieName === "string") {
                                    var cookie = new SERKO.Cookie(cookieName);
                                    cookie.reset();
                                } else {    
                                    $(cookieName).each(function (i) {
                                        var cookie = new SERKO.Cookie(this.toString());
                                        cookie.reset();
                                    });
                                }
                    },
                    
                    toString : function () {
                                return "SERKO.Cookies";
                    }
        },
                    
        
        /* instance class for working with a cookie
        * constructor can be called via 2 ways
        *       1: with the raw cookie eg new Cookie("cookieName=cookieValue")
        *       2: with the actual values, new Cookie(cookieName, cookieValue, [optional expiryDate])
        */
        
        Cookie  : function () {
        
                    // helper function to get options based on the new instance contructor parameters passed in. see help for the class
                    var getOptions = function (args) {
                        if (args.length === 1) {
                            var cookieParts = args[0].split("=");
                            return {
                                name  : cookieParts[0],
                                value : (cookieParts.length < 2 ? "" : cookieParts[1])
                            };
                        } else {
                            return {
                                name : args[0],
                                value : args[1],
                                expiryDate : args[2]
                            }
                        }
                    }
                    var options = getOptions(arguments);
                    
                    /* actual code that creates the Cookie instance */
                    return {
                        name  : options.name,
                        value : options.value,
                        expiryDate : options.expiryDate,
                        
                        getCurrentPath  : function () {
                                    var parts = document.location.pathname.split('/');
                                    var path = '';
                                    for (var i = 0; i < parts.length - 1; i++) {
                                        if (parts[i].length > 0) {
                                            path += '/' + parts[i];
                                        }
                                    }
                                    return path.length === 0 ? '/' : path;        
                                },
                        
                        reset : function () {
                                    this.value = "";
                                    this.expiryDate = new Date();
                                    this.expiryDate.setDate(this.expiryDate.getDate() - 1);
                                    SERKO.Cookies.set(this);
                                },
                                
                        set : function (value) {
                                    this.value = value;
                                    SERKO.Cookies.set(this);
                              },
                              
                        toString : function () {
                                        return this.name + "=" + this.value + (typeof this.expiryDate === "undefined" ? "" : ";expires=" + this.expiryDate.toUTCString()) + ";path=" + this.getCurrentPath();
                              }  
                    };
        }
        
    }
);        

/* create jQuery plugin for display grids as paging */
(function ($) {

    $.extend($.expr[':'], {
            // check for text within text and ignore case
            containsIgnoreCase: "(a.textContent||a.innerText||jQuery(a).text()||'').toLowerCase().indexOf((m[3]||'').toLowerCase())>=0",
            
            // check if element has value, trims of left/right spaces before testing
            blank: function (a) {
                return $.trim($(a).val()) === '';
            },
            
            // check if element is displayed (This returns true if element is visible in a hidden tab)
            displayed: function (a) {
                return $(a).css('display').toLowerCase() !== 'none';
            }
    });
    
    $.fn.fitToParent = function () {
    
        return this.each(function (i) {
            // remove width/height attributes that browser puts on it after it tries to resize
            var context = $(this).removeAttr('width').removeAttr('height'),
                layout = context.layout();
            
            var parent = context.parent();
            
            var parentWidth = parent.width();
            var parentHeight = parent.height();
            
            function scaleDownWidth(newWidth, origWidth, origHeight) {
                context.width(newWidth);
                
                // get percentage reduction
                var percentage = (newWidth / origWidth);                        
                context.height(Math.floor(origHeight * percentage));
            }
            
            function scaleDownHeight(newHeight, origHeight, origWidth) {
                context.height(newHeight);
                
                // get percentage reduction
                var percentage = (newHeight / origHeight);                        
                context.width(Math.floor(origWidth * percentage));
            }
            
            function resize() {
                
                var contextLayout = context.layout(),
                    contextWidth = contextLayout.totalWidth(false),
                    nonimageWidth = contextWidth - contextLayout.width,
                    contextHeight = contextLayout.totalHeight(false),
                    nonimageHeight = contextHeight - contextLayout.height;
                
                // get scaling working correct way                
                if (contextWidth >= contextHeight) {
                    if (contextWidth > parentWidth) {
                        scaleDownWidth(parentWidth - nonimageWidth, contextLayout.width, contextLayout.height);
                    } else if (contextHeight > parentHeight) {
                        scaleDownHeight(parentHeight - nonimageHeight, contextLayout.height, contextLayout.width);
                    }
                } else {
                    if (contextHeight > parentHeight) {
                        scaleDownHeight(parentHeight - nonimageHeight, contextLayout.height, contextLayout.width);
                    } else if (contextWidth > parentWidth) {
                        scaleDownWidth(parentWidth - nonimageWidth, contextLayout.width, contextLayout.height);
                    }
                }
                
                // recheck new dimensions, it still not fit call ones self (recursion)
                contextLayout = context.layout();
                contextWidth = contextLayout.totalWidth(false);
                contextHeight = contextLayout.totalHeight(false);
                
                if (contextHeight > parentHeight || contextWidth > parentWidth) {
                    resize();
                }                    
            }
                        
            if (layout.totalWidth(false) > parentWidth || layout.totalHeight(false) > parentHeight) {
                resize();
            }
            
        });
    };
    
    // vertically align an item inside its parent
    $.fn.verticallyAlign = function () {
        return this.each(function (i) {
            var context = $(this);
            var center = parseInt(context.parent().height()/2);
            var contextHeight = context.height();
            context.css({ 'margin-top' : (center - parseInt(contextHeight / 2)) + 'px' });
        });
    };

    $.fn.removeInvalidAlphaCharacters = function () {
        return this.each(function (i) {
            $(this).val(jQuery.trim($(this).val().replace(/[^A-Za-z\s]+/g, "")));
        });
    };

    $.fn.removeInvalidGDSCharacters = function () {
        return this.each(function (i) {
            $(this).val(jQuery.trim($(this).val().replace(/[\[\]\/\\~/@+;]/, "")));
        });
    };

    $.fn.removeInvalidNumericCharacters = function () {
            return this.each(function (i) {
                $(this).val(jQuery.trim($(this).val().replace(/[^0-9]+/g, "")));
            });
    };
    
    $.extend($, 
        {
            isUndefined : function (object) {
                return typeof object === "undefined";
            },
            emptyFunction : function () { }
        });
        
    /* jQuery plugin for showing tooltips
     * this can be called in various ways
     *
     *  $.toolTip(message, options)
     *      message (optional): optional html string to show
     *      options (optional): { 
     *                              showBy : 'element' or   - show by the element
     *                                       'mouse'        - show by the mouse pointer
     *                              width  : 200 (default)  - set width of tool tip
     *                              heading: null (default) - set to a string value to display a heading, null will hide the heading 
     *
     *  CSS styles : create a style for #tip{} (Div)
     *
     * simple  - find all elements that have a 'title' attribute and attach a tooltip with the 'title' as the tooltip
     *      html      : <div title="tool tip messge"></div>
     *                  <p title="tool tip messge 2"></p>
     *      javascript: $('*', $(document.body)).toolTip();
     *
     * advanced - show html content as the tooltip
     *      html      : <div id="abc">On mouse over the tooltip will show</div>
     *      javascript: $('#abc').toolTip('<b>Tooltip</b><br/>Sample Tooltip');     
    */
    $.fn.toolTip = function (message, options) {
            
        return this.each(function (i) {
            var context = $(this),
                opts = $.extend({}, $.fn.toolTip.defaults, options);            
                
            context.data('tooltip_message', message).tooltip({
                showURL: false,
                left: 2,
                top: 2,
                track: opts.showBy === 'mouse',
                bodyHandler: function () {
                    var tip = $('#tip');
                    if (tip.length === 0) {
                        tip = $.fn.toolTip.createContainer().appendTo($(document.body));                   
                    }
                    if (opts.heading.length === 0) {
                        tip.find('#tip_content_heading').hide();
                    } else {
                        tip.find('#tip_content_heading').html(opts.heading).show();
                    }
                    tip.find('#tip_content_blurb').html(message);
                    tip.css('width', opts.width + 'px');
                    tip.find('#tip_top_center').css('width', (opts.width - 10) + 'px');
                    tip.find('#tip_bottom_center').css('width', (opts.width - 10) + 'px');
                    
                    return tip;
                }
            });
        });
        
    };
    $.fn.toolTip.hide = function (context) {
        // TODO remove this function, left for backwards compatibility before moving to jquery.tooltip
        //$('#ip').hide();
    };
    
    $.fn.toolTip.createContainer = function () {
        var css = $.browser.spriteCssClass();
        return $(document.createElement('ul'))
                .attr('id', 'tip')
                .append($('<li id="tip_top_left" class="float_left"/>').addClass(css))
                .append('<li id="tip_top_center" class="float_left"/>')
                .append($('<li id="tip_top_right" class="float_left"/>').addClass(css))
                .append('<li id="tip_content"><h2 id="tip_content_heading"></h2><p id="tip_content_blurb"></p></li>')
                .append($('<li id="tip_bottom_left" class="float_left"/>').addClass(css))
                .append('<li id="tip_bottom_center" class="float_left"/>')
                .append($('<li id="tip_bottom_right" class="float_left"/>').addClass(css));
    };
    
    $.fn.toolTip.defaults = { showBy : 'element', timeout : 2000, width : 200, heading: '' };
    
    
    $.fn.pageGrid = function (options) {
        
                return this.each(function (i) {
                    var context = $(this);
                    
                    var opts = typeof context.data("savedOptions") === "undefined" 
                                    ? $.extend({}, $.fn.pageGrid.defaults, options)
                                    : $.extend({}, context.data("savedOptions"), options);  
                                    
                    // initialize firsttime through
                    if (typeof opts.initialized === "undefined") {
                        if (typeof opts.filterElement !== "undefined") opts.filterElement.keyup(function () { context.pageGrid( { filterValue : $(this).val() } ); });
                        opts.initialized = true;
                    }
                    
                    var total = context.find('div.total');
                    if (total.length === 0) {
                        total = $(document.createElement('div')).addClass('total');
                        context.append(total);
                    }
                    
                    /* create nav links */
                    var navId = context.attr('id') + '_navigation';
                    var navigation = context.find('#' + navId);
                    if (navigation.length === 0) {
                        navigation = $(document.createElement('div')).attr( { 'id' : navId } ).addClass('navigation');                    
                        navigation.append($(document.createElement('span')).text(opts.first).addClass('link').click(function (event) {
                            context.pageGrid( { navigate : 'first' } );
                        })).append($(document.createElement('span')).text(opts.prev).addClass('link').click(function (event) {
                            context.pageGrid( { navigate : 'prev' } );
                        })).append($(document.createElement('span')).text(opts.next).addClass('link').click(function (event) {
                            context.pageGrid( { navigate : 'next' } );
                        })).append($(document.createElement('span')).text(opts.last).addClass('link').click(function (event) {
                            context.pageGrid( { navigate : 'last' } );
                        })).append($(document.createElement('span')).attr({ 'id' : navId + '_paging' }));
                        context.append(navigation);
                    }
                    
                    var rows = context.find('.row');
                    rows.removeClass('rowalternate');
                    
                    // filter rows if filter applied
                    if (opts.filterCell.length > 0) {
                        // reset to first page if filtering is dirrent from last filter
                        if (typeof opts.filterLastValue !== "undefined" && opts.filterLastValue !== opts.filterValue) {
                            opts.navigate = 'first'
                            opts.currentPage = 1;
                        }
                        var filterValue = typeof opts.filterValue === 'undefined' ? '' : opts.filterValue;
                        opts.filterLastValue = filterValue;
                        if (filterValue.length > 0) {
                            rows.hide(); //hide all rows, so filter will only show filtered rows
                            rows = context.find(opts.filterCell + ':containsIgnoreCase("' + opts.filterValue + '")').parent();
                        }
                    }

                    total.text('Total: ' + rows.length);
                    
                    // dont worry about paging limited rows                                        
                    if (rows.length <= opts.pageSize) { 
                        navigation.hide();
                        rows.show();
                        context.find('.row:visible:even').addClass('rowalternate');
                        // persist opts bewteen page navigation
                        context.data("savedOptions", opts);
                        return; 
                    }
                    
                    // enable paging for lots of rows
                    navigation.show();
                    
                    var lastPage = parseInt(rows.length/opts.pageSize) + 1;
                    switch (opts.navigate) {
                        case 'first' :
                            opts.currentPage = 1;
                            break;
                        case 'last' :
                            opts.currentPage = lastPage;
                            break;
                        case 'next' :    
                            if (opts.currentPage < lastPage) opts.currentPage++;
                            break;
                        case 'prev' :
                            if (opts.currentPage > 1) opts.currentPage--;
                            break;
                    }
                    
                    var startingRow;
                    switch (opts.currentPage) {
                        case 1 : // first
                            startingRow = 0;
                            break;
                        default : // any otherpage in between
                            startingRow = ((opts.pageSize * opts.currentPage) - opts.pageSize);
                            break;
                    }
                    var endingRow = startingRow + (opts.pageSize - 1);
                    
                    rows.each(function (i) {
                        if (i < startingRow || i > endingRow) {
                            $(this).hide();
                        } else { 
                            $(this).show();
                        }
                    });
                    context.find('.row:visible:even').addClass('rowalternate');
                    
                    //if (navigation.is
                    if (opts.currentPage === lastPage) {
                        navigation.find('span.link:contains("' + opts.next + '")').hide();
                        navigation.find('span.link:contains("' + opts.prev + '")').show();
                    } else if (opts.currentPage === 1) {
                        navigation.find('span.link:contains("' + opts.next + '")').show();
                        navigation.find('span.link:contains("' + opts.prev + '")').hide();                    
                    } else {
                        navigation.find('span.link').show();
                    }
                    navigation.find('span:not(.link)').text('Page ' + opts.currentPage + ' of ' + lastPage);
                    
                    // persist options bewteen page navigation
                    context.data("savedOptions", opts);
                });
            };
            
            // private methods
            
            // defaults
            $.fn.pageGrid.defaults = { currentPage : 1, pageSize : 10, navigate : 'first', next : 'Next', prev : 'Prev', first : 'First', last : 'Last', filterCell : '', filterValue : '', filterElement : undefined };
            
                 
    /* Show page error */
    $.fn.showPageError = function (message, options) {
        //Hide the Wait window if it exists.
        if(SERKO.waitWindowShowing || SERKO.LightBox.currentShowMode === SERKO.LightBox.showMode.processing) {
            SERKO.WaitWindow.hide();
            window.scrollTo(0, 0);
        }
        
        return this.each(function (i) {
            var context = $(this),
                opts = $.extend({}, $.fn.showPageError.defaults, options),
                position = context.position();
                
            if (opts.scroll === true) {
                window.scrollTo(position.left, position.top);
            }
            // check if appending to current error
            if (opts.append === true && context.find('div.' + opts.className).length > 0 && context.find('div.' + opts.className).children().length > 0) {
                context.find('div.' + opts.className)
                        .append('<br/>')
                        .append(message);
                return;  
            }
            
            // create new error
            context.removePageError();
            
            var row = $(document.createElement("div")).addClass("row"),
                alertdiv = $(document.createElement("div")).addClass("cell cell2").addClass(opts.className).append(message);
            
            function appendAdditionalMessage(additionalMessage) {
                if (additionalMessage.length > 0) {
                    alertdiv.append($(' <img alt="" class="information icon_help" src="solv2/images/transparent.gif"/>').addClass($.browser.spriteCssClass()).toolTip(additionalMessage, { width: 300, heading: 'Additional Information' }));
                }
            }
            
            if (opts.additionalMessage) {
                appendAdditionalMessage(opts.additionalMessage);
            } else if (opts.json && opts.json.ObjectResponse && opts.json.ObjectResponse.DebugMessage && opts.json.ObjectResponse.DebugMessage !== message) {
                var addMsg = '';
                if (opts.json.ObjectResponse.ErrorNumber) {
                    addMsg += '<span class="small">Error Number:</span><br/>' + opts.json.ObjectResponse.ErrorNumber + '<br/>';
                }
                appendAdditionalMessage(addMsg + '<br/><span class="small">Message:</span><br/>' + opts.json.ObjectResponse.DebugMessage.formatAsHtml());
            }
            
            if (opts.className === 'alert') {
                row.append($('<div class="cell cell1 icon_alert_large"></div>').addClass($.browser.spriteCssClass()));
            }
            row.append(alertdiv)
               .append(SERKO.clearFloat);
            
            if (opts.position === 'end') {
                context.append(row);
            } else {
                context.prepend(row);
            }
            context.fadeIn(SERKO.fadeDuration);
        });
    };
    $.fn.showPageError.defaults = { append : false, scroll : true, className : 'alert', position: 'end', additionalMessage: undefined, json: undefined };
    
    /* Remove page error */
    $.fn.removePageError = function (options) {
        var opts = $.extend({}, $.fn.removePageError.defaults, options);
        
        return this.each(function (i) {
            var context = $(this);
            
            if (!opts.onlyIfBlank || (opts.onlyIfBlank && context.find('div.' + opts.className).children().length === 0)) { 
                context.empty();
            }
        });
    };
    $.fn.removePageError.defaults = {onlyIfBlank: false, className: $.fn.showPageError.defaults.className};
    
    /* Show Information on the page
            - message (string) this is the message shown on the screen
            - options (not required)
                showLoading : true/false (default false) Show the spining loading image
                append      : true/false (default false) Append to the existing information shown
                hide        : true/false (default true) Hides the information after the showDuration period
                showDuration: int (default 5000, 5 Seconds) The number of milliseconds to show the message before it fades out
                scroll      : true/false (default true) Scrolls the message into view
                callback    : function (default empty function) This gets called after the fade out of the message
                aboveLightbox : true / false (default false)  Will add a class to the message to display it above a lightbox.
                maxWidth : (set this to a width, if width is too wide)
                
    */
    $.fn.showInformation = function (message, options) {
        return this.each(function (i) {    
            var context = $(this);
            var opts = $.extend({}, $.fn.showInformation.defaults, options);

            if (opts.scroll === true) {
                var position = context.get(0).tagName.toLowerCase() === 'body'
                                ? { left: 0, top: 0 }
                                : context.position();            
                window.scrollTo(position.left, position.top);
            }
                        
            var container = $('#information');
            
            // create information container
            var item = $('<li/>');
            if (container.length === 0) {
                container = $(document.createElement('ul'))
                    .attr('id','information')
                    .hide()
                    .appendTo($(document.body));
                item.addClass('float_left');
            } else if (opts.append === false) {
                container.empty();
                item.addClass('float_left');
            }

            if (opts.showLoading === true && container.children().length === 0) {
                $('<li/>').addClass('float_left')
                          .append(SERKO.loadingImage.html())
                          .appendTo(container);
            }
            item.append(message).appendTo(container);
            
            if (opts.aboveLightbox) {
                container.addClass('abovelightbox');
            }
            
            if (container.is(':visible')) {
                container.hide();
            }
            
            container.css({ left : context.offset().left + 'px', top : context.offset().top + 'px' });
            
            var containerLayout = container.layout(),
                informationBorderPadding = (containerLayout.padding.left + containerLayout.border.left),
                maxWidth = $.isUndefined(opts.maxWidth) 
                            ? context.width() - (informationBorderPadding * 2) - (40) // 40px = 20px*2 left/right for margin in from context
                            : opts.maxWidth;
                
            if (container.width() > maxWidth) {
                container.css({ width : maxWidth + 'px' });
            } else {
                container.css({width : '' }); // remove the width
            }
            
            // align center
            container.css({left : (context.offset().left + (parseInt(context.width() / 2, 10)) - parseInt(container.width() / 2, 10) - parseInt(informationBorderPadding / 2, 10)) + 'px' });
            if ($.browser.msie6()) {
                container.show();
            } else {
                container.fadeIn(SERKO.fadeDuration);
            }
            
            if (opts.hide === true) {
                setTimeout(function () {
                    if ($.browser.msie6()) {
                        container.hide();
                    } else {
                        container.fadeOut(SERKO.fadeDuration, opts.callback);
                    }
                }, opts.showDuration);
            }
        });
    };
    $.fn.showInformation.defaults = { callback : $.emptyFunction, append : false, hide : true, showLoading : false, showDuration : 5000, scroll : true, aboveLightbox : false };
    
    /* Remove page information */
    $.fn.removeInformation = function () {
        return $('#information').remove();
    };
    
    /* extended jQuery function to add an error message to an element */
    /* will place a red box around the parent element and insert an error message after the element*/
    $.fn.addError = function (messages, setFocusToFirst, options) {
        var opts = $.extend({}, {position: 'append'} ,options),
            setFocus = $.isUndefined(setFocusToFirst) ? true : setFocusToFirst;
        
        
            return this.each(function (i) {
                // check if message is the same for all elements or different message for each element
                var message = typeof messages === "string" ? messages : messages[i];
                var context = $(this);
                context.addClass('error');
                
                // IE select highlight workaround, or INPUT type FILE
                if (context.is('input:file') || (this.tagName.toLowerCase() === 'select' && $.browser.msie)) {
                    if (context.parent().hasClass('errorcontainer')) {
                        context.parent().addClass('error');
                    } else {
                        context.wrap('<div class="error errorcontainer"></div>');
                    }
                }
                
                // add error message if message passed in and a message hasnt already been added (i.e no duplicated messages)
                if (message.length > 0 && !context.parent().hasClass('error_element') ) {
                    var errorElement = $(document.createElement('div')).addClass('error_element alert').html(message);
                    // attach error message to the parent (this is not the IE workaround div)
                    context.parents().each(function (index) {
                        var parent = $(this);
                        if (!parent.hasClass('error')) {
                            if (opts.position === 'after') {
                                context.after(errorElement);
                            } else {
                            parent.append(errorElement);
                            }
                            return false; //break each
                        }
                    });
                }
                
                if (i === 0 && setFocus === true) {
                    $(this).focus();
                }
            });
    };

    $.fn.removeError = function () {
            return this.each(function (i) {
                var context = $(this);
                context.removeClass('error');
                var parent = context.parent();
                if (parent.hasClass('errorcontainer')) {
                    parent.removeClass('error');
                }
                if (context.next().hasClass('error_element')) {
                    context.next().remove();
                }
            });
    };
    
    // populate a combo with SERKO allowd times
    $.fn.times = function (options) {
        var opts = $.extend({}, $.fn.times.defaults, options);
        var times = SERKO.Globals.timesJSON();
        
        function addOption(combo, val) {
            combo.append($(document.createElement('option')).val(val).html(val));
        };
        
        return this.each(function () {
            var context = $(this).html('');
            if (opts.includeFlightTime === true) {
                addOption(context, 'Flight Time');
            }
            $.each(times, function (index, time) {
                if (time.IsAny === false || opts.includeAny === true) {
                    addOption(context, time.Time);
                }
            });
        });
    };    
    $.fn.times.defaults = {
        includeAny : true,
        includeFlightTime : false
    };

    /*Extending the jQuery UI DatePicker*/
    $.fn.serkoPastDatePicker = function (options) {
        var opts = $.extend({}, $.fn.serkoPastDatePicker.defaults, options);
        return this.each(function () {
            var currentYear = (new Date).getFullYear().toString();
            var backDatedYear = (parseInt(currentYear,10) - parseInt(opts.yearsBack,10)).toString();
            $(this).serkoDatePicker($.extend({}, { showStatus: true, changeYear: true, changeMonth: true, yearRange: backDatedYear+':'+currentYear, maxDate: 0 }, opts));
        });
    };
    $.fn.serkoPastDatePicker.defaults = { yearsBack : '110' };
    
    $.fn.serkoFutureDatePicker = function (options) {
        var opts = $.extend({}, $.fn.serkoFutureDatePicker.defaults, options);
        
        return this.each(function () {
            var currentYear = (new Date).getFullYear().toString();
            var futureDatedYear = (parseInt(currentYear,10) + parseInt(opts.yearsOut,10)).toString();
            $(this).serkoDatePicker($.extend({}, { showStatus: true, changeYear: true, changeMonth: true, yearRange: currentYear+':'+futureDatedYear, minDate: 0 }, opts));
        });
    };
    $.fn.serkoFutureDatePicker.defaults = { yearsOut : '110' };
    
    
    $.fn.serkoDatePicker = function (options) {
        var opts = $.extend({}, $.fn.serkoDatePicker.defaults, options);
        
        return this.each(function () {
            $(this).datepicker(opts).addClass('link').attr('readonly', 'readonly');
        });
    };
    
    $.fn.serkoGdsDatePicker = function (options) {
        var opts = $.extend({}, $.fn.serkoDatePicker.defaults, $.fn.serkoGdsDatePicker.defaults, options === undefined ? {} : options);
        // ie6 too slow with standard 3mth calendar
        if ($.browser.msie6() || $.browser.isMobileDevice()) {
            opts.numberOfMonths = 1;
            opts.stepMonths = 1;
            opts.changeMonth = true;
            opts.changeYear = true;
        }
        return this.each(function () {
            $(this).serkoDatePicker(opts);
        });
    };

    $.fn.serkoDatePicker.defaults = {
        dateFormat: 'dd M yy',
        closeText: 'Close',
        closeAtTop: false,
        hideIfNoPrevNext: true,
        navigationAsDateFormat: false, 
        prevText: '<', 
        currentText: 'Current Selected', 
        nextText: '>',
        buttonImage: 'solv2/images/master_button_calendar.gif',
        showOn: 'both', 
        buttonImageOnly: true,
        gotoCurrent: true,  // false = today, true = current selected
        firstDay: 1,
        dayNamesMin: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
        showButtonPanel: true,
        changeMonth: false,
        duration: ''
    };
    
    $.fn.serkoGdsDatePicker.defaults = {
        minDate: 0,
        maxDate: 362,
        mandatory: true,
        //numberOfMonths: [2, 3], // Use this if we want to have 6 months shown at a time in two rows (3 per row).
        numberOfMonths: 3,
        stepMonths: 3
    };

    /*This function will set the min and max dates according to the other calendars that are within the same name; eg dteSeg1 and dteSeg2
    You will need to set the beforeShow to $.fn.serkoGdsDatePicker.setMinMaxFromSameType and set baseCalendarName to the calendar name that is common; 
    eg dteSeg for dteSeg1*/
    $.fn.serkoGdsDatePicker.setMinMaxFromSameType = function () {
        var baseCalendarName = arguments[1].settings.baseCalendarName;
        var thisDateSegment = parseInt(this.id.replace(baseCalendarName, ''));
        
        var previousDateToChange = $('#' + baseCalendarName + (thisDateSegment - 1));
        var nextDateToChange = $('#' + baseCalendarName + (thisDateSegment + 1));

        return {minDate: (previousDateToChange.length > 0 && $(previousDateToChange).val().length > 0 ? $(previousDateToChange).datepicker("getDate") : 0), 
            maxDate: (nextDateToChange.length > 0 && $(nextDateToChange).val().length > 0 ? $(nextDateToChange).datepicker("getDate") : 362)};
    };

    /*This function will set the min and max dates according to the other calendar that is passed in as the options for creating the calendar.
    You will need to set the beforeShow to $.fn.serkoGdsDatePicker.setMinMaxFromOtherCalendar, set otherCalendarId to the other calendar and set setMin
    or setMax for the value that should be set.*/
    $.fn.serkoGdsDatePicker.setMinMaxFromOtherCalendar = function () {
        var baseCalendarName = arguments[1].settings.otherCalendarId;
        
        var otherDate = $('#' + baseCalendarName);

        return {minDate: (arguments[1].settings.setMin ? $(otherDate).datepicker("getDate") : 0), 
            maxDate: (arguments[1].settings.setMax ? $(otherDate).datepicker("getDate") : 362)};
    };
    
    /*Function to get JSON via a Post rather than a Get*/ 
    $.postJSON = function (url, options, successcallback, context, errorcallback) {
        var opts,
            pageOpts,
            formDataSerialized = $('form').serialize(),
            pageOptionsSerialized = '',
            pageStateSerialized = '';
        try {
            pageOpts = pageOptions;
        } catch(e) {
            pageOpts = undefined;
        }
        
        if (pageOpts) {
            pageOptionsSerialized = $.param(pageOpts);
        }
        
        try { 
            pageStateSerialized = $.param(SERKO.PageState);
        } catch (e) {}
        
        if ($.isArray(options)) {
            opts = options;
            opts.push({ name: 'MainObjectType', value: $.isUndefined(SERKO.PageState) ? '' : SERKO.PageState.mainObjectType });
            opts.push({ name: 'BookingId', value: $.isUndefined(pageOpts) ? -1 : $.isUndefined(pageOpts.bookingId) ? -1 : pageOpts.bookingId });
            opts.push({ name: 'ProfileId', value: $.isUndefined(pageOpts) ? -1 : $.isUndefined(pageOpts.profileId) ? -1 : pageOpts.profileId });
            opts.push({ name: 'windowLocation', value: encodeURI(window.location)});
            opts.push({ name: 'debugReferrer', value: encodeURI(document.referrer)});
            opts.push({ name: 'debugFormData', value: formDataSerialized});
            opts.push({ name: 'debugPageOptions', value: pageOptionsSerialized});
            opts.push({ name: 'debugPageState', value: pageStateSerialized});
        } else {
            opts = $.extend(
            {  'MainObjectType' : $.isUndefined(SERKO.PageState) ? '' : SERKO.PageState.mainObjectType,
                'BookingId'     : $.isUndefined(pageOpts) ? -1 : $.isUndefined(pageOpts.bookingId) ? -1 : pageOpts.bookingId,
                'ProfileId'     : $.isUndefined(pageOpts) ? -1 : $.isUndefined(pageOpts.profileId) ? -1 : pageOpts.profileId,
                'windowLocation': encodeURI(window.location),
                'debugReferrer': encodeURI(document.referrer),
                'debugFormData' : formDataSerialized,
                'debugPageOptions': pageOptionsSerialized,
                'debugPageState': pageStateSerialized
            }, options);
        }

        $.ajax({
            type    : 'POST',
            url     : url,
            data    : opts,
            success : function (json) {
                        
                        if (json && json.ObjectResponse && json.ObjectResponse.ErrorSource && json.ObjectResponse.ErrorSource === "SERKO") {
                        
                            // copied from library.asp
                            var serkoErrorCodes = {
                                SERKO_ERROR_QUICKBOOKING_EXPIRED: -3000000001
                            };
                                
                            if (json.ObjectResponse.ErrorNumber === serkoErrorCodes.SERKO_ERROR_QUICKBOOKING_EXPIRED) {
                            
                                var msg = $(document.createElement('div'))
                                            .append('<p>Your Quick Booking has expired.</p>')
                                            .append($('<ol><li>Click \'Ok\' to start a new Quick Booking.</li><li>Click \'Cancel\' to go to SERKO&reg; Online home page.</li></ol>')
                                                        .width(300)
                                                        .css('text-align', 'left'));
                                
                                SERKO.LightBox.showMessageBox('Quick Booking Expired', msg, {
                                    showOk: true,
                                    showCancel: true, 
                                    messageBoxType: 'alert',
                                    width: 350,
                                    beforeHide: function (buttonPressed) {
                                        SERKO.LightBox.disable();
                                        deleteQuickBookingCookies();
                                        SERKO.jumpPage(buttonPressed === SERKO.LightBox.buttonType.Ok ? 'so-qb-setup-policy.asp' : 'so-home.asp');
                                        return false;
                                    }
                                });
                                return;
                            }
                        }
                        
                        if (!$.isUndefined(successcallback)) {
                            successcallback.call(this, json);
                        }
                      },
            error   : function (XMLHttpRequest, textStatus, errorThrown) { 
                        if ($.isFunction(errorcallback)) {
                            errorcallback.call(this, XMLHttpRequest, textStatus, errorThrown, url, opts); 
                        } else {
                            $.postHTML.defaults.error.call(this, XMLHttpRequest, textStatus, errorThrown, url, opts);
                        }
                      },
            dataType: 'json',
            context :  context
        });
    };
    
    /* Function to call AJAX page that returns HTML JSON via a Post
     * sets a default error handler, that you can override
    */
    $.postHTML = function (options) {
        var opts = $.extend($.postHTML.defaults, options),
            pageOpts,
            formDataSerialized = $('form').serialize(),
            pageOptionsSerialized = '',
            pageStateSerialized = '';
            
        try {
            pageOpts = pageOptions;
        } catch(e) {
            pageOpts = undefined;
        }
        
        if (pageOpts) {
            pageOptionsSerialized = $.param(pageOpts);
        }
        
        try { 
            pageStateSerialized = $.param(SERKO.PageState);
        } catch (e) {}
                
        opts.data.windowLocation = encodeURI(window.location);
        opts.data.debugFormData = formDataSerialized;
        opts.data.debugPageOptions = pageOptionsSerialized;
        opts.data.debugPageState = pageStateSerialized;
        opts.data.debugReferrer = encodeURI(document.referrer);
        
        $.ajax({
            type    :   'POST',
            url     :   opts.url,
            data    :   opts.data,
            success :   opts.success,
            error   :   opts.error,
            dataType:   'html'
        });
    };    
    $.postHTML.defaults = {
        error   :   function (XMLHttpRequest, textStatus, errorThrown, url, data) {
                        $.ajaxSetup({ async : true });
                        $.showAjaxError.call(this, $('#page_error'), XMLHttpRequest, textStatus, errorThrown, url, data);
                    }
    };
    
    /* show ajax error */
    $.showAjaxError = function (context, XMLHttpRequest, textStatus, errorThrown, url, data) {
        if (!$.isUndefined(SERKO)) {
            if (SERKO.waitWindowShowing) {
                SERKO.WaitWindow.hide();
            }
            if (SERKO.LightBox.currentShowMode === SERKO.LightBox.showMode.processing) {
                SERKO.LightBox.hide();
            }
        }
        if (!$.isUndefined(window.parent) && !$.isUndefined(window.parent.SERKO)) {
            window.parent.SERKO.LightBox.enable();
            if (!$.isUndefined(window.parent.SERKO.LightBox.buttonCancel)) {
                window.parent.SERKO.LightBox.buttonCancel.show();
            }
        }
        
        function getErrorMessage() {
            var aspErrorCodes = [
                {code:"800A0005",message:"Invalid Procedure Call (VBScript 0x800A0005)"},
                {code:"800A000D",message:"Type Mismatch (VBScript 0x800A000D)"},
                {code:"800A0035",message:"File not found (VBScript 0x800A0035)"},
                {code:"800A003A",message:"File Already Exists (VBScript 0x800A003A)"},
                {code:"800A0046",message:"Permission denied (VBScript 0x800A0046)"},
                {code:"800A004C",message:"Path not found (VBScript 0x800A004C)"},
                {code:"800A01A8",message:"Object required (VBScript 0x800A01A8)"},
                {code:"800A01AD",message:"ActiveX component can't create object (VBScript 0x800A01AD)"},
                {code:"800A01B6",message:"Object doesn't support this property or method (VBScript 0x800A01B6)"},
                {code:"800A01BD",message:"Object doesn't support this ACTION (VBScript 0x800A01BD)"},
                {code:"800A01C3",message:"Object not a collection (VBScript 0x800A01C3)"},
                {code:"800A01C2",message:"Invalid property assignment (VBScript 0x800A01C2)"},
                {code:"800A01CE",message:"Remote Server does not exist (or is unavailable) (VBScript 0x800A01CE)"},
                {code:"800A01F4",message:"Variable undefined (VBScript 0x800A01F4)"},
                {code:"800A01F5",message:"Illegal assignment (VBScript 0x800A01F5)"},
                {code:"800A03EA",message:"Syntax Error (VBScript 0x800A03EA)"},
                {code:"800A03EE",message:"Expected ')' (VBScript 0x800A03EE)"},
                {code:"800A03F1",message:"Variable is undefined (VBScript 0x800A03F1)"},
                {code:"800A03F2",message:"Expected Identifier (VBScript 0x800A03F2)"},
                {code:"800A03F4",message:"Variable Undefined (VBScript 0x800A03F4)"},
                {code:"800A03F6",message:"Expected End (VBScript 0x800A03F6)"},
                {code:"800A03FD",message:"Expected 'Case' (VBScript 0x800A03FD)"},
                {code:"800A0400",message:"Expected statement (VBScript 0x800A0400)"},
                {code:"800A0401",message:"Expected end of statement (VBScript 0x800A0401)"},
                {code:"800A0407",message:"Invalid Number (VBScript 0x800A0407)"},
                {code:"800A0408",message:"Invalid Character (VBScript 0x800A0408)"},
                {code:"800A0409",message:"Unterminated string constant (VBScript 0x800A0409)"},
                {code:"800A041F",message:"Unexpected Next (VBScript 0x800A041F)"},
                {code:"800A139B",message:"- Expected ']' in regular expression (VBScript 0x800A139B)"},
                {code:"800A139A",message:"Expected quantifier in regular expression (VBScript 0x800A139A)"},
                {code:"800A0CC1",message:"Item cannot be found in the collection (VBScript 0x800A0CC1)"},
                {code:"ASP 0100",message:"Out of memory (ASP 0100)"},
                {code:"ASP 0101",message:"Unexpected error (ASP 0101)"},
                {code:"ASP 0102",message:"Expecting string input (ASP 0102)"},
                {code:"ASP 0103",message:"Expecting numeric input (ASP 0103)"},
                {code:"ASP 0104",message:"Operation not Allowed (ASP 0104)"},
                {code:"ASP 0105",message:"Index out of range (ASP 0105)"},
                {code:"ASP 0106",message:"Type Mismatch (ASP 0106)"},
                {code:"ASP 0107",message:"Stack Overflow (ASP 0107)"},
                {code:"ASP 0108",message:"Create object failed (ASP 0108)"},
                {code:"ASP 0109",message:"Member not found (ASP 0109)"},
                {code:"ASP 0110",message:"Unknown name (ASP 0110)"},
                {code:"ASP 0111",message:"Unknown interface (ASP 0111)"},
                {code:"ASP 0112",message:"Missing parameter (ASP 0112)"},
                {code:"ASP 0113",message:"Script timed out (ASP 0113)"},
                {code:"ASP 0114",message:"Object not free threaded (ASP 0114)"},
                {code:"ASP 0115",message:"Unexpected error (ASP 0115)"},
                {code:"ASP 0116",message:"Missing close of script delimiter (ASP 0116)"},
                {code:"ASP 0117",message:"Missing close of script tag (ASP 0117)"},
                {code:"ASP 0118",message:"Missing close of object tag (ASP 0118)"},
                {code:"ASP 0119",message:"Missing Classid or Progid attribute (ASP 0119)"},
                {code:"ASP 0120",message:"Invalid Runat attribute (ASP 0120)"},
                {code:"ASP 0121",message:"Invalid Scope in object tag (ASP 0121)"},
                {code:"ASP 0122",message:"Invalid Scope in object tag (ASP 0122)"},
                {code:"ASP 0123",message:"Missing Id attribute (ASP 0123)"},
                {code:"ASP 0124",message:"Missing Language attribute (ASP 0124)"},
                {code:"ASP 0125",message:"Missing close of attribute (ASP 0125)"},
                {code:"ASP 0126",message:"Include file not found (ASP 0126)"},
                {code:"ASP 0127",message:"Missing close of HTML comment (ASP 0127)"},
                {code:"ASP 0128",message:"Missing File or Virtual attribute (ASP 0128)"},
                {code:"ASP 0129",message:"Unknown scripting language (ASP 0129)"},
                {code:"ASP 0130",message:"Invalid File attribute (ASP 0130)"},
                {code:"ASP 0131",message:"Disallowed Parent Path (ASP 0131)"},
                {code:"ASP 0132",message:"Compilation Error (ASP 0132)"},
                {code:"ASP 0133",message:"Invalid ClassID attribute (ASP 0133)"},
                {code:"ASP 0134",message:"Invalid ProgID attribute (ASP 0134)"},
                {code:"ASP 0135",message:"Cyclic Include (ASP 0135)"},
                {code:"ASP 0136",message:"Invalid object instance name (ASP 0136)"},
                {code:"ASP 0137",message:"Invalid Global Script (ASP 0137)"},
                {code:"ASP 0138",message:"Nested Script Block (ASP 0138)"},
                {code:"ASP 0139",message:"Nested Object (ASP 0139)"},
                {code:"ASP 0140",message:"Page Command Out Of Order (ASP 0140)"},
                {code:"ASP 0141",message:"Page Command Repeated (ASP 0141)"},
                {code:"ASP 0142",message:"Thread token error (ASP 0142)"},
                {code:"ASP 0143",message:"Invalid Application Name (ASP 0143)"},
                {code:"ASP 0144",message:"Initialization Error (ASP 0144)"},
                {code:"ASP 0145",message:"New Application Failed (ASP 0145)"},
                {code:"ASP 0146",message:"New Session Failed (ASP 0146)"},
                {code:"ASP 0147",message:"500 Server Error (ASP 0147)"},
                {code:"ASP 0148",message:"Server Too Busy (ASP 0148)"},
                {code:"ASP 0149",message:"Application Restarting (ASP 0149)"},
                {code:"ASP 0150",message:"Application Directory Error (ASP 0150)"},
                {code:"ASP 0151",message:"Change Notification Error (ASP 0151)"},
                {code:"ASP 0152",message:"Security Error (ASP 0152)"},
                {code:"ASP 0153",message:"Thread Error (ASP 0153)"},
                {code:"ASP 0154",message:"Write HTTP Header Error (ASP 0154)"},
                {code:"ASP 0155",message:"Write Page Content Error (ASP 0155)"},
                {code:"ASP 0156",message:"Header Error (ASP 0156)"},
                {code:"ASP 0157",message:"Buffering On (ASP 0157)"},
                {code:"ASP 0158",message:"Missing URL (ASP 0158)"},
                {code:"ASP 0159",message:"Buffering Off (ASP 0159)"},
                {code:"ASP 0160",message:"Logging Failure (ASP 0160)"},
                {code:"ASP 0161",message:"Data Type Error (ASP 0161)"},
                {code:"ASP 0162",message:"Cannot Modify Cookie (ASP 0162)"},
                {code:"ASP 0163",message:"Invalid Comma Use (ASP 0163)"},
                {code:"ASP 0164",message:"Invalid TimeOut Value (ASP 0164)"},
                {code:"ASP 0165",message:"SessionID Error (ASP 0165)"},
                {code:"ASP 0166",message:"Uninitialized Object (ASP 0166)"},
                {code:"ASP 0167",message:"Session Initialization Error (ASP 0167)"},
                {code:"ASP 0168",message:"Disallowed object use (ASP 0168)"},
                {code:"ASP 0169",message:"Missing object information (ASP 0169)"},
                {code:"ASP 0170",message:"Delete Session Error (ASP 0170)"},
                {code:"ASP 0171",message:"Missing Path (ASP 0171)"},
                {code:"ASP 0172",message:"Invalid Path (ASP 0172)"},
                {code:"ASP 0173",message:"Invalid Path Character (ASP 0173)"},
                {code:"ASP 0174",message:"Invalid Path Character(s) (ASP 0174)"},
                {code:"ASP 0175",message:"Disallowed Path Characters (ASP 0175)"},
                {code:"ASP 0176",message:"Path Not Found (ASP 0176)"},
                {code:"ASP 0177",message:"Server.CreateObject Failed (ASP 0177)"},
                {code:"ASP 0178",message:"Server.CreateObject Access Error (ASP 0178)"},
                {code:"ASP 0179",message:"Application Initialization Error (ASP 0179)"},
                {code:"ASP 0180",message:"Disallowed object use (ASP 0180)"},
                {code:"ASP 0181",message:"Invalid threading model (ASP 0181)"},
                {code:"ASP 0182",message:"Missing object information (ASP 0182)"},
                {code:"ASP 0183",message:"Empty Cookie Key (ASP 0183)"},
                {code:"ASP 0184",message:"Missing Cookie Name (ASP 0184)"},
                {code:"ASP 0185",message:"Missing Default Property (ASP 0185)"},
                {code:"ASP 0186",message:"Error parsing certificate (ASP 0186)"},
                {code:"ASP 0187",message:"Object addition conflict (ASP 0187)"},
                {code:"ASP 0188",message:"Disallowed object use (ASP 0188)"},
                {code:"ASP 0189",message:"Disallowed object use (ASP 0189)"},
                {code:"ASP 0190",message:"Unexpected error (ASP 0190)"},
                {code:"ASP 0191",message:"Unexpected error (ASP 0191)"},
                {code:"ASP 0192",message:"Unexpected error (ASP 0192)"},
                {code:"ASP 0193",message:"OnStartPage Failed (ASP 0193)"},
                {code:"ASP 0194",message:"OnEndPage Failed (ASP 0194)"},
                {code:"ASP 0195",message:"Invalid Server Method Call (ASP 0195)"},
                {code:"ASP 0196",message:"Cannot launch out of process component (ASP 0196)"},
                {code:"ASP 0197",message:"Disallowed object use (ASP 0197)"},
                {code:"ASP 0198",message:"Server shutting down (ASP 0198)"},
                {code:"ASP 0199",message:"Disallowed object use (ASP 0199)"},
                {code:"ASP 0200",message:"Out of Range 'Expires' attribute (ASP 0200)"},
                {code:"ASP 0201",message:"Invalid Default Script Language (ASP 0201)"},
                {code:"ASP 0202",message:"Missing Code Page (ASP 0202)"},
                {code:"ASP 0203",message:"Invalid Code Page (ASP 0203)"},
                {code:"ASP 0204",message:"Invalid CodePage Value (ASP 0204)"},
                {code:"ASP 0205",message:"Change Notification (ASP 0205)"},
                {code:"ASP 0206",message:"Cannot call BinaryRead (ASP 0206)"},
                {code:"ASP 0207",message:"Cannot use Request.Form (ASP 0207)"},
                {code:"ASP 0208",message:"Cannot use generic Request collection (ASP 0208)"},
                {code:"ASP 0209",message:"Illegal value for TRANSACTION property (ASP 0209)"},
                {code:"ASP 0210",message:"Method not implemented (ASP 0210)"},
                {code:"ASP 0211",message:"Object out of scope (ASP 0211)"},
                {code:"ASP 0212",message:"Cannot Clear Buffer (ASP 0212)"},
                {code:"ASP 0214",message:"Invalid Path parameter (ASP 0214)"},
                {code:"ASP 0215",message:"Illegal value for ENABLESESSIONSTATE property (ASP 0215)"},
                {code:"ASP 0216",message:"MSDTC Service not running (ASP 0216)"},
                {code:"ASP 0217",message:"Invalid Scope in object tag (ASP 0217)"},
                {code:"ASP 0218",message:"Missing LCID (ASP 0218)"},
                {code:"ASP 0219",message:"Invalid LCID (ASP 0219)"},
                {code:"ASP 0220",message:"Requests for GLOBAL.ASA Not Allowed (ASP 0220)"},
                {code:"ASP 0221",message:"Invalid @ Command directive (ASP 0221)"},
                {code:"ASP 0222",message:"Invalid TypeLib Specification (ASP 0222)"},
                {code:"ASP 0223",message:"TypeLib Not Found (ASP 0223)"},
                {code:"ASP 0224",message:"Cannot load TypeLib (ASP 0224)"},
                {code:"ASP 0225",message:"Cannot wrap TypeLibs (ASP 0225)"},
                {code:"ASP 0226",message:"Cannot modify StaticObjects (ASP 0226)"},
                {code:"ASP 0227",message:"Server.Execute Failed (ASP 0227)"},
                {code:"ASP 0228",message:"Server.Execute Error (ASP 0228)"},
                {code:"ASP 0229",message:"Server.Transfer Failed (ASP 0229)"},
                {code:"ASP 0230",message:"Server.Transfer Error (ASP 0230)"},
                {code:"ASP 0231",message:"Server.Execute Error (ASP 0231)"},
                {code:"ASP 0232",message:"Invalid Cookie Specification (ASP 0232)"},
                {code:"ASP 0233",message:"Cannot load cookie script source (ASP 0233)"},
                {code:"ASP 0234",message:"Invalid include directive (ASP 0234)"},
                {code:"ASP 0235",message:"Server.Transfer Error (ASP 0235)"},
                {code:"ASP 0236",message:"Invalid Cookie Specification (ASP 0236)"},
                {code:"ASP 0237",message:"Invalid Cookie Specification (ASP 0237)"},
                {code:"ASP 0238",message:"Missing attribute value (ASP 0238)"},
                {code:"ASP 0239",message:"Cannot process file (ASP 0239)"},
                {code:"ASP 0240",message:"Script Engine Exception (ASP 0240)"},
                {code:"ASP 0241",message:"CreateObject Exception (ASP 0241)"},
                {code:"ASP 0242",message:"Query OnStartPage Interface Exception (ASP 0242)"},
                {code:"ASP 0243",message:"Invalid METADATA tag in Global.asa (ASP 0243)"},
                {code:"ASP 0244",message:"Cannot Enable Session State (ASP 0244)"},
                {code:"ASP 0245",message:"Mixed usage of Code Page values (ASP 0245)"},
                {code:"ASP 0246",message:"Too many concurrent users. Please try again later. (ASP 0246)"},
                {code:"ASP 0247",message:"Bad Argument to BinaryRead. (ASP 0247)"},
                {code:"ASP 0248",message:"Script isn't transacted. This ASP file must be transacted in order to use the ObjectContext object. (ASP 0248)"},
                {code:"ASP 0249",message:"Cannot use IStream on Request. Cannot use IStream on Request object after using Request.Form collection or Request.BinaryRead. (ASP 0249)"},
                {code:"ASP 0250",message:"Invalid Default Code Page. The default code page specified for this application is invalid. (ASP 0250)"},
                {code:"ASP 0251",message:"Response Buffer Limit Exceeded. Execution of the ASP page caused the Response Buffer to exceed its configured limit. (ASP 0251)"}];
            
            if ($.isUndefined(XMLHttpRequest.responseText) || XMLHttpRequest.responseText.length === 0) {
                return XMLHttpRequest.statusText;
            }
            
            for (var errorIndex = 0; errorIndex < aspErrorCodes.length; errorIndex++) {
                var aspErrorCode = aspErrorCodes[errorIndex],
                    reg = new RegExp(aspErrorCode.code, "gi");
                if (reg.test(XMLHttpRequest.responseText)) {
                    return aspErrorCode.message;
                }
            }
            return XMLHttpRequest.statusText;
        }
        
        var errorMessage = '',
        exception = false;

        try {
        if (!$.isUndefined(XMLHttpRequest)) {
            if (XMLHttpRequest.status.toString() === '0') {
                return;
            }
            errorMessage = getErrorMessage();
        } else if (!$.isUndefined(textStatus)) {
            errorMessage = textStatus;                            
        } else {
            errorMessage = errorThrown;
        }
        }
        catch(e) {
            exception = true;
            errorMessage = textStatus;
        }
        
        function appendMessage(caption, text) {
            return errorMessage += '<span class="small">' + caption + ':</span> ' + ($.isUndefined(text) ? '' : text) + '<br/>';
        }
        
        if (!$.isUndefined(SERKO) && ((SERKO.tracingEnabled()) || (!$.isUndefined(SERKO.PageState) && !$.isUndefined(SERKO.PageState.developerMachine) && SERKO.PageState.developerMachine === true))) {
            if (XMLHttpRequest && !exception) {
                errorMessage += '<p>';
                appendMessage("XMLHttpRequest.Status", XMLHttpRequest.status);
                appendMessage("XMLHttpRequest.StatusText", XMLHttpRequest.statusText);
                errorMessage += '</p>';
            }
            errorMessage += '<p>';
            appendMessage("jQuery.textStatus", textStatus);                            
            appendMessage("jQuery.errorThrown", errorThrown);                            
            appendMessage("jQuery.url", url);   
            errorMessage += '</p>';   
            
            if (data) {
                errorMessage += '<br/>Data:<ul>';
                var parts = $.param(data).split('&');
                for (var x = 0; x < parts.length; x++) {
                    errorMessage += '<li>' + parts[x] + '</li>';
                }
                errorMessage += '</ul>';
            }

            errorMessage += '<br/>Cookies:<ul>';
            $.each(SERKO.Cookies.get(), function (i, cookie) {
                errorMessage += '<li>' + cookie.name + ': ' + cookie.value + '</li>';
            });
            errorMessage += '</ul>';
            
            if (!$.isUndefined(XMLHttpRequest) && XMLHttpRequest.responseText) {
                errorMessage += '<br/>XMLHttpRequest.responseText:<br/><textarea class="error">';
                var pos = XMLHttpRequest.responseText.search(/Error Type/i);
                if (pos > -1) {
                    errorMessage += XMLHttpRequest.responseText.substr(pos);
                } else {
                    errorMessage += XMLHttpRequest.responseText;
                }
                errorMessage += '</textarea>';
            }
        }
        
        context.showPageError(errorMessage);
    }
    
    $.fn.required = function () {
        return this.each(function (i) {
            var context = $(this);

            //If the Required indicator has already been appended don't append it again.
            if(context.find('.required_indicator').length !== 0) {
                return true;
            }
            
            context.append('<span class="required_indicator information" title="Required">*</span>');
        });
    };
    $.fn.removeRequired = function () {
        return this.each(function (i) {
            var context = $(this);
            $('.required_indicator', context).remove();
        });
    };    
    
    /*
     * This turns a table into a GridList, mainly used on the Lightboxes
     * High lights the alternative rows, adds rowselected for clicks if radio/checkboxes exist
     * for .grid_list div tables following the .row .cell structure
    */
    $.fn.gridList = function (options) {
        return this.each(function (i) {
            var context = $(this);
            var opts = $.extend({}, $.fn.gridList.defaults, options);            
            context.find('.row:visible:even').addClass('rowalternate');
            var rows = $('.row', context);
            context.find('.row input:checkbox, .row input:radio').click(function (event) {
                var input = $(this);
                var row = input.closest('.row');
                if (row.length === 0 ) {
                    return;
                }
                
                if(input.is(':radio')) {
                    rows.removeClass('rowselected');
                    row.addClass('rowselected');
                    $('input:radio', context).removeAttr('checked');
                    input.attr('checked','checked');
                } else {
                    // checkbox
                    if (row.hasClass('rowselected')) {
                        row.removeClass('rowselected'); 
                        input.removeAttr('checked');
                    } else {
                        row.addClass('rowselected'); 
                        input.attr('checked','checked');
                    } 
                }
            });

            $('#selectall',context).click(function (event) {
                $('input:checkbox:not(:checked)', context).attr('checked','checked');
                rows.addClass('rowselected');
            });

            $('#deselectall',context).click(function (event) {
                $('input:checkbox:checked', context).removeAttr('checked');
                rows.removeClass('rowselected');
            });
            
            if (rows.length === 1 && $('input:radio, input:checkbox', context).length === 1) {
                $('input:radio, input:checkbox', context).attr('checked','checked');
                rows.addClass('rowselected');
            }
            
        });
    };    
    $.fn.gridList.defaults = {
    };
    
	
    /*
     * Get the layout style for an single element (border/padding/margin)
    */
    $.fn.layout = function () {
		var getValue = function (s) { s = s.replace('px',''); return parseInt((isNaN(s) ? 0 : s)); };

		if ( this[0] ) {
			return {
			    width   :   this.width(),
			    height  :   this.height(),
			    position:   this.position(),
			    border  :   {
				                top     : getValue(this.css('border-top-width')),
				                left    : getValue(this.css('border-left-width')),
				                right   : getValue(this.css('border-right-width')),
				                bottom  : getValue(this.css('border-bottom-width'))
				            },
                margin  :   {
				                top     : getValue(this.css('margin-top')),
				                left    : getValue(this.css('margin-left')),
				                right   : getValue(this.css('margin-right')),
				                bottom  : getValue(this.css('margin-bottom'))
			                },
			    padding :   {
				                top     : getValue(this.css('padding-top')),
				                left    : getValue(this.css('padding-left')),
				                right   : getValue(this.css('padding-right')),
				                bottom  : getValue(this.css('padding-bottom'))
			                },
			    totalHeight : function (includeMargin) {
			        var total = this.height + this.border.top + this.border.bottom + this.padding.top + this.padding.bottom;
			        if (!$.isUndefined(includeMargin) && includeMargin === true) {
			            total += this.margin.top + this.margin.bottom;
			        }
			        return total;
			    },
			    totalWidth  : function (includeMargin) {
			        var total = this.width + this.border.left + this.border.right + this.padding.left + this.padding.right;
			        if (!$.isUndefined(includeMargin) && includeMargin === true) {
			            total += this.margin.left + this.margin.right;
			        }
			        return total;
			    },
			    toString : function () {
			        return 'padding.left: ' + this.padding.left
			                + '\npadding.right: ' + this.padding.right
			                + '\npadding.top: ' + this.padding.top
			                + '\npadding.bottom: ' + this.padding.bottom
			                + '\nmargin.left: ' + this.margin.left
			                + '\nmargin.right: ' + this.margin.right
			                + '\nmargin.top: ' + this.margin.top
			                + '\nmargin.bottom: ' + this.margin.bottom
			                + '\nborder.left: ' + this.border.left
			                + '\nborder.right: ' + this.border.right
			                + '\nborder.top: ' + this.border.top
			                + '\nborder.bottom: ' + this.border.bottom			                
			                + '\nwidth: ' + this.width
			                + '\nheight: ' + this.height
			                + '\ntotalHeight: ' + this.totalHeight()
			                + '\ntotalHeight with Margin: ' + this.totalHeight(true)
			                + '\ntotalWidth: ' + this.totalWidth()
			                + '\ntotalWidth with Margin: ' + this.totalWidth(true);
			    }
			    
			};
		}

		return undefined;
	};	
	
    /*
     * Moreactions Flyout menus, select the div which contains the flyout conents that you want to assign
     *
    */ 
    $.fn.moreActions = function (trigger, options) {
        if ($.isUndefined(this.each)) {
            return;
        }
        
        return this.each(function (i) {
            var context = $(this);
            var triggerContext = trigger; 
            if ($.isUndefined(triggerContext.each)) {
                triggerContext = $( (trigger.substr(0,1) === '#' ? '' : '#') + trigger); 
            }
            var opts = $.extend({}, $.fn.moreActions.defaults, options);    
            
            context.data('moreactions_trigger', triggerContext)
                   .data('moreactions_options', opts)
                   .addClass( opts.isApplyFilter ? 'moreactions_applyfilter_content' : 'moreactions_content')
                   .hide();
                   
            // keep a store of moreactions
            $.fn.moreActions.cache.push({trigger: triggerContext, options: context});
            
            triggerContext.addClass('link')
                      .data('moreactions_menu', context)
                      .attr('title','Click to see options')
                      .click(function (event) {
                            var clickContext = $(this);
                            clickContext.data('title', context.attr('title')).attr('title', 'Click to hide options');
                            
                            if (context.is(':visible')) {
                                $.fn.moreActions.hide(event);
                                return;
                            }
                            
                            // close all open moreactions
                            $.each($.fn.moreActions.cache, function(index, moreaction) {
                                if (moreaction.options.is(':visible') && moreaction.options.data('moreactions_options').oneOnly === true) {
                                    moreaction.trigger.click();
                                }            
                            });
                            
                            var pos = clickContext.position();
                            var clickContextLayout = clickContext.layout();
                            var contextLayout = context.layout();
                            if (opts.setWidth) {
                                context.width((clickContext.width() - contextLayout.padding.left - contextLayout.padding.right - contextLayout.border.left - contextLayout.border.right));
                            }
                                        
                            var left = opts.align === 'left'
                                        ? (pos.left + clickContextLayout.margin.left)
                                        : pos.left - (context.width() - clickContext.width()),
                                top = ((pos.top - 3) + clickContext.height());
                            context.css({ 'left' : left + 'px', 'top' :  top + 'px' }).slideDown(SERKO.slideDuration, function () {
                                // scroll menu into view if off bottom of page
                                var windowHeight = $(window).height(),
                                    scrollBottom = windowHeight + $(document).scrollTop(),
                                    popupHeight = $(this).height();

                                if ((context.offset().top + popupHeight) >= scrollBottom) {
                                    var scrollTop = (parseInt(pos.top, 10) + popupHeight + clickContext.height() + 10) - windowHeight;
                                    window.scrollTo(parseInt(pos.left, 10), scrollTop < 0 ? 0 : scrollTop);
                                }
                            });
                            
                            event.stopPropagation();
                            event.preventDefault();
                            $(document.body).bind("click.moreactions", $.fn.moreActions.hide);
                            context.bind("click.moreactions", $.fn.moreActions.hide);            
                        });
        });
    };    
    $.fn.moreActions.cache = [];
    $.fn.moreActions.hide = function (event) {
        var context = $(this);
        
        // validate context
        if (!$.isUndefined(context.data('moreactions_menu'))) { // event fired by trigger
        } else if (!$.isUndefined(context.data('moreactions_trigger'))) { // event fired by/within menu
            if ($(event.target).is(':not(input:image, span.link)') ) {
                event.stopPropagation();
                return;
            }
        } else { // fired by something else (body, input)
        }
                
        $('.moreactions_content:visible, .moreactions_applyfilter_content:visible')
            .slideUp(SERKO.slideDuration, function () {
                    $(this).data('moreactions_trigger').attr('title','Click to show options');
                })
             .unbind("click.moreactions")
             .attr('title','');
        $(document.body).unbind("click.moreactions");
    };
    $.fn.moreActions.defaults = {
        className          : 'moreactions_content',
        setWidth           : true,
        ignoreInputClicks  : true,
        isApplyFilter      : false,
        align              : 'left',
        oneOnly            : true // set to false if you want the moreaction to stay open while another is open
    };                    
    $.fn.moreActions.createGroupHeader = function (description) {
        return $(document.createElement('div'))
                    .addClass('moreactions_group')
                    .append( $(document.createElement('div')).addClass('moreactions_group_header label').html(description) );
    };
    $.fn.moreActions.createGroupFooter = function () {    
        return '<div class="moreactions_groupfooter"></div>';
    };
    $.fn.moreActions.createGroupItem = function (description, moreAction, tooltip) {
        var div = $(document.createElement('div')).addClass('moreactions_group_item'),
            span = $(document.createElement('span')).addClass('link').data('moreAction', moreAction).html(description);
        if (!$.isUndefined(tooltip)) {
            span.attr('title', tooltip);
        }
        div.append(span);
        return div;
    };
    $.fn.moreActions.createGroupItemLink = function (description, url, tooltip) {
        var div = $(document.createElement('div')).addClass('moreactions_group_item'),
            link = $(document.createElement('a'))
                .addClass('link')
                .attr({href: url, target: '_blank'})
                .html(description)
                .appendTo(div);
        if (tooltip) {
            link.attr('title', tooltip);
        }
        return div;
    };    
    $.fn.moreActions.createSmallTrigger = function (id, title) {    
        return $(document.createElement('img')).attr({id: id, title: title, alt: '', src: 'SOLV2/Images/button_moreactions_small' + $.browser.imageExtension() })
    };
    
    $(document).ready(function () {
        $('div.required').required();
        
        // error handler
        
        var errorMessage = '';
        try {
            if (!$.isUndefined(pageOptions) && !$.isUndefined(pageOptions.errorMessage)) {
                errorMessage = pageOptions.errorMessage;
            }
        } catch(e) {}
        
        if (errorMessage.length > 0) {
            var element = $('#page_error');
            if (element.length === 0) {
                element = $('#content');
                if (element.length === 0 ) {
                    element = $(document.createElement('div'));
                    $(document.body).append(element);
                }
            }
            element.showPageError(errorMessage, { append : true });
        }
        
        // remove google toolbar background colour fillers
        $(window).load(function (event) {
            // do a quick remove, so if google is quick we remove it before it actually shows
            $('.data_entry').find('input, select').css('background-color','');
            // do a timedout remove as well to catch googel reappying
            window.setTimeout("$('.data_entry').find('input, select').css('background-color','');", 1000);
        });
        
        $('#optimised').click(function(){
            SERKO.LightBox.showPage('so-tools.asp', {showOk: true, width: 600, height: 450});
        });
        
        // resize iframe 
        if ($.browser.isMobileDevice() && !$.isUndefined(window.parent) && !$.isUndefined(window.parent.SERKO) && window.parent.SERKO.LightBox.isVisible()) {
            window.parent.SERKO.LightBox.setMobileHeight();
        }
    });
})(jQuery);

/*Global Variables*/
    if (typeof SERKO.Globals === "undefined") {
        SERKO.Globals = {};
    }
    jQuery.extend(SERKO.Globals, {
            timeAny: '(any)',
                
            timesJSON : 
                function (options) {
                    var opts = $.extend({ includeAny : true }, $.fn.times.defaults, options);
                    var times = [];
                    
                    if (opts.includeAny === true) {
                        times.push({ Time: SERKO.Globals.timeAny, IsAny: true });
                    }
                    for(var count = 0; count < 24; count++) {
                        times.push({ Time: SERKO.formatTime(count + ':00'), IsAny: false });
                    }
                    return times;
            },
            
            monthsJSON : { 'MonthsRs' : [{ 'index': 0, 'display': 'January' }, { 'index': 1, 'display': 'February' }, { 'index': 2, 'display': 'March' }, { 'index': 3, 'display': 'April' },
                { 'index': 4, 'display': 'May' }, { 'index': 5, 'display': 'June' }, { 'index': 6, 'display': 'July' }, { 'index': 7, 'display': 'August' },
                { 'index': 8, 'display': 'September' }, { 'index': 9, 'display': 'October' }, { 'index': 10, 'display': 'November' }, { 'index': 11, 'display': 'December' }] },
                
            objectType : {
                booking : 'oBooking',
                bookingClone : 'oBookingClone',
                customBooking : 'oCustomBooking',
                quickBooking : 'oQB',
                reporting : 'oReporting',
                userProfile : 'oUserProfile',
                databaseAdministration: 'oDatabaseAdministration'
            },
            
            bookingType : {
                travelAgency: 'Travel Agency',
                quickBooking: 'Web QB',
                customBooking: 'Custom Booking',
                travelAgencyTakeover: 'TA Takeover',
                noBooking: 'No Booking'
            },
            
            crs : {
                galileo: 1,
                sabre: 2,
                amadeus: 3,
                abacus: 4,
                getName: function (crsId) {
                    return ["Galileo", "Sabre", "Amadeus", "Abacus"][crsId - 1];
                }
            },
            
            siProviders: {
                sabre: 'Sabre',
                theHotelNetwork: 'TheHotelNetwork',
                virginBlue: 'VirginBlue',
                jetStar: 'Jetstar',
                rex: 'Rex',
                isNonGdsProvider: function (providerName) {
                    return providerName.length > 0 && providerName !== this.sabre;
                },
                isGdsProvider: function (providerName) {
                    return providerName.length === 0 || providerName === this.sabre;
                }
            },
            
            isBookingTypeModifiable : function () {
                return !$.isUndefined(SERKO.PageState.bookingType) && SERKO.PageState.bookingType === SERKO.Globals.bookingType.quickBooking;
            },
            
            objectTypeDescription : function (objectType) {
                switch (objectType) {
                case SERKO.Globals.objectType.quickBooking :
                    return 'booking';
                case SERKO.Globals.objectType.userProfile :
                    return 'profile';
                case SERKO.Globals.objectType.customBooking :
                    return 'custom booking';
                default:
                    return 'UNKNOWN';
                }
            },
            
            cityValueOptions : [
                new SERKO.DropDowns.DisplayOption('CD_sCity_Desc', null, null),
                new SERKO.DropDowns.DisplayOption('CD_sCity', ' (', ')')
            ],
            
            constants : {
                updateResults : {
                    lastSeatFailed : { value: 20, text : 'At least one Flight, Car, or Hotel Room cannot be booked, see below for reason.' },
                    passportExpired : { value: 21, text : 'At least one passport entered has expired or will expire within six months of travel.' },
                    tooManyPassports : { value: 22, text : 'Only one passport may be entered for each traveller on this trip.' },
                    conflictMileage : { value: 23, text : 'More than one Frequent Flyer card can be used.' },
                    pnrDidNotEnd : { value: 24, text : 'Unable to end the Booking.' },
                    pnrNotCreated : { value: 25, text : 'Unable to create the Booking.' },
                    bookingNotCreated : { value: 26, text : 'Unable to create the Booking.' },
                    pnrFoundDoesntMatch : { value: 27, text : 'The Booking reference does not match.' },
                    submitInProgress : { value: 28, text : 'This booking has already been submitted.' },
                    storedProcedureFailed : { value: 29, text : 'Copying to a Booking failed.' },
                    removalOfPreferences : { value: 30, text : 'Failed to remove existing Preferences.' },
                    removalOfSegments : { value: 31, text : 'Failed to remove existing segments.' },
                    noConsultantSelected : { value: 32, text : 'No Consultant has been selected.' },
                    noTicketerSelected : { value: 33, text : 'No Ticketer has been selected.' },
                    noAuthoriserSelected : { value: 34,text : 'No Authoriser has been selected.' },
                    nameOnCardDifferent : { value: 35, text : 'The Payment details for this booking are incorrect.' },
                    noFareAvailable : { value: 36, text : 'There was a problem booking the selected fare - please contact your Travel Management Company.' },
                    seatSellFailed : { value: 37, text : 'There has been a problem Submitting the Booking.' },
                    checkFlightConnectionTimes : { value: 38, text : 'One of the flights selected is within the minimum recommended connecting time.<br/>Please refer to the sector information below.<br/>Click Finish to override this warning and continue with the booking.' },
                    checkFlightOrdering : { value: 39, text : 'The flight continuity is not correct.' },
                    updatePnrWithBookingId : { value: 40, text : 'Unable to Update the PNR with the Booking Number.' },
                    conflictMeal : { value: 41, text : 'A Meal conflict has been detected.' },
                    siError : { value: 42, text : 'Unable to complete the Booking.' },
                    deadlockTimeout : { value: 43, text : 'The booking failed to submit – please try again.' },
                    pnrNotUpdated : { value: 44, text : 'The PNR could not be updated.' },
                    frequentFlyerInvalid : { value: 45, text : 'One of the frequent flyer numbers is incorrect.' },
                    cardHolderNameInvalid : { value: 46, text : 'Booking failed due to invalid name on credit card.<br/>Click \'Finish\' to use the travellers names as the credit card name on card.' },
                    destinationCodeInvalid : { value: 47, text: 'No Destination has been selected, please press \'Ctrl\' + \'F5\' to refresh then click \'Finish\'.' }
            },
                
                displayMessage : function (constantClassName, constantId, additionalMessage, options) {
                    if ($.isUndefined(additionalMessage)) {
                        additionalMessage = '';
                    }
                    var constants = SERKO.Globals.constants[constantClassName],
                    constantId = parseInt(constantId, 10);
                    
                    for (var name in constants) {
                        var constant = constants[name];
                        if (!constant || constant.value !== constantId) {
                            continue;
                        }
                        
                        var error = $('<div></div>')
                            .append('<div class="float_left">' + constant.text + '</div>');
                            
                        $('#page_error').showPageError(error, options);
                        
                        if (additionalMessage.length > 0) {
                            $(document.createElement('li'))
                                .addClass($.browser.spriteCssClass())
                                .addClass('information icon_help float_left')
                                .appendTo(error)
                                .toolTip(additionalMessage, { width: 300, heading: 'Additional Information' });
                        }
                        
                        error.append(SERKO.clearFloat);
                        
                        return;
                    }
                    
                    $('#page_error').showPageError(additionalMessage, options);
                },
                
                changeDate : {
                    departing : { value: 0, text : 'Changing the Departing Flight.' },
                    returning : { value: 1, text : 'Changing the Returning Flight.' },
                    moveTrip :  { value: 2, text : 'Changing both Departing and Returning Flights.' }
                },
                
                policyType : {
                    quickTrip : { value: 0, text: 'Air, Car and Hotel.' },
                    hotel :     { value: 1, text: 'Hotel Only' },
                    car :       { value: 2, text: 'Car Only' },
                    air :       { value: 3, text: 'Air Only' },
                    land :      { value: 4, text: 'Land Only' }
                },
                
                segmentType : {
                    air :   { value: 0, text: 'Air' },
                    car :   { value: 2, text: 'Car' },
                    hotel : { value: 1, text: 'Hotel' }
                },
                
                car : {
                    rateTypes : {
                        daily   :   { value: 'D', text: 'Daily' },
                        weekly  :   { value: 'W', text: 'Weekly' },
                        monthly :   { value: 'M', text: 'Monthly' },
                        yearly  :   { value: 'Y', text: 'Yearly' },
                        prepaid :   { value: 'P', text: 'Prepaid' },
                        weekendE :  { value: 'E', text: 'Weekend' },
                        weekendK :  { value: 'K', text: 'Weekend' }
                    },
                    
                    getRateType : function (rateTypeCode) {
                        for (var value in SERKO.Globals.constants.car.rateTypes) {
                            if (SERKO.Globals.constants.car.rateTypes[value].value.toLowerCase() === rateTypeCode.toLowerCase()) {
                                return SERKO.Globals.constants.car.rateTypes[value];
                            }
                        }
                        return { value: 'Unknown', text: rateTypeCode };
                    }
                    
                },
                
                airBreakPolicyReasons : {
                    defaultBooking: 0,
                    booking: 1,
                    sector: 2
                }
            }
    });
/*End Global Variabled*/

    jQuery.extend(SERKO, {
        WaitWindow : {
        
            context : {
                        airAvailability     : 'AirAvailability',
                        carAvailability     : 'CarAvailability',
                        hotelAvailability   : 'HotelAvailability',
                        site                : 'Site'
                    },
                    
            show : function (title, message, context, lightBoxOptions) {
                SERKO.waitWindowShowing = true;
                var params = [ { name: 'title', value: title },
                               { name: 'message', value: message },
                               { name: 'context', value: context } ];
                    opts = $.extend({}, {
                        params: params,
                        width: 452,
                        height: 390,
                        showButtons: false,
                        showLoading: false
                    }, lightBoxOptions);
                    
                SERKO.LightBox.showPage('wait.asp', opts);
            },
            
            hide : function () {
                SERKO.waitWindowShowing = false;
                if (!$.isUndefined(SERKO.LightBox.currentFrame()) && !$.isUndefined(SERKO.LightBox.currentFrame().stopProgressbar)) {
                    SERKO.LightBox.currentFrame().stopProgressbar();
                };
                SERKO.LightBox.hide();
            }
        }
    });
    

    jQuery.extend(SERKO, {
        PolicyDetailsWindow : {
            show : function (stage, policyName, comment, reason, information) {
                var url = 'crsdetail.asp?stage=' + stage + '&dettype=PolicyDetail&policyName=' + escape(policyName) + '&comment=' + escape(comment.formatAsHtml()) + '&reason=' + escape(reason.formatAsHtml()) + '&information=' + escape(information.formatAsHtml());
                SERKO.LightBox.showPage(url, { showClose: true});
            }
        }
    });

    jQuery.extend(SERKO, {
        CrsDetailsWindow : {
        
            displayTypes    : {
                                Air         : 'AIR',
                                Car         : 'CAR',
                                Hotel       : 'HOTEL',
                                Fare        : 'FARE',
                                AirTicket   : 'AIRTICKET',
                                HotelCancelaltionPolicy : 'HOTELCANCELATION',
                                CarRules : 'CARRULES'
                            },
                            
            show            : function (parameters, lightBoxOptions) {
                                var opts = $.extend({ width: 400 }, lightBoxOptions);
                                
                                var params = $.extend({
                                    stage : 0,
                                    displayType : '', 
                                    bookingId : -1,
                                    segmentId : -1,
                                    isSectorFare : 'true',
                                    acceptFareRules : 'false',
                                    airSegmentId : -1,
                                    segmentNumber : -1,
                                    breakPolicy : 'false',
                                    providerName: ''
                                }, parameters);
                                
                                var url = 'crsdetail.asp?stage=' + params.stage + '&dettype=' + params.displayType + '&bookid=' + params.bookingId + '&segid=' + params.segmentId + '&sectorfare=' + params.isSectorFare + '&acceptfarerules=' + params.acceptFareRules + '&airsegmentid=' + params.airSegmentId +'&segnumber='+ params.segmentNumber +'&breakpolicy=' + params.breakPolicy + '&providerName=' + params.providerName;
                                SERKO.LightBox.showPage(url, opts);
                            }
        }
    });
    
/*Function written by a Third Party to validate Credit Card numbers*/
function checkCreditCard(cardnumber, cardname) {
    // Array to hold the permitted card characteristics
    var cards = [];

    cards [0] = {name: 'Visa',
               code: 'VI',
               length: '13,16', 
               prefixes: '4',
               checkdigit: true};
    cards [1] = {name: 'MasterCard', 
               code: 'CA',
               length: '16', 
               prefixes: '51,52,53,54,55',
               checkdigit: true};
    cards [2] = {name: 'DinersClub', 
               code: 'DC',
               length: '14,16', 
               prefixes: '300,301,302,303,304,305,36,38,55',
               checkdigit: true};
    cards [3] = {name: 'CarteBlanche', 
               code: 'N/A',
               length: '14', 
               prefixes: '300,301,302,303,304,305,36,38',
               checkdigit: true};
    cards [4] = {name: 'AmEx', 
               code: 'AX',
               length: '15', 
               prefixes: '34,37',
               checkdigit: true};
    cards [5] = {name: 'Discover', 
               code: 'N/A',
               length: '16', 
               prefixes: '6011,650',
               checkdigit: true};
    cards [6] = {name: 'JCB', 
               code: 'JC',
               length: '15,16', 
               prefixes: '3,1800,2131',
               checkdigit: true};
    cards [7] = {name: 'enRoute', 
               code: 'N/A',
               length: '15', 
               prefixes: '2014,2149',
               checkdigit: true};
    cards [8] = {name: 'Solo', 
               code: 'N/A',
               length: '16,18,19', 
               prefixes: '6334, 6767',
               checkdigit: true};
    cards [9] = {name: 'Switch', 
               code: 'N/A',
               length: '16,18,19', 
               prefixes: '4903,4905,4911,4936,564182,633110,6333,6759',
               checkdigit: true};
    cards [10] = {name: 'Maestro', 
               code: 'N/A',
               length: '16,18', 
               prefixes: '5020,6',
               checkdigit: true};
    cards [11] = {name: 'VisaElectron', 
               code: 'N/A',
               length: '16', 
               prefixes: '417500,4917,4913',
               checkdigit: true};
               
    // Establish card type
    var cardType = -1;
    for (var i=0; i<cards.length; i++) {

    // See if it is this card (ignoring the case of the string)
    if (cardname.toLowerCase () === cards[i].name.toLowerCase() || cardname.toLowerCase () === cards[i].code.toLowerCase()) {
      cardType = i;
      break;
    }
    }

    // If card type not found
    if (cardType === -1) {
     return true; 
    }

    // Ensure that the user has provided a credit card number
    if (cardnumber.length === 0)  {
     return false; 
    }

    // Now remove any spaces from the credit card number
    cardnumber = cardnumber.replace (/\s/g, '');

    // Check that the number is numeric
    var cardNo = cardnumber
    var cardexp = /^[0-9]{13,19}$/;
    if (!cardexp.exec(cardNo))  {
     return false; 
    }
       
    // Now check the modulus 10 check digit - if required
    if (cards[cardType].checkdigit) {
    var checksum = 0;                                  // running checksum total
    var mychar = '';                                   // next char to process
    var j = 1;                                         // takes value of 1 or 2

    // Process each digit one by one starting at the right
    var calc;
    for (i = cardNo.length - 1; i >= 0; i--) {

      // Extract the next digit and multiply by 1 or 2 on alternative digits.
      calc = Number(cardNo.charAt(i)) * j;

      // If the result is in two digits add 1 to the checksum total
      if (calc > 9) {
        checksum = checksum + 1;
        calc = calc - 10;
      }

      // Add the units element to the checksum total
      checksum = checksum + calc;

      // Switch the value of j
      if (j ==1) {j = 2} else {j = 1};
    } 

    // All done - if checksum is divisible by 10, it is a valid modulus 10.
    // If not, report an error.
    if (checksum % 10 !== 0)  {
     return false; 
    }
    }  

    // The following are the card-specific checks we undertake.
    var LengthValid = false;
    var PrefixValid = false; 
    var undefined; 

    // We use these for holding the valid lengths and prefixes of a card type
    var prefix = new Array ();
    var lengths = new Array ();

    // Load an array with the valid prefixes for this card
    prefix = cards[cardType].prefixes.split(',');
      
    // Now see if any of them match what we have in the card number
    for (i=0; i<prefix.length; i++) {
    var exp = new RegExp ('^' + prefix[i]);
    if (exp.test (cardNo)) PrefixValid = true;
    }
      
    // If it isn't a valid prefix there's no point at looking at the length
    if (!PrefixValid) {
     return false; 
    }

    // See if the length is valid for this card
    lengths = cards[cardType].length.split(',');
    for (j=0; j<lengths.length; j++) {
    if (cardNo.length === parseInt(lengths[j], 10)) LengthValid = true;
    }

    // See if all is OK by seeing if the length was valid. We only check the 
    // length if all else was hunky dory.
    if (!LengthValid) {
     return false; 
    };   

    // The credit card is in the required format.
    return true;
}
/*Function above written by a Third Party to validate Credit Card numbers*/


/*
 * Extensions to the Javascript string class
 *
*/  
jQuery.extend(String.prototype, {

    convertToFloat: function () {
        var number = (this + '').replace(/\$|,/gi,'');
        if (number.length === 0 || isNaN(number)) {
            return 0.00;
        }
        
        return parseFloat(number);
    },
    
    convertToInt: function () {
        var number = (this + '').replace(/\$|,/gi,'');
        if (number.length === 0 || isNaN(number)) {
            return 0;
        }
        
        return parseInt(number, 10);
    },    

    /* format a number with commas,
     * pass in decimal places as first argument, optional will not format decimal places
     * pass in leading zeros as second argument, optional will not add leading zeros if omitted
     * pass in ommit comma as third argument
     */ 
    formatAsNumber: function () {
        var decimalPlaces = arguments.length === 0 ? -1 : arguments[0],
            leadingZeros = arguments[1],
            excludeComma = arguments[2];
        
        // private helper function to padd zeros
        function paddZeros(zerosToAdd) {
            var format = '';
            for (var i = 0; i < zerosToAdd; i++) {
                format += '0';
            }
            return format;
        };
        
        // helper function to format the decimal places
        function formatDecimalPlaces (value) {
            if (decimalPlaces === -1) {
                return value.length === 0 ? '' : '.' + value;
            } else if (decimalPlaces === 0) {
                return '';
            }
            
            // no more decimal places than required
            if (value.length === 0) {
                return '.' + paddZeros(decimalPlaces);
            }
            
            // more decimal places than required
            if (value.length > decimalPlaces) {
                var fixedPrecision = parseFloat('0.' + value).toFixed(decimalPlaces);
                return fixedPrecision.toString().substring(1);
            }
            
            return '.' + value + paddZeros(decimalPlaces - value.length);
        };
        
        function addLeadingZeros(value) {
            if ($.isUndefined(leadingZeros) || value.length >= leadingZeros) {
                return value;
            }
            return paddZeros(leadingZeros - value.length) + value;
        };
        
        var s = this + '';
        var x = s.split('.');
        var x1 = addLeadingZeros(x[0]);
        var x2 = x.length > 1 ? formatDecimalPlaces(x[1]) : formatDecimalPlaces('');
        var rgx = /(\d+)(\d{3})/;
        if (!excludeComma) {
            while (rgx.test(x1)) {
	            x1 = x1.replace(rgx, '$1' + ',' + '$2');
            }
        }
        return x1 + x2;
    },
    
    formatAsCurrency: function () {
        var value = this.formatAsNumber.apply(this, arguments);
        return isNaN(value.replace(/,/g,'')) ? value : '$' + value;
    },
    
    formatAsHtml: function () {
        return this.replace(/\n/g,'<br/>').replace(/\|\|/g, '<br/>');
    },
    
    formatAsTime: function () {
        if (this.length === 0) {
            return '';
        }
        
        var defaultTime = Date.today().clearTime().toString(SERKO.timeFormat);
        var time = defaultTime;
        
        if (this.length > 2) {
            if (this.substr(1,1) === '.' || this.substr(2,1) === '.') {
                time = this.replace(/\./, ':');  // dont replace globally because only want to replace first occurence
            } else {
                time = this;
            }
                
            if (time.indexOf(':') === -1) {
                if (time.length === 4) {
                    time = time.substr(0, 2) + ':' + time.substr(2,2);
                } else if (time.length === 3) {
                    time = time.substr(0, 1) + ':' + time.substr(1,2);
                } else {
                    time = defaultTime;
                }
            } else {
                var parts = time.split(':');
                if (isNaN(parts[0]) || parts[1].length < 2 || isNaN(parts[1].substr(0,2))) {
                    time = defaultTime;
                }
            }
        }
        return Date.parse(time).toString(SERKO.timeFormat);
    },
    
    formatAsFlyingTime: function (abbreviated) {
        var parts = this.split(':'),
            hours = abbreviated ? 'h ' : ' hour(s) and ',
            minutes = abbreviated ? 'm' : ' minute(s)';
        return parseInt(parts[0], 10) + hours + parseInt(parts[1], 10) + minutes;
    },
    
    blank: function () {
        return /^\s*$/.test(this);
    }
});    

jQuery.extend(SERKO, {

    /* Show the TravellerSearch screen 
     * 
     *  options (optional)
     *      surname         : (string) pass a preselected surname to run automatically, leave blank to allow user to enter criteria
     *      successCallback : (function (array of passengerId)) to run when user has selected one to many travellers and clicked Ok
     *      oneMode         : boolean - true, will show travellers as radio, false will show as checkbox
    */
    TravellerSearch : {
                        show: function (options) {
                                var opts = jQuery.extend({}, SERKO.TravellerSearch.defaults, options);
                        
                                SERKO.LightBox.showPage('so-traveller-search.asp', {
                                    width       : $.browser.isMobileDevice() ? 900 : 650,
                                    height      : 415,
                                    params      : [ { name: 'surname', value: opts.surname },
                                                    { name: 'oneMode', value: opts.oneMode },
                                                    { name: 'passengerCount', value: opts.passengerCount },
                                                    { name: 'maxPassengerCount', value: opts.maxPassengerCount } ],
                                    showOk      : true,
                                    showCancel  : true,
                                    beforeHide  : function (buttonPressed) {
                                                    if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                        return true;
                                                    }
                                                    if (buttonPressed === 'CreateTraveller') {
                                                        opts.createTraveller.click();
                                                        return;
                                                    }
                                                    if (!this.currentFrame().searchComplete_BeforeHide()) {
                                                        return false;
                                                    }
                                                    
                                                    if (!$.isFunction(opts.successCallback)) {
                                                        return true;
                                                    }

                                                    opts.successCallback.call(this, this.currentFrame().getSelectedItems());
                                                    return true;
                                                },
                                    customButtons: opts.customButtons,
                                    afterShow   : function () {
                                                    this.currentFrame().pageOptions.costCentreId = opts.costCentreId;
                                                }
                                    });
                            },
                        defaults: { surname: '', successCallback: null, oneMode: false, customButtons: [], createTraveller: $('#btn_create'), passengerCount: 0, maxPassengerCount: 0 }
                    },
                    
    TravellerCreate : {
                        show: function(options) {
                                    var opts = jQuery.extend({}, SERKO.TravellerCreate.defaults, options);
                                    
                                    SERKO.LightBox.showPage('so-qb-create-traveller.asp', {
                                        width       : 370,
                                        height      : 375,
                                        showOk      : true,
                                        showCancel  : true,
                                        beforeHide  : function (buttonPressed) {
                                                        if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                            return true;
                                                        }
                                                        if (buttonPressed === "Search") {
                                                            opts.showTravellerSearchCallback.call(this, ($(this.currentFrame().document.body).find('#surname').val()));
                                                            return false;
                                                        }
                                                        return this.currentFrame().createTraveller_BeforeHide(buttonPressed);
                                                    },
                                        afterHide   : function (buttonPressed) {
                                                        if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                            return;
                                                        }
                                                        opts.travellerCreatedCallback.call(this, this.currentFrame().getTraveller());
                                                    },
                                        afterShow   : function () {
                                                        this.currentFrame().pageOptions.costCentreId = opts.costCentreId;
                                                    }
                                     });
                        
                            },
                        defaults: { travellerCreatedCallback: function (traveller) {}, showTravellerSearchCallback: function (surname) {} }
                            
    },
    
    /* Used to show user input message box to capture Date Of Births */
    TravellerSecureFlight:  {
    
        /*
         * Show the Date Of Birth capture message box
         *    travellers: array of traveller objects
         *    updateTraveller: function which gets called for each traveller to update, (this) relates to the input box
         *    afterHideCallback: function that gets called after the message box is closed
         */
        show: function (travellers, updateTraveller, afterHideCallback) {
        
            var travellerDateOfBirths = $(document.createElement('div')).append('<p>The proposed itinerary for this booking requires the Date of Birth for each Traveller.</p>');
            var innerDiv = $(document.createElement('div')).addClass('data_entry').attr({id: 'traveller_dateofbirths'});
            travellerDateOfBirths.append(innerDiv);
            
            $.each(travellers, function (travellerIndex, traveller) {
                var row = $(document.createElement('div')).addClass('row');  
                innerDiv.append(row);
                
                row.append($(document.createElement('div')).addClass('cell label').html(traveller.PA_sPassenger_Name));
                row.append($(document.createElement('div')).addClass('cell field').append(traveller.PA_dtDate_Birth !== null && traveller.PA_dtDate_Birth.toString().length > 0  ? ($.isFunction(traveller.PA_dtDate_Birth.getDate) ? traveller.PA_dtDate_Birth.toString(SERKO.dateFormat) : Date.parse(traveller.PA_dtDate_Birth).toString(SERKO.dateFormat)) : $(document.createElement('input')).attr({type: 'text'}).addClass('select_date required').data('traveller', traveller)));
                row.append(SERKO.clearFloat);
            });
            travellerDateOfBirths.find('input:text').serkoPastDatePicker();
            
            SERKO.LightBox.showMessageBox('Secure Flight Information', travellerDateOfBirths, { showOk: true, showCancel: true, width: 400, beforeHide: function (buttonPressed) {
                if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                    return true;
                }
                var inputs = travellerDateOfBirths.find('input:text').removeError();
                var invalids = inputs.filter(':blank').addError('');
                if (invalids.length > 0) {
                    return false;
                }
                
                return true;
            }, afterHide: function (buttonPressed) {
                if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                    return;
                }      
                
                // singal traveller to update with the input box as the 'this' context
                travellerDateOfBirths.find('input:text').each(updateTraveller);
    
                afterHideCallback();
            }});
            
            
        }
    },
                    
    /* Show the ProfileSearch screen 
     * 
     *  options (optional)
     *      successCallback : (function (array of items)) to run when user has selected one to many items and clicked Ok
     *      oneMode          : boolean - false, will show options as radio, false will show as checkbox
    */
    ProfileSearch : {
                        show: function (options) {
                                var opts = jQuery.extend({}, SERKO.ProfileSearch.defaults, options);
                        
                                SERKO.LightBox.showPage('so-profilesearch.asp', {
                                    width       : 700,
                                    height      : 375,
                                    params      : [ { name: 'oneMode', value: opts.oneMode } ],
                                    showOk      : true,
                                    showCancel  : true,
                                    beforeHide  : function (buttonPressed) {
                                                    if (buttonPressed === SERKO.LightBox.buttonType.Cancel) return true;
                                                    if (!this.currentFrame().searchComplete_BeforeHide()) return false;
                                                    
                                                    if (!$.isFunction(opts.successCallback))  return true;

                                                    opts.successCallback.call(this, this.currentFrame().getSelectedItems());
                                                    return true;
                                                }
                                    });
                            },
                        defaults: { successCallback: null, oneMode: true }
                    },
                    
                    
    /* Show the UserSearch screen 
     * 
     *  options (optional)
     *      surname         : (string) pass a preselected surname to run automatically, leave blank to allow user to enter criteria
     *      successCallback : (function (array of selected users)) to run when user has selected one to many travellers and clicked Ok
     *      oneMode         : boolean - true, will show travellers as radio, false will show as checkbox
     *      role            : string, (type of user to search for)
     *
     *      NOTE : In future may need to pass in the RoleName 
    */                    
    UserSearch    : {
                        show: function (options) {
                                var opts = jQuery.extend({}, SERKO.UserSearch.defaults, options);
                        
                                SERKO.LightBox.showPage('so-user-search.asp', {
                                    width       : 590,
                                    height      : 385,
                                    params      : [ { name: 'surname', value: opts.surname },
                                                    { name: 'oneMode', value: opts.oneMode },
                                                    { name: 'roleName', value: opts.roleName } ],
                                    showOk      : true,
                                    showCancel  : true,
                                    beforeHide  : function (buttonPressed) {
                                                    if (buttonPressed === SERKO.LightBox.buttonType.Cancel) return true;
                                                    if (!this.currentFrame().searchComplete_BeforeHide()) return false;
                                                    
                                                    if (!$.isFunction(opts.successCallback))  return true;

                                                    opts.successCallback.call(this, this.currentFrame().getSelectedItems());
                                                    return true;
                                                }
                                    });
                            },
                        defaults: { surname: '', successCallback: null, oneMode: false, roleName: 'User' }
                    },
      
    /*
     * This loads the Order Number Search window
     */              
    OrderNumberSearch : {
                        show : function (optionTypeCode, options) {
                                    var opts = jQuery.extend({}, SERKO.OrderNumberSearch.defaults, options);
                                    
                                    SERKO.LightBox.showPage(
                                        'so-order-number-search.asp',
                                        {
                                            width       : 850,
                                            height      : 400,
                                            showOk      : true,
                                            showCancel  : true,
                                            params      : [ { name: 'OptionTypeCode',   value: optionTypeCode },
                                                            { name: 'oneMode',          value: opts.oneMode },
                                                            { name: 'costCentreId',     value: opts.costCentreId } ],
                                            beforeHide  : function (buttonPressed) {
                                                                if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                                    return true;
                                                                }
                                                                return this.currentFrame().returnSearchCode_BeforeHide();
                                                        },
                                            afterHide   : function (buttonPressed) {
                                                                if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                                    return;
                                                                }
                                                                
                                                                opts.orderSelectedCallback.call(this, this.currentFrame().getSelectedOrderNumber());
                                                        }
                                        });
                                    
                                },
                        defaults : { oneMode: true, costCentreId: 0, orderSelectedCallback: function (ordernumber) {} }
    },
                    
    /*
     * This loads the Cost Centre Search window
     *  options (optional)
     *      successcallBack : (function (array of selected cost centres)) to run when user has selected one to many travellers and clicked Ok
     *      oneMode         : boolean - true, will show travellers as radio, false will show as checkbox
     *
     */

    CostCentreSearch : {
                        show : function (options) {
                                    var opts = jQuery.extend({}, SERKO.CostCentreSearch.defaults, options);
                                    
                                    SERKO.LightBox.showPage(
                                        'so-cost-centre-search.asp',
                                        {
                                            width : 500,
                                            height: 400,
                                            params: [ { name: 'oneMode', value: opts.oneMode },
                                                      { name: 'masterLevelOnly', value: opts.masterLevelOnly } ],
                                            showOk: true,
                                            showCancel: true,
                                            beforeHide: function (buttonPressed) {
                                                    if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                                                        return true;
                                                    }
                                                    if (!this.currentFrame().searchComplete_BeforeHide()) {
                                                        return false;
                                                    }
                                                    if (!$.isFunction(opts.successCallback)) {
                                                        return true;
                                                    }

                                                    opts.successCallback.call(this, this.currentFrame().getSelectedItems());
                                                    return true;                                            }
                                        });
                                },
                        defaults: { successCallback: null, oneMode: true, masterLevelOnly: false }
                    },
    land : {
        deleteItem : function (landId, confirmationNo, UpdateOnly, callback) {
            $.postJSON('solv2/ajax/common.asp',
                {
                    'FunctionToRun' : 'DeleteCarOrHotel',
                    'SegmentId' : landId,
                    'ConfirmationNo' : confirmationNo,
                    'UpdateOnly' : UpdateOnly
                },
                function(json) {
                    $('#page_error').removePageError();
                    if (json.Exception.length !== 0) {
                        $('#page_error').showPageError(json.Exception, {json: json});
                    } else {
                        if (!$.isUndefined(callback)) {
                            callback.call(this);
                        }
                    }
                }
            );
        },
        create : function (landType, itineraryItem, callback, landId) {
            $.postJSON('solv2/ajax/so-qb-land-functions.asp',
            {
                'FunctionToRun' : 'CreateLand',
                'AirSegmentId' : itineraryItem === undefined ? '' : itineraryItem.ID,
                'LandType' : landType,
                'LandId'    : $.isUndefined(landId) ? 0 : landId
            },
            function (json) {
                if (json.Exception.length !== 0) {
                    $('#page_error').showPageError(json.Exception, {json: json});
                    SERKO.WaitWindow.hide();
                } else {
                    if ($.isFunction(callback)) {
                        callback.apply(this, arguments);
                    }
                }
            });
        },
        
        /* options = {landType, itineraryItem, callback } */
        changeDates: function (options) {
            var startDate = Date.parse(options.landType === 'Hotel' ? options.itineraryItem.CheckInDate + ' ' + options.itineraryItem.CheckInTime: options.itineraryItem.PickUpDate + ' ' + options.itineraryItem.PickUpTime),
                endDate = Date.parse(options.landType === 'Hotel' ? options.itineraryItem.CheckOutDate + ' ' + options.itineraryItem.CheckOutTime : options.itineraryItem.DropOffDate + ' ' + options.itineraryItem.DropOffTime),
                start = $('<input/>')
                            .attr({id:'itinerary_land_changedates_start', type:'text'})
                            .addClass('required'),
                startTime = $('<select/>')
                                .attr({id:'itinerary_land_changedates_starttime'})
                                .addClass('required search_time'),
                end   = $('<input/>')
                            .attr({id:'itinerary_land_changedates_end', type:'text'})
                            .addClass('required'),
                endTime = $('<select/>')
                                .attr({id:'itinerary_land_changedates_endtime'})
                                .addClass('required search_time'),                            
                content = $('<div/>')
                            .attr({id:'itinerary_land_changedates'})
                            .addClass('data_entry')
                            .append(
                                $('<div/>')
                                    .addClass('strong')
                                    .text('Current')
                            )
                            .append(
                                $('<div/>')
                                    .addClass('row')
                                    .append(
                                        $('<div/>')
                                            .addClass('cell label')
                                            .text(options.landType === 'Hotel' ? 'Hotel' : 'Car Company')
                                        )
                                    .append(
                                        $('<div/>')
                                            .addClass('cell field')
                                            .append(options.itineraryItem.SupplierName)
                                        )
                                    .append(SERKO.clearFloat)
                                )                            
                            .append(
                                $('<div/>')
                                    .addClass('row')
                                    .append(
                                        $('<div/>')
                                            .addClass('cell label')
                                            .text(options.landType === 'Hotel' ? 'Check-in' : 'Pick-up')
                                        )
                                    .append(
                                        $('<div/>')
                                            .addClass('cell field')
                                            .append(startDate.toString(SERKO.dateTimeFormat()))
                                        )
                                    .append(SERKO.clearFloat)
                                )
                            .append(
                                $('<div/>')
                                    .addClass('row')
                                    .append(
                                        $('<div/>')
                                            .addClass('cell label')
                                            .text(options.landType === 'Hotel' ? 'Check-out' : 'Drop-off')
                                        )
                                    .append(
                                        $('<div/>')
                                            .addClass('cell field')
                                            .append(endDate.toString(SERKO.dateTimeFormat()))
                                        )
                                    .append(SERKO.clearFloat)
                                )                                                            
                            .append(
                                $('<div/>')
                                    .addClass('strong')
                                    .text('Change to')
                            )                                
                            .append(
                                $('<div/>')
                                    .addClass('row')
                                    .append(
                                        $('<div/>')
                                            .addClass('cell label required')
                                            .text(options.landType === 'Hotel' ? 'Check-in' : 'Pick-up')
                                            .required()
                                        )
                                    .append(
                                        $('<div/>')
                                            .addClass('cell field')
                                            .append(start)
                                            .append(startTime)
                                        )
                                    .append(SERKO.clearFloat)
                                )
                            .append(
                                $('<div/>')
                                    .addClass('row')
                                    .append(
                                        $('<div/>')
                                            .addClass('cell label required')
                                            .text(options.landType === 'Hotel' ? 'Check-out' : 'Drop-off')
                                            .required()
                                        )
                                    .append(
                                        $('<div/>')
                                            .addClass('cell field')
                                            .append(end)
                                            .append(endTime)
                                        )
                                    .append(SERKO.clearFloat)
                                );
                                
            if (options.itineraryItem.Status === 'Confirmed' && options.itineraryItem.ProviderName === SERKO.Globals.siProviders.theHotelNetwork) {
                content.prepend($('<p/>').addClass('warning').text('Changing dates for this ' + options.landType + ' will be processed offline by The Hotel Network and the ' + options.landType + ' booking will be changed to On Request'));
            }
            
            SERKO.DropDowns.setAjaxRsOptions(startTime.get(0), 'Time', 'Time', SERKO.Globals.timesJSON({includeAny: false}), function () {
                startTime.val(SERKO.formatTime(startDate));
            });
            SERKO.DropDowns.setAjaxRsOptions(endTime.get(0), 'Time', 'Time', SERKO.Globals.timesJSON({includeAny: false}), function () {
                endTime.val(SERKO.formatTime(endDate));
            });
                
            SERKO.LightBox.showMessageBox('Change ' + options.landType + ' Dates', content, {
                width: 450,
                showOk: true,
                showCancel: true,
                beforeHide: function (buttonPressed) {
                    if (buttonPressed === SERKO.LightBox.buttonType.Cancel) {
                        return true;
                    }
                    
                    if (SERKO.validateRequireds({context: content}).length > 0) {
                        return false;
                    }
                    
                    var data = {
                        FunctionToRun: 'ChangeLandDates',
                        landId: options.itineraryItem.ID,
                        startDate: start.val(),
                        startTime: startTime.val(),
                        endDate: end.val(),
                        endTime: endTime.val()
                    };
                    
                    SERKO.LightBox.disable();
                    
                    $.postJSON('solv2/ajax/common.asp', data, function (json) {
                    
                        SERKO.LightBox.hide();
                        
                        if (json.Exception.length !== 0) {
                            $('#page_error').showPageError(json.Exception, {json: json});
                            return;
                        }
                        
                        $('#itinerary_message').showInformation(options.landType + ': ' + options.itineraryItem.SupplierName + ' dates have been successfull changed');
                        SERKO.Itinerary.load();

                    });
                    
                    return false;
                    
                },
                afterShow: function () {
                    start.serkoGdsDatePicker({
                            beforeShow      : $.fn.serkoGdsDatePicker.setMinMaxFromOtherCalendar,
                            onClose         : function() {},
                            otherCalendarId : end.attr('id'),
                            setMax          : true,
                            setMin          : false
                        });
                            
                    end.serkoGdsDatePicker({
                            beforeShow      : $.fn.serkoGdsDatePicker.setMinMaxFromOtherCalendar,
                            onClose         : function() {},
                            otherCalendarId : start.attr('id'),
                            setMax          : false,
                            setMin          : true
                        });
                        
                    start.datepicker('setDate', startDate);                        
                    end.datepicker('setDate', endDate);                        
                }
            });
        }
    },
    
    air: {
        deleteItem : function (type, segmentNumber, callback) {
            $.postJSON('solv2/ajax/common.asp',
                {
                    'FunctionToRun' : 'DeleteAirSegment',
                    'Type' : type,
                    'SegmentNumber' : segmentNumber
                },
                function(json) {
                    $('#page_error').removePageError();
                    if (json.Exception.length !== 0) {
                        $('#page_error').showPageError(json.Exception, {json: json});
                        return;
                    }
                    if (!$.isUndefined(callback)) {
                        callback.call(this);
                    }
                }
            );    
        }    
    },
    
    checkSiRunning : function() {
        $.postJSON('solv2/ajax/common.asp',
            {
                'FunctionToRun' : 'CheckSiRunning'
            },
            function (json) {
                // success callbakc, do nothing
            }, undefined, function () {
                // error callback, do nothing
            }
        );
    },
    
    ImageViewer: {
        /*  options.data = {
                heading: '', // page header
                intro: '', // page intro blurb
                images: [ // arrray of images to display
                    {
                        
                    }
                ]
            }
        */
        show: function (options) {
            SERKO.LightBox.showPage('so-imageviewer.asp', {showClose: true, width: 875, height: 445, afterShow: function () {
                this.currentFrame().paint(options.data);
            }});
        },
        hide: function () {
             SERKO.LightBox.hide();
        }
    },
       
    FeedBack: {
        show: function (principalId, principalName, costCentreId, typeOfLand) {
            SERKO.LightBox.showPage('so-feedback.asp', {
                showClose   : true,
                width       : 500,
                params      : [{name: 'principalId', value: principalId},
                               {name: 'principalName', value: principalName},
                               {name: 'costCentreId', value: costCentreId},
                               {name: 'typeOfLand', value: typeOfLand}]
            });
        },
        add: function (principalId, costCentreId, description, userRating, landId, callback) {
            $.postJSON('solv2/ajax/so-feedback.asp',
                {
                    'FunctionToRun' : 'Add',
                    'principalId'   : principalId,
                    'costCentreId'  : costCentreId,
                    'description'   : description,
                    'userRating'    : userRating,
                    'landId'        : landId
                },
                callback
            );
        }
    },

    DatabaseImage : {
        load: function(dataId, imageType) {
            return 'solv2/ajax/displayimage.ashx?dataid=' + dataId + '&imagetype=' + imageType;
        },
        type: {
            agency : 0,
            corporate: 1
        }
    }
});

//Used to set any required fields that the TMC may want as required but are not require by default.
function setOptionalRequiredFields() {
     $.postJSON('solv2/ajax/common.asp',
            {
                'FunctionToRun' : 'LoadOptionalRequiredFields',
                'PageName' : location.pathname
            }, function (json) {
                if (json.Exception.length !== 0) {
                    $('#page_error').showPageError(json.Exception, {json: json});
                } else {
                    $(json.Items).each(function() {
                       $(this.OR_sField_Lookup).closest('.row').find('.label').addClass('required').required();
                       $(this.OR_sField_Lookup).addClass('required');
                    });
                }
        });
}