﻿(function(jq) {

    Input = jq.klass({
        // Base behaviour for FAT inputs. Each input maps to a hidden field
        // with the same class name as the input (id's are out of bounds)
        // where it's values are stored for submission (legacy of the orginal
        // FAT system). Any additionally required data, i.e for display purposes
        // is stored as a jQuery data property on this hidden field;
        
        map: {},
        store: null,
        group: null,
    
        initialize: function() {
            // default props for display;
            $.extend(this.map, {items: [
                {name: 'adults', label: 'adults:', prefix: 'Passengers:'},
                {name: 'children', label: 'children:', prefix: 'Passengers:'},
                {name: 'railcardQty1', label: 'qty:', prefix: 'Railcards:'},
                {name: 'railcard1', label: '', prefix: 'Railcards:'}, 
                {name: 'railcardQty2', label: 'qty:', prefix: 'Railcards:'},
                {name: 'railcard2', label: '', prefix: 'Railcards:'},
                {name: 'railcardQty3', label: 'qty:', prefix: 'Railcards:'},
                {name: 'railcard3', label: '', prefix: 'Railcards:'},
                {name: 'viaAvoid', label: '', prefix: 'Via or avoid:'},
                {name: 'viaAvoidStationCode', label: '', prefix: 'Via or avoid:'},
                {name: 'originStationCode', label: ''},
                {name: 'destinationStationCode', label: ''},
                {name: 'promotionalCode', label:'', prefix: 'Promotional code:'},
                {name: 'directServices', label:'', prefix: 'Services:'}]
            });
            
            this.name = this.getName();
            this.store = this.getStore(this.name);
            this.group = this.getGroup();
        },
        
        
        getName: function() {
            return this.element.attr('class');
        },
        
        
        getGroup: function() {
            return this.element.closest('fieldset').attr('class');
        },
        
        
        getStore: function(className) {
            return $('#searchCriteria').find('.'+className);
        },
        

        getItemByName: function(name) {
            var ret = null;
            $.each(this.map.items, function(i, item) {
                if (name === item.name) {
                    ret = this;
                    return false;
                };    
            });
            return ret;
        },
        
        
        getItemsByGroup: function(group) {
            return $.grep(this.map.items, function(item, i) {
                    return group === item.group;    
            });
        },
        
        
        notify: function(evt, val) {
            $('#preferences').trigger(evt, {val: val, name: this.name, group: this.group});
        },
        
        
        onchange: function() {
            this.update();
        },
        
        
        update: function() {
            
            var val = this.getValue();
            var text = this.getDisplayText(val);
            var item = this.getItemByName(this.name);
            
            this.store.val(val)
                .data('preference', 
                    $.extend({}, item, {
                        value: val, 
                        display: text,
                        group: this.group
                })); 
     
            this.notify('preferencechange', val);
            return val;
        },
        
        
        getValue: function() {
            return this.element.val();
        },
        
        
        getDisplayText: function(val) {
            return val;
        },
        
        
        log: function(msg) {
            if (window.console && console.log) {
                console.log(msg);
            } else {
                alert(msg);
            }
        }

    });
    
    
    
    
    Select = jq.klass(Input, {
    
        initialize: function($super) {
            $super();
            var that = this;
            $('.railcards').bind('showrailcard', function(evt, o) {
                that.show.call(that, o);    
            });
            return this;
        },
        
        
        getDisplayText: function(val, override) {
            var ret = val;
            if (isNaN(parseInt(val)) || override) {
                this.element.find('option').each(function() {
                    var el = $(this);
                    if(el.val() == val) {
                        ret = el.text().toLowerCase().capitalise();
                        return false;
                    };    
                }); 
            };
            return ret;  
        },
        
        
        show: function(o) {
            if (this.name.indexOf('railcard') != -1 &&
                parseInt(this.name.match(/\d+/).shift()) === o.indx) {
                    this.element.show();
            };
        }

    });

    
    
    SelectTimePoint = jq.klass(Select, {
    
        initialize: function($super) {
            $super();
            this.store = this.getStore(this.name);
        },
        
        getName: function() {
            return this.element.closest('fieldset').attr('class')
                .match(/(outbound|return)ArrivalDepartureIndicator/).shift();
        },
        
        getDisplayText: function($super, val) {
            return $super(val, true);
        }
        
    });
     
    
    
    SelectService = jq.klass(Select, {
    
        initialize: function($super) {
            $super();
            return this;
        },
        
        getDisplayText: function($super, val) {
            return $super(val, true);
        }
        
    });
    
    
    Textbox = jq.klass(Input, {
    
        initialize: function($super) {
            $super();
            return this;
        }, 
        
        
        onblur: function() {
            this.update();
        },       
        
        
        getDisplayText: function() {
            return this.element.val();
        }
    
   }); 
    
    
    StnTextbox = jq.klass(Textbox, {
    
        initialize: function($super) {
            $super();
            var that = this;
            this.name = this.getName();
            this.store = this.getStore(this.name);
            $(document).bind('autocompletechange', function(evt, o) {
                if (that.element.get(0) === o.element) {
                    that.update.call(that, o);   
                };
            });
            return this;
        },
        
        
        onblur: function() {},
        
        
        onchange: function() {},
        
        
        update: function(o) {

            var rel = $(o.selected.firstChild).attr('rel');
            var val = rel || this.getValue();
            var text = this.getDisplayText(val);
            var item = this.getItemByName(this.name);
            
            this.store.val(val)
                .data('preference', 
                    $.extend({}, item, {
                        value: val, 
                        display: text,
                        group: this.group
                })); 
     
            this.notify('preferencechange', val);
            return val;
        },
        
        
        getStationName: function(val) {
            return val = val.replace(/\s\[\w+\]$/, '');
        },
        
        
        getValue: function() {
            return this.element.val()
                .match(/\[\w+\]/g).pop().replace(/^\[|\]$/g, '');
        },
        
        
        
        getName: function() {
            return this.element.attr('class')
                .match(/^uiTxt(\w+)/).pop().uncapitalise();
        }

    });
    
    
    
    Radio = jq.klass(Input, {
    
        initialize: function($super) {
            $super();
            this.store = this.getStore('viaAvoid');
            return this;
        },
        
        
        getValue: function() {
            var id = this.element.attr('id').toLowerCase();
            return id.match(/uirdo(\w+)/).pop();
        }

    });
    
    
    
    
    Summary = jq.klass({
    
        group: null,
    
        initialize: function() {
            var that = this;
            this.group = this.getGroup();
            $(document).bind('preferencechange', function(evt, o) {
                that.update.apply(that, arguments);
            });
            $('#preferences').bind('preferencechange', function(evt, o) {
                that.update.apply(that, arguments);
            });
            return this;
        },
        
        
        update: function(evt, o) {

            var text = [];
            var group = null;
            
            if (o.group === this.group) {
                $('#searchCriteria .'+o.group).each(function(i) {
                    var data = $(this).data('preference');
                    if (data && data.display) {
                        if (data.prefix && group !== data.group) {
                            group = data.group;
                            text.push(data.prefix);
                        };
                        text.push(data.label+' '+data.display);
                    };
                });
            };
            
            if (text.length !== 0) {
                this.element.hide().html(this.format(text)).fadeIn(500);
            };
        },
        
        
        getField: function(name) {
            return $('#searchCriteria').find('.'+name);
        },
        
        
        format: function(text, o) {
            return '<span>'+text.join(' ')+'</span>';
        },
        
        
        getGroup: function() {
            return this.element.closest('fieldset').attr('class');
        }

    });
    

    
    HeadlineSummary = jq.klass(Summary, {
    
        html: {
            passengers: 'Passengers: Adult <span>1</span> ',  
            railcards: 'Railcard: <span>0</span> ', 
            viaOrAvoid: 'Via or avoid: <span>N/A</span> ',
            services: 'Services: <span>All services</span> ',
            promotionalCode: 'Promotional code: <span>none</span>'
        },
        
        
        bestFareActive: false,
        
        
        initialize: function($super) {
            $super();
            return this;
        },
        
        
        update: function(evt, o) {
            
            var that = this;
            var text = [];
            var group = null;
            var html = $.extend({}, this.html);
            
            if (o.name === 'bestFare' && o.val) {
                // // PB 19/08/10; very bad things, hackery :-( ;
                this.bestFareActive = true;
                text.push('You have chosen a flexible search you can add your travel preferences as part of the booking process.');
                this.element.removeClass('active').addClass('inactive');
                temp = text.join();
            } else {
                this.bestFareActive = false;
                this.element.removeClass('inactive').addClass('active');
                $('#searchCriteria .preference').each(function(i) {
                    var data = $(this).data('preference');
                    if (data && data.display) {
                        if (data.prefix && group !== data.group) {
                            group = data.group;
                            text.push(data.prefix);
                        };
                        text[text.length-1] = text[text.length-1]+(' '+data.label+' <span>'+data.display+'</span>');
                    };
                });
                
                
                $.each(text, function(i, val) {
                    var s = val.match(/([a-zA-Z0-9\s]+)\:/).pop()
                        .camelise().replace(/^\w/, function(m){
                            return m.toLowerCase();
                    });
                    html[s] = val;
                });
                
                var temp = '';
                $.each(html, function() {
                    temp += ('<span class="preference">'+this+'</span>');
                });
            };
            
            if (text.length !== 0) {
                this.element.hide().html(temp).fadeIn(500);
            };
        },
        
        
        setGroup: function() {
            this.group = 'all';
            return;
        },
        
        
        format: function(text) {
            return text.join(' ');
        },
        
        
        onclick: function(evt) {
            if (this.bestFareActive === true) return false;
            this.element.trigger('showpanel', {element: this.element.get(0)});
            return false;
        },
        
        
        onkeydown: function(evt) {
            if (evt.keyCode === 32 || evt.keyCode === 13) {
                this.element.trigger('showpanel', {element: this.element.get(0)});
                return false;
            }; 
        }

    });
    
    
    
    
    Panel = jq.klass({
    
        visible: null,
        name: null,
    
        initialize: function(o) {
        
            var that = this; 
            this.setVisibility(false);
            
            
            $('a').bind('showpanel', function(e, o) {
                var el = that.getTrigger.call(that);
                that.setVisibility.call(that, el === o.element && ! that.visible);
            });
            
            
            // handle close button;
            this.element.find('input[type=image]').click(function(e, o) {
                $(document).trigger('closepanel', {name: this.className});
                that.setVisibility.call(that, false);
                return false;
            });
            
        },
        
        
        getTrigger: function() {
            return this.element.prev().find('a').get(0);
        },
        
        
        setVisibility: function(visible) {
            if (visible) {
                this.element.fadeIn(400);
            } else {
                this.element.hide();
            }; 
            this.visible = visible;
        }
        
    });
    
    
    
    
    DatePanel = jq.klass(Panel, {
    
        initialize: function($super, o) {
            $super(o);
            return this;
        }
        
    });
    
    
    
    
    PrefPanel = jq.klass(Panel, {

        initialize: function($super, o) {
            $super(o);
            return this;
        },
        
        
        getTrigger: function() {
            return this.element.siblings('.headlineSummary').find('a').get(0);
        }
        
    });
    
    
    
    
    PanelTrigger = jq.klass({
    
        name: null,
        getField: null,
        
        initialize: function() {
        
            var that = this;
            this.name = this.getName();
            
            
            $('a').bind('showpanel', function(e, o) {
                if (o.element !== that.element.get(0)) {
                    that.element.parent().removeClass('active');
                };
            }); 
 
                        
            $(document).bind('closepanel', function(e, o) {
                if (that.name.indexOf(o.name) != -1) {
                    that.setActive.call(that);
                };
            });
            
            
            $(document).bind('datechange', function(e, o) {
                if (that.name === o.name) {
                    that.update.call(that, o);
                };
            });
            
            
            $(document).bind('preferencechange', function(e, o) {
                if (that.isValidPreference.call(that, o.name)) {
                    var type;
                    if (o.name.indexOf('bestFare') != -1) {
                        type = 'bestFare';
                    } 
                    else if (o.name.indexOf('StationCode') != -1) {
                        type = 'stationCode';
                    } 
                    else {
                        type = 'indicator';
                    }
                    that.update.call(that, $.extend(o, {type: type}));
                };
            });
            
            // short cut to access search criteria fields;
            this.getField = (function() {
                var search = $('#searchCriteria');
                return function(field) {
                    return search.find('.'+field);   
                };
            })();
            
            
            this.setDefault();
            return this;
        },
        
        
        isValidPreference: function(name) {
            return name.indexOf('StationCode') != -1 ||
                name.indexOf('bestFare') != -1 ||
                name.indexOf('DepartureIndicator') != -1 && (
                name.indexOf('outbound') != -1 && this.name.indexOf('outbound') != -1 ||
                name.indexOf('return') != -1 && this.name.indexOf('return') != -1);
        },
        
        
        setDefault: function() {
        
            this.update({name: this.name, type: "date"});
            
            this.update({
                name: this.name.indexOf("outbound") != -1 ?
                    "outboundArrivalDepartureIndicator" :
                    "returnArrivalDepartureIndicator",
                type: "indicator"
            });
        },
        
        
        update: function(o) {
            
            var html = [];
            var cont = this.name.indexOf('return') != -1 ? 'return' : 'outbound';
            var date = this.getField(cont+'DateTime').data('search');
            var pref = this.getField(cont+'ArrivalDepartureIndicator').data('preference');
            var mode = this.getField('journeyMode').val();
            var that = this;
            
            // PB 19/08/10; very bad things, hack for best fare
            // ensure (hope) best fare hidden field value has been 
            // written before testing for it's value;
            setTimeout(function() {
                if ($('#searchCriteria .bestFareSearch').val() === '1' && o.type !== 'mode') {
                    // don't set return panel trigger for one way journey;
                    if (that.name.indexOf('return') != -1 && mode ==="0") {
                        return false;
                    }
                    html = [date.standard, '<span>Flexible</span>'];  
                }
                else if (cont === 'return' && mode === "0") {
                    html = ['One way', '<span></span>'];  
                } else {
                    html = [date.standard, '<span>', pref.display, ' ', date.time,'</span>'];
                };
                
                return that.element.html(html.join(''));
            }, 500);    
        },
        
        
        getName: function() {
            return this.element.closest('li').attr('class')
                .match(/(outbound|return)DateTime/).shift();
        },
        
        
        setActive: function(el) {
            var parent = this.element.parent();
            if (el === this.element.get(0) && ! parent.hasClass('active')) {
                parent.addClass('active');
            } else {
                parent.removeClass('active');
            };
        },
        
        
        onclick: function(e) {
            this.setActive(e.currentTarget);
            this.element.trigger('showpanel', {element: this.element.get(0)});
            return false;
        },
        
        
        onkeydown: function(e) {
            if (e.keyCode === 32 || e.keyCode === 13) {
                this.setActive(e.currentTarget);
                this.element.trigger('showpanel', {element: this.element.get(0)});
                return false;
            }; 
        } 
                   
    });
    
    
    
    
    AddRailcard = jq.klass({
        // Add rail card link behaviour;
         
        indx: null,
        max: null,

        initialize: function() {
            this.indx = 1;  
            this.max = 3;  
        },
        
        
        handle: function() {
            if (this.indx === this.max) {
                this.element.text('Add railcard (max 3)');  
                return;  
            };
            $('.railcards').trigger('showrailcard', {indx: ++this.indx});
        },
        
        
        onclick: function(e) {
            this.handle();
            return false;
        },
        
        
        onkeydown: function(e) {
            if (e.keyCode === 32 || e.keyCode === 13) {
                this.handle();
                return false;
            }; 
        }
        
    });
    
        
        
        
    QuickDates = jq.klass({
    // Quick dates links, with in the going/coming back panels; 
       
        initialize: function() {},
        
        
        onclick: function() {
            this.element.trigger('quckdatechange', {type: this.element.attr('class')});
            return false;
        }

    });
    
    
    // #best buy;
    BestFare = jq.klass({
    
        BESTFARES_SERVICE: BasePath + "_WebServices/BestFareFinderService.svc/IsBestFareRoute",
        bestFareLimit: null,
        
        initialize: function() {
            var that = this;
            this.setBestFareLimit(); 
            BestFare.dateChecked;   // static property, prevents date window frrom being checked twice;
            this.element.hide();
            $(document).bind('preferencechange', function(e, o) {
                var name = o.name.toLowerCase(); 
                if (name.indexOf("stationcode") != -1) {
                    that.checkBestFareMatch.call(that);
                } 
                else if (name.indexOf("bestfare") != -1) {
                    that.element.find('input[type=checkbox]').attr('checked', o.val);
                    if (o.hide) {
                        that.element.hide();
                    };
                };
            });
            $(document).bind('datechange', function(e, o) {
                that.checkDateWindow.call(that, o);
            });
        },
        
        setBestFareLimit: function() {
            var d = new Date();
            d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
            this.bestFareLimit = new Date((d.getTime()+(12*7*24*60*60*1000))).getTime();
        },

        update: function(o) {
            if (o.IsBestFareRouteResult) {
                this.element.show();
            } else {
                this.element.hide().find('input[type=checkbox]').attr('checked', false);
                $('#searchCriteria .bestFareSearch').val(0);
            };
        },
        
        getStations: function() {
            return $.map($('#searchCriteria input'), function(el) {
                if (el.className.toLowerCase().match(/(origin|destination)stationcode/)) {
                    return el.value;
                };
            });    
        },
        
        checkDateWindow: function(date) {
            var dates = [];
            // unset best fare fare option and hide;
            var a = [
                $('#searchCriteria .outboundDateTime').val(),
                $('#searchCriteria .returnDateTime').val()
            ];
            if (a[0] > this.bestFareLimit || a[1] > this.bestFareLimit) {
                if (BestFare.dateChecked !== date.name) {
                    $('#searchCriteria .bestFareSearch').val(0);
                    $(document).trigger('preferencechange', {name: 'bestFare', val: false, hide: true});
                } else {
                    // this.checkBestFareMatch();
                    BestFare.dateChecked = null;
                };
            } else {
                this.checkBestFareMatch();
            };  
            /*
            if (BestFare.dateChecked !== date.name) {
                // unset best fare fare option and hide;
                if (date.millisecs > this.bestFareLimit) {
                    $('#searchCriteria .bestFareSearch').val(0);
                    $(document).trigger('preferencechange', {name: 'bestFare', val: false, hide: true});
                } else {};  
                BestFare.dateChecked = date.name;
            } else {
                // this.checkBestFareMatch();
                BestFare.dateChecked = null;
            };
            */
        },
        
        checkBestFareMatch: function() {
            var arr = this.getStations();
            var that = this;
            if (arr[0] !== '' && arr[1] !== '') {
                $.ajax({
                    data: $.toJSON({
		                "originStationCode": arr[0], 
		                "destinationStationCode": arr[1]
		            }),
		            contentType: "application/json; charset=utf-8",
		            dataType: "json",
			        type: "POST",
			        url: this.BESTFARES_SERVICE,
			        success: function(){
				        return that.update.apply(that, arguments);
			        },
			        error: function(){
				        return that.update.apply(that, arguments);
			        }
		        });
		    };
        },
        
        onclick: function(e) {
            if (e.target.nodeName.toLowerCase() === 'input') {
                var val = e.target.checked ? 1 : 0;
                $('#searchCriteria .bestFareSearch').val(val);
                $(document).trigger('preferencechange', {name: 'bestFare', val: e.target.checked});
            };
        }
        
    });    
    // end best buy;
    
    
    // best buy hint;
    $.fn.hint = function() {
        return this.each(function(i) {
            $.fn.hint.widgets.push(new Hint($(this), i));        
        });
    };

    $.fn.hint.widgets = [];

    // override-able method proving hint content;
    $.fn.hint.getContent = function(el) {
        return '<span>Flexible searches allow you to view the cheapest fares within your desired period of travel up to 12 weeks in the future. When choosing a flexible search you can add your travel preferences as part of the booking process.</span>';
    };

    var Hint = function(el, i) {
        this.indx = i;
        this.element = el;
        this.init();
    };


    Hint.prototype = {
        init: function() {
            this.setId('hint');
            this.hide();
        },

        setId: function(prefix, i) {
            return this.element.attr('id', prefix+'_'+this.indx);
        },

        hide: function() {
            return this.element.css({'visibility': 'hidden', 'top': '-9999px'});
        },

        show: function(el, x, y) {
            var content = $.fn.hint.getContent(el);
            return this.element.appendTo(el).find('.gutter')
                .empty().append(content).end()
                .css({'visibility': 'visible', 'top': y-(parseInt(this.element.height())/2), 'left': x+70});
        }
    };


    $("<div class='hint'><div class='gutter'>\
        </div><div class='ft'></div>\
    </div>").appendTo('body').hint();

    
    $('.help').live('mouseenter', function(e) {
        var o = $.fn.hint.widgets[0];
        var w = $(window);
        var p = $(e.target).position();
        o.show.call(o, $(e.target).parent(), p.left, p.top); 
    });

    $('.help').live('mouseleave', function(e) {
        var o = $.fn.hint.widgets[0];
        o.hide.call(o); 
    });
    // end bestFare buy hint;


    jq(document).ready(function() {
    
        // create fat namespace;
        var fat = (function() {
            
            
            // add private members;
            var _search = $('#searchCriteria');
            var _start = new Date();
            
            
            var _getStore = function(name) {
                // get hidden input used to store fat panel values;
                return _search.find(name);
            };
            
            
            var _setIndicator = function() {
                return $.each(['outbound', 'return'], function(i, s) {
                    _getStore('.'+s+"ArrivalDepartureIndicator")
                        .data('preference', {display: 'Leaving', type: 'indicator'});
                });
            };
            
            
            var _setPassengers = function() {
                return $.each(['adults', 'children'], function(i, s) {
                    var store = _getStore('.'+s);
                    store.data('preference', {
                        val: store.val(),
                        display: store.val(),
                        group: 'passengers',
                        prefix: 'Passengers:',
                        label: s
                    });
                });
            };
            
            
            var _setDates = function() {
                // set default start and finish dates;
                var dates = [ret.start(), ret.finish()]; 
                return $.each(['outbound', 'return'], function(i, s) {
                    ret.onDateChange($.extend(dates[i].format(), {name: s+'DateTime'}));
                }); 
            };
            
            
            // create public members;
            var ret = {
            
            
                init: function() {
                    _setIndicator();
                    _setPassengers();
                    _setDates();
                    return this;
                },
                
                
                start: function() {
                    var mins = (15*Math.floor(_start.getMinutes()/15));
                    return new Date(_start.copy().setMinutes(mins+15));
                },
                
                
                finish: function() {
                    var mins = (15*Math.floor(_start.getMinutes()/15));
                    return new Date(_start.copy().setMinutes(mins+135));
                },
                
                
                today: function() {
                    return this.start();
                },
                
                
                tomorrow: function() {
                    return new Date(this.today().setDate(this.today().getDate()+1));
                },
            
            
                getStore: function(selector) {
                    return _getStore(selector);
                },
            
            
                onDateChange: function(o) {
                    // validates new date against current 
                    // dates before writting to hidden field;
                    
                    // **HACK** reset single return field on return selection,
                    // doesn't seem the right place to be doing this;
                    if (o.name.indexOf('return') != -1) {
                        _getStore('.journeyMode').val(1);    
                    }
                
                    var obj = {update: o}
                    var out = _getStore('.outboundDateTime').data('search');
                    var ret = _getStore('.returnDateTime').data('search');
                    
                    obj.update.sin = _getStore('.journeyMode').val();
                    
                    if (o.name.indexOf('outbound') != -1) {
                        obj.current = out;
                        obj.inverse = ret;
                    } else {
                        obj.current = ret;
                        obj.inverse = out;
                    };

                    var dates = validator.validate(obj);
                    
                    $.each(dates, function(i, date) {
                        if (date.name) {
                            _getStore('.'+date.name).val(date.millisecs).data('search', date);  
                            $(document).trigger('datechange', date);
                        }; 
                    });
                },
                
                
                onQuickDateChange: function(e, o) {
                    
                    // create date update object;
                    var obj = {element: e.target, type: 'date'};
                    
                    if(o.type === 'single') {
                    
                        _getStore('.journeyMode').val(0);
                                
                        // reset return values;
                        /*
                        _getStore('.destinationStationCode').val('');
                        _getStore('.returnArrivalDepartureIndicator').val('');
                        */
                        _getStore('.returnDateTime').val('');
                        
                        // **HACK** not really the place to fire this review;
                        $(document).trigger('datechange', {name: 'returnDateTime', type: 'mode'});
                        
                    } else {
                        // decorate update;
                        switch (o.type) {
                        
                            case 'today' :
	                            $.extend(obj, fat.today().format(), {name: 'outboundDateTime'});
                                break;
                                
                            case 'tomorrow' :
                                $.extend(obj, fat.tomorrow().format(), {name: 'outboundDateTime'});
                                break;
                                
                            case 'sameDay' :
                                var data = _getStore('.outboundDateTime').data('search');
                                var date = new Date(data.date.copy().setHours(data.hours+2));
                                $.extend(obj, date.format(), {name: 'returnDateTime'});
                                break;
                                
                           case 'nextDay' :
                                var data = _getStore('.outboundDateTime').data('search');
                                var date = new Date(data.date.copy().setDate(data.day+1));
                                date = new Date(date.setHours(date.getHours()+2));
                                $.extend(obj, date.format(), {name: 'returnDateTime'});
                                break;
                                
                        };
                        
                        this.onDateChange(obj);
                    
                    };   
                    
                },
                
                
                onModeChange: function(o) {
                    /*
                    var el = this.getStore('.journeyMode');
                    if (o.name.indexOf('return') != -1 && el.val() !== 1) {
                        el.val(0);
                    };
                    */
                }
                
                
            };
            
            return ret;
            
        })();
    
        // add preferences panel behaviour; 
        
        // hide all but the first railcard selector;
        $('.railcards select:gt(1)').hide();
        
        $('#addRailcard').attach(AddRailcard);
        $('#preferences .passengers select').attach(Select); 
        $('#preferences .railcards select').attach(Select);   
        $('#preferences input[type=radio]').attach(Radio); 
        $('#preferences .summary').attach(Summary);    
        $('#preferences .headlineSummary a').attach(HeadlineSummary);
        $('#preferences .panel').attach(PrefPanel, {visible: false});
        $('#preferences .yui-ac-input').attach(StnTextbox);
        $('#preferences .promotionalCode').attach(Textbox);
        $('#preferences .directServices').attach(SelectService); 
        // end preferences panel;
        
        
        // add date panel behaviour; 
        $('#search .trigger a').attach(PanelTrigger);
        $('#search .panel').attach(DatePanel, {visible: false});
        $('#search .quickDates a').attach(QuickDates);
        $('#search fieldset fieldset select').attach(SelectTimePoint);
        $('#search .yui-ac-input').attach(StnTextbox);
        $('#search .bestFares').attach(BestFare);
        
        // add YUI autocomplete;
        $.each(['Origin', 'Destination', 'ViaAvoid'], function(i, val) {
            addAutoComplete({
                container: $('.uiAcc'+val+'StationCode').attr('id'),
                input: $('.uiTxt'+val+'StationCode').attr('id') 
            });
        });
        // end date panel behaviour;

        
        // add date picker;
        jq("#search .datePicker").datepicker({
            changeMonth: true,
            changeYear: true,
            minDate: fat.start(),
            onSelect: function(d, o) {
            
                var date = new Date(d).format();
                date.type = 'date';
                date.name = $('#'+o.id).closest('li').attr('class')
                    .match(/(outbound|return)DateTime/g).shift();
                
                delete date.mins;
                delete date.hours;  
                delete date.time;
                //delete date.millisecs;
                
                fat.onDateChange(date);
                
            }
        });
        // end date picker;
       
        
        // add slider picker;
        jq("#fat .slider").slider({
            stop: function(e, o) {
            
                var mins = parseInt((60*24)*(o.value/100));
                // set to 15min increment;
                mins = (15*Math.floor(mins/15));
                // set at 23.45 max;
                mins = mins < 1425 ? mins : 1425;
                var today = new Date(fat.start().setHours(0, 0, 0, 0));
                
                var d = new Date(today.copy().setMinutes(mins)).format();
                d.type = 'time';
                d.name = $(o.handle).closest('li').attr('class')
                    .match(/(outbound|return)DateTime/g).shift();
                    
                delete d.day;
                delete d.dayName;
                //delete d.millisecs;
                delete d.month;
                delete d.monthName;
                delete d.standard;
                delete d.usa;
                delete d.year;
                    
                fat.onDateChange(d);	
            }
        });
        
        // set slider defaults;
        jq("#outboundDateTime .slider").slider( "value",
            parseInt(((fat.start().getHours()*60 + fat.start().getMinutes())/(24*60))*100));
        jq("#returnDateTime .slider").slider( "value",
            parseInt(((fat.finish().getHours()*60 + fat.finish().getMinutes())/(24*60))*100));
        // end slider;
        	
        	            
        // **TO DO** these should be moved into the fat namespace.
        // listen for events that require some validation.
        $('#search a').bind('quckdatechange', function(e, o) {
            fat.onQuickDateChange(e, o);
        });
        
        $('#preferences').bind('preferencechange', function(e, o) {
            fat.onModeChange(o)    
        });
        
        $(document).bind('datechange', function(e, o) {
            fat.onModeChange(o)
        });
        // end quick dates;
        
        
        // add bookmark;
        
        var bookmark = (function() {
            var _isSafari = $.browser.safari;
            var _handle = function() {
                var title = document.title; 
                var url = window.location.href;

                if (window.sidebar) { // Mozilla Firefox Bookmark
	                window.sidebar.addPanel(title, url,"");
	                return false;
                } 
                else if( window.external ) { // IE Favorite
	                window.external.AddFavorite( url, title); 
	                return false;
	            }
                else if(window.opera && window.print) { // Opera Hotlist
                    $(this).attr('rel','sidebar');
	                return true; 
                } else if (_isSafari) {
                    alert('Please use Command D to bookmark this page');
                };
            };
            
            return function() {
                $('#account .section').append("<div id='bookmark'>\
                        <a href='#'>Bookmark this page</a>\
                    </div>")
                    .find('#bookmark')
                    .click(function(){
                        _handle();    
                    });
            };
        })();
        
        bookmark();
        fat.init();
        
    }); 

})(jQuery);