/**
 * Vidyagamez namespace.
 * @author anemonevgz@gmail.com
 * @namespace
 * @since 1
 * @version 1
 */
if (!window.Vidyagamez) {
  var Vidyagamez = {};
}

/**
 * Handles front-end behavior.
 * @author anemonevgz@gmail.com
 * @constructor
 * @requires JQuery 1.7.1
 * @requires JQuery cookie Plugin 1.0
 * @requires JQuery resize Plugin 1.1
 * @requires JQuery simplemodal Plugin 1.4.1
 * @requires JQuery tools Plugin 1.2.6
 * @since 1
 * @version 1
 */
Vidyagamez.Content = new function() {
  /**
   * Active channel.
   * @private
   * @type {Object.<String, Boolean|Number|String>}
   */
  var Channel = {};
  
  /**
   * Player channels.
   * @private
   * @type {Array.<Object.<String, Boolean|Number|String>>}
   */
  var Channels = [];

  /**
   * Event listeners.
   * @private
   * @type {Array.<Function>}
   */
  var Listeners = {};

  /**
    * Last, manual channel load time.
    * @private
    * @type {Number}
    */
  var ManualChannelLoadTime = 0;  

  /**
   * User settings.
   * @private
   * @type {Array.<String>}
   */
  var Settings = {};  

  /**
   * User settings cookie name.
   * @const
   * @private
   * @type {String}
   */
  var SettingsCookieName = 'Settings';

  /**
   * Adds the "ballsofsteel" code that triggers the display of the Troll-Tan (Duke Nukem Edition) soundboard.
   * @private
   * @return {Undefined}
   */
  var AddBallsOfSteelCode = function() {
    AddCode(
      [66, 65, 76, 76, 83, 79, 70, 83, 84, 69, 69, 76],
      function() {
        $('#trolltan-duke-nukem-edition').modal(
          {
            containerId: 'trolltan-duke-nukem-edition-modal-container',
            overlayId: 'trolltan-duke-nukem-edition-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Adds channels and loading functionality to the player menu.
   * @private
   * @return {Undefined}
   */
  var AddChannels = function() {
    var channels = $('#player-menu .channels');
    var html = '';
    $.each(
      Channels,
      function() {
        html += '<li class="channel">' +
          '<a';
        if (this.live) {
          html += ' class="live"';
        }
        html += ' href="#' + this.name + '">' + this.alias + '</a>' +
          '</li>';
      }
    );
    channels.find('ul').html(html);
    channels.find('a').each(
      function() {
        var link = $(this);
        var name = link.attr('href').substr(1);
        link.removeAttr('href');
        link.bind(
          'click',
          function(event) {
            $.each(
              Channels,
              function() {
                if (this.name == name) {
                  var date = new Date();
                  ManualChannelLoadTime = date.getTime();
                  LoadChannel(this);
                }
                return this.name != name;
              }
            );
            event.preventDefault();
          }
        );
      }
    );
  };

  /**
   * Adds a code listener and associates it with a callback.
   * @param code {Array.<Number>} Key sequence to associate with a callback.
   * @param callback {Function} Called when code is entered.
   * @private
   * @return {Undefined}
   */
  var AddCode = function(code, callback) {
    var index = 0;
    $(document).bind(
      'keyup',
      function(event) {
        var keyCode = event.keyCode ? event.keyCode : event.which;
        if (keyCode == code[index]) {
          index++;
        } else if (keyCode == code[0]) {
          index = 1;
        } else {
          index = 0;
        }
        if (index == code.length) {
          callback();
          index = 0;
        }
      }
    );
  };

  /**
   * Adds all the easter egg codes.
   * @private
   * @return {Undefined}
   */
  var AddCodes = function() {
    AddBallsOfSteelCode();
    AddGaloSengenCode();
    AddKonamiCode();
    AddNyannersCode();
    AddSimsCode();
    AddSonicCode();
    AddUboaCode();
    AddZellCode();
  };

  /**
   * Adds multistream popup functionality to the custom stream form.
   * @param defaultChannels {Object.<String, Number|String>|Undefined|false} Channels to add as defaults.
   * @private
   * @return {Undefined}
   */
  var AddCustomStreamForm = function(defaultChannels) {
    var channels = [];
    var channelList = $('#channel-list');
    var addChannel = function(name, service) {
      var html = '<li class="channel"><a class="deletion-link">X</a>' + name + ' - ' + service + '</li>';
      var listElement = $(html);
      listElement.find('.deletion-link').bind(
        'click',
        function(event) {
          $(this).parent().remove();
          for (var i = 0; i < channels.length; i++) {
            if (channels[i].name == name) {
              channels.splice(i, 1);
              break;
            }
          }
          event.preventDefault();
        }
      );
      channelList.append(listElement);
      channels.push(
        {
          name: name,
          service: service
        }
      );
    }
    $.each(
      defaultChannels,
      function() {
        addChannel(this.name, this.service);
      }
    );
    var addChannelForm = $('#add-channel-form');
    addChannelForm.bind(
      'submit',
      function(event) {
        var name = addChannelForm.find('input[name="name"]').val();
        var service = addChannelForm.find('select[name="service"]').val();
        addChannel(name, service); 
        event.preventDefault();
      }
    );
    var createMultistreamForm = $('#create-multistream-form');
    createMultistreamForm.bind(
      'submit',
      function(event) {
        var vertical = createMultistreamForm.find('input[name="vertical"]:checked').val();
        var url = 'http://vidyagamez.com/content/multistream/?names=';
        for (var j = 0; j < channels.length; j++) {
          if (j) {
            url += ',';
          }
          url += channels[j].name;
        }
        url += '&services=';
        for (var k = 0; k < channels.length; k++) {
          if (k) {
            url += ',';
          }
          url += channels[k].service;
        }
        if (vertical) {
          url += '&vertical';
        }
        var windowHeight = Math.floor(screen.height * 0.7);
        var windowWidth = Math.floor(screen.width * 0.7);
        var options = 'directories=0,height=' + windowHeight +
          ',location=1,menubar=0,resizable=1,scrollbars=0,status=0,width=' + windowWidth; 
        window.open(url, 'multistream', options);
        event.preventDefault();
      }
    );
  };

  /**
   * Adds the "gogogo" code that triggers the Gal-o Sengen track's playback.
   * @private
   * @return {Undefined}
   */
  var AddGaloSengenCode = function() {
    var isPlaying = false;
    AddCode(
      [71, 79, 71, 79, 71, 79],
      function() {
        var audio = '#galosengen';
        if (!isPlaying) {
          PlaySound(audio, true);
        } else {
          PauseSound(audio);
        }
        isPlaying = !isPlaying;
      }
    );
  };

  /**
   * Adds the Konami code that triggers a dancing kitten on its entry.
   * @private
   * @return {Undefined}
   */
  var AddKonamiCode = function() {
    AddCode(
      [38, 38, 40, 40, 37, 39, 37, 39, 66, 65],
      function() {
        $('#dancing-kitten').modal(
          {
            containerId: 'dancing-kitten-modal-container',
            overlayId: 'dancing-kitten-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Adds layout functionality to the player menu.
   * @private
   * @return {Undefined}
   */
  var AddLayouts = function() {
    $('#player-menu .layouts a').bind(
      'click',
      function() {
        var name = $(this).attr('class'); 
        SetLayout(name);
      }
    ); 
    $('.layouts a[title]').tooltip().dynamic();
  };

  /**
   * Resizes the mibbit iframe based on the window size.
   * @private
   * @return {Undefined}
   */
  var AddMibbitResize = function() {
    var mibbit = $('#mibbit');
    var windowObject = $(window);
    Listeners['AddMibbitResize.resize'] = function() {
      var mibbitHeight = windowObject.height() - parseInt(mibbit.css('margin-top'), 10);
      mibbit.height(mibbitHeight);
    };
    windowObject.bind('resize', Listeners['AddMibbitResize.resize']);
    Listeners['AddMibbitResize.resize']();
  };

  /**
   * Adds the "nyanners" code that triggers the display of the Troll-Tan soundboard.
   * @private
   * @return {Undefined}
   */
  var AddNyannersCode = function() {
    AddCode(
      [78, 89, 65, 78, 78, 69, 82, 83],
      function() {
        $('#trolltan').modal(
          {
            containerId: 'trolltan-modal-container',
            overlayId: 'trolltan-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Adds the Sims code that triggers a dancing Bianca on its entry.
   * @private
   * @return {Undefined}
   */
  var AddSimsCode = function() {
    AddCode(
      [82, 79, 83, 69, 66, 85, 68],
      function() {
        var audio = '#nyanyanyanyanyanyanya-audio';
        PlaySound(audio, true);
        $('#nyanyanyanyanyanyanya').modal(
          {
            containerId: 'nyanyanyanyanyanyanya-modal-container',
            onClose: function() {
              PauseSound(audio);
              $.modal.close();
            },
            overlayId: 'nyanyanyanyanyanyanya-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Adds the Sonic code that triggers the Sonic Rap's playback.
   * @private
   * @return {Undefined}
   */
  var AddSonicCode = function() {
    var isPlaying = false;
    AddCode(
      [83, 79, 78, 73, 67],
      function() {
        var audio = '#sonic';
        if (!isPlaying) {
          PlaySound(audio, true);
        } else {
          PauseSound(audio);
        }
        isPlaying = !isPlaying;
      }
    );
  };

  /**
   * Adds custom styles to the page footer.
   * @private
   * @return {Undefined}
   */
  var AddStyles = function() {
    $('#styles a').each(
      function() {
        var link = $(this);
        var name = link.attr('href').substr(1);
        link.removeAttr('href');
        link.bind(
          'click',
          function(event) {
            SetStyle(name);
            event.preventDefault();
          }
        );
      }
    );
  };

  /**
   * Adds the "uboa" code that triggers the Uboa face surrounded by rainbows.
   * @private
   * @return {Undefined}
   */
  var AddUboaCode = function() {
    AddCode(
      [85, 66, 79, 65],
      function() {
        $('#uboa').modal(
          {
            containerId: 'uboa-modal-container',
            minHeight: '100%',
            minWidth: '100%',
            onClose: function() {
              LoadChannel(Channel);
              $.modal.close();
            },
            onShow: function() {
              LoadChannel(Channel, false);
            },
            overlayId: 'uboa-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Adds the "zell" code that triggers a kitten walking in place, while surrounded by rainbows.
   * @private
   * @return {Undefined}
   */
  var AddZellCode = function() {
    AddCode(
      [90, 69, 76, 76],
      function() {
        $('#zell').modal(
          {
            containerId: 'zell-modal-container',
            overlayId: 'zell-modal-overlay'
          }
        );
      }
    );
  };

  /**
   * Applies user settings outside the MVC application.
   * @param name External content name.
   * @private
   * @return {Undefined}
   */
  var ApplyExternalSettings = function(name) {
    Settings.style = Settings.style ? Settings.style : 'default';
    SetExternalStyle(name, Settings.style);
  };

  /**
   * Applies user settings.
   * @param settings {Array<String>|Undefined} Settings to apply.  If undefined, all settings are applied.
   * @private
   * @return {Undefined}
   */
  var ApplySettings = function(settings) {
    if (!settings) {
      settings = ['layout', 'style'];
    }
    if ($.inArray('layout', settings) != -1) {
      Settings.layout = Settings.layout ? Settings.layout : 'default'; 
      SetLayout(Settings.layout);
    }
    if ($.inArray('style', settings) != -1) {
      Settings.style = Settings.style ? Settings.style : 'default';
      SetStyle(Settings.style);
    }
  };

  /**
   * Fixes an Internet Explorer bug with iframe borders and adds background transparency.
   * @private
   * @return {Undefined}
   */
  var FixIframes = function() {
    var iframes = $('iframe');
    iframes.attr('allowTransparency', 'true');
    if ($.browser.msie) {
      iframes.each(
        function() {
          var iframe = $(this)[0];
          iframe.frameBorder = 0;
          var outerHtml = iframe.outerHTML;
          iframe.outerHTML = outerHtml;
        }
      );
    }
  };

  /**
   * Retrieves channel data and calls a callback on success.
   * @param callback {Function} Called on success.
   * @param getStatuses {Boolean} Whether the statuses should be retrieved.  Defaults to true.
   * @private
   * @return {Undefined}
   */
  var GetChannels = function(callback, getStatuses) {
    getStatuses = getStatuses || getStatuses === undefined ? true : false;
    $.ajax(
      {
        cache: false,
        success: function(json) {
          Channels = json;
          if (getStatuses) {
            var statusesRetrieved = 0;
            for (var i = 0; i < Channels.length; i++) {
              GetChannelStatus(
                i,
                function() {
                  statusesRetrieved++;
                  if (statusesRetrieved == Channels.length) {
                    for (var j = 0; j < Channels.length; j++) {
                      Channels[j].active = false;
                    }
                    var activeChannelSet = false;
                    for (var k = 0; k < Channels.length; k++) {
                      if (Channels[k].live) {
                        Channels[k].active = true;
                        activeChannelSet = true;
                        break;
                      }
                    }
                    if (!activeChannelSet) {
                      for (var l = 0; l < Channels.length; l++) {
                        if (Channels[l].autopilot) {
                          Channels[l].active = true;
                          activeChannelSet = true;
                          break;
                        }
                      }
                    }
                    if (!activeChannelSet && Channels.length) {
                      Channels[0].active = true;
                    }
                    callback();
                  }
                }
              );
            }
          } else {
            for (var m = 0; m < Channels.length; m++) {
              Channels[m].active = Channels[m].autopilot;
              Channels[m].live = false;
            }
            callback();
          }
        },
        url: 'data/channels.json'
      }
    );
  };

  /**
   * Retrieves a channel's status.
   * @param i {Number} Channel index.
   * @param callback {Function} Called on success.
   * @return {Undefined}
   */
  var GetChannelStatus = function(i, callback) {
    var url = '';
    if (Channels[i].service == 'justin.tv') {
      url = 'http://api.justin.tv/api/stream/list.xml?channel=' + Channels[i].name;
    } else if (Channels[i].service == 'livestream.com') {
      url = 'http://x' + Channels[i].name + 'x.api.channel.livestream.com/2.0/info.xml';
    } else if (Channels[i].service == 'own3d.tv') {
      url = 'http://api.own3d.tv/liveCheck.php?live_id=' + Channels[i].name;
    }
    url = encodeURIComponent(url);
    url = 'http://query.yahooapis.com/v1/public/yql?format=json&q=select%20*%20from%20xml%20where%20url%3D%22' +
      url + '%22&_maxage=60&callback=?';
    $.getJSON(
      url,
      function(json) {
        var date = new Date();
        var time = date.getTime();
        var aspectRatio = 1.33;
        var results = json.query.results;
        if (Channels[i].service == 'justin.tv') {
          Channels[i].live = results.results !== null;
          if (Channels[i].live) {
            var stream = results.streams.stream;
            aspectRatio = stream.video_width / stream.video_height;
          }
        } else if (Channels[i].service == 'livestream.com') {
          var channel = results.rss.channel;
          Channels[i].live = channel.isLive == 'true';
          if (Channels[i].live) {
            aspectRatio = channel.aspectWidth / channel.aspectHeight;
          }
        } else if (Channels[i].service == 'own3d.tv') {
          Channels[i].live = results.own3dReply.liveEvent.isLive == 'true';
          aspectRatio = '16:9';
        }
        if (Channels[i].live) {
          Channels[i].lastLiveTime = time;
        } else {
          aspectRatio = '4:3';
        }
        Channels[i].aspectRatio = aspectRatio > 1.34 ? '16:9' : '4:3';
        callback();
      }
    );
  };

  /**
   * Highlights the active channel in the player menu.
   * @private
   * @return {Undefined}
   */
  var HighlightActiveChannel = function() {
    var links = $('#player-menu .channels a');
    links.removeClass('active');
    for (var i = 0; i < Channels.length; i++) {
      if (Channels[i].name == Channel.name) {
        links.eq(i).addClass('active');
        break;
      }
    }
  };

  /**
   * Initializes universal interface elements.
   * @public
   * @return {Undefined}
   */
  this.Initialize = function() {
    InitializeModal();
    $(
      function() {
        RemoveNoScript();
        AddStyles();
        LoadSettings();
        ApplySettings(['style']);
        AddCodes();
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Initializes JQuery's AJAX functionality.
   * @private
   * @return {Undefined}
   */
  var InitializeAjax = function() {
    $.ajaxSetup(
      {
        cache: true,
        dataType: 'json',
        timeout: 30000,
        type: 'GET'
      }
    );
  };

  /**
   * Initializes the blog interface.
   * @public
   * @return {Undefined}
   */
  this.InitializeBlog = function() {
    if (!window.location.href.indexOf('http://blog.vidyagamez.com/')) {
      window.location.replace('http://vidyagamez.com/blog/' + window.location.href.substr(27));
    }
    InitializeModal();
    $(
      function() {
        LoadSettings();
        ApplyExternalSettings('blog');
        AddCodes();
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Initializes the chat interface.
   * @public
   * @return {Undefined}
   */
  this.InitializeChat = function() {
    $(
      function() {
        RemoveNoScript();
        ShowChat();
        AddMibbitResize();
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Initializes the customstream interface.
   * @param defaultChannels {Object.<String, Number|String>|Undefined|false} Channels to add as defaults.
   * @public
   * @return {Undefined}
   */
  this.InitializeCustomStream = function(defaultChannels) {
    $(
      function() {
        AddCustomStreamForm(defaultChannels);
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Initializes the forum interface.
   * @public
   * @return {Undefined}
   */
  this.InitializeForum = function() {
    InitializeModal();
    $(
      function() {
        LoadSettings();
        ApplyExternalSettings('forum');
        AddCodes();
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Initializes the Google Analytics tracker.
   * @private
   * @return {Undefined}
   */
  var InitializeGoogleAnalytics = function() {
    try {
      var tracker = _gat._getTracker('UA-17919885-1');
      tracker._trackPageview();
    } catch(exception) {
    }
  };

  /**
   * Initializes the index interface.
   * @public
   * @return {Undefined}
   */
  this.InitializeIndex = function() {
    InitializeAjax();
    Vidyagamez.Content.Initialize();
    $(
      function() {
        FixIframes();
        ApplySettings(['layout']);
        AddLayouts();
        GetChannels(
          function() {
            AddChannels();
            GetChannels(
              function() {
                AddChannels();
                LoadChannel();
                UpdateChannels();
              }
            );
          },
          false
        );
      }
    );
  };

  /**
   * Initializes the modal plugin's defaults.
   * @private
   * @return {Undefined}
   */
  var InitializeModal = function() {
    $.extend(
      $.modal.defaults,
      {
        opacity: 80,
        overlayClose: true
      }
    );
  };

  /**
   * Initializes the multistream interface.
   * @public
   * @return {Undefined}
   */
  this.InitializeMultistream = function() {
    $(
      function() {
        InitializeGoogleAnalytics();
      }
    );
  };

  /**
   * Loads a channel.  If no channel is specified, the active channel will be loaded.
   * @param channel {Object.<String, Number|String>|Undefined|false} Channel to load.
   * @param autoplay {Boolean} Whether to play the channel.
   * @private
   * @return {Undefined}
   */
  var LoadChannel = function(channel, autoplay) {
    autoplay = autoplay || autoplay === undefined ? 'true' : 'false';
    if (!channel) {
      $.each(
        Channels,
        function() {
          if (this.active) {
            channel = this;
          }
          return !this.active;
        }
      );
    }
    if (channel) {
      var url = '';
      if (channel.service == 'justin.tv') {
        url = 'http://www.justin.tv/widgets/live_embed_player.swf?auto_play=' + autoplay + '&amp;channel=' +
          channel.name;
      } else if (channel.service == 'livestream.com') {
        url = 'http://cdn.livestream.com/grid/LSPlayer.swf?autoPlay=' + autoplay + '&amp;channel=' + channel.name;
      } else if (channel.service == 'own3d.tv') {
        url = 'http://www.own3d.tv/livestream/' + channel.name + ';autoplay=' + autoplay;
      }
      var html = '<object data="' + url + '" id="player-object" type="application/x-shockwave-flash">' +
        '<param name="AllowScriptAccess" value="always" />' +
        '<param name="allowNetworking" value="all" />' +
        '<param name="allowFullScreen" value="true" />' +
        '<param name="movie" value="' + url + '" />' +
        '<param name="wmode" value="transparent" />' +
        '<section class="plugin-notice">VidyaGameZ.com requires Flash.  You can download it ' +
        '<a href="http://get.adobe.com/flashplayer/">here</a>' +
        '.</section>' +
        '</object>';
      $('#player').html(html);
      Channel = channel;
      UpdatePlayer();
    }
  };

  /**
   * Loads the user's settings from a cookie.
   * @private
   * @return {Undefined}
   */
  var LoadSettings = function() {
    if ($.cookie(SettingsCookieName) !== null) {
      var settings = $.cookie(SettingsCookieName).split('&');
      $.each(
        settings,
        function() {
          var settingKeyValue = this.split('=', 2);
          if (settingKeyValue.length == 2) {
            var key = decodeURIComponent(settingKeyValue[0]);
            var value = decodeURIComponent(settingKeyValue[1]);
            Settings[key] = value;
          }
        }
      );
    }
  };

  /*
   * Pauses a sound.
   * @param selector {String} Audio tag selector.
   * @private
   * @return {Undefined}
   */
  var PauseSound = function(selector) {
    try {
      $(selector)[0].pause();
    } catch (exception) {
    }
  };

  /*
   * Plays a sound.
   * @param selector {String} Audio tag selector.
   * @param loop {Boolean} Whether to loop the sound.
   * @private
   * @return {Undefined}
   */
  var PlaySound = function(selector, loop) {
    var audio = $(selector);
    var name = 'PlaySound.ended.' + selector;
    if (Listeners[name]) {
      audio.unbind('ended', Listeners[name]);
      Listeners[name] = null;
    }
    var audioTag = audio[0];
    Listeners[name] = function() {
      try {
        audioTag.currentTime = 0;
      } catch (exception) {
      }
      try {
        audioTag.play();
      } catch (exception) {
      }
    };
    Listeners[name]();
    if (loop) {
      audio.bind('ended', Listeners[name]);
    }
  };

  /**
   * Removes the noscript tag.
   * @private
   * @return {Undefined}
   */
  var RemoveNoScript = function() {
    $('noscript').remove();
  };

  /**
   * Saves the user's settings.
   * @private
   * @return {Undefined}
   */
  var SaveSettings = function() {
    $.cookie(
      SettingsCookieName,
      $.param(Settings),
      {
        expires: 365,
        path: '/'
      }
    );
  };

  /**
   * Sets the style outside the MVC application.
   * @param name {String} External content name. 
   * @param styleName {String} Style name.
   * @private
   * @return {Undefined}
   */
  var SetExternalStyle = function(name, styleName) { 
    $('link[title="Custom"]').remove();
    if (styleName != 'Default') {
      var html ='<link href="http://vidyagamez.com/content/stylesheets/' + name + '/' + styleName + '_style.css" rel="stylesheet" title="Custom" />';
      $('head').append(html);
      if (name == 'forum') {
        setActiveStyleSheet('Custom');
      }
    }
    TogglePonies(styleName == 'mylittlepony');
    ToggleScience(styleName == 'science!');
    ToggleAhaha(styleName == 'umineko');
    Settings.style = styleName;
    SaveSettings();
  };

  /**
   * Sets the layout.
   * @param name {String} Layout name.
   * @private
   * @return {Undefined}
   */
  var SetLayout = function(name) {
    if (name == 'custom-stream') {
      LoadChannel(Channel, false);
      window.open('http://vidyagamez.com/content/customstream/', 'custom_stream',
        'directories=0,height=420,location=1,menubar=0,resizable=1,scrollbars=1,status=0,width=500');
    } else if (name == 'popup-chat') {
      if (Settings.layout == 'default') {
        SetLayout('default-sans-chat');
      } else if (Settings.layout == 'fullscreen') {
        SetLayout('fullscreen-sans-chat');
      }
      window.open('http://vidyagamez.com/content/chat/', 'popup_chat',
        'directories=0,height=300,location=1,menubar=0,resizable=1,scrollbars=0,status=0,width=400');
   } else {
      var body = $('body');
      body.removeAttr('class');
      var className = 'layout-' + name;
      body.addClass(className);
      var player = $('#player');
      player.attr('style', '');
      player.removeAttr('style');
      var mibbit = $('#mibbit');
      mibbit.attr('style', '');
      mibbit.removeAttr('style');
      var windowObject = $(window);
      if (Listeners['SetLayout.resize']) {
        windowObject.unbind('resize', Listeners['SetLayout.resize']);
        Listeners['SetLayout.resize'] = null;
      }
      if ($.inArray(name, ['fullscreen', 'fullscreen-sans-chat']) != -1) {
        Listeners['SetLayout.resize'] = function() {
          var windowHeight = windowObject.height();
          var playerHeight = windowHeight - $('#player-menu').height();
          player.height(playerHeight);
          var mibbitHeight = windowHeight - parseInt(mibbit.css('margin-top'), 10);
          mibbit.height(mibbitHeight);
        };
        windowObject.bind('resize', Listeners['SetLayout.resize']);
        Listeners['SetLayout.resize']();
      }
      var layouts = $('#player-menu .layouts');
      layouts.find('a').removeClass('active');
      layouts.find('.' + name).addClass('active');
      Settings.layout = name;
      SaveSettings();
    }
  };

  /**
   * Sets the style.
   * @param name {String} Style name.
   * @private
   * @return {Undefined}
   */
  var SetStyle = function(name) {
    $('link[title="Custom"]').remove();
    if (name != 'Default') {
      var html ='<link href="content/stylesheets/content/' + name + '_style.css" rel="stylesheet" title="Custom" />';
      $('head').append(html);
    }
    TogglePonies(name == 'mylittlepony');
    ToggleRussianAnthem(name == 'putin');
    ToggleScience(name == 'science!');
    ToggleAhaha(name == 'umineko');
    ToggleYahahahahahaha(name == 'yuno');
    Settings.style = name;
    SaveSettings();
  };

  /**
   * Shows the chat section.
   * @private
   * @return {Undefined}
   */
  var ShowChat = function() {
    $('#chat').show();
  };

  /**
   * Toggles whether ahaha.wav will play when a link is clicked.
   * @param state {Boolean} Whether to enable or disable the behavior.
   * @private
   * @return {Undefined}
   */
  var ToggleAhaha = function(state) {
    var links = $('a');
    var listenerName = 'ToggleAhaha.click';
    if (Listeners[listenerName]) {
      links.die('click', Listeners[listenerName]);
      Listeners[listenerName] = null;
    }
    if (state) {
      var audio = '#ahaha';
      Listeners[listenerName] = function() {
        PlaySound(audio);
      };
      links.live('click', Listeners[listenerName]);
      PlaySound(audio);
    }
  };

  /**
   * Toggles whether various sounds and videos will play when parts of the header are clicked.
   * @param state {Boolean} Whether to enable or disable the behavior.
   * @private
   * @return {Undefined}
   */
  var TogglePonies = function(state) {
    var logo = $('#logo');
    var logoListenerName = 'TogglePonies.logo.click';
    if (Listeners[logoListenerName]) {
      logo.unbind('click', Listeners[logoListenerName]);
      Listeners[logoListenerName] = null;
    }
    var header = $('header,#header,#masthead');
    var headerListenerName = 'TogglePonies.header.click';
    if (Listeners[headerListenerName]) {
      header.unbind('click', Listeners[headerListenerName]);
      Listeners[headerListenerName] = null;
    }
    var audio = '#ponies-ponies-ponies-swag-audio';
    if (state) {
      var isPlaying = false;
      Listeners[logoListenerName] = function() {
        if (isPlaying) {
          PauseSound(audio);
        } else {
          PlaySound(audio, true);
        }
        isPlaying = !isPlaying;
      };
      logo.bind('click', Listeners[logoListenerName]);
      Listeners[headerListenerName] = function(event) {
        var x = event.pageX - this.offsetLeft;
        if (x > 70 && x < 200) {
          var moreApples = $('#more-apples');
          moreApples.modal(
            {
              containerId: 'more-apples-modal-container',
              overlayId: 'more-apples-modal-overlay'
            }
          );
          moreApples = moreApples[0];
          moreApples.play();
          var moreApplesIntervalId = 0;
          moreApplesIntervalId = window.setInterval(
            function() {
              if (moreApples.ended) {
                moreApples.currentTime = 0;
                moreApples.pause();
                $.modal.close();
                window.clearInterval(moreApplesIntervalId);
              }
            },
            100
          );
        } else if (x > 800 && x < 950) {
          var squeal = $('#squeal');
          squeal.modal(
            {
              containerId: 'squeal-modal-container',
              overlayId: 'squeal-modal-overlay'
            }
          );
          squeal = squeal[0];
          squeal.play();
          var squealIntervalId = 0;
          squealIntervalId = window.setInterval(
            function() {
              if (squeal.ended) {
                squeal.currentTime = 0;
                squeal.pause();
                $.modal.close();
                window.clearInterval(squealIntervalId);
              }
            },
            100
          );
        }
      };
      header.bind('click', Listeners[headerListenerName]);
    } else {
      PauseSound(audio);
    }
  };

  /**
   * Toggles whether the Russian anthem will play/pause when the logo is clicked.
   * @param state {Boolean} Whether to enable or disable the behavior.
   * @private
   * @return {Undefined}
   */
  var ToggleRussianAnthem = function(state) {
    var logo = $('#logo');
    var listenerName = 'ToggleRussianAnthem.click';
    if (Listeners[listenerName]) {
      logo.unbind('click', Listeners[listenerName]);
      Listeners[listenerName] = null;
    }
    var audio = '#russian-anthem';
    if (state) {
      var isPlaying = false;
      Listeners[listenerName] = function() {
        if (isPlaying) {
          PauseSound(audio);
        } else {
          PlaySound(audio, true);
        }
        isPlaying = !isPlaying;
      };
      logo.bind('click', Listeners[listenerName]);
    } else {
      PauseSound(audio);
    }
  };

  /**
   * Toggles whether various sounds will play when parts of the header are clicked.
   * @param state {Boolean} Whether to enable or disable the behavior.
   * @private
   * @return {Undefined}
   */
  var ToggleScience = function(state) {
    var header = $('header,#header,#masthead');
    var listenerName = 'ToggleScience.click';
    if (Listeners[listenerName]) {
      header.unbind('click', Listeners[listenerName]);
      Listeners[listenerName] = null;
    }
    if (state) {
      Listeners[listenerName] = function(event) {
        var x = event.pageX - this.offsetLeft;
        if (x < 115) {
          PlaySound('#hmhmfuahahahahahaha');
        } else if (x >= 155 && x < 230) {
          PlaySound('#hakasehakasehakasehakase');
        } else if (x >= 750 && x < 870) {
          PlaySound('#nanonanonanonano');
        } else if (x >= 870) {
          PlaySound('#fuahahahahaha');
        }
      };
      header.bind('click', Listeners[listenerName]);
    }
  };

  /**
   * Toggles whether Yuno's insane laugh will play when the logo is clicked.
   * @param state {Boolean} Whether to enable or disable the behavior.
   * @private
   * @return {Undefined}
   */
  var ToggleYahahahahahaha = function(state) {
    var logo = $('#logo');
    var listenerName = 'ToggleYahahahahahaha.click';
    if (Listeners[listenerName]) {
      logo.unbind('click', Listeners[listenerName]);
      Listeners[listenerName] = null;
    }
    if (state) {
      Listeners[listenerName] = function() {
        PlaySound('#yahahahahahaha');
      };
      logo.bind('click', Listeners[listenerName]);
    }
  };

  /**
   * Updates the current channel's aspect ratio.
   * @private
   * @return {Undefined}
   */
  var UpdateAspectRatio = function() {
    var content = $('#content');
    content.removeAttr('class');
    if (Channel) {
      if (Channel.aspectRatio == '16:9') {
        content.addClass('layout-16-by-9');
      } else {
        content.addClass('layout-4-by-3');
      }
      if (Channel.service == 'own3d.tv') {
        content.addClass('layout-own3d-tv');
      }
    } else {
      content.addClass('layout-16-by-9');
    }
  };

  /**
   * Updates channel data.
   * @private
   * @return {Undefined}
   */
  var UpdateChannels = function() {
    window.setInterval(
      function() {
        GetChannels(
          function() {
            AddChannels();
            var channelFound = false;
            $.each(
              Channels,
              function() {
                if (this.name == Channel.name) {
                  Channel = this;
                  channelFound = true;
                }
                return !channelFound;
              }
            );
            if (!channelFound) {
              Channel.live = false;
            }
            var channelLoaded = false;
            if (!Channel.live) {
              var isAnyChannelLive = false;
              $.each(
                Channels,
                function() {
                  if (this.live) {
                    isAnyChannelLive = true;
                  }
                  return !isAnyChannelLive;
                }
              );
              var date = new Date();
              var time = date.getTime();
              var isRecentlyLive = (time - Channel.lastLiveTime <= 120000);
              var isRecentlyLoaded = (time - ManualChannelLoadTime <= 120000);
              var isDeadChannel = !isRecentlyLive && !isRecentlyLoaded;
              var isLiveChange = isAnyChannelLive && isDeadChannel;
              var isAutopilotChange = !isAnyChannelLive && !Channel.autopilot && isDeadChannel;
              if (isLiveChange || isAutopilotChange) {
                LoadChannel(false, isAnyChannelLive);
                channelLoaded = true;
              }
            }
            if (!channelLoaded) {
              UpdatePlayer();
            }
          }
        );
      },
      30000
    );
  };

  /**
   * Highlights the active channel and updates the player's aspect ratio.
   * @private
   * @return {Undefined}
   */
  var UpdatePlayer = function() {
    HighlightActiveChannel();
    UpdateAspectRatio();
  };
};
