﻿var TTTX = {},
  player;

TTTX.Lightbox = (function ($) {
  // Close overlaying iframe
  function close() {
    $('.ad iframe').show();
    $('#lightbox').fadeOut(500, function () {
      $(this).remove();
    });
    
    my22Load();
    
    return false;
  }

  // Open overlaying iframe with url
  function show(page_url) {
    // Hide ad to prevent Flash z-index trouble
    $('.ad iframe').hide();

    // Build html
    var lb_template = $('#lightbox-template').html(),
      $lb = $(_.template(lb_template, {page_url: page_url}));

    // Attach to dom
    $('.container').append($lb);
    $lb.hide().fadeIn(250);
    $('#lightbox-back').click(close);
  }

  // Public methods
  return {
    close: close,
    show: show
  };
}(jQuery));

TTTX.Log = (function ($) {
  var START = 'START',
    END = 'END',
    COMPLETE = 'COMPLETE',
    THRESHOLD = 'THRESHOLD',
    THRESHOLD_TIME = 15000,
    ENABLED = false;
  
  // Do the call to the server
  function log (log_type, track_id, playback_time) {
    if (!ENABLED) {
      return;
    }
    var url = ROOT + 'log';
    url += '?log_type=' + log_type;
    url += '&track_id=' + track_id;
    url += '&playback_time=' + playback_time;
    $.ajax(url);
  }
  
  // Log the starting playback of a track by id
  function track_start(track_id) {
    log(START, track_id, 0);
  }
  
  // Log the end of listening to a track by id and listening time
  function track_threshold(track_id) {
    log(THRESHOLD, track_id, THRESHOLD_TIME);
  }
  
  // Log the end of listening to a track by id and listening time
  function track_end(track_id, playback_time) {
    log(END, track_id, playback_time);
  }
  
  // Log the end of listening to a track by id and listening time
  function track_complete(track_id, playback_time) {
    log(COMPLETE, track_id, playback_time);
  }
  
  // Getter for threshold_time
  function get_threshold () {
    return THRESHOLD_TIME;
  }
  
  return {
    track_start: track_start,
    track_threshold: track_threshold,
    track_end: track_end,
    track_complete: track_complete,
    get_threshold: get_threshold
  }
})(jQuery);

TTTX.Scrobble = (function ($) {  
  // Start scrobbling the track
  function scrobble (track_id) {
    var url = ROOT + 'api/lastfm_scrobble_url/' + track_id;
    $.ajax({
      url: url,
      success: url_received
    });
  }
  
  // Call the generated url
  function url_received(url) {
    if (url && url !== "false") {
      $.post(url);
    }
  }
  
  return {
    scrobble: scrobble
  }
})(jQuery);
var my22;

// Add track with id to my22
function my22Add(track_id) {
  var url = ROOT + 'api/my22_add/' + track_id;

  // Call api to save track to favorites
  $.ajax({
    url: url,
    success: function (data) {
      // Add to already loaded my22 tracks
      my22.tracks.push(track_id);

      // Over the limit?
      if (my22.tracks.length === 22 + 1) {
        TTTX.Lightbox.show(ROOT + 'modal/my22_overflow_error');
      }

      // Make track add link active
      $('#track' + track_id + ' .add a').addClass('added');
    }
  });
}
///////////////////////////////////
// Remove track with id from my22
function my22Remove(track_id) {
  var url = ROOT + 'api/my22_remove/' + track_id;

  // Call api to save track to favorites
  $.ajax({
    url: url,
    success: function (data) {
      // Remove from already loaded my22 tracks
      my22.tracks.splice(my22.tracks.indexOf(track_id), 1);

      // Viewing own my22?
      if (my22.id === player.genre.id) {
        // Remove from track collection
        player.tracks.remove(player.tracks.get(track_id));

        // Remove from visible tracks
        $('#track' + track_id).fadeOut(250, function () {
          $(this).remove();

          // Restripe
          var even = true;
          $('.track-nav tr').removeClass('even').removeClass('odd').each(function () {
            if (even) {
              $(this).addClass('even');
            } else {
              $(this).addClass('odd');
            }
            even = !even;
          });

          // Enter 22nd track
          if (player.tracks.length < 22) {
            return;
          }
          // Get 22nd track
          var json = player.tracks.at(21).toJSON();

          // Add class for zebra striping
          json.class_name = "even";

          // Is currently playing?
          if (json.id === player.track_id) {
            json.class_name += " active";
          }

          // Is new
          json.is_new = json.created > player.last_visit;

          // Build link
          json.link = '#' + player.genre_slug + '/' + json.id;

          // Is added to the current users' my22?
          json.added = true;

          var template = $('#track-list-template').html(),
            html = _.template(template, json);
        
          $('.track-nav').append(html).hide().fadeIn(250);
        });
      } else {
        // Make track add link active
        $('#track' + track_id + ' .add a').removeClass('added');
      }
    }
  });
}

// Save in iframe succesful
function my22Saved() {
  my22Load();
  if (window.location.hash.indexOf('#my22-'+my22.id) === 0) {
    player.switch_genre(true);
  } else {
    window.location.hash = '#my22-' + my22.id;
  }
}

function my22Load() {
  var url = ROOT + 'api/my22';
  
  var cache_breaker = Math.round(new Date().getTime());
  url = url + '?' + cache_breaker;
  
  // Call api to save track to favorites
  $.getJSON(url, function (data) {
    my22 = data;
    
    $('.my22-nav a').css('display', 'inline-block');
    
    // No my22 found for user?
    if (my22 === null) {
      $('.my22-nav a').hide();
      $('#my22-login-link').show();
      return;
    }
    
    $('#my22-login-link').hide();
    $('#my22-link').attr('href', '#my22-' + my22.id);
    
    // Tracks already loaded?
    if (player.tracks && player.tracks.length > 0) {
      $('.track-nav tr').each(function () {
        var $this = $(this),
          track_id;
        track_id = $this.attr('id').replace('track', '');
        
        // Track is in my22?
        if (my22.tracks.indexOf(track_id) >= 0) {
          $this.find('.add a').addClass('added');
        }
      });
    }
    
    if (player.genre && player.genre.get('slug') === 'my22-' + data.id) {
      my22Saved();
    }
  });
}

// Add my22 functionality to my22 add links
function my22AddClicks() {
  $('.add a').live('click', function () {
    var track_id = $(this).parents('tr').attr('id').replace('track', '');
    
    // Not logged in?
    if (!my22) {
      $('#my22-login-link').click();
      return false;
    }
    
    // Already added?
    if ($(this).hasClass('added')) {
      // Remove
      my22Remove(track_id);
    } else {
      // Add
      my22Add(track_id);
    }
    return false;
  });
  $('.list .aged .artist a, .list .aged .title a, .list .aged .album a').live('click', function () {
    
    TTTX.Lightbox.show(ROOT + 'modal/greyed_error/');
    return false;
  });
}

// Login in iframe succesful
function my22LoginSuccess() {
  my22Load();
}

function my22Logout() {
  my22 = null;
  $('.add .added').removeClass('added');
  $('.my22-nav a').hide();
  $('#my22-login-link').show();
  
  // Is a my22?
  if (player.genre && player.genre.get('slug').indexOf('my22-') === 0) {
    // Hard refresh genre
    player.switch_genre(true);
  }
}

// Make my22 related links work
function startMy22() {
  // Make add links clickable
  my22AddClicks();
  
  // Load my22
  my22Load();
  
  // Make my22 nav work
  $('#my22-login-link').click(function () {
    TTTX.Lightbox.show(ROOT + 'connect/');
    return false;
  });
  $('#my22-logout-link').click(function () {
    TTTX.Lightbox.show(ROOT + 'connect/logout/');
    return false;
  });
  $('#my22-settings-link').click(function () {
    TTTX.Lightbox.show(ROOT + 'connect/settings/');
    return false;
  });
}

// main 22tracks class
(function ($) {
  'use strict';
  
  // Convert milliseconds to nicelooking minutes:seconds
  function toMinutes(milliseconds) {
    var seconds = Math.floor((milliseconds / 1000) % 60),
      minutes = Math.floor(milliseconds / (60 * 1000));
    if (seconds < 10) {
      seconds = "0" + seconds;
    }
    return minutes + ":" + seconds;
  }
  
  // Refresh impressions for background-ad every so many time
  var impressions_settings = {
    interval: null,
    interval_time: 10 * 60 * 1000,
    log_url: '',
    append_html: ''
  };
  // Track impressions in openx and externally
  function impression_refresh() {
    
    var log_url = impressions_settings.log_url + Math.floor(Math.random() * 99999999999),
      append_html = impressions_settings.append_html;
    
    // Log impression to openx (Call log url in iframe) 
    $('#openx_frame').attr('src', "")
      .attr('src', log_url);
    
    // External impression tracker with cachebreaker
    if (append_html !== "") {
      var cb = new Date().getTime();
      append_html = append_html.replace("[cachebreaker]", cb);
      $('#external-impression').empty().append(append_html);
    }
  }
  
  // Scroll long titles
  var scroll_interval_id = null;
  function autoScrollHorizontal($element, target_width) {
    $element.css('width', 'auto');
    var going_left = true,
      step = 1,
      scroll_interval_time = 60,
      min_margin = -($element.width() - target_width),
      new_margin = 0;
    
    function scroll() {
      var wait = scroll_interval_time;
      
      if (going_left) {
        new_margin -= step;
        if (new_margin <= min_margin) {
          wait = 1200;
          going_left = false;
        }
      } else {
        new_margin += step;
        if (new_margin >= 0) {
          wait = 1200;
          going_left = true;
        }
      }
      $element.css('margin-left', new_margin);
      
      scroll_interval_id = setTimeout(scroll, wait);
    }
    
    clearTimeout(scroll_interval_id);
    if ($element.width() > target_width) {
      setTimeout(scroll, 1200);
    } else {
      $element.css('width', 302);
    }
  }
  
  function addBrandedCustom(genre) {
    var branded = genre.get('branded'),
      $style;
    
    // Remove previous
    $('#branded-style').remove();
    
    // Add new
    if (branded && branded.css) {
      $style = $('<style type="text/css" id="branded-style">' + branded.css + '</style>');
      $('body').append($style);
    }
    
    
    if (branded && branded.javascript) {
      $('body').append('<sc' + 'ript type="text/javascript" id="branded-script">' + branded.javascript + '</s' + 'cript>');
    } else {
      $('#branded-script').remove();
    }
  }
  
  // Resize branded and openx genre
  function resizeBackground() {
    var $brand_img = $('#background-branded img'),
      $openx_img = $('#background-openx img'),
      $container = $('.container'),
      openxH = $openx_img.height(),
      openxW = $openx_img.width(),
      openxR = openxW / openxH,
      brandedR = $brand_img.width() / $brand_img.height(),
      screenW = $container.width(),
      screenH = $container.height(),
      screenR = screenW / screenH,
	  full_width = true;
    
    // Openx magic
    if (openxH > 0 && openxW > 0) {
      if (screenR > openxR && full_width) {
        $openx_img.attr('width', screenW);
        $openx_img.attr('height', screenW / openxR);
        $openx_img.css('margin-top', (screenH - screenW / openxR));
        $openx_img.css('margin-left', 0);
      } else {
        $openx_img.attr('width', screenH * openxR);
        $openx_img.attr('height', screenH);
        $openx_img.css('margin-top', 0);
        $openx_img.css('margin-left', (screenW - screenH * openxR) / 2);
        if (! full_width) {
          $openx_img.css('box-shadow', '0 0 30px rgba(0,0,0,0.25)');
        }
      }
    }
    
    // Branded magic
    if (screenR > brandedR) {
      $brand_img.attr('width', screenW);
      $brand_img.attr('height', screenW / brandedR);
      $brand_img.css('margin-top', ((screenW / screenR - screenW / brandedR) / 2));
    } else {
      $brand_img.attr('width', screenH * brandedR);
      $brand_img.attr('height', screenH);
      $brand_img.css('margin-top', 0);
    }
  }
  
  function setFade($el) {
    $el.live('mouseenter', function () {
      $(this).stop().fadeTo(250, 0.66);
    }).live('mouseleave', function () {
      $(this).stop().fadeTo(500, 1);
    });
  }
  
  function fadeGenreNavs() {
    $('ul.genre-nav a:not(.active)').live('mouseenter', function () {
      $(this).css('background-color', $(this).attr('data-col'));
    }).live('mouseleave', function () {
      $(this).css('background-color', 'transparent');
    });
  }
  
  // Adds click class for css styling on mouse down
  function addClickClass() {
    $('a').live('mousedown', function () {
      $(this).addClass('click');
    }).live('mouseup', function () {
      $(this).removeClass('click');
    }).live('mouseleave', function () {
      $(this).removeClass('click');
    });
  }
  
  // Adds hover style to whole tr in track list
  function addHoverListTr() {
    $('.list td:not(.shop,.add)').live('mouseenter', function () {
      $(this).parent().addClass('hover');
    }).live('mouseleave', function () {
      $(this).parent().removeClass('hover');
    });
  }
  
  // Allows switching between two expandable panels in sidebar
  function addExpandableInfo() {
    $('.expandable a.expand').live('click', function () {
      var $parent = $(this).parent(),
        $target_div;
      
      // Parent expanded XOR parent is artist-info?
      if ($parent.hasClass('expanded') !==  $parent.hasClass('artist-info')) {
        $target_div = $('.artist-info');
      } else {
        $target_div = $('.genre-info');
      }
      
      $('.expanded').removeClass('expanded');
      $target_div.addClass('expanded');
      
      return false;
    });
  }
  
  var ad_interval = null,
    ad_interval_time = 10 * 60 * 1000; // ms
  
  // Show a different ad for the genre
  function rotateAd() {
    if (player.ad_zone === null) {
      return;
    }
    var random_num = Math.floor(Math.random() * 99999999999),
      ad_src = "http://ads.22tracks.com/www/delivery/afr.php?zoneid=" + player.ad_zone + "&amp;cb=" + random_num;

    $('#ad_frame').attr('src', ad_src);
  }
  
  // Reset timer and show new ad
  function resetAdTimer() {
    if (ad_interval !== null) {
      clearInterval(ad_interval);
    }
    ad_interval = setInterval(rotateAd, ad_interval_time);
    rotateAd();
  }
  
  // Models
  var Track = Backbone.Model.extend({}),
    Tracks = Backbone.Collection.extend({
      model: Track
    }),
    Genre = Backbone.Model.extend({
      initialize: function () {
        this.tracks = new Tracks();
        this.tracks.url = ROOT + 'api/tracks/' + this.get('id');
        this.url = ROOT + 'api/genre/' + this.get('id');
      }
    }),
    My22_Genre = Backbone.Model.extend({
      initialize: function () {
        var cachebreaker = "?" + new Date().getTime();
        this.tracks = new Tracks();
        this.tracks.url = ROOT + 'api/my22_tracks/' + this.get('id') + cachebreaker;
        this.url = ROOT + 'api/my22_genre/' + this.get('id') + cachebreaker;
      }
    }),
    Genres = Backbone.Collection.extend({
      model: Genre,
      url: ROOT + 'api/genres/' + CITY_ID
    }),
    City = Backbone.Model.extend({
      url: ROOT + 'api/city/' + CITY_ID,
      genres: null,
      branded_genre: null,
      initialize: function () {
        // Load model data for city
        this.genres = new Genres();
      }
    }),
    
    // Views
    GenreListView = Backbone.View.extend({
      el: '.genre-nav',
      template: '#genre-list-template',
      render: function () {
        var template = this.template,
          $el = $(this.el),
          html = "";
        
        this.collection.each(
          function (genre) {
            if (!genre.get('branded_id')) {
              var json = genre.toJSON();
              html = html + _.template($(template).html(), json);
            }
          }
        );
        $el.empty().append(html);
        
        if (FLASHY) {
          var delay = 0; // Delay for fading in
          var $genres = $el.children(),
            len = $genres.length;
          for (var i = 0 ; i < len ; i++) {
            $genres.eq(i).hide().delay(delay).fadeIn(750);
            delay += 75;
          };
        }
        
        $('.shuffler').click(function (e) {
          var $link = $(this).parent(),
            genre_link = $link.attr('href').substring(1);
          if (!$link.hasClass('shuffle-active')) {
            player.add_shuffle_genre(genre_link);
            $link.addClass('shuffle-active');
          } else {
            if (player.shuffle_genres.length <= 1) {
              return false;
            }
            player.remove_shuffle_genre(genre_link);
            $link.removeClass('shuffle-active');
          }
          
          e.stopPropagation(); // Dont't call switch genre link
          return false;
        });
      },
      toggle_shuffle: function () {
        var $el = $(this.el);
        if (player.shuffle) {
          $el.addClass('shuffle');
        } else {
          $el.removeClass('shuffle');
        }
      }
    }),
    GenreView = Backbone.View.extend({
      el: '.genre-info',
      template: '#genre-template',
      render: function () {
        var html = _.template($(this.template).html(), this.model.toJSON());
        $(this.el).empty().html(html).hide().fadeIn(750);
        $('.genre-info .image a, .genre-info .image').css('background-color', this.model.get('color'));
      }
    }),
    TrackView = Backbone.View.extend({
      el: '.artist-info',
      template: '#artist-template',
      render: function () {
        var html = _.template($(this.template).html(), this.model.toJSON());
        $(this.el).empty().html(html).hide().fadeIn(750);
      }
    }),
    BrandedGenreView = Backbone.View.extend({
      el: '.branded-link',
      template: '#branded-template',
      render: function () {
        var html = _.template($(this.template).html(), this.model.toJSON());
        $(this.el).empty().html(html).hide().fadeIn(750);
      }
    }),
    TrackListView = Backbone.View.extend({
      el: $('.track-nav'),
      template: '#track-list-template',
      render: function () {
        var even = true, // Even or odd row
          delay = 0, // Delay for fading in
          template = $(this.template).html(),
          html = "",
          i = 0,
          models = this.collection.models,
          size = Math.min(22, models.length),
          last_visit = player.last_visit;
        
        for (; i < size; i += 1) {
          // Only list non-brandeds
          var json = models[i].toJSON();
          
          // Add class for zebra striping
          if (even) {
            json.class_name = "even";
          } else {
            json.class_name = 'odd';
          }
          
          // Is currently playing?
          if (json.id === player.track_id) {
            json.class_name += " active";
          }
          
          // Is new since last_visit?
          json.is_new = json.created > last_visit;
          
          // Build link
          json.link = '#' + player.genre_slug + '/' + json.id;
          
          // Is added to the current users' my22?
          if (my22) {
            json.added = my22.tracks.indexOf(json.id) >= 0;
          } else {
            json.added = false;
          }
          
          // Concat output
          html = html + _.template(template, json);
          
          // Toggle even/odd
          even = !even;
        }
        
        this.el.empty().append(html);
        
        // Round off 'new' labels
        var $new_labels = $('tr .new');
        $new_labels.first().addClass('first');
        $new_labels.last().addClass('last');
        $new_labels
          .css({width: 0, left: 1})
          .delay(500)
          .animate({width: 50, left: -51}, 750)
          .delay(5000)
          .animate(
            {width: 0, left: 1}, 
            1000, 
            function () {
              $(this).remove();
            }
          );
        
        if (FLASHY) {
          var $tracks = this.el.children(),
            len = $tracks.length;
          for (var i = 0 ; i < len ; i++) {
            $tracks.eq(i).hide().delay(delay).fadeIn(750);
            delay += 75;
          };
        }
      }
    }),
    BackgroundView = Backbone.View.extend({
      impression_interval: null,
      impression_log_url: '',
      impression_append_html: '',
      el: $('.container'),
      render: function () {
        var color = this.model.get('color');
        
        if (! $('html').hasClass('csstransitions')) {
          this.el.stop().animate({backgroundColor: color}, 750);
        } else {
          this.el.css('background-color', color);
        }
        
        // Stop refreshing impressions
        if (impressions_settings.interval !== null) {
          clearInterval(impressions_settings.interval);
        }
        
        // Show custom background
        this.show_branded();
      },
      change: function (genre) {
        this.model = genre;
        
        // Auto render on data
        this.render();
      },
      show_openx_background: function () {
        var $openx_bg = this.el.find('#background-openx'),
          bg_zone = this.model.get('ad_background_zone');
        
        $openx_bg.find('img').stop().hide();
        $('#custom-openx').empty();
        
        // No openx background zone
        if (!bg_zone) {
          return;
        }
        
        $openx_bg.show();
        
        function success(data){
          var $data = $(data),
            img_url = $data.find('creativeUrl').text(),
            log_url = $data.find('logUrl').text(),
            link_url = $data.find('clickUrl').text(),
            append_html = $data.find('append').text(),
            $img = $openx_bg.find('img');
          
          if (link_url === "" || img_url === "" || log_url === "") {
            return;
          }
          
          impressions_settings.log_url = 'http://ads.22tracks.com/www/delivery/afr.php?zoneid=' + bg_zone + '&amp;cb=';
          impressions_settings.append_html = append_html;
          impressions_settings.interval = setInterval(impression_refresh, impressions_settings.interval_time); //10 * 60 * 1000);
          impression_refresh();
          
          // Make openx image scale
          $(window).bind('resize', resizeBackground);
          $img.bind('load', resizeBackground)
            .attr('src', img_url)
            .stop().delay(2000).fadeTo(500, 1);
            
          // Hide overlay
          $('#background-overlay').stop().fadeOut(2000);
          
          // Add click
          $('#backgrounds').click(function () {
            window.open(link_url);
          }).css('cursor', 'pointer');
          
          // Custom ad things
          
          // WAR Project X
          if (img_url == 'http://ads.22tracks.com/www/images/d85d5a05f3eb124ed1db1c3437eb7a2a.jpg') {
            $('#custom-openx')
              .append('<img src="http://22tracks.com/drop/branded_content/warprojectx/text.png" />')
              .hide()
              .delay(3000)
              .fadeTo(1000, 1);
            $('#custom-openx img')
              .css('position', 'absolute')
              .css('left', '50%')
              .css('top', '50%')
              .css('margin-left', '360px')
              .css('margin-top', '-400px');
          }
        }
        
        // Load ad data as xml
        var cb = new Date().getTime(),
          openx_xml_url = ROOT+'api/proxy/?url=http://ads.22tracks.com/www/delivery/ax.php?zoneid=' + bg_zone + '&cb=' + cb;
          
        $.ajax({
          url: openx_xml_url,
          success: success,
          error: function(jqXHR, textStatus, errorThrown) {
            console.log(jqXHR);
            console.log(textStatus);
            console.log(errorThrown);
          }
        });
      },
      show_branded: function () {
        $('#background-openx').hide();
        $('#background-overlay').stop().css('opacity', 1).show();
        
        var $branded = this.el.find('#background-branded'),
          branded_data = this.model.get('branded'),
          $img,
          background_type;
        
        // Remove any background links
        $('#backgrounds').unbind('click')
          .css('cursor', 'auto');
        
        // Not a branded genre?
        if (!branded_data) {
          $branded.fadeOut(750);
          
          this.show_openx_background();
          return;
        }
        
        $img = $branded.find('img');
        if (branded_data.background_tiling === 'stretch') {
          $branded.css('background-image', 'none');
          
          $(window).bind('resize', resizeBackground);
          
          $img.bind('load', resizeBackground)
            .attr('src', ROOT + branded_data.background_path)
            .show();
        } else {
          $img.hide();
          if (branded_data.background_tiling === 'tile') {
            background_type = 'repeat';
          } else {
            background_type = 'no-repeat';
          }
          $branded.css('background-image', 'url(' + ROOT + branded_data.background_path + ')')
            .css('background-repeat', background_type);
        }
        $branded.fadeIn(750);
      }
    }),
    
    // Controller
    PlayerController = Backbone.Controller.extend({
      ad_zone: null,
      city: null,
      tracks: null,
      genre_slug: null,
      genre: null,
      genre_title: null,
      genre_list: null, // Genre navigation
      genre_hashtag: '22tracks', // default hashtag (before genre)
      track_id: null,
      track_url: 'http://22tracks.com/', // default share link (before track)
      genre_view: null,
      track_view: null,
      artist_info_view: null,
      background_view: null,
      sound: null,
      volume: 100,
      shuffle: false,
      shuffle_genres: [],
      current_track: null,
      last_visit: null,
      use_rtmpt: false,
      track_playing_time: 0,
      threshold_logged: false,
      track_scrobbled: false,
      initialize: function () {
        // Allow using 'this' in these functions
        _.bindAll(this, "genres_loaded", "tracks_loaded");

        // Start Background
        this.background_view = new BackgroundView();

        this.bind_player_events();
      },
      // Routing, based on jquery history. Backbone history bugs in ie7
      hashchange: function (hash) {
        // Index link
        if (hash === "") {
          player.index();
          return;
        }
        // Genre link
        if (hash.indexOf('/') < 0) {
          player.genre_link(hash);
        } else {
          // Track link
          var route_parts = hash.split('/'),
            genre_slug = route_parts[0],
            track_id = route_parts[1];
          player.track_link(genre_slug, track_id);
        }
      },
      bind_player_events: function () {
        // Pause Button
        $('.controls .pause').live('click', function () {
          if (player.sound !== null) {
            player.sound.pause();
            
            $(this).removeClass('pause').addClass('play');
          }
        });
        // Play Button
        $('.controls .play').live('click', function () {
          if (player.sound !== null) {
            player.sound.play();
            
            $(this).removeClass('play').addClass('pause');
          }
        });
        // Next Button
        $('.controls .next').live('click', function () {
          player.next_track();
        });
        // Prev Button
        $('.controls .prev').live('click', function () {
          player.prev_track();
        });
        // Volume slider
        var handle_settings = {
          axis: 'y',
          handle: '.controls .volume',
          containment: 'parent',
          snap: true,
          drag: function () {
            var perc = (33 - $(this).position().top) / 33;
            player.set_volume(perc);
          }
        };
        $('.controls .handle').draggable(handle_settings);
        $('.controls .volume').click(function () {
          var new_volume = 0;
          if (player.volume === 0) {
            new_volume = 1;
          }
          player.set_volume(new_volume);
          $('.handle').css('top', (1 - new_volume) * 33);
        });
        // Shuffle Button
        $('.controls .shuffle').click(function () {
          player.toggle_shuffle();
        });
        // Seek bar
        $('.controls .progress').mouseup(function (e) {
          var offsetX = e.offsetX,
            perc;
          if (typeof offsetX === 'undefined') {
            offsetX = e.pageX - $(this).offset().left;
          }
          perc = offsetX / $(this).width();
          player.seek(perc);
          $(this).unbind('mousemove');
        }).mousedown(function (e) {
          $(this).mousemove(function (e) {
            var offsetX = e.offsetX,
              perc;
            if (typeof offsetX === 'undefined') {
              offsetX = e.pageX - $(this).offset().left;
            }
            perc = offsetX / $(this).width();
            player.seek(perc);
          });
          return false;
        });
        // Facebook share
        $('.controls .facebook').click(function () {
          var sharer_url = player.track_url,
            share_url,
            separator = (sharer_url.indexOf("/#") > 0) ? "/#" : "#",
            url_array = sharer_url.split(separator),
            url_prefix = url_array[0].substring(0, url_array[0].lastIndexOf('/')),
            city_slug = url_array[0].substring(url_array[0].lastIndexOf('/'));
          
          sharer_url = url_prefix + '/share' + city_slug + '/' + url_array[1];
          share_url = 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(sharer_url) + '&t=' + encodeURIComponent($('title').text());
          
          window.open(share_url, 'sharer', 'toolbar=0,status=0,width=626,height=436');
          return false;
        });
        // Twitter share
        $('.controls .twitter').click(function () {
          var tweet = '#nowplaying ',
            track_title = $('.controls .title').text(),
            default_tweet = '22tracks.com';
          
          if (track_title === '') {
            tweet = default_tweet;
          } else {
            tweet += track_title + ' #' + player.genre_hashtag;
          }
          
          var sharer_url = player.track_url,
            share_url,
            separator = (sharer_url.indexOf("/#") > 0) ? "/#" : "#",
            url_array = sharer_url.split(separator),
            url_prefix = url_array[0].substring(0, url_array[0].lastIndexOf('/')),
            city_slug = url_array[0].substring(url_array[0].lastIndexOf('/'));
          
          sharer_url = url_prefix + '/share' + city_slug + '/' + url_array[1];
          
          tweet = encodeURIComponent(tweet);
          window.open('http://twitter.com/share?url=' + encodeURIComponent(sharer_url) + '&via=22tracks&text=' + tweet, 'sharer', 'toolbar=0,status=0,width=626,height=436');
          return false;
        });
        
        // Keyboard controls
        $(document).bind('keyup', 'right', function () {$('.controls .next').click(); });
        $(document).bind('keyup', 'left', function () {$('.controls .prev').click(); });
        $(document).bind('keydown', 'up', function () {
          var new_volume = Math.min(1, (player.volume + 10) / 100);
          player.set_volume(new_volume);
          $('.handle').css('top', (1 - new_volume) * 33);
          return false;
        });
        $(document).bind('keydown', 'down', function () {
          var new_volume = Math.max(0, (player.volume - 10) / 100);
          player.set_volume(new_volume);
          $('.handle').css('top', (1 - new_volume) * 33);
          return false;
        });
        $(document).bind('keyup', 's', function () {$('.controls .shuffle').click(); });
        $(document).bind('keyup', 'space', function () {$('.controls .playpause').click(); return false; });
        $(document).bind('keyup', 'return', function () {$('.controls .playpause').click(); return false; });
        $(document).bind('keyup', 'f', function () {$('.controls .facebook').click(); return false; });
        $(document).bind('keyup', 't', function () {$('.controls .twitter').click(); return false; });
      },
      index: function () {
        // Load genres
        if (this.city === null) {
          this.city = new City();
          this.city.genres.bind('refresh', this.genres_loaded);
          this.city.bind('change', function () {
            player.city.genres.fetch();
          });
          this.city.fetch();
        } else {
          this.genre_slug = null;
          this.city.genres.trigger('refresh');
        }
      },
      genre_link: function (genre_slug) {
        this.genre_slug = genre_slug;
        
        // If city/genres not loaded do this in index
        if (this.city === null) {
          this.index();
          return;
        }
        
        // Show genre
        this.switch_genre();
      },
      track_link: function (genre_slug, track_id) {
        this.track_id = track_id;
        
        if (this.tracks === null) {
          this.genre_link(genre_slug);
          return;
        }
        // Play track
        this.switch_track();
      },
      genres_loaded: function () {
        var  city = this.city;
        
        // Render genrelistview
        this.genre_list = new GenreListView({collection: this.city.genres});
        this.genre_list.render();
        
        // Render branded button
        if (this.city.get('branded_id')) {
          var brandedview,
            branded = this.city.genres.find(
              function (genre) {
                // Match genre.id to city.branded_id
                return genre.get('id') === city.get('branded_id');
              }
            );
          brandedview = new BrandedGenreView({model: branded});
          brandedview.render();
        }
        
        if (this.city.genres.length <= 0) {
          // Empty city
          return;
        }
        
        if (this.genre_slug !== null) {
          // Genre is already given through URL
          this.genre_link(this.genre_slug);
          return;
        }
        // Select a genre to start playing
        var genre,
          randomized_genres;
        
        // Try to find branded playlist for city
        if (this.city.get('branded_id')) {
          genre = this.city.genres.find(
            function (genre) {
              // Match genre.id to city.branded_id
              return genre.get('id') === city.get('branded_id');
            }
          );
        }
        // No branded genre found, default to a random unbranded one
        if (typeof genre === "undefined") {
          // Order by not being branded and random
          randomized_genres = this.city.genres.sortBy(
            function (genre) {
              if (genre.get('branded_id')) {
                return 0;
              } else {
                return -Math.random();
              }
            }
          );
          // Select first by sort
          genre = randomized_genres[0];
        }
        
        // Change url and trigger route in controller
        window.location.hash = genre.get('slug');
      },
      tracks_loaded: function () {
        // Empty genre
        if (this.tracks.length <= 0) {
          return;
        }
        
        // Render tracklistview
        if (this.tracklist === null) {
          this.tracklist = new TrackListView({collection: this.tracks});
          this.tracklist.render();
        }
        
        // Already playing
        if (this.sound !== null) {
          return;
        }
        
        // Track is already given through URL
        if (this.track_id !== null) {
          this.track_link(this.genre_slug, this.track_id);
          return;
        }
        
        // Start from the top
        var track_no = 0;
        
        // Or ad random
        if (player.shuffle) {
          track_no = Math.floor(Math.random() * this.tracks.length);
        }
        
        var track = this.tracks.at(track_no);
        
        // Change url and trigger route in controller
        var new_hash = this.genre_slug + '/' + track.get('id');
        if (window.location.hash !== new_hash) {
          window.location.hash = new_hash;
        } else {
          this.track_link();
        }
      },
      switch_genre: function (reload) {
        // Select genre
		
        // Already loaded?
        var slug = this.genre_slug;
        if ((reload !== true) && this.genre && this.genre.get('slug') === slug) {
          this.tracks_loaded();
          return;
        }
		
        // Throw event (for custom js)
        $('body').trigger('switchgenre');

        // Process NEWcookie
        var cookie_parts = document.cookie.split(';'),
          last_cookie = (new Date().getTime()) - (7 * 24 * 60 * 60 * 1000);
        
        for(var i=0;i < cookie_parts.length;i++) {
          var c = cookie_parts[i];
          while (c.charAt(0)==' ') c = c.substring(1,c.length);
          if (c.indexOf('last_visit_' + slug + '=') == 0) last_cookie = c.substring('last_visit_' + slug + '='.length,c.length);
        }
        this.last_visit = last_cookie / 1000;
        
        // Set NEWcookie
        var now = new Date().getTime(),
          expiration_time = 30 * 24 * 60 * 60 * 1000,
          expiration_date = new Date(),
          expiration_string;
        
        expiration_date.setTime(now + expiration_time);
        expiration_string = expiration_date.toUTCString();
        document.cookie = 'last_visit_' + slug + '=' + now + '; expires='+ expiration_string;
        
        // Is a my22?
        if (this.genre_slug.indexOf('my22') === 0) {
          this.switch_my22(this.genre_slug);
          return;
        }
        
        // Not a my22 so not sortable
        $('.track-nav').sortable('destroy');
        
        
        // Select genre by slug
        this.genre = this.city.genres.find(function (genre) {
          return genre.get('slug') === slug;
        });
        
        // Genre not found?
        if (typeof this.genre === 'undefined') {
          this.genre_slug = null;
          this.genre = null;
          this.track_id = null;
          window.location.hash = "";
          return;
        }
        
        // Set genre variables
        this.genre_title = this.genre.get('title');
        this.genre_hashtag = this.genre.get('hashtag');
        if (this.genre_hashtag === '') {
          this.genre_hashtag = this.genre.get('slug');
        }
        
        // Set body className and remove all other body classnames
        $('body').attr('class', this.genre.get('slug'));
        
        // Show genre info
        this.genre_view = new GenreView({model: this.genre});
        this.genre_view.render();
        
        // Change background
        this.background_view.change(this.genre);
        
        // Highlight link
        $('ul.genre-nav .active').removeClass('active').css('background-color', 'transparent');
        var link = $('ul.genre-nav .' + this.genre.get('slug') + ' a');
        link.css('background-color', link.attr('data-col')).addClass('active');
        
        // Hide old tracks
        $('.track-nav').empty();
        this.tracklist = null;
        
        // Load tracks
        this.tracks = this.genre.tracks;
        this.tracks.unbind('refresh', this.tracks_loaded);
        this.tracks.bind('refresh', this.tracks_loaded);
        this.tracks.fetch();
        
        // Add branded styles/scripts
        addBrandedCustom(this.genre);
        
        // Make genre shufflable, if not set by user
        if (this.shuffle_genres.length <= 1) {
          this.shuffle_genres = [];
          this.add_shuffle_genre(this.genre_slug);
          $('.shuffle-active').removeClass('shuffle-active');
          $('.genre-nav a[href=#' + this.genre_slug + '] .shuffler').click();
        }
        
        // Show ad for genre
        player.ad_zone = this.genre.get('ad_rectangle_zone');
        setTimeout(resetAdTimer, 500);
      },
      switch_my22: function (my22_slug) {
        // Remove any genre highlighting
        $('ul.genre-nav .active').removeClass('active').css('background-color', 'transparent');
        
        // Hide old tracks
        $('.track-nav').empty();
        this.tracklist = null;
        this.genre = null;
        
        // Load my22 genre
        this.my22_genre = new My22_Genre({'id': my22_slug.substr(5)});
        this.my22_genre.bind('change', this.my22_loaded);
        this.my22_genre.fetch();
      },
      my22_loaded: function () {
      
        // Set sidebar text if own my22
        if (my22 && my22.id === this.get('id')) {
          this.set({'description_html': $('#my22-instructions-template').html()}, {silent: true});
        }
        
        player.genre = this;
        
        // Change background
        player.background_view.change(this);
        
        // Add branded styles/scripts
        addBrandedCustom(player.genre);
        
        // Show genre info
        player.genre_view = new GenreView({model: this});
        player.genre_view.render();
        
        
        // Set genre variables
        player.genre_title = this.get('title');
        player.genre_hashtag = this.get('hashtag');
        if (player.genre_hashtag === '') {
          player.genre_hashtag = this.get('slug');
        }
        
        $('body').attr('class', this.get('slug'));
        
        // Is current visitor's own my22?
        if (my22 && my22.id === this.get('id')) {
          $('#my22-share-twitter').click(function () {
            var my22_url = window.location.href,
              hash = window.location.hash;
            
            my22_url = my22_url.slice(0, my22_url.indexOf('#'));
            
            if (hash.indexOf('/') >= 0) {
              hash = hash.slice(0, hash.indexOf('/'));
            }
            my22_url += hash;
            
            var tweet = 'Check out my own My22 playlist on 22tracks ';
            
            tweet = encodeURIComponent(tweet);
            window.open('http://twitter.com/share?url=' + encodeURIComponent(my22_url) + '&via=22tracks&text=' + tweet, 'sharer', 'toolbar=0,status=0,width=626,height=436');
            return false;
          });
          $('#my22-share-facebook').click(function () {
            var my22_url = window.location.href,
              hash = window.location.hash,
              title = $('.genre-info .expand').text();
            
            my22_url = my22_url.slice(0, my22_url.indexOf('#'));
            
            if (hash.indexOf('/') >= 0) {
              hash = hash.slice(0, hash.indexOf('/'));
            }
            my22_url += hash;
            
            var share_url = 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(my22_url) + '&t=' + encodeURIComponent(title);
            window.open(share_url, 'sharer', 'toolbar=0,status=0,width=626,height=436');
            return false;
          });
          
          // Make sortable if own my22
          $('.track-nav').sortable({
            cursor:      'move',
            axis:        'y',
            containment: 'parent',
            delay:       250,
            tolerance:   'pointer',
            helper: function (e, ui) {
              ui.children().each(function () {
                $(this).width($(this).width());
              });
              ui.css('left', 0);
              return ui;
            },
            update:      function (e, ui) {
              var new_sort,
                track_id = ui.item.attr('id').substr(5),
                sort_above = parseFloat(ui.item.prev().attr('data-sort')),
                sort_below = parseFloat(ui.item.next().attr('data-sort'));
                
              // Reset striping
              $('.track-nav tr:odd').removeClass('even').addClass('odd');
              $('.track-nav tr:even').removeClass('odd').addClass('even');
              
              // Moved to topmost?
              if (isNaN(sort_above)) {
                // Also bottom most
                if (isNaN(sort_below)) {
                  // Only 1 item so no ordering needed
                  return;
                }
                
                // Else put above the topmost
                new_sort = sort_below + 1;
              }
              
              // Moved to bottom?
              if (isNaN(sort_below)) {
                // Move to below lowest order (TODO may cause bugs because of more than 22 tracks in my22 but not in view)
                new_sort = sort_above - 0.05;
              }
              
              // Not set yet?
              if (typeof new_sort === 'undefined') {
                new_sort = (sort_above + sort_below) / 2;
              }
              
              // Write new sort to db
              var sort_url = ROOT + 'api/my22_order/' + track_id + '?sort=' + new_sort.toString();
              $.ajax({
                url: sort_url,
                success: function (data) {
                  // update in list
                  
                  ui.item.attr('data-sort', new_sort);
                }
              });
            }
          });
        } else {
          $('.track-nav').sortable('destroy');
        }
        
        // Make genre shufflable, if not set by user
        if (player.shuffle_genres.length <= 1) {
          player.shuffle_genres = [];
          player.add_shuffle_genre(player.genre_slug);
          $('.shuffle-active').removeClass('shuffle-active');
          $('.genre-nav a[href=#' + player.genre_slug + '] .shuffler').click();
        }
        
        // Load tracks
        player.tracks = player.my22_genre.tracks;
        player.tracks.unbind('refresh', player.tracks_loaded);
        player.tracks.bind('refresh', player.tracks_loaded);
        player.tracks.fetch();
        
        // Show ad for genre
        player.ad_zone = this.get('ad_rectangle_zone');
        setTimeout(resetAdTimer, 500);
      },
      switch_track: function () {
        // Stop previous track
        player.stop();
        
        // Select track
        var track_id = this.track_id,
          track = this.tracks.find(function (track) {
            return track.get('id') === track_id;
          });
        
        if (typeof track === 'undefined') {
          this.track_id = null;
          this.next_track();
          
          return;
        }
        
        // Track is not live?
        if (track.get('is_live') != 1) {
          this.current_track = this.track_id;
          
          this.next_track();
          return;
        }
        
        // Set current track url for sharing
        this.track_url = window.location.href;
        
        // Show track info
        var title = track.get('artist') + ' - ' + track.get('title'),
          page_title = this.city.get('slug').toUpperCase() + ' - ' + this.genre_title + ' | ' + title,
          $title = $('.controls .title'),
          track_genre_slug = player.genre_slug;
        
        // Set title in player
        $title.html(title)
          .css('margin-left', 0)
          .unbind('click')
          .bind('click', function () {
            window.location.hash = track_genre_slug;
          });
        autoScrollHorizontal($title, 302);
        
        // Show artistinfo in sidebar
        this.track_view = new TrackView({model: track});
        this.track_view.render();
        
        // Set page title
        document.title = '22tracks | ' + page_title;
        
        // Highlight track
        $('.track-nav .active').removeClass('active');
        $('.track-nav #track' + track_id).addClass('active');
        
        // Start playback
        var bit_rate = 128,
          mp3_folder = ROOT + 'done/' + bit_rate + '/',
          filename = track.get('filename'),
          mp3_path = mp3_folder + escape(filename);
        
        var $done = $('.controls .done'),
          $slide = $('.controls .progress .slide'),
          $remaining = $('.controls .remaining'),
          last_draw;
        
        var next_track = this.next_track;
        var sound_settings = {
          id: 'sound',
          url: mp3_path,
          volume: this.volume,
          useWaveformData : true,
          useEQData : true,
          onconnect: function (connected) {
              // Fall back to RTMPT
            if (IS_LIVE_SERVER  && !connected && !player.use_rtmpt) {
              player.use_rtmpt = true;
              player.switch_track();
            }
          },
          onplay: function () {
            $('.controls .playpause').removeClass('play').addClass('pause');
            
            last_draw = new Date().getTime();
            
            // Add Triangle to title
            if (document.title.indexOf('\u25B6') !== 0) {
              document.title = '\u25B6 ' + document.title;
            }
            
            // Log playback start
            TTTX.Log.track_start(track_id);
          },
          onresume: function () {
            last_draw = new Date().getTime();
            
            // Add Triangle to title
            if (document.title.indexOf('\u25B6') !== 0) {
              document.title = '\u25B6 ' + document.title;
            }
          },
          onpause: function () {
            // Remove Triangle from title
            if (document.title.indexOf('\u25B6') === 0) {
              document.title = document.title.substr(2);
            }
          },
          onfinish: function () {
            // Remove Triangle from title
            if (document.title.indexOf('\u25B6') === 0) {
              document.title = document.title.substr(2);
            }
            
            TTTX.Log.track_complete(track_id, this.duration);
            
            next_track();
           },
          whileplaying: function () {
            $('body').trigger('whileplaying', [this]);
            var current_draw = new Date().getTime();
            if (current_draw - last_draw > 500) {
              var perc = Math.round(100 * this.position / this.duration);
              $done.text(toMinutes(this.position));
              $slide.css('width', perc + '%');
              $remaining.text(toMinutes(this.duration));
              
              // Update playing counter
              if (!this.paused) {
                player.track_playing_time += current_draw - last_draw;
                
                // Over logging threshold?
                if (!player.threshold_logged && player.track_playing_time > TTTX.Log.get_threshold()) {
                  TTTX.Log.track_threshold(track_id);
                  player.threshold_logged = true;
                }
                
                // Scrobbletime yet?
                if (my22 && my22.lastfm && !player.track_scrobbled && player.track_playing_time > this.duration / 2) {
                  TTTX.Scrobble.scrobble(track_id);
                  player.track_scrobbled = true;
                }
              }
              
              last_draw = current_draw;
            }
          },
          onload: function (success) {
            if (console && !success) {
              console.log('error');
            }
          }
        };
        
        // Wowza settings
        if (IS_LIVE_SERVER) {
          var protocol = 'rtmp://';
          if (player.use_rtmpt) {
            protocol = 'rtmpt://';
          }
          sound_settings.serverURL = protocol + STREAM_IP + '/22tracks/';
          
          sound_settings.url = 'mp3:' + bit_rate + '/' + filename;
        }
        
        // Create sound
        this.sound = soundManager.createSound(sound_settings);
        this.sound.play();
        this.current_track = this.track_id;
        
        // Track analytics
        var tracking_url = '/' + this.city.get('slug') + ' - ' + this.genre_slug + ' | ' + track.get('artist') + ' - ' + track.get('title');
        if (pageTracker) {
          pageTracker._trackPageview(tracking_url);
        }
        // Track chartbeat
        if (pSUPERFLY) {
          pSUPERFLY.virtualPage(window.location.pathname + window.location.hash.substring(1), track.get('artist') + ' - ' + track.get('title'));
        }
        
        // Show notification (webkit)
        if (window.webkitNotifications) {
          var permission = window.webkitNotifications.checkPermission();
          if (permission == 0) {
            var icon = ROOT + 'res/img/icon32.png',
              title = '22tracks.com now playing:',
              message = track.get('artist') + ' - ' + track.get('title'),
              notification = window.webkitNotifications.createNotification(icon, title, message),
              notificationTimeout = -1;
            notification.onclick = function () {
              window.focus(); this.cancel();
            };
            notification.show();
            
            notificationTimeout = setTimeout(function () {
              notification.cancel();
            }, 7500);
            
          } else if (permission == 1) {
            $('.notification').remove();
            $('.container').append('<div class="notification"><p><a href="javascript:window.webkitNotifications.requestPermission()">Enable Desktop notifications</a></p></div>');
            $('.notification').css('bottom', '15px').css('opacity', 0).animate({
                bottom: '0',
                opacity: 1
              }, 750
            ).delay(5000).fadeOut(1000, function() {
              $(this).remove();
            });
          }
        }
        
        // Throw event (for custom js)
        $('body').trigger('switchtrack');
      },
      stop: function () {
        if (player.sound === null) {
          return;
        }
        
        // Finish logging
        TTTX.Log.track_end(player.current_track, player.track_playing_time);
        player.threshold_logged = false;
        player.track_playing_time = 0;
        
        // Reset scrobbling
        player.track_scrobbled = false;
        
        // Clear track info
        $('.controls .title').html('');
        $('.controls .done').text('');
        $('.controls .progress .slide').css('width', '0%');
        $('.controls .remaining').text('');
        
        soundManager.destroySound('sound');
        player.sound = null;
        
      },
      next_track: function () {
        if (player.shuffle) {
          player.random_track();
          return;
        }
        if (player.current_track === null) {
          return;
        }
        var $current_track = $('#track' + player.current_track),
          $next_track = $current_track.next(),
          next_track_id;
        
        if ($current_track.length > 0) {
          if ($next_track.length > 0) {
            next_track_id = $next_track.attr('id').substring(5);
          } else {
            return false;
          }
        } else {
          next_track_id = player.tracks.at(0).get('id');
        }
        if (next_track_id) {
          window.location.hash = player.genre_slug + '/' + next_track_id;
        }
      },
      prev_track: function () {
        if (player.shuffle) {
          player.random_track();
          return;
        }
        if (player.current_track === null) {
          return;
        }
        var $current_track = $('#track' + player.current_track),
          $prev_track = $current_track.prev(),
          prev_track_id;
        
        if ($current_track.length > 0 && $prev_track.length > 0) {
          prev_track_id = $prev_track.attr('id').substring(5);
        } else {
          prev_track_id = player.tracks.at(0).get('id');
        }
        if (prev_track_id) {
          window.location.hash = player.genre_slug + '/' + prev_track_id;
        }
      },
      random_track: function () {
        var genres = player.shuffle_genres,
          choices = genres.length,
          choice = Math.floor(Math.random() * choices),
          chosen_genre;
        
        player.stop();
        player.track_id = null;
        
        chosen_genre = genres[choice];
        if (window.location.hash === '#' + chosen_genre) {
          player.genre_link(chosen_genre);
          return;
        }
        window.location.hash = chosen_genre;
        
        return;
      },
      set_volume: function (perc) {
        // Default volume for following tracks
        this.volume = perc * 100;
        if (this.sound === null) {
          return;
        }
        this.sound.setVolume(this.volume);
      },
      seek: function (perc) {
        if (this.sound === null) {
          return;
        }
        var pos = this.sound.duration * perc;
        this.sound.setPosition(pos);
      },
      toggle_shuffle: function () {
        var $shuffle = $('.controls .shuffle'),
          $shuffle_hint = $('.shuffle-hint');
        
        player.shuffle = !player.shuffle;
        
        if (this.shuffle) {
          $shuffle.addClass('active');
          $shuffle_hint.fadeIn(500);
        } else {
          $shuffle.removeClass('active');
          $shuffle_hint.fadeOut(500);
        }
        player.genre_list.toggle_shuffle();
      },
      // Add genre to shuffled
      add_shuffle_genre: function (genre_link) {
        // No duplicates
        if (_.indexOf(player.shuffle_genres, genre_link) === -1) {
          player.shuffle_genres.push(genre_link);
        }
      },
      remove_shuffle_genre: function (genre_link) {
        var i = 0;
        for (; i < player.shuffle_genres.length; i += 1) {
          if (player.shuffle_genres[i] === genre_link) {
            player.shuffle_genres.splice(i, 1);
            return;
          }
        }
        return;
      }
    });
  
  
  // DOM ready
  $(function () {
    // animateFolds();
    setFade($('.my22-nav a, .bottom-nav a, .info .image a, .info .icons a, .branded-link a'));
    addClickClass();
    addHoverListTr();
    fadeGenreNavs();
    addExpandableInfo();
    $('#privacy').click(function () {
      TTTX.Lightbox.show(ROOT + 'modal/privacy');
      return false;
    })
    
    window.soundManager = new SoundManager();
    soundManager.url = ROOT + 'res/js/libs/soundmanager2/soundmanager2_flash9.swf';
    soundManager.flashVersion = 9;
    soundManager.flashLoadTimeout = 6000;
    soundManager.onready(function () {
      startMy22();
      player = new PlayerController();
      
      $.history.init(player.hashchange);
    });
    
    soundManager.ontimeout(function () {
      // Show helpful error message in lightbox
      TTTX.Lightbox.show(ROOT + 'modal/flash_error');
    });
	
    soundManager.beginDelayedInit();
  });
}(jQuery));
