/******************************************************************************
TVSchedules Module Singleton
******************************************************************************/


PBS.tvschedules.tvschedules = function () {


    /* Private attributes and methods. */
    
    
    /* Public Object */
    var obj = {

        /* Public attributes and methods. */

        
        // On module init, this stores the grid container height as set in the
        // implementer customizable CSS so we can always revert to this height after
        // having previosly collapsed.
        grid_container_height: null,
        
        active_feed: '',
        
        // We need to be able to perform look ups in a few directions.
        // These are set at module init.
        feed_ids_to_tr_ids: {},
        tr_ids_to_feed_ids: {},
        feed_ids_to_star_ids: {},
        feed_ids_to_tvdata_name: {},
        feed_ids_to_station_td_ids: {},
        star_ids_to_feed_ids: {},
        tvdata_name_to_feed_tr_ids: {},
        star_ids: {},
        tvdata_names: {}, // all tvdata names in the lineup.
        feed_ids: [], // all feed_ids in the lineup.

        // Highlight indexes.
        highlight_ids: [],
        parents_ids: [],
        kids_ids: [],
        favorites_ids: [],

        provider_name: '',

        // The defaults for the module implementation customizations.
        // These can be overridden by the query string to the module JS include.
        config: {
            'localization': '',
            'provider': 'enabled',
            'search': 'enabled',
            'favorites': 'enabled',
            'highlight': 'enabled',
            'wrapper': 'enabled',
            'shop': 'enabled'
        },

        // These are empty strings because we need them to evaluate to false AND
        // be safe to send over GET without converting to a true value like
        // the strings 'false' or 'undefined'.
        localization_headend_id: '',
        provider_finder: '',
        disable_search: '',
        disable_favorites: '',
        disable_highlight: '',
        disable_wrapper: '',
        disable_shop: '',

        /*********************************************************************
        Search 
        *********************************************************************/
        // We will be instantiating a search_lib object at init time and hanging
        // it on the search_lib attributes here.
        // search_lib_config config provides the search_lib constructor with the
        // info it needs to support this particular application.
        search_lib: '', // stub

        search_lib_config: {
            'search_lib': this.search_lib,
            'search_lib_str': 'PBS.tvschedules.tvschedules.search_lib',
            'form_id': 'pbs_tvschedules_modules-tvschedules-search_form',
            'results_container_id': 'pbs_tvschedules_modules-tvschedules-results_global_container',
            'identifier': 'search'
        },

        keywords: '',
        previously: '',
        lineup: '',
        filter: '',

        // This flag tracks whether or not a search has been performed since the
        // last module initialization. This is handy so that we can spare
        // ourselves a search_lib initPreferences (which expects certain pieces
        // of it's own results UI to be present in the DOM) if the user has not
        // performed any searches.
        searched: false,

        search: function (mode) {

            var form_id = this.search_lib_config['form_id'];
            if (mode == 'full') {
                form_id = 'pbs_tvschedules_modules-tvschedules-full_search_form';
                this.search_lib.setFormId(form_id);
                //alert(this.search_lib.setFormId);
                var form = document.getElementById(form_id);
                var lineup = form.lineup.value;
                var filter = form.filter.value;
                var previously = ''
                if (form.previously.checked) {
                    var previously = form.previously.checked;
                }
            } else {
                var form = document.getElementById(form_id);
                // The short form has these values pre-set.
                var lineup = 'provider';
                var filter = 'none';
                var previously = ''
            }

            this.keywords = form.keywords.value;
            this.previously = previously;
            this.lineup = lineup;
            this.filter = filter;

            var finalize = function () {

                var provider_container_id = 'pbs_tvschedules_modules-tvschedules-optionsheader_provider_container';
                var header_container_id = 'pbs_tvschedules_modules-tvschedules-header_container';
                var schedule_container_id = 'pbs_tvschedules_modules-tvschedules-schedule_container';
                var back_to_grid_container_id = 'pbs_tvschedules_modules-search-back_to_grid';
                var results_container_id = 'pbs_tvschedules_modules-tvschedules-results_global_container';
                var search_form_container_id = 'pbs_tvschedules_modules-search-search_form_container';

                var provider_container = document.getElementById(provider_container_id);
                var header_container = document.getElementById(header_container_id);
                var schedule_container = document.getElementById(schedule_container_id);
                var back_to_grid_container = document.getElementById(back_to_grid_container_id);
                var results_container = document.getElementById(results_container_id);
                var search_form_container = document.getElementById(search_form_container_id);

                provider_container.style.display = 'none';
                header_container.style.display = 'none';
                schedule_container.style.display = 'none';
                back_to_grid_container.style.display = '';
                results_container.style.display = '';
                search_form_container.style.display = '';

            }

            /*
            alert(
                'this.schedule.zip: ' + this.schedule.zip + '\n' +
                'this.schedule.headend_id: ' + this.schedule.headend_id + '\n' +
                'this.schedule.tvdata_names: ' + this.schedule.tvdata_names + '\n' +
                'this.keywords: ' + this.keywords + '\n' +
                'this.previously: ' + this.previously + '\n' + 
                'this.lineup: ' + this.lineup + '\n' + 
                'this.filter: ' + this.filter + '\n' + 
                'this.localization_tvdata_name: ' + this.config['localization'] + '\n' +
                'this.localization_headend_id: ' + this.localization_headend_id + '\n' + 
                'this.provider_finder: ' + this.provider_finder + '\n' +
                'this.disable_favorites: ' + this.disable_favorites + '\n' +
                'this.disable_shop: ' + this.disable_shop + '\n'
            );
            */

            this.search_lib.getSearchResults(
                this.schedule.zip,
                this.schedule.headend_id,
                this.schedule.tvdata_names,
                this.keywords,
                this.previously,
                this.lineup,
                this.filter,
                this.config['localization'],
                this.localization_headend_id,
                this.provider_finder,
                this.disable_favorites,
                this.disable_shop,
                finalize,
                this
            );

            this.searched = true;

        },


        // When a user switches from one navigation transport to another, we
        // automatically update the selected lineup for future searches.
        setSearchLineup: function (lineup) {
            var search_form = document.getElementById('pbs_tvschedules_modules-tvschedules-search_form');
            var lineup_select = search_form.lineup;
            for (var i=0; i<lineup_select.options.length; i++) {
                var option = lineup_select.options[i];
                if (option.value == lineup) {
                    option.selected = true;
                }
            }
        },


        // Activate any airdate reminder tabs that correspond to
        // airdate reminders. Deactivate any that don't.
        // Populate forms with remembered email addresses where appropriate.
        manageReminders: function () {
            var email_reminders = PBS.tvschedules.user.getEmailReminders(
                this.config['localization']
            );
            var airdate_reminders = email_reminders['airdate_reminders'];
            var airdate_infos = PBS.tvschedules.tvschedules.search_lib.matrix['airdate_infos'];
            for (var k in airdate_infos) {
                var info = airdate_infos[k];
                var tab_id = info['tab_id'];
                var r_id = info['r_id'];
                PBS.tvschedules.tvschedules.search_lib.airdate_reminders.deactivateAirdateReminder(tab_id);
                for (var j in airdate_reminders) {
                    var airdate_reminder = airdate_reminders[j];
                    var reminder_id = airdate_reminder['reminder_id'];
                    var program_id = airdate_reminder['program_id'];
                    if (reminder_id == r_id) {
                        PBS.tvschedules.tvschedules.search_lib.airdate_reminders.activateAirdateReminder(tab_id, reminder_id);
                    }
                }
            }

            if ('email_address' in email_reminders) {
                var email = email_reminders['email_address'];
                if (email) {
                    for (var i in airdate_infos) {
                        var info = airdate_infos[i];
                        var input_id = info['email_input_id'];
                        var input = document.getElementById(input_id);
                        var confirm_id = info['email_confirm_id'];
                        var confirm = document.getElementById(confirm_id);
                        var checkbox_id = info['checkbox_id'];
                        var checkbox = document.getElementById(checkbox_id);
                        if (input) {
                            input.value = email;
                        }
                        if (confirm) {
                            confirm.value = email;
                        }
                        if (checkbox) {
                            checkbox.checked = true;
                        }
                    }
                }
            } else {
                for (var i in airdate_infos) {
                    var info = airdate_infos[i];
                    var input_id = info['email_input_id'];
                    var input = document.getElementById(input_id);
                    var confirm_id = info['email_confirm_id'];
                    var confirm = document.getElementById(confirm_id);
                    var checkbox_id = info['checkbox_id'];
                    var checkbox = document.getElementById(checkbox_id);
                    if (input) {
                        input.value = '';
                    }
                    if (confirm) {
                        confirm.value = '';
                    }
                    if (checkbox) {
                        checkbox.checked = false;
                    }
                }
            }
        },



        /*********************************************************************
        Utility functions
        *********************************************************************/
        manageTabs: function (tab) {
            if (tab == 'now') {
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_now').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab_on';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_daily').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_weekly').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
            } else if (tab == 'day') {
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_now').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_daily').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab_on';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_weekly').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
            } else if (tab == 'week') {
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_now').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_daily').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab';
                document.getElementById('pbs_tvschedules_modules-tvschedules-viewheader_views_weekly').className = 'pbs_tvschedules_modules-tvschedules-viewheader_views_tab_on';
            }
            PBS.tvschedules.tvschedules.schedule.manageJumpTo();
        },

        backToGrid: function () {

            var provider_container_id = 'pbs_tvschedules_modules-tvschedules-optionsheader_provider_container';
            var header_container_id = 'pbs_tvschedules_modules-tvschedules-header_container';
            var schedule_container_id = 'pbs_tvschedules_modules-tvschedules-schedule_container';
            var back_to_grid_container_id = 'pbs_tvschedules_modules-search-back_to_grid';
            var results_container_id = 'pbs_tvschedules_modules-tvschedules-results_global_container';
            var search_form_container_id = 'pbs_tvschedules_modules-search-search_form_container';

            var provider_container = document.getElementById(provider_container_id);
            var header_container = document.getElementById(header_container_id);
            var schedule_container = document.getElementById(schedule_container_id);
            var back_to_grid_container = document.getElementById(back_to_grid_container_id);
            var results_container = document.getElementById(results_container_id);
            var search_form_container = document.getElementById(search_form_container_id);

            provider_container.style.display = '';
            header_container.style.display = '';
            schedule_container.style.display = '';
            back_to_grid_container.style.display = 'none';
            results_container.style.display = 'none';
            search_form_container.style.display = 'none';

        },


        // Hide or show advanced search options.
        showSearchOptions: function () {

            var advanced_search_dom_node = document.getElementById('pbs_tvschedules_modules-search-search_form_advanced_options');
            var toggle_dom_node = document.getElementById('pbs_tvschedules_modules-search-search_form_advanced_options_toggle');

            if (!(advanced_search_dom_node.style.display == "block")) {
                advanced_search_dom_node.style.display = "block";
                toggle_dom_node.innerHTML = 'hide options';
            } else {
                advanced_search_dom_node.style.display = "none";
                toggle_dom_node.innerHTML = 'show options';
            }

        },


        // Change feed block color on mouseover and mouseout.
        changeColor: function (id) {

            // If it's on, turn it off.
            if (YAHOO.util.Dom.hasClass(id, 'pbs_tvschedules_modules-tvschedules-griddata_feedid_on')) {
                YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-griddata_feedid_on', 'pbs_tvschedules_modules-tvschedules-griddata_feedid_off');
            } else { // If it's off, turn it on.
                YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-griddata_feedid_off', 'pbs_tvschedules_modules-tvschedules-griddata_feedid_on');
            }

        }, 


        // Change favorite icons color on mouseover and mouseout.
        gridRollChannelFavorite: function (id) {

            // If it's already 'on', don't do anything.
            if (!YAHOO.util.Dom.hasClass(id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on')) {

                // If it's over, turn it icon.
                if (YAHOO.util.Dom.hasClass(id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over')) {
                    YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over', 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon');
                } else { // If it's icon, turn it over.
                    YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon', 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over');
                }

            }

        },


        // Designate a channel feed as a favorite and modify the user interface accordingly.
        gridSetChannelFavorite: function (clicked_star_id, feed_id, event) {

            // Kill event bubbling.
            if (window.event) {
                window.event.cancelBubble = true;
            } else {
                event.stopPropagation();
            }
        
            // If it's over, turn it on.
            if (YAHOO.util.Dom.hasClass(clicked_star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over')) {
                var finalize = function () {
                    // Turn all stars corresponding to this feed on.
                    var star_ids = PBS.tvschedules.tvschedules.feed_ids_to_star_ids[feed_id];
                    for (var i in star_ids) {
                        var star_id = star_ids[i];
                        YAHOO.util.Dom.removeClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon');
                        YAHOO.util.Dom.removeClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over');
                        YAHOO.util.Dom.addClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on');
                    }
                }
                if (PBS.tvschedules.tvschedules.config['localization']) {
                    PBS.tvschedules.user.addStationFavoriteChannel.go(
                        PBS.tvschedules.tvschedules.config['localization'],
                        feed_id,
                        finalize,
                        this
                    );
                } else {
                    PBS.tvschedules.user.addFavoriteChannel.go(
                        feed_id,
                        finalize,
                        this
                    );
                }
            } else if (YAHOO.util.Dom.hasClass(clicked_star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on')) { // If it's on turn it over.
                var finalize = function () {
                    // Turn all stars corresponding to this feed off.
                    var star_ids = PBS.tvschedules.tvschedules.feed_ids_to_star_ids[feed_id];
                    for (var i in star_ids) {
                        var star_id = star_ids[i];
                        if (star_id == clicked_star_id) {
                            continue;
                        }
                        YAHOO.util.Dom.removeClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on');
                        YAHOO.util.Dom.removeClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over');
                        YAHOO.util.Dom.addClass(star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon');
                    }
                }
                if (PBS.tvschedules.tvschedules.config['localization']) {
                    PBS.tvschedules.user.removeStationFavoriteChannel.go(
                        PBS.tvschedules.tvschedules.config['localization'],
                        feed_id,
                        finalize,
                        this
                    );
                } else {
                    PBS.tvschedules.user.removeFavoriteChannel.go(
                        feed_id,
                        finalize,
                        this
                    );
                }
                YAHOO.util.Dom.removeClass(clicked_star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on');
                YAHOO.util.Dom.addClass(clicked_star_id, 'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over');
            }


        },



        // Change favorite icons color on mouseover and mouseout.
        fullDayRollChannelFavorite: function (id) {

            // If it's already 'on', don't do anything.
            if (!YAHOO.util.Dom.hasClass(id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on')) {

                // If it's over, turn it off.
                if (YAHOO.util.Dom.hasClass(id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over')) {
                    YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over', 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite');
                } else { // If it's off, turn it over.
                    YAHOO.util.Dom.replaceClass(id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite', 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over');
                }

            }

        },


        // Designate a channel feed as a favorite and modify the user interface accordingly.
        fullDaySetChannelFavorite: function (span_id, feed_id) {

            // If it's over, turn it on.
            if (YAHOO.util.Dom.hasClass(span_id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over')) {
                var finalize = function () {
                    YAHOO.util.Dom.replaceClass(span_id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over', 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on');
                }
                if (PBS.tvschedules.tvschedules.config['localization']) {
                    PBS.tvschedules.user.addStationFavoriteChannel.go(
                        PBS.tvschedules.tvschedules.config['localization'],
                        feed_id,
                        finalize,
                        this
                    );
                } else {
                    PBS.tvschedules.user.addFavoriteChannel.go(
                        feed_id,
                        finalize,
                        this
                    );
                }
            } else if (YAHOO.util.Dom.hasClass(span_id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on')) { // If it's on turn it over.
                var finalize = function () {
                    YAHOO.util.Dom.replaceClass(span_id, 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on', 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_over');
                }
                if (PBS.tvschedules.tvschedules.config['localization']) {
                    PBS.tvschedules.user.removeStationFavoriteChannel.go(
                        PBS.tvschedules.tvschedules.config['localization'],
                        feed_id,
                        finalize,
                        this
                    );
                } else {
                    PBS.tvschedules.user.removeFavoriteChannel.go(
                        feed_id,
                        finalize,
                        this
                    );
                }
            }

        },



        hideChannels: function () {

            var favorite_feed_ids = PBS.tvschedules.tvschedules.getFavoriteFeeds();

            var seen = {};

            // For each displayed feed id ...
            for (var i in PBS.tvschedules.tvschedules.feed_ids) {
                var feed_id = PBS.tvschedules.tvschedules.feed_ids[i];

                var is_a_favorite = false;
                // ... get the feed id's tr ...
                var tr_ids = PBS.tvschedules.tvschedules.feed_ids_to_tr_ids[feed_id];

                //... and for each favorite feed id ...
                for (var j in favorite_feed_ids) {
                    var favorite_feed_id = favorite_feed_ids[j];
                    // ... check to see if the feed id we are currently examining is one of the favorite feed ids...
                    if (feed_id == favorite_feed_id) {
                        is_a_favorite = true;
                    }
                }

                if (is_a_favorite) {
                    // Show the station graphic for the first feed still visible.
                    var tvdata_name = PBS.tvschedules.tvschedules.feed_ids_to_tvdata_name[feed_id];
                    /*
                    alert(
                        'tvdata_name: ' + tvdata_name + '\n' +
                        'feed_id: ' + feed_id + '\n'
                    );
                    */

                    if (typeof(seen[tvdata_name]) == 'undefined') {
                        //alert('initing list');
                        seen[tvdata_name] = [feed_id];
                        // The station td ids for just this feed id.
                        var station_td_ids = PBS.tvschedules.tvschedules.feed_ids_to_station_td_ids[feed_id];
                        var first_id = station_td_ids[0];
                        var first = document.getElementById(first_id);
                        first.style.visibility = '';
                        //alert('first_id: ' + first_id);
                    } else {
                        seen[tvdata_name].push(feed_id); // Don't really need this.
                    }

                } else {
                    // ... and if it indeed is not a favorite, then hide any related tr elements.
                    for (var k in tr_ids) {
                        var tr_id = tr_ids[k];
                        document.getElementById(tr_id).style.display = 'none';
                    }
                }

            }

            var show_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_feedfilter_link_show';
            var hide_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_feedfilter_link_hide';

            var show_link = document.getElementById(show_link_id);
            var hide_link = document.getElementById(hide_link_id);

            show_link.style.display = '';
            hide_link.style.display = 'none';

            this.sizeGridContainer();

            // Set a flag so we can test whether non-favorite channels are
            // currently hidden when "earlier" and "later" temporal navigation
            // is used.
            PBS.tvschedules.tvschedules.schedule.channels_hidden = true;

        },


        showChannels: function () {
            // For each feed id ...
            for (var feed_id in PBS.tvschedules.tvschedules.feed_ids_to_tr_ids) {
                // ... get the feed id's tr ...
                var tr_ids = PBS.tvschedules.tvschedules.feed_ids_to_tr_ids[feed_id];
                for (var i in tr_ids) {
                    var tr_id = tr_ids[i];
                    var tr = document.getElementById(tr_id);
                    // TODO test a style.display = '' tactic.
                    try {
                        tr.style.display = 'table-row';
                    } catch (e) { // For IE 6, which can't seem to assign 'table-row' to the style.display property of a tr object.
                        tr.style.display = 'block';
                    }
                }
            }

            var show_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_feedfilter_link_show';
            var hide_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_feedfilter_link_hide';

            var show_link = document.getElementById(show_link_id);
            var hide_link = document.getElementById(hide_link_id);

            show_link.style.display = 'none';
            hide_link.style.display = '';

            this.sizeGridContainer();

            // Set a flag so we can test whether non-favorite channels are
            // currently hidden when "earlier" and "later" temporal navigation
            // is used.
            PBS.tvschedules.tvschedules.schedule.channels_hidden = false;
            
            // There are two show/hide toggles for the grid: one for stations
            // and one for channels. Each show/hide toggle needs to be aware of
            // the other so that when one is executed, it will also apply the
            // second hide filter if applicable.
            if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                PBS.tvschedules.tvschedules.hideStations();
            }
            if (!PBS.tvschedules.tvschedules.config['localization']) {
                PBS.tvschedules.tvschedules.manageStationGraphics();
            }
        },


        hideStations: function () {

            // Get cookie value from user utility.
            var tvdata_name = PBS.tvschedules.user.cookies['pbsol.station'];
            var station_feed_tr_ids = PBS.tvschedules.tvschedules.tvdata_name_to_feed_tr_ids[tvdata_name];
            // For each displayed feed tr ...
            for (var feed_id in PBS.tvschedules.tvschedules.feed_ids_to_tr_ids) {
                var belongs_to_primary = false;
                // ... get the tr's id ...
                var tr_ids = PBS.tvschedules.tvschedules.feed_ids_to_tr_ids[feed_id];
                //... and for each station feed tr ...
                for (var i in station_feed_tr_ids) {
                    var station_feed_tr_id = station_feed_tr_ids[i];
                    // ... check to see if the tr id we are currently examining
                    // is one of the primary station's feed trs...
                    for (var j in tr_ids) {
                        var tr_id = tr_ids[j];
                        if (tr_id == station_feed_tr_id) {
                            belongs_to_primary = true;
                        }
                    }
                }
                if (!belongs_to_primary) {
                    // ... and if it indeed is does not belong to the primary
                    // station, then hide it.
                    for (var k in tr_ids) {
                        var tr_id = tr_ids[k];
                        document.getElementById(tr_id).style.display = 'none';
                    }
                }
            }

            // Swap the control label.
            var show_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_stationfilter_link_show';
            var hide_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_stationfilter_link_hide';

            var show_link = document.getElementById(show_link_id);
            var hide_link = document.getElementById(hide_link_id);

            show_link.style.display = '';
            hide_link.style.display = 'none';

            this.sizeGridContainer();

            // Set a flag so we can test whether non-favorite stations are
            // currently hidden when "earlier" and "later" temporal navigation
            // etc is used.
            PBS.tvschedules.tvschedules.schedule.stations_hidden = true;
        },


        showStations: function () {
            // For each feed id ...
            for (var feed_id in PBS.tvschedules.tvschedules.feed_ids_to_tr_ids) {
                // ... get the feed id's tr ...
                var tr_ids = PBS.tvschedules.tvschedules.feed_ids_to_tr_ids[feed_id];
                for (var i in tr_ids) {
                    var tr_id = tr_ids[i];
                    var tr = document.getElementById(tr_id);
                    try {
                        tr.style.display = 'table-row';
                    } catch (e) { // For IE 6, which can't seem to assign 'table-row' to the style.display property of a tr object.
                        tr.style.display = 'block';
                    }
                }
            }


            // Swap the control label.
            var show_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_stationfilter_link_show';
            var hide_link_id = 'pbs_tvschedules_modules-tvschedules-gridheader_stationfilter_link_hide';

            var show_link = document.getElementById(show_link_id);
            var hide_link = document.getElementById(hide_link_id);

            show_link.style.display = 'none';
            hide_link.style.display = '';

            this.sizeGridContainer();

            // Set a flag so we can test whether non-favorite stations are
            // currently hidden when "earlier" and "later" temporal navigation
            // is used.
            PBS.tvschedules.tvschedules.schedule.stations_hidden = false;

            // There are two show/hide toggles for the grid: one for stations
            // and one for channels. Each show/hide toggle needs to be aware of
            // the other so that when one is executed, it will also apply the
            // second hide filter if applicable.
            if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                PBS.tvschedules.tvschedules.hideChannels();
            }
        },


        changePrimaryStation: function (id, tvdata_name) {

            var unselected_classname = "pbs_tvschedules_modules-tvschedules-griddata_stationid";
            var selected_classname = "pbs_tvschedules_modules-tvschedules-griddata_stationid_selected";

            PBS.tvschedules.user.changeFavoriteStation.go(
                tvdata_name,
                PBS.tvschedules.tvschedules.changePrimaryStationCallback,
                this
            );

            // Deprecated. This as used before we decided that primary
            // station should always appear first. Back then, all we
            // needed to do was initPreferences on the underlying
            // modules to update their indication of which station was
            // primary. Now we have to re-init the entire underlying
            // modules to update the order.
            for (var i in PBS.tvschedules.tvschedules.stationid_station_dom_ids) {
                var dom_id = PBS.tvschedules.tvschedules.stationid_station_dom_ids[i];
                var dom_node = document.getElementById(dom_id);
                // Normalize them all to unselected briefly.
                dom_node.className = unselected_classname;
                if (id == dom_id) {
                    // And toggle the class on the appropriate element.
                    dom_node.className = selected_classname;
                }
            }

        },


        changePrimaryStationCallback: function () {
            // This will be hidden automatically on the module init.
            PBS.tvschedules.loading.show();
            var args = {};
            PBS.tvschedules.initModules(args);
        },


        // Measure the widths of some important columns, sum them, and from that
        // determine the quantum program cell width.
        // Next, assign the appropriate program cell width based on the correct
        // multiple of the quanta.
        sizeTDs: function () {

            var module_container_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-schedule_container');
            var scroll_bar_width = PBS.tvschedules.getScrollBarWidth()
            
            // Grab all the tds in the document.
            var tds = document.getElementsByTagName('td');


            // This block resizes td elements in the full day view.
            if (PBS.tvschedules.tvschedules.schedule.view == 'w') {
                // Here we go through all of the tds to get the id of the first
                // timeheader.
                var time_header_id;
                for (i=0; i<tds.length; i++) {
                    var td = tds[i];
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_fullweek_timeheader')) {
                        time_header_id = td.id;
                        //alert(td.id);
                        break;
                    }
                }

                // If we don't find a timeheader, we don't need to size.
                // E.G. maybe we have no airdates returned.
                if (!time_header_id) {
                    return false;
                }

                var time_column_info = PBS.tvschedules.getElementInfo(time_header_id);
                var scroll_bar_width = PBS.tvschedules.getScrollBarWidth()

                // Set the width of the blank box above the time headers.
                var blank_box = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_weekdate_header_anchorcol');
                //alert(time_column_info.width);
                blank_box.setAttribute('width', time_column_info.width - 2);
            
                // Compute the total width of the static width columns.
                var border_correction = 12;
                var static_width = time_column_info.width + border_correction;
                //var static_width = time_column_info.width + scroll_bar_width + border_correction;

                // Compute the remaining available width for use by dynamic width
                // program cells.
                var dynamic_width = module_container_info.width - static_width;
                var block_width = dynamic_width / 7; // days in the week.

                /*
                alert(
                    'dynamic_width: ' + dynamic_width + '\n' +
                    'module_container_info.width: ' + module_container_info.width + '\n' +
                    'static_width: ' + static_width + '\n' +
                    'block_width: ' + block_width + '\n' +
                    'time_column_info.width: ' + time_column_info.width + '\n' +
                    'scroll_bar_width: ' + scroll_bar_width + '\n' +
                    'border_correction: ' + border_correction + '\n'
                );
                */

                for (i=0; i<tds.length; i++) {
                    var td = tds[i];
                    // Inactive date headers.
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-gridheader_weekdate_header')) {
                        td.setAttribute('width', block_width);
                    }
                    // Active date headers.
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-gridheader_weekdate_header_on')) {
                        td.setAttribute('width', block_width);
                    }
                    // Program cells.
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock')) {
                        td.setAttribute('width', block_width);
                    }
                }

            }

            // This block resizes td elements in the full day view.
            if (PBS.tvschedules.tvschedules.schedule.view == 'f') {

                // Here we go through all of the tds to get the id of the first
                // timeheader.
                var time_header_id;
                for (i=0; i<tds.length; i++) {
                    var td = tds[i];
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_fullday_timeheader')) {
                        time_header_id = td.id;
                        break;
                    }
                }

                // If we don't find a timeheader, we don't need to size.
                // E.G. maybe we have no airdates returned.
                if (!time_header_id) {
                    return false;
                }

                var time_column_info = PBS.tvschedules.getElementInfo(time_header_id);
                var scroll_bar_width = PBS.tvschedules.getScrollBarWidth()
            
                // Compute the total width of the static width columns.
                var static_width = time_column_info.width + 2;
                //var static_width = time_column_info.width + scroll_bar_width + 2;

                // Compute the remaining available width for use by dynamic width
                // program cells.
                var dynamic_width = module_container_info.width - static_width;

                for (i=0; i<tds.length; i++) {
                    var td = tds[i];
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_fullday_timeheader')) {
                        td.setAttribute('width', dynamic_width);
                    }
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock')) {
                        td.setAttribute('width', dynamic_width);
                    }
                }

            }


            // This block resizes td elements in the grid view.
            if (PBS.tvschedules.tvschedules.schedule.view == 'g') {
            
                var station_column_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-gridheader_stationfilter_block');
                var station_column_width = station_column_info.width;
                // If the station column has been disappeared due to a forced
                // localization then we'll explicitly record it's width as 0
                // pixels.
                if (PBS.tvschedules.tvschedules.config['localization']) {
                    station_column_width = 0;
                }
                var feed_column_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-gridheader_feedfilter_block');

                // divisor is the number of chunks into which we want to divide an hour.
                var divisor = 4;

                // border_correction assumes a 1 pixel border-left and a 1 pixel
                // border-right property value on the program cells in the
                // grid - hence the 2.
                var border_correction = divisor * 2;

                // Compute the total width of the static width columns.
                var static_width = station_column_width + feed_column_info.width + scroll_bar_width + 6;
                //var static_width = station_column_info.width + feed_column_info.width + scroll_bar_width + border_correction;

                // Compute the remaining available width for use by dynamic width
                // program cells.
                var dynamic_width = module_container_info.width - static_width;

                // Compute base width for program blocks.
                var base_width = dynamic_width / divisor;

                /*
                alert(
                    'divisor: ' + divisor + '\n' +
                    'border_correction: ' + border_correction + '\n' +
                    'station_column_width: ' + station_column_width + '\n' +
                    'feed_column_info.width: ' + feed_column_info.width + '\n' +
                    'scroll_bar_width: ' + scroll_bar_width + '\n' +
                    'static_width: ' + static_width + '\n' +
                    'dynamic_width: ' + dynamic_width + '\n' +
                    'module_container_info.width: ' + module_container_info.width + '\n' +
                    'base_width: ' + base_width + '\n'
                );
                */

                // Loop through the tds, and set the appropriate cell widths for grid program cells.
                // Depending on the length of the program, and how many quantum program cell widths that represents,
                // the server components that generate the program gird table markup will assign the appropriate css class name. 
                // pbs_tvschedules_modules-tvschedules-griddata_programblock1 is assigned if the length of the program deserves 1 quantum
                // pbs_tvschedules_modules-tvschedules-griddata_programblock2 is assigned if the length of the program deserves 2 quanta
                // etc.
                var td_ids = [];
                for (i=0; i<tds.length; i++) {
                    var td = tds.item(i);
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-gridheader_timeheader')) {
                        td.setAttribute('width', base_width);
                        //td.style.width = base_width + 'px'; // Gets 'invalid argument' error in IE 6
                    }
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock1')) {
                        td_ids.push(td.id + ' =  base_width: ' + base_width);
                        td.setAttribute('width', base_width);
                        //td.style.width = base_width + 'px'; // Gets 'invalid argument' error in IE 6
                    }
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock2')) {
                        td_ids.push(td.id + ' =  base_width: ' + (2 * base_width));
                        td.setAttribute('width', base_width * 2);
                        //td.style.width = (base_width * 2) + 'px'; // Gets 'invalid argument' error in IE 6
                    }
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock3')) {
                        td_ids.push(td.id + ' =  base_width: ' + (3 * base_width));
                        td.setAttribute('width', base_width * 3);
                        //td.style.width = (base_width * 3) + 'px'; // Gets 'invalid argument' error in IE 6
                    }
                    if (YAHOO.util.Dom.hasClass(td, 'pbs_tvschedules_modules-tvschedules-griddata_programblock4')) {
                        td_ids.push(td.id + ' =  base_width: ' + (4 * base_width));
                        td.setAttribute('width', base_width * 4);
                        //td.style.width = (base_width * 4) + 'px'; // Gets 'invalid argument' error in IE 6
                    }
                }
                var td_ids_str = td_ids.join('\n'); 
                //alert(td_ids_str);

            }


        },


        // Size the grid container appropriately.
        // If the grid is shorter than the grid container, shrink the container to fit.
        // If the grid is taller than the grid container, but shorter than the original container height (assigned by CSS) then reset the container to the original height.
        sizeGridContainer: function () {

            var grid_container_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-griddata_container');
            var grid_table_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-griddata_table');

            // Get the dom node of the grid container.
            var grid_container_dom_node = grid_container_info.dom_node;

            // Get the height of the grid container.
            var grid_container_height = grid_container_info.height;
            
            // Get the height of the grid table itself.
            // TODO Calculate the border width compensation factor based upon the number of feeds curently displayed.
            //var grid_table_height = grid_table_info.height + 5; // The extra pixels compensate for border widths and ensure that the scroll bar dissapears in all supported browsers.
            var grid_table_height = grid_table_info.height;
            
            // If the grid table is shorter than the grid container, shrink the container to fit.
            if (grid_container_height > grid_table_height) { 
                grid_container_dom_node.style.height = grid_table_height + 'px';
            }
            
            // If the grid table is taller than the grid container, but shorter than the original container height (assigned by CSS) then reset the container to the original height.
            if ((grid_container_height < grid_table_height) && (grid_container_height < this.grid_container_height)) { 
                grid_container_dom_node.style.height = this.grid_container_height + 'px';
            }

        },


        clearGridFavoritesStars: function () {
            // Turn all star ids off.
            var star_ids = PBS.tvschedules.tvschedules.star_ids;
            for (var i in star_ids) {
                var star_id = star_ids[i];
                YAHOO.util.Dom.removeClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_over'
                );
                YAHOO.util.Dom.removeClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on'
                );
                YAHOO.util.Dom.addClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon'
                );
            }
        },


        // Get the list of favorite feed ids.
        getFavoriteFeeds: function () {
            var favorite_feed_ids = [];
            if (PBS.tvschedules.tvschedules.config['localization']) {
                favorite_feed_ids = PBS.tvschedules.user.getStationFavoriteChannels(
                    PBS.tvschedules.tvschedules.config['localization']
                )
            } else {
                favorite_feed_ids = PBS.tvschedules.user.getFavoriteChannels()
            }
            return favorite_feed_ids;
        },


        manageGridFavorites: function () {
            PBS.tvschedules.tvschedules.clearGridFavoritesStars();
            var favorite_feed_ids = PBS.tvschedules.tvschedules.getFavoriteFeeds();
            // Turn stars that correspond to favorite feed ids on.
            for (var i in favorite_feed_ids) {
                var feed_id = favorite_feed_ids[i];
                var star_ids = PBS.tvschedules.tvschedules.feed_ids_to_star_ids[feed_id];

                // Change the CSS classname assignments on the appropriate elements to
                // reflect favorite status.
                // If it's a favorite, turn it on.
                for (var j in star_ids) {
                    var star_id = star_ids[j];
                    if (
                        YAHOO.util.Dom.hasClass(
                            star_id,
                            'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon'
                        )
                    ) {
                        YAHOO.util.Dom.replaceClass(
                            star_id,
                            'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon',
                            'pbs_tvschedules_modules-tvschedules-favorites_addremove_icon_on'
                        );
                    }
                }
            }
        },


        manageFullDayFavorites: function () {
            // Turn star off as a baseline.
            var star_id = 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite';
            if (
                YAHOO.util.Dom.hasClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on'
                )
            ) {
                YAHOO.util.Dom.replaceClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on',
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite'
                );
            }

            var favorite_feed_ids = PBS.tvschedules.tvschedules.getFavoriteFeeds();
            // Turn star on if it corresponds to a favorite feed id.
            if (
                PBS.tvschedules.isInArray(
                    favorite_feed_ids,
                    PBS.tvschedules.tvschedules.schedule.getFullDay.feed_id
                )
            ) {
                if (
                    YAHOO.util.Dom.hasClass(
                        star_id,
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite'
                    )
                ) {
                    YAHOO.util.Dom.replaceClass(
                        star_id,
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite',
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on'
                    );
                }
            }
        },


        manageFullWeekFavorites: function () {
            // Turn star off as a baseline.
            var star_id = 'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite';
            if (
                YAHOO.util.Dom.hasClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on'
                )
            ) {
                YAHOO.util.Dom.replaceClass(
                    star_id,
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on',
                    'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite'
                );
            }

            var favorite_feed_ids = PBS.tvschedules.tvschedules.getFavoriteFeeds();
            // Turn star on if it corresponds to a favorite feed id.
            if (
                PBS.tvschedules.isInArray(
                    favorite_feed_ids,
                    PBS.tvschedules.tvschedules.schedule.getFullWeek.feed_id
                )
            ) {
                if (
                    YAHOO.util.Dom.hasClass(
                        star_id,
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite'
                    )
                ) {
                    YAHOO.util.Dom.replaceClass(
                        star_id,
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite',
                        'pbs_tvschedules_modules-tvschedules-gridheader_selected_feed_favorite_on'
                    );
                }
            }
        },


        managePrimaryStation: function () {

            // Get primary station tvdata_name.
            var tvdata_name = PBS.tvschedules.user.cookies['pbsol.station'];

            // Get dom ids for station id td
            var station_td_ids = PBS.tvschedules.tvschedules.tvdata_name_to_station_td_ids[tvdata_name];

            // Change the CSS classname assignments on the appropriate element to reflect primary status.
            // Go through all of the stations.
            for (var i in station_td_ids) {
                var station_td_id = station_td_ids[i];

                // Deselect.
                if (
                    YAHOO.util.Dom.hasClass(
                        station_td_id,
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid_selected'
                    )
                ) {
                    YAHOO.util.Dom.replaceClass(
                        station_td_id,
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid_selected',
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid'
                    );
                }

                // If it's a primary, make it selected.
                if (
                    YAHOO.util.Dom.hasClass(
                        station_td_id,
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid'
                    )
                ) {
                    YAHOO.util.Dom.replaceClass(
                        station_td_id,
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid',
                        'pbs_tvschedules_modules-tvschedules-griddata_stationid_selected'
                    );
                }

            }

        },


        manageStationGraphics: function () {
            var tvdata_names = PBS.tvschedules.tvschedules.tvdata_names;
            for (var i in tvdata_names) {
                var tvdata_name = tvdata_names[i];
                var station_td_ids = PBS.tvschedules.tvschedules.tvdata_name_to_station_td_ids[tvdata_name];
                for (var j in station_td_ids) {
                    var station_td_id = station_td_ids[j];
                    var station_td = document.getElementById(station_td_id);
                    station_td.style.visibility = 'hidden';
                }
                var first_id = station_td_ids[0];
                var first = document.getElementById(first_id);
                first.style.visibility = '';
            }
        },


        initPreferences: function () {

            /* Handle lineup selector */
            PBS.tvschedules.tvschedules.schedule.manageLineupSelector();

            /* Handle provider name */
            PBS.tvschedules.tvschedules.schedule.manageProviderName();

            /* Handle highlight */
            if (!PBS.tvschedules.tvschedules.disable_highlight) {
                PBS.tvschedules.tvschedules.schedule.highlight();
            }

            // We want search_lib to initPreferences whenever this module does,
            // but only if search has been called at least once so the results
            // markup exists in this DOM.
            if (PBS.tvschedules.tvschedules.searched) {
                PBS.tvschedules.tvschedules.search_lib.initPreferences();
            }

            /* Cookie-enabled specific initializations. */
            if (PBS.tvschedules.user.cookie_settable) {

                // If favorites are disabled, no stars will exist to
                // manipulate.
                if (!PBS.tvschedules.tvschedules.disable_favorites) {
                    // Grid view favorites.
                    if (PBS.tvschedules.tvschedules.schedule.view == 'g') {
                        PBS.tvschedules.tvschedules.manageGridFavorites();
                    }

                    // Full day view favorites.
                    if (PBS.tvschedules.tvschedules.schedule.view == 'f') {
                        PBS.tvschedules.tvschedules.manageFullDayFavorites();
                    }

                    // Full week view favorites.
                    if (PBS.tvschedules.tvschedules.schedule.view == 'w') {
                        PBS.tvschedules.tvschedules.manageFullWeekFavorites();
                    }
                }

                /* Handle primary station */
                if (!PBS.tvschedules.tvschedules.config['localization']) {
                    PBS.tvschedules.tvschedules.managePrimaryStation();
                    if (PBS.tvschedules.tvschedules.schedule.view == 'g') {
                        PBS.tvschedules.tvschedules.manageStationGraphics();
                    }
                }

                /* Handle email reminders on search results */
                PBS.tvschedules.tvschedules.manageReminders();

            } else { /* Non-cookie-enabled specific initializations. */

                // pass

            }

        },


        submitSearchShort: function (evt) {
            evt = evt || window.event;
            var keyCode = evt.keyCode ? evt.keyCode :
                evt.charCode ? evt.charCode : evt.which;
            if (keyCode == '13') {
                PBS.tvschedules.tvschedules.search('short');
                return false;
            }
        },


        submitSearchFull: function (evt) {
            evt = evt || window.event;
            var keyCode = evt.keyCode ? evt.keyCode :
                evt.charCode ? evt.charCode : evt.which;
            if (keyCode == '13') {
                PBS.tvschedules.tvschedules.search('full');
                return false;
            }
        },


        help: function () {
            var obj = {
                open: function () {
                    var help_markup_container = document.getElementById('pbs_tvschedules_modules-tvschedules-help_markup_container');
                    var help_markup = help_markup_container.innerHTML
                    PBS.tvschedules.overlay.setBody(help_markup);
                    PBS.tvschedules.overlay.show();
                },
                close: function () {
                    PBS.tvschedules.overlay.hide();
                }
            }

            return obj;

        }(),


        print: function () {
            window.print();
        },


        /* Event chains. */
        // Initialize the module!
        init: function (args) {
            // A caller that is re-initializing a module can pass arbitrary
            // arguments for use by module initialization.
            if (args.hasOwnProperty('navigation_transport')) {
                PBS.tvschedules.tvschedules.schedule.navigation_transport = args['navigation_transport'];
            }

            // By default we load the grid view.
            // If you load the week view, and then change provider/location,
            // this init method will be fired to re-init the module. In this
            // case we need to declare that we are back on the grid view.
            // You might ask "why not stay on the week view after re-initing?"
            // The answer is that we can't guarantee that the feed you were
            // viewing on the week view will exist on your new
            // provider/location. In fact we can't even gurantee that the
            //station to which it belonged will still be on your new lineup. 
            PBS.tvschedules.tvschedules.schedule.view = 'g';

            // This was added because external operations can change or delete
            // the headend id stored in a users cookie. Specifically if a user
            // has a zip/provider set and then re-localizes by state, it wipes
            // headend id from the cookie. In this case the value that was
            // previously cached in the schedules object needs to be wiped. We
            // wipe it here in all initialization cases because it seems
            // harmless. We'll see how this works out.
            PBS.tvschedules.tvschedules.schedule.headend_id = '';
            /*
            alert(
                'pbsol.station: ' + PBS.tvschedules.user.cookies['pbsol.station']
            );
            */
            // We clear the searched flag here to support the case where a user
            // has loaded this module, performed a search, and then made a
            // localization change via preferences signififcant enough to
            // trigger the re-initialization of this module.
            this.searched = false;

            // At init time we need to make a search lib instance for use by
            // this module. We do not, however want to make a new instance
            // every time the module is re-initialized by preferences etc after
            // that, so we check for it's existence and only make ourselves a
            // search_lib instance if one is not currently in service.
            if (!this.search_lib) {
                PBS.tvschedules.tvschedules.search_lib = new PBS.tvschedules.search_lib(PBS.tvschedules.tvschedules.search_lib_config);
            }
            PBS.tvschedules.tvschedules.initChain.go();
        },


        // This event chain initializes the tvschedules module.
        // It manages default module configuration, implementation
        // customizations, and reacts appropriately to user state. 
        // If we don't have what we need in terms of localization, we pre-empt
        // module initialization with a stationfinder entry point.
        initChain: function () {
            var obj = {

                script_id: 'PBS.tvschedules.tvschedules.script',
                script_dom_node: '',

                go: function () {
                    // External systems can (re)-intialize this module, and at
                    // initialization we shouldn't have a navigation transport
                    // value set.
                    // Let's hope we don't need this anymore. It's a pain. Now
                    // that we are computing navigation transport at the end of
                    // the init chain, I think we can remove it. -Jer
                    //PBS.tvschedules.tvschedules.schedule.navigation_transport = '';
                    this.customizations();
                },

                customizations: function () {
                    // Grab the implementation customizations from the module query 
                    // string (the query string of the module JS include, not 
                    // the including page).
                    this.script_dom_node = YAHOO.util.Dom.get(this.script_id);
                    var module_params = PBS.tvschedules.parseURL(this.script_dom_node.src);
                    for (key in module_params) {
                        value = module_params[key];
                        PBS.tvschedules.tvschedules.config[key] = value;
                    }
                    if (
                        (PBS.tvschedules.tvschedules.config['provider'] != 'disabled') &&
                        (PBS.tvschedules.tvschedules.config['localization'])
                    ) {
                        PBS.tvschedules.tvschedules.provider_finder = true;
                    }
                    if (PBS.tvschedules.tvschedules.config['search'] != 'enabled') {
                        PBS.tvschedules.tvschedules.disable_search = true;
                    }
                    if (PBS.tvschedules.tvschedules.config['favorites'] != 'enabled') {
                        PBS.tvschedules.tvschedules.disable_favorites = true;
                    }
                    if (PBS.tvschedules.tvschedules.config['highlight'] != 'enabled') {
                        PBS.tvschedules.tvschedules.disable_highlight = true;
                    }
                    if (PBS.tvschedules.tvschedules.config['wrapper'] != 'enabled') {
                        PBS.tvschedules.tvschedules.disable_wrapper = true;
                    }
                    if (PBS.tvschedules.tvschedules.config['shop'] != 'enabled') {
                        PBS.tvschedules.tvschedules.disable_shop = true;
                    }
                    /*
                    alert(
                        'localization: ' + PBS.tvschedules.tvschedules.config['localization'] + '\n' +
                        'provider: ' + PBS.tvschedules.tvschedules.config['provider'] + '\n' +
                        'search: ' + PBS.tvschedules.tvschedules.config['search'] + '\n' +
                        'favorites: '  + PBS.tvschedules.tvschedules.config['favorites'] + '\n' +
                        'highlight: ' + PBS.tvschedules.tvschedules.config['highlight'] + '\n'
                    );
                    */

                    // Grab the overrides from the query string of the including page.
                    var url = document.location.toString();
                    var page_params = PBS.tvschedules.parseURL(url);
                    PBS.tvschedules.user.page_params = page_params;

                    // Auto-localization.
                    if (
                        PBS.tvschedules.tvschedules.config['localization'] &&
                        PBS.tvschedules.user.cookie_settable &&
                        !PBS.tvschedules.user.cookies['pbsol.station']
                    ) {
                        //alert('autolocalize!');
                        PBS.tvschedules.cookie.setCookieValue(
                            'pbsol.station',
                            PBS.tvschedules.tvschedules.config['localization'],
                            'PBS.tvschedules.tvschedules.initChain.autolocalizeCallback'
                        );
                    // Stationfinder localization.
                    } else if (
                        (PBS.tvschedules.user.cookies['pbsol.station'] == '') &&
                        (PBS.tvschedules.tvschedules.config['localization'] == '') &&
                        !page_params['pbs_tvsm_sf_station']
                    ) {
                        // If we don't have a station value from the cookie, module 
                        // query string, or page query string, we'd better render 
                        // the stationfinder in-page zip entry.
                        this.stationFinder();
                    } else {
                        // Fire up the TVSchedules module.
                        this.initHTML();
                    }

                },

                autolocalizeCallback: function (args) {
                    PBS.tvschedules.tvschedules.initChain.initHTML();
                },

                stationFinder: function () {
                    var URL = PBS.tvschedules.config.base + '/py-publisher/modules/dtv/stationfinder/' + PBS.tvschedules.config.version + '/views/zipInPage';
                    var callback = 'PBS.tvschedules.tvschedules.initChain.stationFinderCallback';
                    PBS.tvschedules.data.get(URL, callback, {});
                },

                stationFinderCallback: function (args) {
                    var markup = args['markup'];
                    var div = document.createElement('div');
                    div.id = 'pbs_tvschedules_modules-tvschedules-zip_in_page_fab';
                    div.style.display = 'none';
                    div.innerHTML = markup;
                    PBS.tvschedules.stationfinder.zip_in_page_dom_ids.push(div.id);
                    this.script_dom_node.parentNode.insertBefore(div, this.script_dom_node);
                    div.style.display = 'block';
                    var zip_entry = document.getElementById('pbs_tvschedules_modules-sf-zipcode_input');
                    zip_entry.onkeydown = PBS.tvschedules.stationfinder.submitZip;
                },

                // Use the data utility to get the initial UI from the server.
                initHTML: function () {

                    // Extract query string arguments to support browsers that
                    // can't set cookies across domains.
                    // If there are no query string arguments, then this data
                    // will be extracted by the server component from the cookie
                    // data in the request headers.
                    var query_params = PBS.tvschedules.parseURL(
                        document.location.toString()
                    );
                    PBS.tvschedules.tvschedules.schedule.getParams();

                    var zip = '';
                    if (query_params['pbs_tvsm_sf_zip']) {
                        zip = query_params['pbs_tvsm_sf_zip'];
                    }

                    var headend_id = '';
                    if (query_params['pbs_tvsm_sf_headend_id']) {
                        headend_id = query_params['pbs_tvsm_sf_headend_id'];
                    }

                    var transport = PBS.tvschedules.tvschedules.schedule.transport;
                    if (query_params['pbs_tvsm_sf_transport']) {
                        transport = query_params['pbs_tvsm_sf_transport'];
                    }

                    var tvdata_names = '';
                    if (query_params['pbs_tvsm_sf_tvdata_names']) {
                        tvdata_names = query_params['pbs_tvsm_sf_tvdata_names'];
                    }

                    var start_time_str = PBS.tvschedules.tvschedules.schedule.getStartTimeStr();

                    var URL = PBS.tvschedules.config.base + '/py-publisher/modules/dtv/tvschedules/' + PBS.tvschedules.config.version + '/views/tvschedules';
                    var callback = 'PBS.tvschedules.tvschedules.initChain.initCallback';

                    /*
                    alert(
                        'initHTML()' + '\n' + 
                        'URL: ' + URL + '\n' +
                        'callback: ' + callback + '\n' +
                        'pbs_tvsm_sf_zip: ' + zip + '\n' +
                        'pbs_tvsm_sf_headend_id: ' + headend_id + '\n' +
                        'pbs_tvsm_sf_transport: ' + transport + '\n' +
                        'pbs_tvsm_sf_nav_transport: ' + PBS.tvschedules.tvschedules.schedule.navigation_transport + '\n' +
                        'pbs_tvsm_tv_start_time: ' + start_time_str + '\n' +
                        'pbs_tvsm_sf_tvdata_names: ' + tvdata_names + '\n' +
                        'pbs_tvsm_localization_tvdata_name: ' + PBS.tvschedules.tvschedules.config['localization'] + '\n' +
                        'pbs_tvsm_cookie_settable: ' + PBS.tvschedules.user.cookie_settable + '\n' +
                        'pbs_tvsm_provider_finder: ' + PBS.tvschedules.tvschedules.provider_finder + '\n' +
                        'pbs_tvsm_disable_search: ' + PBS.tvschedules.tvschedules.disable_search + '\n' +
                        'pbs_tvsm_disable_favorites: ' + PBS.tvschedules.tvschedules.disable_favorites + '\n' +
                        'pbs_tvsm_disable_highlight: ' + PBS.tvschedules.tvschedules.disable_highlight + '\n' +
                        'pbs_tvsm_disable_wrapper: ' + PBS.tvschedules.tvschedules.disable_wrapper + '\n' +
                        'pbs_tvsm_disable_shop: ' + PBS.tvschedules.tvschedules.disable_shop
                    );
                    */
                    /*
                        'pbs_tvsm_localization_headend_id: ' + PBS.tvschedules.tvschedules.localization_headend_id + '\n' +
                    */

                    // Send the arguments to the tvschedules view as overrides.
                    PBS.tvschedules.data.get(
                        URL,
                        callback,
                        {
                            'pbs_tvsm_sf_zip': zip,
                            'pbs_tvsm_sf_headend_id': headend_id,
                            'pbs_tvsm_sf_transport': transport,
                            'pbs_tvsm_sf_nav_transport': PBS.tvschedules.tvschedules.schedule.navigation_transport,
                            'pbs_tvsm_tv_start_time': start_time_str,
                            'pbs_tvsm_sf_tvdata_names': tvdata_names,
                            'pbs_tvsm_localization_tvdata_name': PBS.tvschedules.tvschedules.config['localization'],
                            'pbs_tvsm_provider_finder': PBS.tvschedules.tvschedules.provider_finder,
                            'pbs_tvsm_cookie_settable': PBS.tvschedules.user.cookie_settable,
                            'pbs_tvsm_disable_search': PBS.tvschedules.tvschedules.disable_search,
                            'pbs_tvsm_disable_favorites': PBS.tvschedules.tvschedules.disable_favorites,
                            'pbs_tvsm_disable_highlight': PBS.tvschedules.tvschedules.disable_highlight,
                            'pbs_tvsm_disable_wrapper': PBS.tvschedules.tvschedules.disable_wrapper,
                            'pbs_tvsm_disable_shop': PBS.tvschedules.tvschedules.disable_shop
                        }
                    );
                    /*
                            'pbs_tvsm_localization_headend_id': PBS.tvschedules.tvschedules.localization_headend_id,
                    */
    
                },

                initCallback: function (args) {

                    // Grab the shell markup.
                    var shell_markup = args['shell_markup'];

                    // Grab the default active feed (the first feed of the current primary station).
                    PBS.tvschedules.tvschedules.active_feed = args['active_feed'];

                    // Grab station logo td dom ids
                    PBS.tvschedules.tvschedules.stationid_station_dom_ids = args['stationid_station_dom_ids'];

                    // Grab feed tr dom ids
                    PBS.tvschedules.tvschedules.feed_ids_to_tr_ids = args['feed_ids_to_tr_ids'];
                    PBS.tvschedules.tvschedules.tr_ids_to_feed_ids = args['tr_ids_to_feed_ids'];

                    // Grab star dom ids
                    PBS.tvschedules.tvschedules.feed_ids_to_star_ids = args['feed_ids_to_star_ids'];
                    PBS.tvschedules.tvschedules.feed_ids_to_tvdata_name = args['feed_ids_to_tvdata_name'];
                    PBS.tvschedules.tvschedules.feed_ids_to_station_td_ids = args['feed_ids_to_station_td_ids'];
                    PBS.tvschedules.tvschedules.star_ids_to_feed_ids = args['star_ids_to_feed_ids'];

                    PBS.tvschedules.tvschedules.tvdata_name_to_station_td_ids = args['tvdata_name_to_station_td_ids'];
                    PBS.tvschedules.tvschedules.tvdata_name_to_feed_tr_ids = args['tvdata_name_to_feed_tr_ids'];
                    PBS.tvschedules.tvschedules.star_ids = args['star_ids'];
                    PBS.tvschedules.tvschedules.tvdata_names = args['tvdata_names'];
                    PBS.tvschedules.tvschedules.feed_ids = args['feed_ids'];

                    // Highlight indexes.
                    PBS.tvschedules.tvschedules.highlight_ids = args['highlight_ids'];
                    PBS.tvschedules.tvschedules.parents_ids = args['parents_ids'];
                    PBS.tvschedules.tvschedules.kids_ids = args['kids_ids'];

                    PBS.tvschedules.tvschedules.provider_name = args['provider_name'];
                    var weekday = args['weekday'];
                    var date = args['date'];
                    PBS.tvschedules.tvschedules.offset = args['offset'];
                    //alert(args['offset'])

                    PBS.tvschedules.tvschedules.localization_headend_id = args['localization_headend_id'];
                    PBS.tvschedules.tvschedules.schedule.navigation_transport = args['navigation_transport'];
                    //alert(PBS.tvschedules.tvschedules.localization_headend_id);
                    //alert(PBS.tvschedules.tvschedules.navigation_transport);

                    // Get station specific cookie value.
                    // In forced localization scenarios, we get the station
                    // specific cookie value (conveniently read by this
                    // module's server components during module init) and store
                    // it on the user utility.
                    if (PBS.tvschedules.tvschedules.config['localization']) {
                        var cookie_name = 'pbsol.' + PBS.tvschedules.tvschedules.config['localization'];
                        var cookie = args['station_specific_cookie']; // Comes back as an object.
                        PBS.tvschedules.user.cookies[cookie_name] = cookie;
                    }

                    // The module might be initialized from an external system
                    // (e.g. preferences when a new provider is selected.)
                    // Because of this, we should check for the pre-existence
                    // of the module dom node and remove it.
                    try {
                        var fab = document.getElementById('pbs_tvschedules_modules-tvschedules-module_fab');
                        this.script_dom_node.parentNode.removeChild(fab);
                    } catch(e) {
                    }

                    var div = document.createElement('div');
                    div.id = 'pbs_tvschedules_modules-tvschedules-module_fab';
                    div.style.display = 'none';
                    div.style.width = '';
                    div.innerHTML = shell_markup;

                    this.script_dom_node.parentNode.insertBefore(div, this.script_dom_node);

                    // Size the program cells when the DOM is ready.
                    YAHOO.util.Event.onDOMReady(PBS.tvschedules.tvschedules.sizeTDs, '', true);


                    /* Implementation customizations enacted here. */
                    //PBS.tvschedules.tvschedules.implementationCustomizations(); // Doing this client side is depracted.
                    /* End implementation customizations enacted here. */


                    // Resize the program cells on all subsequent window resize events.
                    // Don't step on pre-existing resize subscribers.
                    // Also, make it very difficult for others to step on our event listener.
                    PBS.tvschedules.addOnresize(PBS.tvschedules.tvschedules.sizeTDs);
                    //PBS.tvschedules.addResizeEvent(PBS.tvschedules.tvschedules.sizeTDs);

                    // Take care of all of the preference-related user interface initializations.
                    PBS.tvschedules.tvschedules.initPreferences();
                    PBS.tvschedules.tvschedules.schedule.manageLineupSelector();
                    PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                    // This was added to support showing the loading screen
                    // before triggering a module init, for example when a user
                    // changes primary station and we have to re-order the grid
                    // to show the new primary station first.
                    PBS.tvschedules.loading.hide();

                    // Register key bindings handlers for the search forms keywords fields.
                    var short_keywords_entry_id = 'pbs_tvschedules_modules-tvschedules_search_keywords_input';
                    var short_keywords_entry = document.getElementById(short_keywords_entry_id);
                    short_keywords_entry.onkeydown = PBS.tvschedules.tvschedules.submitSearchShort;

                    var full_keywords_entry_id = 'pbs_tvschedules_modules-tvschedules_full_search_keywords_input';
                    var full_keywords_entry = document.getElementById(full_keywords_entry_id);
                    full_keywords_entry.onkeydown = PBS.tvschedules.tvschedules.submitSearchFull;

                    // Show the grid! Booyah!!
                    div.style.display = 'block';

                    // Size the grid container.
                    PBS.tvschedules.tvschedules.sizeGridContainer();

                    // Grab the height of the grid container and store it.
                    // This value is useful later so we can always have the ability to restore the grid container to it's original size ( as specified in the CSS ).
                    // ( CSS can be customized by module implementors, so this is required. )
                    var grid_container_info = PBS.tvschedules.getElementInfo('pbs_tvschedules_modules-tvschedules-griddata_container');
                    PBS.tvschedules.tvschedules.grid_container_height = grid_container_info.height;

                    PBS.tvschedules.preferences.module_init_preferences.push(PBS.tvschedules.tvschedules.initPreferences);

                }

            };

            return obj;

        }(),



        // The master schedule management event chains.
        // Manages the getGrid event chain and primary callers.
        // Manages the getFullDay event chain and primary callers.
        schedule: function () {

            var obj = {

                zip: '',
                headend_id: '',
                // We need two transport values:
                // 1] The transport value from the cookie or the query string.
                // This represents the users preferences - based on thier
                // localization they are either over the air broadcast, or
                // cable/satellite capable.
                // This is a permenent and is not mutated by any user interface
                // methods.
                transport: 'b',
                // 2] A navigation transport value that represents the
                // currently displayed transport mode.
                navigation_transport: '',
                // g=grid.
                // f=full day.
                // w=full week.
                view: 'g',
                tvdata_names: '',
                start_time: new Date(),

                now: true,
                primetime: false,
                today: false,
                thisweek: false,

                full_day_matrix: {},
                full_week_matrix: {},

                // Set some flags so we can test whether non-favorite stations
                // or channels are currently hidden when "earlier" and "later"
                // temporal navigation is used.
                stations_hidden: false,
                channels_hidden: false,

                /*********************** utility **********************/
                getParams: function () {

                    var cookie_value = PBS.tvschedules.user.cookies['pbsol.sta_extended'];
                    var cookie_params = PBS.tvschedules.user.parseStaExtendedCookieValue(cookie_value);
                    var query_params = PBS.tvschedules.parseURL(document.location.toString());

                    // We're going to look for values in the cookie first, and
                    // on the query string as an override.
                    if (cookie_params['z']) {
                        this.zip = cookie_params['z'];
                    }
                    if (query_params['pbs_tvsm_sf_zip']) {
                        this.zip = query_params['pbs_tvsm_sf_zip'];
                    }

                    if (cookie_params['t']) {
                        this.transport = cookie_params['t'];
                    }
                    if (query_params['pbs_tvsm_sf_transport']) {
                        this.transport = query_params['pbs_tvsm_sf_transport'];
                    }

                    if (cookie_params['p']) {
                        this.headend_id = cookie_params['p'];
                    }
                    if (query_params['pbs_tvsm_sf_headend_id']) {
                        this.headend_id = query_params['pbs_tvsm_sf_headend_id'];
                    }

                    if (cookie_params['s']) {
                        this.tvdata_names = cookie_params['s'].join('|');
                    }
                    if (query_params['pbs_tvsm_sf_tvdata_names']) {
                        this.tvdata_names = query_params['pbs_tvsm_sf_tvdata_names'];
                    }

                },


                getStartTimeStr: function () {
                    var start_time = PBS.tvschedules.tvschedules.schedule.start_time;
                    var year = start_time.getUTCFullYear();
                    var month = start_time.getUTCMonth() + 1;
                    var month_str = PBS.tvschedules.padString(month + '', '0', 2);
                    var date = start_time.getUTCDate();
                    var date_str = PBS.tvschedules.padString(date + '', '0', 2);
                    var hours = start_time.getUTCHours();
                    var hours_str = PBS.tvschedules.padString(hours + '', '0', 2);
                    var start_time_str = year + '-' + month_str + '-' + date_str + ' ' + hours_str + ':00:00';
                    /*
                    alert(
                        'start_time_str: ' + start_time_str + '\n'
                    );
                    */
                    return start_time_str;
                },


                // Manage the Lineup selector user interface component.
                // The manageLineupSelector method can potentially be
                // called from multiple contexts. The 'this' JavaScript reserved
                // word will not always reference the same context. So we make
                // some handy references to the module and to the schedule.
                manageLineupSelector: function () {

                    var module = PBS.tvschedules.tvschedules;
                    var schedule = PBS.tvschedules.tvschedules.schedule;

                    // Get the dom nodes for the Lineup display.
                    var cable_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_select_cable');
                    var over_the_air_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_select_over_the_air');
                    var cable_only = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_select_cable_only');
                    var over_the_air_only = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_select_over_the_air_only');

                    /*
                    alert(
                        'schedule.headend_id: ' + schedule.headend_id + '\n' +
                        'module.localization_headend_id: ' + module.localization_headend_id + '\n' +
                        'schedule.navigation_transport: ' + schedule.navigation_transport + '\n' +
                        'schedule.view: ' + schedule.view + '\n'
                    );
                    */
                    if (
                        (!module.config['localization'] && schedule.headend_id) || 
                        (module.config['localization'] && module.localization_headend_id)
                    ) {
                        if (schedule.navigation_transport == 'c') {
                            if (schedule.view == 'g') {
                                cable_selected.style.display = '';
                                over_the_air_selected.style.display = 'none';
                                cable_only.style.display = 'none';
                                over_the_air_only.style.display = 'none';
                            }
                            if (schedule.view == 'f') {
                                cable_selected.style.display = 'none';
                                over_the_air_selected.style.display = 'none';
                                cable_only.style.display = '';
                                over_the_air_only.style.display = 'none';
                            }
                        } else if (schedule.navigation_transport == 'b') {
                            if (schedule.view == 'g') {
                                cable_selected.style.display = 'none';
                                over_the_air_selected.style.display = '';
                                cable_only.style.display = 'none';
                                over_the_air_only.style.display = 'none';
                            }
                            if (schedule.view == 'f') {
                                cable_selected.style.display = 'none';
                                over_the_air_selected.style.display = 'none';
                                cable_only.style.display = 'none';
                                over_the_air_only.style.display = '';
                            }
                        }
                    } else {
                        cable_selected.style.display = 'none';
                        over_the_air_selected.style.display = 'none';
                        cable_only.style.display = 'none';
                        over_the_air_only.style.display = '';
                    }

                },


                manageProviderName: function () {
                    var provider_name_node = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_name');
                    provider_name_node.innerHTML = PBS.tvschedules.tvschedules.provider_name;
                    //provider_name_node.innerHTML = PBS.tvschedules.tvschedules.provider_name + ' ' + PBS.tvschedules.tvschedules.schedule.headend_id;
                    //var provider_note_node = document.getElementById('pbs_tvschedules_modules-tvschedules-provider_note');
                    //if (PBS.tvschedules.tvschedules.schedule.navigation_transport == 'b') {
                        //provider_note_node.innerHTML = 'all PBS channels broadcast locally over-the-air may or may not reach your location';
                    //}
                    //if (PBS.tvschedules.tvschedules.schedule.navigation_transport == 'c') {
                        //provider_note_node.innerHTML = 'the following PBS channels are carried by your provider';
                        //provider_note_node.innerHTML = 'the following PBS channels are carried by your provider' + ' ' + PBS.tvschedules.tvschedules.schedule.headend_id;
                    //}
                },


                // Manage the now / primetime selector user interface component.
                manageNowPrimetimeSelector: function () {

                    var self = PBS.tvschedules.tvschedules.schedule;

                    var now_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_when_now');
                    var primetime_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_when_primetime');
                    var neither_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_when_neither');
                    var both_selected = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_when_both');
                    
                    // Here we make now and primetime links 'self aware'. In
                    // other words, if you happen to navigate through any means
                    // to now, the now link will be disabled. Similarly if you
                    // navigate to primetime via any method, primetime gets
                    // disabled.
                    var now = new Date();
                    var year = now.getUTCFullYear();
                    var month = now.getUTCMonth();
                    var date = now.getUTCDate();
                    var hours = now.getUTCHours();
                    var minutes = now.getUTCMinutes();
                    var seconds = now.getUTCSeconds();

                    var display_year = self.start_time.getUTCFullYear();
                    var display_month = self.start_time.getUTCMonth();
                    var display_date = self.start_time.getUTCDate();
                    var display_hours = self.start_time.getUTCHours();
                    var display_minutes = self.start_time.getUTCMinutes();
                    var display_seconds = self.start_time.getUTCSeconds();
                    
                    self.now = false;
                    self.primetime = false;

                    // Now can really only occur on this exact day so we match
                    // year, month, date and hours.
                    if (
                        year == display_year &&
                        month == display_month &&
                        date == display_date &&
                        hours == display_hours
                    ) {
                        if (minutes < 60) {
                            self.now = true;
                        }
                    }

                    // Primetime can happen any day.
                    var primetime_comparison = new Date();
                    var compare_year = display_year;
                    var compare_month = display_month;
                    var compare_date = display_date;
                    var compare_hours = display_hours;
                    var compare_minutes = display_minutes;
                    var compare_seconds = display_seconds;
                    
                    primetime_comparison.setUTCHours(19);
                    var minutes = primetime_comparison.getUTCMinutes();
                    primetime_comparison.setUTCMinutes(minutes - PBS.tvschedules.tvschedules.offset);
                    var primetime_hour = primetime_comparison.getUTCHours();
                    if (display_hours == primetime_hour) {
                        self.primetime = true;
                    }

                    if (self.now == true && self.primetime == false) {
                        now_selected.style.display = '';
                        primetime_selected.style.display = 'none';
                        neither_selected.style.display = 'none';
                        both_selected.style.display = 'none';
                    }

                    if (self.now == false && self.primetime == true) {
                        now_selected.style.display = 'none';
                        primetime_selected.style.display = '';
                        neither_selected.style.display = 'none';
                        both_selected.style.display = 'none';
                    }

                    if (self.now == false && self.primetime == false) {
                        now_selected.style.display = 'none';
                        primetime_selected.style.display = 'none';
                        neither_selected.style.display = '';
                        both_selected.style.display = 'none';
                    }

                    if (self.now == true && self.primetime == true) {
                        now_selected.style.display = 'none';
                        primetime_selected.style.display = 'none';
                        neither_selected.style.display = 'none';
                        both_selected.style.display = '';
                    }

                },


                manageThisWeek: function () {

                    var self = PBS.tvschedules.tvschedules.schedule;

                    // Get the dom nodes for the today display.
                    var thisweek = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_fullweek_thisweek');
                    var not_thisweek = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_fullweek_not_thisweek');

                    var today_date = new Date();

                    var year = today_date.getFullYear();
                    var month = today_date.getMonth();
                    var date = today_date.getDate();

                    var display_year = self.start_time.getFullYear();
                    var display_month = self.start_time.getMonth();
                    var display_date = self.start_time.getDate();

                    // TODO - implement week processing.

                    if (
                        display_year == year &&
                        display_month == month &&
                        display_date == date
                    ) {
                        self.thisweek = true;
                        thisweek.style.display = '';
                        not_thisweek.style.display = 'none';
                    } else {
                        self.thisweek = false;
                        thisweek.style.display = 'none';
                        not_thisweek.style.display = '';
                    }

                },


                manageToday: function () {

                    var self = PBS.tvschedules.tvschedules.schedule;

                    // Get the dom nodes for the today display.
                    var today = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_fullday_today');
                    var not_today = document.getElementById('pbs_tvschedules_modules-tvschedules-gridheader_fullday_not_today');

                    var today_date = new Date();

                    var year = today_date.getFullYear();
                    var month = today_date.getMonth();
                    var date = today_date.getDate();

                    var display_year = self.start_time.getFullYear();
                    var display_month = self.start_time.getMonth();
                    var display_date = self.start_time.getDate();

                    if (
                        display_year == year &&
                        display_month == month &&
                        display_date == date
                    ) {
                        self.today = true;
                        today.style.display = '';
                        not_today.style.display = 'none';
                    } else {
                        self.today = false;
                        today.style.display = 'none';
                        not_today.style.display = '';
                    }

                },


                manageJumpTo: function () {

                    var self = PBS.tvschedules.tvschedules.schedule;
                    var jump_to_time_input = document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_form_jumptotime');

                    if (self.view == 'g') {
                        jump_to_time_input.style.display = 'inline';
                    }
                    if (self.view == 'f') {
                        jump_to_time_input.style.display = 'none';
                    }
                    if (self.view == 'w') {
                        jump_to_time_input.style.display = 'none';
                    }

                },


                // Program highlighting - kids, parents, and favorites.
                // Grid and full day views.
                highlight: function () {
                    var form = document.forms['pbs_tvschedules_modules-optionsheader_form'];
                    var highlight = form.highlight.value;
                    var swatch_id = 'pbs_tvschedules_modules-optionsheader_highlight';
                    var swatch = document.getElementById(swatch_id);

                    if (PBS.tvschedules.tvschedules.schedule.view == 'g') {
                        var highlight_ids = PBS.tvschedules.tvschedules.highlight_ids;
                        var parents_ids = PBS.tvschedules.tvschedules.parents_ids;
                        var kids_ids = PBS.tvschedules.tvschedules.kids_ids;
                    }
                    if (PBS.tvschedules.tvschedules.schedule.view == 'f') {
                        var matrix = PBS.tvschedules.tvschedules.schedule.full_day_matrix;
                        var highlight_ids = matrix['highlight_ids'];
                        var parents_ids = matrix['parents_ids'];
                        var kids_ids = matrix['kids_ids'];
                    }
                    if (PBS.tvschedules.tvschedules.schedule.view == 'w') {
                        var matrix = PBS.tvschedules.tvschedules.schedule.full_week_matrix;
                        var highlight_ids = matrix['highlight_ids'];
                        var parents_ids = matrix['parents_ids'];
                        var kids_ids = matrix['kids_ids'];
                    }

                    // Clear any previous highlight.
                    for (var i in highlight_ids) {
                        var dom_id = highlight_ids[i]['dom_id'];
                        YAHOO.util.Dom.removeClass(
                            dom_id,
                            'pbs_tvschedules_modules-tvschedules-griddata_programblock_parents'
                        );
                        YAHOO.util.Dom.removeClass(
                            dom_id,
                            'pbs_tvschedules_modules-tvschedules-griddata_programblock_kids'
                        );
                        YAHOO.util.Dom.removeClass(
                            dom_id,
                            'pbs_tvschedules_modules-tvschedules-griddata_programblock_favorites'
                        );
                    }

                    swatch.className = 'pbs_tvschedules_modules-optionsheader_highlight_key';

                    // Determine what should be highlighted and do it.
                    if (highlight == 'none') {
                        // We've already cleared all highlights, so this is a no-op.
                    } else if (highlight == 'parents') {
                        for (var i in parents_ids) {
                            var id = parents_ids[i];
                            YAHOO.util.Dom.addClass(
                                id,
                                'pbs_tvschedules_modules-tvschedules-griddata_programblock_parents'
                            );
                        }
                        swatch.className = 'pbs_tvschedules_modules-optionsheader_highlight_key_parents';
                    } else if (highlight == 'kids') {
                        for (var i in kids_ids) {
                            var id = kids_ids[i];
                            YAHOO.util.Dom.addClass(
                                id,
                                'pbs_tvschedules_modules-tvschedules-griddata_programblock_kids'
                            );
                        }
                        swatch.className = 'pbs_tvschedules_modules-optionsheader_highlight_key_kids';
                    } else if (highlight == 'favorites') {
                        // Get favorite feeds from user utility, and build a corresponding list of td_ids.
                        var favorites_ids = [];

                        for (var i in highlight_ids) {
                            var dom_id = highlight_ids[i]['dom_id'];
                            var favorite_id = highlight_ids[i]['favorite_id'];
                            var favorite_type = highlight_ids[i]['favorite_type'];
                            if (PBS.tvschedules.tvschedules.config['localization']) {
                                var is_favorite = PBS.tvschedules.user.isStationFavoriteProgram(
                                    PBS.tvschedules.tvschedules.config['localization'],
                                    favorite_id,
                                    favorite_type
                                )
                            } else {
                                var is_favorite = PBS.tvschedules.user.isFavoriteProgram(
                                    favorite_id,
                                    favorite_type
                                )
                            }
                            if (is_favorite) {
                                favorites_ids.push(dom_id);
                            }
                        }

                        for (var i in favorites_ids) {
                            var id = favorites_ids[i];
                            YAHOO.util.Dom.addClass(
                                id,
                                'pbs_tvschedules_modules-tvschedules-griddata_programblock_favorites'
                            );
                        }
                        swatch.className = 'pbs_tvschedules_modules-optionsheader_highlight_key_favorite';
                    }

                },


                manageHighlightMenu: function () {
                    var highlight_menu = document.getElementById('pbs_tvschedules_modules-optionsheader_highlight_favorites');
                    highlight_menu.style.display = '';
                },


                /******** callers that branch to getGrid, getFullDay, or getFullWeek ********/
                jumpTo: function () {
                    
                    var jump_to_form_id = 'pbs_tvschedules_modules-tvschedules-dateheader_form';
                    var jump_to_form = document.getElementById(jump_to_form_id);
                    var date_json = jump_to_form.jumptodate.value;
                    var date = JSON.parse(date_json);

                    var start_time = new Date(
                        date['year'],
                        0,
                        1
                    );

                    if (this.view == 'g') {

                        var time_json = jump_to_form.jumptotime.value;
                        var time = JSON.parse(time_json);

                        //start_time.setFullYear(date['year']);
                        // Correct for index base mismatch
                        //start_time.setMonth(date['month'] - 1);
                        //start_time.setDate(date['day']);
                        //start_time.setHours(time['hour']);
                        //start_time.setMinutes(time['minute']);
                        //start_time.setSeconds(time['second']);

                        start_time.setUTCFullYear(date['year']);
                        // Correct for index base mismatch
                        start_time.setUTCMonth(date['month'] - 1);
                        start_time.setUTCDate(date['day']);
                        start_time.setUTCHours(time['hour']);
                        //start_time.setUTCMinutes(time['minute']);
                        start_time.setUTCMinutes(time['minute'] - PBS.tvschedules.tvschedules.offset);
                        start_time.setUTCSeconds(time['second']);

                        /*
                        alert(
                            'date_json: ' + date_json + '\n' + 
                            'date["year"]: ' + date["year"] + '\n' + 
                            'date["month"]: ' + date["month"] + '\n' + 
                            'date["day"]: ' + date["day"] + '\n' + 
                            'time["hour"]: ' + time["hour"] + '\n' + 
                            'time["minute"]: ' + time["minute"] + '\n' + 
                            'time["second"]: ' + time["second"] + '\n' + 
                            'time["minute"] - PBS.tvschedules.tvschedules.offset: ' + (time["minute"] - PBS.tvschedules.tvschedules.offset) + '\n' + 
                            'start_time: ' + start_time + '\n' + 
                            'offset: ' + PBS.tvschedules.tvschedules.offset + '\n'
                        )
                        */

                        this.gridJumpTo(start_time);

                    }

                    if (this.view == 'f') {

                        start_time.setUTCFullYear(date['year']);
                        // Correct for index base mismatch
                        start_time.setUTCMonth(date['month'] - 1);
                        start_time.setUTCDate(date['day']);
                        start_time.setUTCHours(0);
                        start_time.setUTCMinutes(0 - PBS.tvschedules.tvschedules.offset);
                        start_time.setUTCSeconds(0);

                        this.fullDayJumpTo(start_time);

                    }

                    if (this.view == 'w') {

                        start_time.setUTCFullYear(date['year']);
                        // Correct for index base mismatch
                        start_time.setUTCMonth(date['month'] - 1);
                        start_time.setUTCDate(date['day']);
                        start_time.setUTCHours(0);
                        start_time.setUTCMinutes(0 - PBS.tvschedules.tvschedules.offset);
                        start_time.setUTCSeconds(0);

                        this.fullWeekJumpTo(start_time);

                    }

                },


                /*********************** getGrid callers **********************/
                // This method simply delegates to getOverTheAirGrid or
                // getProviderGrid based on the current schedule.view.
                getGridView: function () {
                    //this.start_time = new Date();
                    if (this.navigation_transport == 'b') {
                        this.getOverTheAirGrid();
                    }
                    if (this.navigation_transport == 'c') {
                        this.getProviderGrid();
                    }
                },


                // Get the over-the-air grid view.
                getOverTheAirGrid: function () {
                    var navigation_transport = 'b';
                    var view = 'g';
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        PBS.tvschedules.tvschedules.schedule.manageLineupSelector();
                        PBS.tvschedules.tvschedules.schedule.manageJumpTo();
                        PBS.tvschedules.tvschedules.setSearchLineup('ota');
                    }
                    this.getGrid.go(navigation_transport, view, this.start_time, finalize);
                },


                // Get the provider specific grid view.
                getProviderGrid: function () {
                    var navigation_transport = 'c';
                    var view = 'g';
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        PBS.tvschedules.tvschedules.schedule.manageLineupSelector();
                        PBS.tvschedules.tvschedules.schedule.manageJumpTo();
                        PBS.tvschedules.tvschedules.setSearchLineup('provider');
                    }
                    this.getGrid.go(navigation_transport, view, this.start_time, finalize);
                },


                // Refresh the grid view to two hours ago.
                getEarlierGrid: function () {
                    var view = 'g';
                    var start_time = this.start_time;
                    start_time.setUTCHours(start_time.getUTCHours() - 2);
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                            PBS.tvschedules.tvschedules.hideChannels();
                        }
                        if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                            PBS.tvschedules.tvschedules.hideStations();
                        }
                    }
                    this.getGrid.go(this.navigation_transport, view, start_time, finalize);
                },


                // Refresh the grid view to two hours from now.
                getLaterGrid: function () {
                    var view = 'g';
                    var start_time = this.start_time;
                    start_time.setUTCHours(start_time.getUTCHours() + 2);
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                            PBS.tvschedules.tvschedules.hideChannels();
                        }
                        if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                            PBS.tvschedules.tvschedules.hideStations();
                        }
                    }
                    this.getGrid.go(this.navigation_transport, view, start_time, finalize);
                },


                // Refresh the grid view to two hours from now.
                getNowGrid: function () {
                    var view = 'g';
                    var start_time = new Date();
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                            PBS.tvschedules.tvschedules.hideChannels();
                        }
                        if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                            PBS.tvschedules.tvschedules.hideStations();
                        }
                    }
                    this.getGrid.go(this.navigation_transport, view, start_time, finalize);
                },


                // Refresh the grid view to two hours from now.
                getPrimetimeGrid: function () {
                    var view = 'g';
                    var start_time = this.start_time;
                    start_time.setUTCHours(19);
                    var minutes = start_time.getUTCMinutes();
                    start_time.setUTCMinutes(minutes - PBS.tvschedules.tvschedules.offset);
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                            PBS.tvschedules.tvschedules.hideChannels();
                        }
                        if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                            PBS.tvschedules.tvschedules.hideStations();
                        }
                    }
                    this.getGrid.go(this.navigation_transport, view, start_time, finalize);
                },


                gridJumpTo: function (start_time) {
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageNowPrimetimeSelector();
                        if (PBS.tvschedules.tvschedules.schedule.channels_hidden) {
                            PBS.tvschedules.tvschedules.hideChannels();
                        }
                        if (PBS.tvschedules.tvschedules.schedule.stations_hidden) {
                            PBS.tvschedules.tvschedules.hideStations();
                        }
                    }
                    this.getGrid.go(this.navigation_transport, this.view, start_time, finalize);
                },


                getGrid: function () {

                    var obj = {
                        finalize: null,

                        go: function (navigation_transport, view, start_time, finalize) {
                            PBS.tvschedules.tvschedules.schedule.getParams();
                            if (navigation_transport) {
                                PBS.tvschedules.tvschedules.schedule.navigation_transport = navigation_transport
                                //if (navigation_transport == 'b') {
                                    //PBS.tvschedules.tvschedules.schedule.headend_id = '';
                                //}
                            }
                            if (view) {
                                PBS.tvschedules.tvschedules.schedule.view = view;
                            }
                            if (start_time) {
                                //alert(start_time);
                                PBS.tvschedules.tvschedules.schedule.start_time = start_time;
                            }
                            this.finalize = finalize;
                            PBS.tvschedules.loading.show();
                            this.getView();
                        },

                        getView: function () {
                            // Get the start time in an appropriate string format.
                            var start_time_str = PBS.tvschedules.tvschedules.schedule.getStartTimeStr();
                            var URL = PBS.tvschedules.config.base + '/py-publisher/modules/dtv/tvschedules/' + PBS.tvschedules.config.version + '/views/grid';
                            var callback = 'PBS.tvschedules.tvschedules.schedule.getGrid.viewCallback';
                            /*
                            alert(
                                'getGrid()' + '\n' +
                                'pbs_tvsm_sf_zip: ' + PBS.tvschedules.tvschedules.schedule.zip + '\n' +
                                'pbs_tvsm_sf_headend_id: ' + PBS.tvschedules.tvschedules.schedule.headend_id + '\n' +
                                'pbs_tvsm_sf_transport: ' + PBS.tvschedules.tvschedules.schedule.transport + '\n' +
                                'pbs_tvsm_sf_nav_transport: ' + PBS.tvschedules.tvschedules.schedule.navigation_transport + '\n' +
                                'pbs_tvsm_sf_tvdata_names: ' + PBS.tvschedules.tvschedules.schedule.tvdata_names + '\n' +
                                'pbs_tvsm_tv_start_time: ' + start_time_str + '\n' +
                                'pbs_tvsm_localization_tvdata_name: ' + PBS.tvschedules.tvschedules.config['localization'] + '\n' +
                                'pbs_tvsm_localization_headend_id: ' + PBS.tvschedules.tvschedules.localization_headend_id + '\n' +
                                'pbs_tvsm_cookie_settable: ' + PBS.tvschedules.user.cookie_settable + '\n' +
                                'pbs_tvsm_disable_favorites: ' + PBS.tvschedules.tvschedules.disable_favorites + '\n' +
                                'pbs_tvsm_disable_shop: ' + PBS.tvschedules.tvschedules.disable_shop + '\n'
                            );
                            */
                            PBS.tvschedules.data.get(
                                URL,
                                callback,
                                {
                                    'pbs_tvsm_sf_zip': PBS.tvschedules.tvschedules.schedule.zip,
                                    'pbs_tvsm_sf_headend_id': PBS.tvschedules.tvschedules.schedule.headend_id,
                                    'pbs_tvsm_sf_transport': PBS.tvschedules.tvschedules.schedule.transport,
                                    'pbs_tvsm_sf_nav_transport': PBS.tvschedules.tvschedules.schedule.navigation_transport,
                                    'pbs_tvsm_sf_tvdata_names': PBS.tvschedules.tvschedules.schedule.tvdata_names,
                                    'pbs_tvsm_tv_start_time': start_time_str,
                                    'pbs_tvsm_localization_tvdata_name': PBS.tvschedules.tvschedules.config['localization'],
                                    'pbs_tvsm_localization_headend_id': PBS.tvschedules.tvschedules.localization_headend_id,
                                    'pbs_tvsm_cookie_settable': PBS.tvschedules.user.cookie_settable,
                                    'pbs_tvsm_disable_favorites': PBS.tvschedules.tvschedules.disable_favorites,
                                    'pbs_tvsm_disable_shop': PBS.tvschedules.tvschedules.disable_shop
                                }
                            );
                        },

                        viewCallback: function (args) {

                            // Grab the grid markup.
                            var grid_markup = args['grid_markup'];

                            // Update all of the module level user interface manipulation lookup indexes.
                            PBS.tvschedules.tvschedules.stationid_station_dom_ids = args['stationid_station_dom_ids'];

                            // Grab feed tr dom ids
                            PBS.tvschedules.tvschedules.feed_ids_to_tr_ids = args['feed_ids_to_tr_ids'];
                            PBS.tvschedules.tvschedules.tr_ids_to_feed_ids = args['tr_ids_to_feed_ids'];

                            // Grab star dom ids
                            PBS.tvschedules.tvschedules.feed_ids_to_star_ids = args['feed_ids_to_star_ids'];
                            PBS.tvschedules.tvschedules.feed_ids_to_tvdata_name = args['feed_ids_to_tvdata_name'];
                            PBS.tvschedules.tvschedules.feed_ids_to_station_td_ids = args['feed_ids_to_station_td_ids'];
                            PBS.tvschedules.tvschedules.star_ids_to_feed_ids = args['star_ids_to_feed_ids'];

                            PBS.tvschedules.tvschedules.tvdata_name_to_station_td_ids = args['tvdata_name_to_station_td_ids'];
                            PBS.tvschedules.tvschedules.tvdata_name_to_feed_tr_ids = args['tvdata_name_to_feed_tr_ids'];
                            PBS.tvschedules.tvschedules.star_ids = args['star_ids'];
                            PBS.tvschedules.tvschedules.tvdata_names = args['tvdata_names'];
                            PBS.tvschedules.tvschedules.feed_ids = args['feed_ids'];

                            // Highlight indexes.
                            PBS.tvschedules.tvschedules.highlight_ids = args['highlight_ids'];
                            PBS.tvschedules.tvschedules.parents_ids = args['parents_ids'];
                            PBS.tvschedules.tvschedules.kids_ids = args['kids_ids'];

                            PBS.tvschedules.tvschedules.provider_name = args['provider_name'];
                            var weekday = args['weekday'];
                            var date = args['date'];
                            PBS.tvschedules.tvschedules.offset = args['offset'];
                            //alert(args['offset'])

                            // Put the markup in play.
                            document.getElementById('pbs_tvschedules_modules-tvschedules-schedule_container').innerHTML = grid_markup;

                            // Update the user interface components that are
                            // external to the grid, but that are dependent on
                            // the grid state.
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selectedday').innerHTML = weekday;
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selecteddate').innerHTML = date;

                            // We've updated the grid markup, so let's size the
                            // TDs and re-initialize user interface preference
                            // state.
                            PBS.tvschedules.tvschedules.sizeTDs();
                            //PBS.tvschedules.tvschedules.implementationCustomizations();
                            PBS.tvschedules.tvschedules.initPreferences();
                            if (this.finalize) {
                                this.finalize();
                            }
                            PBS.tvschedules.tvschedules.manageTabs('now');
                            PBS.tvschedules.tvschedules.sizeGridContainer();
                            PBS.tvschedules.loading.hide();
                        }

                    };

                    return obj;

                }(),



                /****************************** FULL DAY ******************************/

                // Show the full day schedule view.
                showFullDay: function (feed_id) {

                    var view = 'f'; 
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageLineupSelector();
                        PBS.tvschedules.tvschedules.schedule.manageToday();
                        PBS.tvschedules.tvschedules.schedule.manageJumpTo();
                    }
                    this.getFullDay.go(
                        feed_id,
                        this.navigation_transport,
                        view,
                        this.start_time,
                        finalize
                    );

                },


                // Show today in the full day schedule view.
                showToday: function (feed_id) {

                    var view = 'f'; 
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageToday;
                    var start_time = new Date();
                    this.getFullDay.go(
                        PBS.tvschedules.tvschedules.schedule.getFullDay.feed_id,
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                showNextDay: function () {

                    var view = 'f'; 
                    var start_time = this.start_time;
                    start_time.setDate(start_time.getDate() + 1);
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageToday;
                    this.getFullDay.go(
                        PBS.tvschedules.tvschedules.schedule.getFullDay.feed_id,
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                showPreviousDay: function () {

                    var view = 'f'; 
                    var start_time = this.start_time;
                    start_time.setDate(start_time.getDate() - 1);
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageToday;
                    this.getFullDay.go(
                        PBS.tvschedules.tvschedules.schedule.getFullDay.feed_id,
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                fullDayJumpTo: function (start_time) {

                    var finalize = PBS.tvschedules.tvschedules.schedule.manageToday;
                    this.getFullDay.go(
                        PBS.tvschedules.tvschedules.schedule.getFullDay.feed_id,
                        this.navigation_transport,
                        this.view,
                        start_time,
                        finalize
                    );
                    
                },


                fullWeekJumpTo: function (start_time) {

                    var finalize = PBS.tvschedules.tvschedules.schedule.manageThisWeek;
                    this.getFullWeek.go(
                        this.navigation_transport,
                        this.view,
                        start_time,
                        finalize
                    );
                    
                },


                changeChannel: function () {
                    var self = PBS.tvschedules.tvschedules.schedule;
                    var form = document.getElementById('pbs_tvschedules_module-fullday_feedselectmenu_form');
                    var feed_id = form.fullday_feedselect.value;
                    PBS.tvschedules.tvschedules.active_feed = feed_id;

                    if (self.view == 'f') {
                        var finalize = function () {
                            PBS.tvschedules.tvschedules.schedule.manageToday();
                        }
                        this.getFullDay.go(
                            feed_id,
                            this.navigation_transport,
                            this.view,
                            this.start_time,
                            finalize
                        );
                    }
                    if (self.view == 'w') {
                        var finalize = function () {
                            PBS.tvschedules.tvschedules.schedule.manageThisWeek();
                        }
                        this.getFullWeek.go(
                            this.navigation_transport,
                            this.view,
                            this.start_time,
                            finalize
                        );
                    }

                },


                getFullDay: function () {

                    var obj = {

                        feed_id: null,
                        finalize: null, 

                        go: function (feed_id, navigation_transport, view, start_time, finalize) {

                            this.feed_id = feed_id;
                            PBS.tvschedules.tvschedules.active_feed = feed_id;

                            // Refresh parameters.
                            PBS.tvschedules.tvschedules.schedule.getParams();

                            // Apply parameter overrides.
                            if (navigation_transport) {
                                PBS.tvschedules.tvschedules.schedule.navigation_transport = navigation_transport
                                // And specifically, if we are dealing with
                                // over-the-air broadcast, we need to wipe any
                                // previous headend_id parameter value.
                                if (navigation_transport == 'b') {
                                    PBS.tvschedules.tvschedules.schedule.headend_id = '';
                                    //PBS.tvschedules.tvschedules.schedule.headend_id = undefined;
                                    //PBS.tvschedules.tvschedules.schedule.headend_id = null;
                                }
                            }

                            if (view) {
                                PBS.tvschedules.tvschedules.schedule.view = view;
                            }

                            if (start_time) {
                                PBS.tvschedules.tvschedules.schedule.start_time = start_time;
                            }

                            this.finalize = finalize;
                            PBS.tvschedules.loading.show();

                            this.getView();

                        },

                        getView: function () {

                            // Get the start time in an appropriate string format.
                            var start_time_str = PBS.tvschedules.tvschedules.schedule.getStartTimeStr();

                            var URL = PBS.tvschedules.config.base + '/py-publisher/modules/dtv/tvschedules/' + PBS.tvschedules.config.version + '/views/full_day';
                            var callback = 'PBS.tvschedules.tvschedules.schedule.getFullDay.viewCallback';

                            /*
                            alert(
                                'getFullDay()' + '\n' + 
                                'pbs_tvsm_sf_zip: ' + PBS.tvschedules.tvschedules.schedule.zip + '\n' +
                                'pbs_tvsm_sf_headend_id: ' + PBS.tvschedules.tvschedules.schedule.headend_id + '\n' +
                                'pbs_tvsm_sf_transport: ' + PBS.tvschedules.tvschedules.schedule.transport + '\n' +
                                'pbs_tvsm_sf_nav_transport: ' + PBS.tvschedules.tvschedules.schedule.navigation_transport + '\n' +
                                'pbs_tvsm_sf_tvdata_names: ' + PBS.tvschedules.tvschedules.schedule.tvdata_names + '\n' +
                                'pbs_tvsm_tv_start_time: ' + start_time_str + '\n' +
                                'pbs_tvsm_tv_feed_id: ' + this.feed_id + '\n' +
                                'pbs_tvsm_localization_tvdata_name: ' + PBS.tvschedules.tvschedules.config['localization'] + '\n' +
                                'pbs_tvsm_localization_headend_id: ' + PBS.tvschedules.tvschedules.localization_headend_id + '\n' +
                                'pbs_tvsm_cookie_settable: ' + PBS.tvschedules.user.cookie_settable + '\n' +
                                'pbs_tvsm_disable_favorites: ' + PBS.tvschedules.tvschedules.disable_favorites + '\n' +
                                'pbs_tvsm_disable_shop: ' + PBS.tvschedules.tvschedules.disable_shop + '\n'
                            );
                            */
                            PBS.tvschedules.data.get(
                                URL,
                                callback,
                                {
                                    'pbs_tvsm_sf_zip': PBS.tvschedules.tvschedules.schedule.zip,
                                    'pbs_tvsm_sf_headend_id': PBS.tvschedules.tvschedules.schedule.headend_id,
                                    'pbs_tvsm_sf_transport': PBS.tvschedules.tvschedules.schedule.transport,
                                    'pbs_tvsm_sf_nav_transport': PBS.tvschedules.tvschedules.schedule.navigation_transport,
                                    'pbs_tvsm_sf_tvdata_names': PBS.tvschedules.tvschedules.schedule.tvdata_names,
                                    'pbs_tvsm_tv_start_time': start_time_str,
                                    'pbs_tvsm_tv_feed_id': this.feed_id,
                                    'pbs_tvsm_localization_tvdata_name': PBS.tvschedules.tvschedules.config['localization'],
                                    'pbs_tvsm_localization_headend_id': PBS.tvschedules.tvschedules.localization_headend_id,
                                    'pbs_tvsm_cookie_settable': PBS.tvschedules.user.cookie_settable,
                                    'pbs_tvsm_disable_favorites': PBS.tvschedules.tvschedules.disable_favorites,
                                    'pbs_tvsm_disable_shop': PBS.tvschedules.tvschedules.disable_shop
                                }
                            );
                        },
                        
                        viewCallback: function (args) {

                            var markup = args['markup'];
                            var matrix = args['matrix'];
                            PBS.tvschedules.tvschedules.schedule.full_day_matrix = matrix;

                            // TODO Get full day highlight indexes here.

                            // Put the markup in play.
                            document.getElementById('pbs_tvschedules_modules-tvschedules-schedule_container').innerHTML = markup;

                            // TODO The following few lines are duplicated in
                            // getGrid.viewCallback... should we factor out for
                            //just these two cases? Maybe manageShell() ?

                            // Update the user interface components that are
                            // external to the grid, but that are dependent on
                            // the grid state.
                            var new_weekday = PBS.tvschedules.days_of_week[PBS.tvschedules.tvschedules.schedule.start_time.getDay()];
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selectedday').innerHTML = new_weekday;

                            var new_date = PBS.tvschedules.months_of_year[PBS.tvschedules.tvschedules.schedule.start_time.getMonth()] + ' ' + PBS.tvschedules.tvschedules.schedule.start_time.getDate();
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selecteddate').innerHTML = new_date;

                            // We've updated the grid markup, so let's size the
                            // TDs and re-initialize user interface preference
                            // state.
                            PBS.tvschedules.tvschedules.sizeTDs();
                            //PBS.tvschedules.tvschedules.implementationCustomizations();
                            PBS.tvschedules.tvschedules.initPreferences();

                            // And finally, we finalize. ;)
                            if (this.finalize) {
                                this.finalize();
                            }
                            PBS.tvschedules.tvschedules.manageTabs('day');
                            PBS.tvschedules.tvschedules.sizeGridContainer();
                            PBS.tvschedules.loading.hide();
                        }

                    };

                    return obj;

                }(),

                /****************************** FULL WEEK ******************************/

                // Show the full day schedule view.
                showFullWeek: function () {

                    var view = 'w'; 
                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageLineupSelector();
                        PBS.tvschedules.tvschedules.schedule.manageThisWeek();
                        PBS.tvschedules.tvschedules.schedule.manageJumpTo();
                    }
                    this.getFullWeek.go(
                        this.navigation_transport,
                        view,
                        this.start_time,
                        finalize
                    );

                },


                // Show today in the full day schedule view.
                showThisWeek: function () {

                    var view = 'w'; 
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageThisWeek;
                    var start_time = new Date();
                    this.getFullWeek.go(
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                showNextWeek: function () {

                    var view = 'w'; 
                    var start_time = this.start_time;
                    start_time.setDate(start_time.getDate() + 7);
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageThisWeek;
                    this.getFullWeek.go(
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                showPreviousWeek: function () {

                    var view = 'w'; 
                    var start_time = this.start_time;
                    start_time.setDate(start_time.getDate() - 7);
                    var finalize = PBS.tvschedules.tvschedules.schedule.manageThisWeek;
                    this.getFullWeek.go(
                        this.navigation_transport,
                        view,
                        start_time,
                        finalize
                    );

                },


                fullWeekJumpTo: function (start_time) {

                    var finalize = PBS.tvschedules.tvschedules.schedule.manageThisWeek;
                    this.getFullWeek.go(
                        this.navigation_transport,
                        this.view,
                        start_time,
                        finalize
                    );
                    
                },


                // TODO Refactor this and full day changeChannel into single view-aware utility function.
                changeWeekChannel: function () {
                    var form = document.getElementById('pbs_tvschedules_module-fullweek_feedselectmenu_form');
                    var feed_id = form.fullweek_feedselect.value;

                    var finalize = function () {
                        PBS.tvschedules.tvschedules.schedule.manageThisWeek();
                    }

                    this.getFullWeek.go(
                        this.navigation_transport,
                        this.view,
                        this.start_time,
                        finalize
                    );

                },


                getFullWeek: function () {

                    var obj = {

                        feed_id: null,
                        finalize: null, 

                        go: function (navigation_transport, view, start_time, finalize) {

                            this.feed_id = PBS.tvschedules.tvschedules.active_feed;

                            // Refresh parameters.
                            PBS.tvschedules.tvschedules.schedule.getParams();

                            // Apply parameter overrides.
                            if (navigation_transport) {
                                PBS.tvschedules.tvschedules.schedule.navigation_transport = navigation_transport
                                // And specifically, if we are dealing with
                                // over-the-air broadcast, we need to wipe any
                                // previous headend_id parameter value.
                                if (navigation_transport == 'b') {
                                    PBS.tvschedules.tvschedules.schedule.headend_id = '';
                                    //PBS.tvschedules.tvschedules.schedule.headend_id = undefined;
                                    //PBS.tvschedules.tvschedules.schedule.headend_id = null;
                                }
                            }

                            if (view) {
                                PBS.tvschedules.tvschedules.schedule.view = view;
                            }

                            if (start_time) {
                                PBS.tvschedules.tvschedules.schedule.start_time = start_time;
                            }

                            this.finalize = finalize;
                            PBS.tvschedules.loading.show();

                            this.getView();

                        },

                        getView: function () {

                            // Get the start time in an appropriate string format.
                            var start_time_str = PBS.tvschedules.tvschedules.schedule.getStartTimeStr();

                            var URL = PBS.tvschedules.config.base + '/py-publisher/modules/dtv/tvschedules/' + PBS.tvschedules.config.version + '/views/full_week';
                            var callback = 'PBS.tvschedules.tvschedules.schedule.getFullWeek.viewCallback';

                            /*
                            alert(
                                'getFullDay()' + '\n' + 
                                'pbs_tvsm_sf_zip: ' + PBS.tvschedules.tvschedules.schedule.zip + '\n' +
                                'pbs_tvsm_sf_headend_id: ' + PBS.tvschedules.tvschedules.schedule.headend_id + '\n' +
                                'pbs_tvsm_sf_transport: ' + PBS.tvschedules.tvschedules.schedule.transport + '\n' +
                                'pbs_tvsm_sf_nav_transport: ' + PBS.tvschedules.tvschedules.schedule.navigation_transport + '\n' +
                                'pbs_tvsm_sf_tvdata_names: ' + PBS.tvschedules.tvschedules.schedule.tvdata_names + '\n' +
                                'pbs_tvsm_tv_start_time: ' + start_time_str + '\n' +
                                'pbs_tvsm_tv_feed_id: ' + this.feed_id + '\n' +
                                'pbs_tvsm_localization_tvdata_name: ' + PBS.tvschedules.tvschedules.config['localization'] + '\n' +
                                'pbs_tvsm_localization_headend_id: ' + PBS.tvschedules.tvschedules.localization_headend_id + '\n' +
                                'pbs_tvsm_cookie_settable: ' + PBS.tvschedules.user.cookie_settable + '\n' +
                                'pbs_tvsm_disable_favorites: ' + PBS.tvschedules.tvschedules.disable_favorites + '\n' +
                                'pbs_tvsm_disable_shop: ' + PBS.tvschedules.tvschedules.disable_shop + '\n'
                            );
                            */
                            PBS.tvschedules.data.get(
                                URL,
                                callback,
                                {
                                    'pbs_tvsm_sf_zip': PBS.tvschedules.tvschedules.schedule.zip,
                                    'pbs_tvsm_sf_headend_id': PBS.tvschedules.tvschedules.schedule.headend_id,
                                    'pbs_tvsm_sf_transport': PBS.tvschedules.tvschedules.schedule.transport,
                                    'pbs_tvsm_sf_nav_transport': PBS.tvschedules.tvschedules.schedule.navigation_transport,
                                    'pbs_tvsm_sf_tvdata_names': PBS.tvschedules.tvschedules.schedule.tvdata_names,
                                    'pbs_tvsm_tv_start_time': start_time_str,
                                    'pbs_tvsm_tv_feed_id': this.feed_id,
                                    'pbs_tvsm_localization_tvdata_name': PBS.tvschedules.tvschedules.config['localization'],
                                    'pbs_tvsm_localization_headend_id': PBS.tvschedules.tvschedules.localization_headend_id,
                                    'pbs_tvsm_cookie_settable': PBS.tvschedules.user.cookie_settable,
                                    'pbs_tvsm_disable_favorites': PBS.tvschedules.tvschedules.disable_favorites,
                                    'pbs_tvsm_disable_shop': PBS.tvschedules.tvschedules.disable_shop
                                }
                            );
                        },
                        
                        viewCallback: function (args) {

                            var markup = args['markup'];
                            var matrix = args['matrix'];
                            PBS.tvschedules.tvschedules.schedule.full_week_matrix = matrix;

                            // TODO Get full day highlight indexes here.

                            // Put the markup in play.
                            document.getElementById('pbs_tvschedules_modules-tvschedules-schedule_container').innerHTML = markup;

                            // TODO The following few lines are duplicated in
                            // getGrid.viewCallback... should we factor out for
                            //just these two cases? Maybe manageShell() ?

                            // Update the user interface components that are
                            // external to the grid, but that are dependent on
                            // the grid state.
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selectedday').innerHTML = 'Week of';

                            var week_of = matrix['week_of'];
                            document.getElementById('pbs_tvschedules_modules-tvschedules-dateheader_selecteddate').innerHTML = week_of;

                            // We've updated the grid markup, so let's size the
                            // TDs and re-initialize user interface preference
                            // state.
                            PBS.tvschedules.tvschedules.sizeTDs();
                            //PBS.tvschedules.tvschedules.implementationCustomizations();
                            PBS.tvschedules.tvschedules.initPreferences();

                            // And finally, we finalize. ;)
                            if (this.finalize) {
                                this.finalize();
                            }
                            PBS.tvschedules.tvschedules.manageTabs('week');
                            PBS.tvschedules.tvschedules.sizeGridContainer();
                            PBS.tvschedules.loading.hide();
                        }

                    };

                    return obj;

                }()

            };

            return obj;

        }()

    };

    return obj;

}(); // The parens here cause the anonymous function to execute and return.
PBS.tvschedules.module_inits.push({ method: 'PBS.tvschedules.tvschedules.init', data: 'PBS.tvschedules.tvschedules', scope: true });
