Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/zshall/program-guide/llms.txt

Use this file to discover all available pages before exploring further.

Television Simulator ‘99 uses an extensible channel architecture where every channel is an ES6 class that inherits from a common Channel base. The base class handles shared concerns — container management, timer tracking, and teardown — while subclasses override lifecycle hooks to build their own UI. Adding a new channel means writing one class and registering it in the TV constructor; the rest of the framework picks it up automatically.

The Channel Base Class

class Channel {
    constructor(container, guideData) {
        this.container = container;
        this.guideData = guideData;
        this.timeouts  = {};
        this.intervals = {};
    }

    show() {
        console.log('Showing channel');
    }

    teardown() {
        console.log('Tearing down');
        for (var i in this.timeouts)  { clearTimeout(this.timeouts[i]);   }
        for (var i in this.intervals) { clearInterval(this.intervals[i]); }
        if (this.player) {
            YouTubeApi.stopVideo(this.player);
        }
        this.container.empty();
        this.container.attr('id', '');
    }
}

Constructor Parameters

container
jQuery
A jQuery-wrapped DOM element (.current-channel) into which the channel renders its UI. The channel’s layout HTML has already been injected by TV.showChannel before the constructor is called.
guideData
jQuery (parsed XML)
The full parsed guide.xml document stored as a jQuery object on window.guideData. Channels use standard jQuery traversal methods (find, each, attr) to query listings, ads, and notices.

Instance Properties

PropertyTypePurpose
containerjQueryThe channel’s root DOM node. Emptied by teardown().
guideDatajQueryParsed XML; shared read-only reference across channels.
timeoutsObjectNamed setTimeout handles. Keys are arbitrary strings chosen by the subclass.
intervalsObjectNamed setInterval handles. Cleared automatically on teardown.

Lifecycle Hooks

show() — Called immediately after the channel class is instantiated, before the fade-in animation begins. Subclasses override this to create DOM elements, start intervals, and initialise the YouTube player. The base implementation logs a message to the console. teardown() — Called by TV.showChannel before loading the next channel. The base implementation clears all entries in this.timeouts and this.intervals, calls YouTubeApi.stopVideo(this.player) if a player reference exists, empties the container, and removes its id attribute. Subclasses that manage additional resources (such as a marquee plugin) should call super.teardown() after their own cleanup.
ready() is not defined on the base Channel class. It is called by TV.showChannel at the end of the fade-in animation. Override it in a subclass if you need to run code after the channel is fully visible — for example to trigger an entrance animation that would be invisible during the fade.

Channel12 — The Prevue Guide Channel

Channel12 is the only channel implemented in the simulator. It extends Channel to produce the classic Prevue-style scrolling program grid with an embedded YouTube background, a live clock, and rotating ad panels.

Constructor

class Channel12 extends Channel {
    constructor(container, guideData) {
        super(container, guideData);
        this.curTsStart      = 19;   // starting timeslot offset (9:30 AM)
        this.adList          = [];
        this.currentAd       = 0;
        this.defaultAdDuration = 30;
    }
curTsStart is the timeslot index from which the grid starts rendering columns. A value of 19 corresponds to the 9:30 AM half-hour slot. The ad-related properties seed the ad rotation loop that begins in show().

show() — Building the Channel UI

show() calls super.show() then performs all channel construction in order:
1

Create the YouTube player

Instantiates YT.Player targeting the #ytplayer div injected by layout.html. Autoplay is enabled and native controls are hidden (controls: 0).
this.player = new YT.Player('ytplayer', {
    height: '360',
    width:  '425',
    videoId: '9NSVU4Gv_wA',
    events: {
        'onReady':       this.onPlayerReady,
        'onStateChange': this.onPlayerStateChange
    },
    playerVars: {
        'autoplay': 1,
        'controls': 0,
        'rel':      0
    }
});
2

Initialise the marquee and cache DOM references

Activates the jQuery marquee plugin on the <marquee> element. DOM references to key layout nodes (#listing-grid, .video-left, .realtime, etc.) are cached after this step because the marquee plugin rewrites the DOM, invalidating earlier references.
3

Start the live clock interval

A one-second setInterval updates the realtime clock and the three time-header columns (:00, :30, +1hr) using moment.js. The handle is stored as this.intervals.timeInterval so teardown() clears it automatically.
this.intervals.timeInterval = setInterval(() => {
    this.realtime.text(moment().format('h:mm:ss'));
    this.timePlus00.text(moment().startOf('hour').format('h:mm A'));
    this.timePlus30.text(moment().startOf('hour').minutes(30).format('h:mm A'));
    this.timePlus60.text(moment().startOf('hour').add(1, 'hours').format('h:mm A'));
    this.date.text(moment().format('dddd MMMM D YYYY'));
}, 1000);
4

Build the listings grid

Iterates every <channel> element in guideData. For each channel it renders any <notice> elements as full-width red-background rows, then builds a channel row with up to three listing columns starting at curTsStart.
guideData.find('channel').each((i, channel) => {
    const ch = $(channel);
    // render notices
    $(ch.find('notice')).each((i, notice) => {
        const noticeRow = $('<tr>');
        noticeRow.append(
            $('<td>').addClass('notice').attr('colspan', 4).html($(notice).html())
        );
        this.listingGrid.append(noticeRow);
    });

    if (ch.attr('noticeonly')) return;

    // channel identity cell
    const channelBox = $('<td>').addClass('channel');
    channelBox.append($('<div>').addClass('number').text(ch.attr('number')));
    channelBox.append($('<div>').addClass('station').text(ch.attr('name')));
    // … listing columns follow
});
getFirstListing(listings, timeslot) walks backwards from the target timeslot index until it finds the most recent listing that covers that time, handling programmes that span multiple half-hour slots. The method returns undefined for channels with no relevant listings.
5

Load and rotate ads

Collects every <ad> element from guideData into this.adList, then calls nextAd() to begin the rotation cycle. If no ads exist, the left panel shows the placeholder text "THIS SPACE FOR RENT".

teardown()

Channel12 overrides teardown() to stop the marquee plugin before delegating to the base:
teardown() {
    this.marquee.trigger('stop');
    super.teardown();
}
The super.teardown() call clears all timers, stops the YouTube player, and empties the container — making the sequence safe from leftover state.

Full Class Extension Pattern

class Channel12 extends Channel {
    constructor(container, guideData) {
        super(container, guideData);
        // channel-specific state goes here
    }

    show() {
        super.show();
        // build DOM, start timers, create YT.Player
    }

    teardown() {
        // clean up channel-specific resources
        super.teardown(); // always call last
    }

    // ready() is optional — override only if needed after fade-in
}

Channel Registration

Each channel class must be registered in the TV constructor’s classes object using the naming convention Channel{number}:
this.classes = {
    Channel12   // shorthand for Channel12: Channel12
};
When showChannel(number) runs, it performs the lookup:
this.channel = new this.classes['Channel' + number](
    $('.current-channel'),
    window.guideData
);
So a class named Channel5 would be instantiated whenever channel 5 is selected.

File and Naming Conventions

ConventionPatternExample
Class nameChannel{number}Channel12, Channel5
Script filechannels/{NNN}/script.jschannels/012/script.js
Layout filechannels/{NNN}/layout.htmlchannels/012/layout.html
The three-digit zero-padded folder number is produced at runtime by Helpers.padLeft(number, 3), which TV.showChannel uses when constructing the .load() URL.

Build docs developers (and LLMs) love