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.

The TV class is the top-level controller for Television Simulator ‘99. It owns the application lifecycle from the initial page load through YouTube API readiness, guide data fetching, and channel rendering. Every feature visible to the viewer — channel switching, mute state, on-screen messages, and viewport scaling — is coordinated through a single TV instance created in index.html.

Constructor Parameters

The constructor accepts four optional parameters that tune startup behaviour and layout constraints.
maxWidth
number
default:"1200"
Maximum pixel width of the TV element before scaling is applied. When the viewport is wider than this value the CSS transform: scale() is removed entirely, leaving the TV at its natural size.
maxHeight
number
default:"1000"
Maximum pixel height before scaling stops. The scale factor is calculated against both dimensions and the smaller result wins, so neither axis is clipped.
warmupTime
number
default:"3000"
Duration in milliseconds of the fade-in animation when a channel first becomes visible. On the very first channel load this value is used as-is; on subsequent channel switches it is divided by 10, giving a quick 300 ms crossfade instead of a full warm-up.
firstStart
boolean
default:"true"
Tracks whether this is the simulator’s first startup. showChannel sets it to false after the first channel loads, ensuring the long warm-up animation only plays once per page load.
The constructor also builds the classes registry that maps channel numbers to their implementation classes:
constructor(maxWidth = 1200, maxHeight = 1000, warmupTime = 3000, firstStart = true) {
    this.maxWidth = maxWidth;
    this.maxHeight = maxHeight;
    this.warmupTime = warmupTime;
    this.firstStart = firstStart;
    this.classes = {
        Channel12
    };
}

The classes Object

this.classes is a plain object whose keys follow the convention Channel{number}. When showChannel is called with a channel number it performs a dynamic lookup:
this.channel = new this.classes['Channel' + number]($('.current-channel'), window.guideData);
Adding a new channel to the simulator means defining a class (e.g. Channel5) and registering it here:
this.classes = {
    Channel12,
    Channel5,   // hypothetical new channel
};

startUp()

startUp() defers all initialisation until the DOM is ready, then wires up every event handler and kicks off the asynchronous loading chain.
1

Attach resize handler

On non-mobile devices, a $(window).resize listener is registered that calls handleResize() whenever the viewport changes. scanlines CSS is also applied to .screen for desktop users.
2

Wire UI buttons

The about panel toggle and the hardware mute button (#tv-mute) are bound via jQuery click handlers. The about panel is also toggled immediately so it starts hidden.
3

Listen for youtubeReady

A document event listener waits for the custom youtubeReady event (fired by onYouTubeIframeAPIReady in youtube-ctrl.js). When it fires, a jQuery AJAX GET fetches data/guide.xml, parses it with $.parseXML, and stores the result as window.guideData.
4

Find default channel and show it

After parsing the XML, the code queries for the channel element that carries both watchable and default attributes and calls showChannel with its number attribute.
5

Inject YouTube IFrame API

YouTubeApi.loadYouTubeAPI() is called last. It injects the YouTube script tag; when the script loads it fires onYouTubeIframeAPIReady, which dispatches youtubeReady and unblocks step 3.
startUp() {
    $(document).ready(() => {
        if (!Helpers.isMobile().any()) {
            $(window).resize(() => { this.handleResize(); });
            $('.screen').addClass('scanlines');
        }

        this.handleResize();

        $('#tv-mute').click(() => { this.toggleMute(); });

        document.addEventListener('youtubeReady', () => {
            $.ajax({
                type: 'GET',
                url: 'data/guide.xml',
                dataType: 'xml',
                complete: (data, status) => {
                    window.guideData = $($.parseXML(data.responseText));
                    var defaultChannel = guideData.find('channel[watchable][default]').attr('number');
                    if (undefined != defaultChannel && null != defaultChannel) {
                        this.showChannel(defaultChannel);
                    }
                }
            });
        });

        YouTubeApi.loadYouTubeAPI();
    });
}

showChannel(number)

showChannel tears down the currently active channel, loads the replacement channel’s HTML layout, and fades it in.
showChannel(number, callback) {
    if (this.channel) {
        this.channel.teardown();
    }

    $('#tvm-top-right').css('opacity', '0');
    $('.current-channel')
        .css('opacity', '0')
        .attr('id', `ch-${number}`)
        .load(`channels/${Helpers.padLeft(number, 3)}/layout.html`, () => {
            this.channel = new this.classes['Channel' + number](
                $('.current-channel'),
                window.guideData
            );
            this.channel.show();
            $('.current-channel, #tvm-top-right').animate(
                { opacity: 1 },
                this.firstStart ? this.warmupTime : this.warmupTime / 10,
                0,
                () => { this.channel.ready(); }
            );
            $('#tvm-top-right').text(Helpers.padLeft(number, 2));
            setTimeout(() => { $('#tvm-top-right').text(''); }, 4000);
            this.firstStart = false;
        });
}
The sequence is:
  1. If a channel is already loaded, teardown() is called on it — clearing timers, stopping video, and emptying the container.
  2. The .current-channel div is hidden (opacity: 0), and jQuery’s .load() fetches channels/012/layout.html (zero-padded to 3 digits by Helpers.padLeft).
  3. Once the HTML is injected, a new channel class is instantiated and show() is called synchronously.
  4. jQuery .animate() fades the channel and the channel-number overlay in. When the animation finishes, ready() is called on the channel.
  5. The channel number appears in #tvm-top-right for four seconds, then clears.
  6. this.firstStart is set to false, switching all future transitions to the fast warmupTime / 10 duration.

toggleMute(), mute(), and unmute()

Mute control is delegated to YouTubeApi.toggleMuteVideo, which talks directly to the IFrame player. The result — a boolean indicating whether the player is now muted — is stored on this.muted and reflected in the on-screen overlay.
toggleMute() {
    this.muted = YouTubeApi.toggleMuteVideo(this.channel.player);
    this.afterMute();
}

afterMute() {
    if (this.muted) {
        $('#tvm-bottom-left').text('MUTING');
    } else {
        $('#tvm-bottom-left').text('');
    }
}
mute() and unmute() each call this.toggleMute() (passing a boolean argument that toggleMute does not use) followed by a second call to this.afterMute(). Because toggleMute() already calls afterMute() internally, calling mute() or unmute() results in afterMute() running twice. Both methods require that the active channel exposes a this.player property pointing to its YT.Player instance.

handleResize()

handleResize keeps the TV element sized correctly inside any viewport by computing a uniform scale factor and applying it as a CSS transform.
handleResize() {
    let $window = $(window);
    let width   = $window.width();
    let height  = $window.height();
    let scale;

    if (width >= this.maxWidth && this.height >= this.maxHeight) {
        $('.tv').css({ 'transform': '' });
        return;
    }

    scale = Math.min(width / this.maxWidth, height / this.maxHeight);
    $('.tv').css({ 'transform': 'scale(' + scale + ')' });
}
Math.min ensures the smaller of the two ratios (width-based or height-based) is used, so the TV never overflows either dimension. On viewports larger than maxWidth × maxHeight the transform is cleared entirely and the TV renders at natural size.

Instantiation

The entire application starts with two lines at the bottom of index.html:
var tv = new TV();
tv.startUp();
All constructor parameters use their defaults, so the TV is configured for a 1200 × 1000 maximum size with a 3-second warm-up fade. Pass custom values to the constructor to override any of these for a different deployment.

Build docs developers (and LLMs) love