Saturday, March 3, 2012

YouTube Center


// ==UserScript==
// @name            YouTube Center
// @version         1.24.4
// @author          Jeppe Rune Mortensen (YePpHa)
// @description     Automatic changes the video quality to your desired quality or lower if not available. Adds a download button. Adds a repeat button.
// @include         http://*.youtube.com/watch?*
// @include         https://*.youtube.com/watch?*
// @updateVersion   55
// ==/UserScript==
/** TODO
 * Ability to change language.
 * Add more options to video filename.
 * Disable Annotations/Captions.
 * Fix Prevent Auto-Play with skip ads.
 * Do a cleanup in settings +optimize design (including new design for auto update +popup).
 * Make this script do something on all of YouTube.
 * Make another way to show if there is a new update (with changelog, version etc.)
 * Make an introduction if it's the first time you try this script.
 * Add Manual
 */
(function(){
  if (!location.href.match(/^(http|https)\:\/\/(.*?)\.youtube\.com\/watch\?/)) return;
  var updateVersion = 55;
  var image_gear = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAg5JREFUeNqs1F1oznEUB/DP85ht8nphEhdewoWXscm7GOXlKUloSinlSuROChfjSsqFaElRXnLjnSIp5aUWWrsQK8sNVpslxWy2Zo+b869/j6fHLpz69f+d8/uf7++c7++ck8nn8/6nlEEulyt2NgbfkcUZLEMD7iGDSvQWOmWLAGWwD3dRjWHYiBqsiH+O4AaWFI2wiGxDHS7iOKrCXo2dYcvgHV6WirACeVwIfSEuYXToG+Isgz5cTvn9BXgAzTiE4fgW9nHh3JniDjqwADfxGrPSKY/AOszBCfQHdx04hiZ0Yzx2Yw8mpyKEHDoTwF7sxSA2oRztcUlryulDcNaK02EbQGNQ0Z1O+QdO4WPoDQVgaTmLJ7F/FmU1kE65EaswFSPRhccl6ncQd7A21vMIpDkB3BycJPIlOCsln1P7ibEmJYBbUYv52BWRVuFrCcC58e3EI7xFa8LhK5wLPn5H2jtKgI3C9ti3R+ecxP0EsBJHcTV6GA5HeRTK2HjR6tBr8SLasrIsVeB18e2K112K81iPh1EF06L1aiLVgeB+CmajJQHswUEsxnWsxO24oD5WofzEaqzBTFxDT3o4tMRKHimLX3gQOrwJ2yJMj9a78q/xVR6RJmNqf5QR3IpJlJTMvKGMr35swfKYNBXB6wS04VNUwIwYDEOah++j8vNxwdP4tsV5U0ymvkLHPwMAXZ17Gns2DUMAAAAASUVORK5CYII=";
  var feather = false;
  var YouTubeType = {'video/x-flv': 'FLV', 'video/mp4': 'MP4', 'video/webm': 'WebM'};
  var YouTubeQuality = {'small': 'Low Definition', 'medium': 'Standard Definition', 'large': 'High Definition', 'hd720': 'HD Definition', 'hd1080': 'Full HD Definition', 'highres': 'Original Definition'};
  var YouTubeQualityResolution = {'small': '240p', 'medium': '360p', 'large': '480p', 'hd720': '720p', 'hd1080': '1080p', 'highres': 'Original'};
  var YouTubeVariables = {};
  var YouTubeStreamInformation = [];
  var YouTubeStreamSortedByFormat = {};
  var DownloadIFrame = null;
  var YouTubeVideoTitle = null;
  var YouTubeVideoAuthor = null;
  var CookieSettings = "YouTubeCenterSettings";
  var elements = {};
  
  var settings = {
    'AutoResolution': 'highres', // YouTubeQuality [small = 240p, medium = 360p, large = 480p, hd720 = 720p, hd1080 = 1080p, highres = 2304p]
    'DownloadFormat': 'video/mp4', // [video/x-flv = FLV, video/mp4 = MP4, video/webm = WebM]
    'DownloadQuality': 'highres', // YouTubeQuality [small = 240p, medium = 360p, large = 480p, hd720 = 720p, hd1080 = 1080p, highres = 2304p]
    'EnableAutoResolution': true,
    'EnableDownloadButton': true,
    'EnableRepeatButton': true,
    'Language': 'en',
    'EnableMP3': false,
    'MP3URL': 'http://www.youtube-mp3.org/#v={videoid}',
    'AutoActivateRepeat': false,
    'EnableDocumentShortcuts': true,
    'Show3DInMenu': false,
    'PreventAutoPlay': false,
    'PreventAutoBuffering': false,
    'EnableUpdate': true,
    'UpdateInterval': 2*1000*60*60*24, // 2 Days
    'LastUpdateCheck': 0, // Never
    'DownloadFilename': '{title}',
    'EnableFilenameFix': false, // Only use this if you have problems downloading
    'AutoExpandDescription': false,
    'RemoveAdsPlayer': true,
    'PlayerSize': 'small', // small : Normal Size, large : Large Size, fill : Fill the whole window
    'AutoHideBar': 2 // 0 : None, 1 : All, 2 : Only Progressbar, 3 : Only Controlbar
  };
  var session = {'repeat': false};
  
  function getFilename(format) {
    var downloadFilenameReplace = {
      title: YouTubeVideoTitle,
      videoid: YouTubeVariables['args']['video_id'],
      author: YouTubeVideoAuthor,
      resolution: getQualityResolution(format.quality),
      itag: format.itag,
      dimension: format.dimension,
      width: format.dimension.split("x")[0],
      height: format.dimension.split("x")[1]
    };
    var filename = settings.DownloadFilename;
    var tmp = "";
    var startB = false;
    var func = "";
    var tmpName = "";
    var tmpFunc = "";
    var inFunc = false;
    for (var i = 0; i < filename.length; i++) {
      if (filename[i] == "{" && !startB && !inFunc) {
        startB = true;
      } else if (filename[i] == "}" && startB) {
        var t = "";
        for (var key in downloadFilenameReplace) {
          if (key == tmpName) {
            tmpName = "";
            t = downloadFilenameReplace[key];
            break;
          }
        }
        tmp += t;
        startB = false;
      } else if (startB) {
        tmpName += filename[i];
      } else {
        tmp += filename[i];
      }
    }
    
    return tmp;
  }
  
  function getFormatType(format) {
    if (YouTubeType[format]) {
      return YouTubeType[format];
    }
    return "Unknown";
  }
  
  function getQuality(quality) {
    if (YouTubeQuality[quality]) {
      return YouTubeQuality[quality];
    }
    return "Unknown";
  }
  
  function getQualityResolution(quality) {
    if (YouTubeQualityResolution[quality]) {
      return YouTubeQualityResolution[quality];
    }
    return "Unknown";
  }

  function getStream(format, quality) {
    for (var i = 0; i < YouTubeStreamInformation.length; i++) {
      if (YouTubeStreamInformation[i].type.format == format && YouTubeStreamInformation[i].quality == quality) return YouTubeStreamInformation[i];
    }
    return null;
  }

  function getStreamOrQualityLower(format, quality) {
    var s;
    for (var i = 0; i < 10; i++) {
      var s = getStream(format, quality);
      if (!s) {
        switch (quality) {
          case 'highres': quality = 'hd1080'; break;
          case 'hd1080': quality = 'hd720'; break;
          case 'hd720': quality = 'large'; break;
          case 'large': quality = 'medium'; break;
          case 'medium': quality = 'small'; break;
          default: break;
        }
      } else {
        break;
      }
    }
    if (s == null) {
      var qualities = ['small', 'medium', 'large', 'hd720', 'hd1080', 'highres'];
      for (var i = 0; i < qualities.length; i++) {
        var s = getStream(format, qualities[i]);
        if (!s) continue;
        return s;
      }
    }
    return s;
  }
  
  function downloadFileStreamQuality() {
    downloadFile(getStreamOrQualityLower(settings.DownloadFormat, settings.DownloadQuality).itag);
  }
  
  function downloadMP3File() {
    if (!settings.EnableMP3) return;
    if (settings.MP3URL == "") {
      alert("Please specify the mp3 url in the settings.");
    } else {
      window.open(settings.MP3URL.replace("{videoid}", YouTubeVariables.args.video_id));
    }
  }

  function downloadFile(itag) {
    if (!DownloadIFrame) {
      DownloadIFrame = document.createElement("iframe");
      DownloadIFrame.style.position = "absolute";
      DownloadIFrame.style.top = "-100px";
      DownloadIFrame.style.left = "-100px";
      DownloadIFrame.style.width = "1px";
      DownloadIFrame.style.height = "1px";
      DownloadIFrame.style.border = "0";
      DownloadIFrame.style.margin = "0";
      DownloadIFrame.style.padding = "0";
      document.body.appendChild(DownloadIFrame);
    }
    
    for (var i = 0; i < YouTubeStreamInformation.length; i++) {
      if (YouTubeStreamInformation[i].itag == itag) {
        DownloadIFrame.setAttribute("src", YouTubeStreamInformation[i].url + "&title=" + getVideoFilename(YouTubeStreamInformation[i]));
        break;
      }
    }
  }
  
  function stripCharacters(txt, chars) {
    var tmp = "";
    for (var i = 0; i < txt.length; i++) {
      var b = true;
      for (var key in chars) {
        if (txt[i] == key) {
          tmp += chars[key];
          b = false;
        }
      }
      if (b) tmp += txt[i];
    }
    
    return tmp;
  }
  
  function getVideoFilename(format) {
    var fn = getFilename(format);
    if (settings.EnableFilenameFix) {
      var tmp = "";
      for (var i = 0; i < fn.length; i++) {
        if (fn[i].match(/[0-9a-zA-Z ]/i)) {
          tmp += fn[i];
        }
      }
      return encodeURIComponent(tmp);
    }
    return encodeURIComponent(stripCharacters(fn, {'\\': '-', '/': '-', ':': '-', '*': '_', '"': '_', '?': '_', '<': '_', '>': '_', '|': '-'}));
  }
  
  function getFormatTitle(f) {
    return getQuality(f.quality) + ", " + getQualityResolution(f.quality) + " (" + f.dimension + ")" + (f.stereo3d && f.stereo3d == 1 ? "<span style=\"float:right\">&nbsp;3D</span>" : "");
  }
  
  function is3D(f) {
    return (f.stereo3d && f.stereo3d == 1 ? true : false);
  }
  
  function doSettingsChange() {
    if (settings.EnableDownloadButton) {
      elements.downloadButton.style.display = "inline";
    } else {
      elements.downloadButton.style.display = "none";
    }
    if (settings.EnableRepeatButton) {
      elements.repeatButton.style.display = "inline";
      setGlobalInformation("Repeat", session.repeat);
    } else {
      elements.repeatButton.style.display = "none";
      setGlobalInformation("Repeat", 0);
    }
    if (settings.PlayerSize == 'small') {
      $RemoveClass(document.getElementById("page"), "watch-wide");
      $RemoveClass(document.getElementById("watch-video"), "medium");
      $RemoveClass(document.getElementById("page"), "ytcenterfill");
      document.getElementById("page").style.height = "";
      document.getElementById("content-container").style.height = "";
      document.getElementById("content").style.height = "";
      document.getElementById("watch-container").style.height = "";
      document.getElementById("watch-video-container").style.height = "";
      document.getElementById("watch-video").style.width = "";
      document.getElementById("watch-video").style.height = "";
      document.getElementById("watch-player").style.width = "";
      document.getElementById("watch-player").style.height = "";
    } else if (settings.PlayerSize == 'large') {
      $AddClass(document.getElementById("page"), "watch-wide");
      $AddClass(document.getElementById("watch-video"), "medium");
      $RemoveClass(document.getElementById("page"), "ytcenterfill");
      document.getElementById("page").style.height = "";
      document.getElementById("content-container").style.height = "";
      document.getElementById("content").style.height = "";
      document.getElementById("watch-container").style.height = "";
      document.getElementById("watch-video-container").style.height = "";
      document.getElementById("watch-video").style.width = "";
      document.getElementById("watch-video").style.height = "";
      document.getElementById("watch-player").style.width = "";
      document.getElementById("watch-player").style.height = "";
    } else if (settings.PlayerSize == 'fill') {
      $AddClass(document.getElementById("page"), "watch-wide");
      $RemoveClass(document.getElementById("watch-video"), "medium");
      $AddClass(document.getElementById("page"), "ytcenterfill");
      document.getElementById("page").style.height = "100%";
      document.getElementById("content-container").style.height = "100%";
      document.getElementById("content").style.height = "100%";
      document.getElementById("watch-container").style.height = "100%";
      document.getElementById("watch-video-container").style.height = "100%";
      document.getElementById("watch-video").style.width = "100%";
      document.getElementById("watch-video").style.height = "100%";
      document.getElementById("watch-player").style.width = "100%";
      document.getElementById("watch-player").style.height = "100%";
      document.getElementById("watch-player").scrollIntoView();
    }
    var a = getStreamOrQualityLower(settings.DownloadFormat, settings.DownloadQuality);
    var t = "Download " + getFormatTitle(a) + " " + getFormatType(a.type.format);
    elements.downloadButton.children[0].title = t;
    elements.downloadButton.children[0].setAttribute("data-tooltip-text", t);
    if (settings.EnableMP3) {
      elements.mp3title.style.display = "block";
      elements.mp3link.style.display = "block";
    } else {
      elements.mp3title.style.display = "none";
      elements.mp3link.style.display = "none";
    }
    if (settings.Show3DInMenu) {
      if (!elements.downloadmenu3d) elements.downloadmenu3d = [];
      for (var i = 0; i < elements.downloadmenu3d.length; i++) {
        elements.downloadmenu3d[i].style.display = "block";
      }
    } else {
      if (!elements.downloadmenu3d) elements.downloadmenu3d = [];
      for (var i = 0; i < elements.downloadmenu3d.length; i++) {
        elements.downloadmenu3d[i].style.display = "none";
      }
    }
  }
  
  function createSettingsMenu(div, e) {
    var g = document.createElement("span");
    g.className = "yt-uix-button-group";
    var l = 0;
    for (var key in e) {
      if (e.hasOwnProperty(key)) {
        l++;
      }
    }
    var i = 0;
    var builds = [];
    for (var key in e) {
      if (e.hasOwnProperty(key)) {
        var b = document.createElement("button");
        b.setAttribute("onclick", ";return false;");
        b.type = "button";
        b.className = "yt-uix-button yt-uix-button-default" + (i == 0 ? " start yt-uix-button-toggled" : "") + (i+1 == l ? " end" : "");
        b.setAttribute("role", "button");
        var s = document.createElement("span");
        s.className = "yt-uix-button-content";
        s.appendChild(document.createTextNode(e[key].text));
        b.appendChild(s);
        g.appendChild(b);
        var bu = document.createElement("div");
        if (i > 0) {
          bu.className = "hid";
        }
        
        var items = buildSettingItem(e[key].build);
        for (var k = 0; k < items.length; k++) {
          bu.appendChild(items[k]);
        }
        builds.push({
          element: b,
          build: bu
        });
        div.appendChild(bu);
        b.addEventListener('click', function(){
          var parent = this.parentNode;
          for (var i = 0; i < parent.children.length; i++) {
            $RemoveClass(parent.children[i], "yt-uix-button-toggled");
          }
          $AddClass(this, "yt-uix-button-toggled");
          for (var i = 0; i < builds.length; i++) {
            if (builds[i].element == this) {
              $RemoveClass(builds[i].build, "hid");
            } else {
              $AddClass(builds[i].build, "hid");
            }
          }
        }, false);
        i++;
      }
    }
    return g;
  }
  
  function buildSettingItem(build) {
    var labelWidth = "175px";
    var e = [];
    for (var i = 0; i < build.length; i++) {
      if (build[i].type === "checkbox") {
        var wrapper = document.createElement("div");
        var label = document.createElement("span");
        label.style.display = "inline-block";
        label.style.width = labelWidth;
        label.appendChild(document.createTextNode(build[i].label));
        var ts = document.createElement("span");
        ts.className = "yt-uix-form-input-checkbox-container" + (build[i].checked ? " checked" : "");
        var checkbox = document.createElement("input");
        checkbox.className = "yt-uix-form-input-checkbox";
        checkbox.type = "checkbox";
        if (build[i].checked) {
          checkbox.checked = true;
        }
        checkbox.addEventListener("click", build[i].event, false);
        ts.appendChild(checkbox);
        var tss = document.createElement("span");
        tss.className = "yt-uix-form-input-checkbox-element";
        ts.appendChild(tss);
        wrapper.appendChild(label);
        wrapper.appendChild(ts);
        e[e.length] = wrapper;
      } else if (build[i].type === "select") {
        var wrapper = document.createElement("div");
        var label = document.createElement("span");
        label.style.display = "inline-block";
        label.style.width = labelWidth;
        label.appendChild(document.createTextNode(build[i].label));
        var select = document.createElement("select");
        for (var key in build[i].options) {
          var option = document.createElement("option");
          option.appendChild(document.createTextNode(key));
          option.value = build[i].options[key];
          if (build[i].options[key] == build[i].selected) {
            option.selected = true;
          }
          select.appendChild(option);
        }
        select.addEventListener("change", build[i].event, false);
        select.addEventListener("change", function(){
          if (this.parentNode.lastChild == this) return;
          this.parentNode.lastChild.style.display = "inline-block";
        }, false);
        wrapper.appendChild(label);
        wrapper.appendChild(select);
        if (build[i].onApply) {
          var apply = document.createElement("a");
          apply.style.background = "URL(" + image_gear + ")";
          apply.style.width = "20px";
          apply.style.height = "20px";
          apply.style.display = "none";
          apply.addEventListener("click", build[i].onApply, false);
          wrapper.appendChild(apply);
        }
        e[e.length] = wrapper;
      } else if (build[i].type === "multichoice") {
        var name = "YouTube MP3 Service" + Math.random()*11;
        var wrapper = document.createElement("div");
        var label = document.createElement("span");
        label.style.display = "inline-block";
        label.style['font-weight'] = "bold";
        label.style.width = labelWidth;
        label.appendChild(document.createTextNode(build[i].label));
        var multichoice = document.createElement("div");
        for (var j = 0; j < build[i].options.length; j++) {
          var option = document.createElement("div");
          option.addEventListener("click", build[i].event, false);
          option.setAttribute("data-url", build[i].options[j].value);
          var rt = document.createElement("span");
          rt.className = "yt-uix-form-input-radio-container" + (build[i].options[j].checked ? " checked" : "");
          var r = document.createElement("input");
          r.className = "yt-uix-form-input-radio";
          r.type = "radio";
          r.id = name;
          if (build[i].options[j].checked) {
            r.checked = true;
          }
          rt.appendChild(r);
          var rtt = document.createElement("span");
          rtt.className = "yt-uix-form-input-radio-element";
          rt.appendChild(rtt);
          option.appendChild(rt);
          var la = document.createElement("label");
          la.setAttribute("for", name);
          la.appendChild(document.createTextNode(build[i].options[j].name));
          option.appendChild(la);
          multichoice.appendChild(option);
        }
        wrapper.appendChild(label);
        wrapper.appendChild(multichoice);
        e[e.length] = wrapper;
      } else if (build[i].type === "button") {
        var wrapper = document.createElement("div");
        var b = document.createElement("button");
        if (build[i].register) {
          build[i].register(b, wrapper);
        }
        b.className = "yt-uix-button yt-uix-button-default";
        b.setAttribute("role", "button");
        b.innerHTML = build[i].label;
        b.addEventListener("click", build[i].event, false);
        wrapper.appendChild(b);
        if (build[i].children) {
          for (var j = 0; j < build[i].children.length; j++) {
            wrapper.appendChild(build[i].children[j]);
          }
        }
        e[e.length] = wrapper;
      } else if (build[i].type === "text") {
        var wrapper = document.createElement("div");
        var label = document.createElement("span");
        label.style.display = "inline-block";
        label.style.width = labelWidth;
        label.appendChild(document.createTextNode(build[i].label));
        var t = document.createElement("input");
        t.type = "text";
        //t.className = "yt-uix-form-input-text text-input"; // I will use this when I find a better way to show settings because it looks fat otherwise.
        if (build[i].value) {
          t.value = build[i].value;
        }
        t.addEventListener("change", build[i].event, false);
        wrapper.appendChild(label);
        wrapper.appendChild(t);
        e[e.length] = wrapper;
      } else if (build[i].type === "div") {
        var wrapper = document.createElement("div");
        wrapper.innerHTML = build[i].html;
        e[e.length] = wrapper;
      }
    }
    return e;
  }
  
  function initSettingsUI() {
    /* Main Settings Div */
    elements.settingsDiv = document.createElement("div");
    elements.settingsDiv.style.display = "none";
    elements.settingsDiv.className = "yt-rounded";
    
    elements.settingsInnerDiv = document.createElement("div");
    elements.settingsInnerDiv.setAttribute("style", "background:#FFFFFF;border:1px solid #CCC;padding:10px;position:relative;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;box-shadow:0 1px 1px #ccc;-moz-box-shadow:0 1px 1px #ccc;-ms-box-shadow:0 1px 1px #ccc;-webkit-box-shadow:0 1px 1px #ccc;");

    elements.settingsInnerDiv.className = "watch-actions-panel";
    
    elements.settingsCloseDiv = document.createElement("div");
    elements.settingsCloseDiv.className = "close";
    elements.settingsCloseDiv.setAttribute("style", "position:absolute;top:5px;right:5px;cursor:pointer;");
    
    elements.settingsCloseImg = document.createElement("img");
    elements.settingsCloseImg.src = "//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif";
    elements.settingsCloseImg.className = "close-button";
    elements.settingsCloseImg.addEventListener("click", function(){
      $RemoveClass(elements.settingsButton, "yt-uix-button-toggled");
      elements.settingsDiv.style.display = "none";
    }, false);
    
    elements.settingsCloseDiv.appendChild(elements.settingsCloseImg);
    elements.settingsInnerDiv.appendChild(elements.settingsCloseDiv);
    /* Settings Button */
    elements.settingsButton = createYouTubeButton("Toggle Settings Panel", "Settings", null, "yt-uix-button-toggle", function(){
      if (elements.settingsDiv.style.display != "none") {
        elements.settingsDiv.style.display = "none";
        $RemoveClass(this, "yt-uix-button-toggled");
      } else {
        elements.settingsDiv.style.display = "block";
        $AddClass(this, "yt-uix-button-toggled");
      }
    });
    elements.ytelementLeft.appendChild(elements.settingsButton);
    elements.updateNote = document.createElement("span");
    elements.updateNote.setAttribute("style", "margin-left: 5px");
    elements.ytelementLeft.appendChild(elements.updateNote);
    
    /* Settings Menu Buttons */
    elements.group = createSettingsMenu(elements.settingsInnerDiv, {
      generalButton: {
        text: 'General',
        build: [{
          label: 'Enable Auto Resolution',
          type: 'checkbox',
          checked: (settings.EnableAutoResolution ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableAutoResolution = true;
            } else {
              settings.EnableAutoResolution = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Enable Download Button',
          type: 'checkbox',
          checked: (settings.EnableDownloadButton ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableDownloadButton = true;
            } else {
              settings.EnableDownloadButton = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Enable Repeat Button',
          type: 'checkbox',
          checked: (settings.EnableRepeatButton ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableRepeatButton = true;
            } else {
              settings.EnableRepeatButton = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Enable Shortcuts on Page',
          type: 'checkbox',
          checked: (settings.EnableDocumentShortcuts ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableDocumentShortcuts = true;
              setGlobalInformation("EnableDocumentShortcuts", "true");
            } else {
              settings.EnableDocumentShortcuts = false;
              setGlobalInformation("EnableDocumentShortcuts", "false");
            }
            saveSettings();
          }
        }, {
          label: 'Auto Expand Description',
          type: 'checkbox',
          checked: (settings.AutoExpandDescription ? true : false),
          event: function(){
            if (this.checked) {
              settings.AutoExpandDescription = true;
            } else {
              settings.AutoExpandDescription = false;
            }
            saveSettings();
          }
        }, {
          label: 'Remove Advertisement',
          type: 'checkbox',
          checked: (settings.RemoveAdsPlayer ? true : false),
          event: function(){
            if (this.checked) {
              settings.RemoveAdsPlayer = true;
            } else {
              settings.RemoveAdsPlayer = false;
            }
            saveSettings();
          }
        }]
      },
      videoButton: {
        text: 'Video',
        build: [{
          label: 'Auto Resolution',
          type: 'select',
          options: {
            'Original Definition': 'highres',
            'Full HD Definition': 'hd1080',
            'HD Definition': 'hd720',
            'High Definition': 'large',
            'Standard Definition': 'medium',
            'Low Definition': 'small'
          },
          selected: settings.AutoResolution,
          event: function(){
            settings.AutoResolution = this.value;
            saveSettings();
          }
        }, {
          label: 'Auto Activate Repeat',
          type: 'checkbox',
          checked: (settings.AutoActivateRepeat ? true : false),
          event: function() {
            if (this.checked) {
              settings.AutoActivateRepeat = true;
            } else {
              settings.AutoActivateRepeat = false;
            }
            saveSettings();
          }
        }, {
          label: 'Prevent Auto-Play',
          type: 'checkbox',
          checked: (settings.PreventAutoPlay ? true : false),
          event: function() {
            if (this.checked) {
              settings.PreventAutoPlay = true;
            } else {
              settings.PreventAutoPlay = false;
            }
            saveSettings();
          },
          register: function(elm){
            elements.settingsPreventAutoPlayer = elm;
          }
        }, {
          label: 'Prevent Auto-Buffering',
          type: 'checkbox',
          checked: (settings.PreventAutoBuffering ? true : false),
          event: function() {
            if (this.checked) {
              settings.PreventAutoBuffering = true;
            } else {
              settings.PreventAutoBuffering = false;
            }
            saveSettings();
          }
        }, {
          label: 'Player Size',
          type: 'select',
          options: {
            'Small': 'small',
            'Large': 'large',
            'Fill': 'fill'
          },
          selected: settings.PlayerSize,
          event: function(){
            settings.PlayerSize = this.value;
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Auto Hide Bar',
          type: 'select',
          options: {
            'None': 0,
            'Both Progressbar & Controlbar': 1,
            'Only Progressbar': 2,
            'Only Controlbar': 3
          },
          selected: settings.AutoHideBar,
          event: function(){
            settings.AutoHideBar = parseInt(this.value);
            saveSettings();
          }
        }
      ]},
      downloadButton: {
        text: 'Download',
        build: [{
          label: 'Download Quality',
          type: 'select',
          options: {
            'Original Definition': 'highres',
            'Full HD Definition': 'hd1080',
            'HD Definition': 'hd720',
            'High Definition': 'large',
            'Standard Definition': 'medium',
            'Low Definition': 'small'
          },
          selected: settings.DownloadQuality,
          event: function(){
            settings.DownloadQuality = this.value;
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Download Format',
          type: 'select',
          options: {
            'MP4': 'video/mp4',
            'WebM': 'video/webm',
            'FLV': 'video/x-flv'
          },
          selected: settings.DownloadFormat,
          event: function(){
            settings.DownloadFormat = this.value;
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Show 3D in Download Menu',
          type: 'checkbox',
          checked: (settings.Show3DInMenu ? true : false),
          event: function() {
            if (this.checked) {
              settings.Show3DInMenu = true;
            } else {
              settings.Show3DInMenu = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Filename',
          type: 'text',
          value: settings.DownloadFilename,
          event: function(){
            settings.DownloadFilename = this.value;
            saveSettings();
          }
        }, {
          label: 'Remove Non-Alphanumeric Characters',
          type: 'checkbox',
          checked: (settings.EnableFilenameFix ? true : false),
          event: function() {
            if (this.checked) {
              settings.EnableFilenameFix = true;
            } else {
              settings.EnableFilenameFix = false;
            }
            saveSettings();
          }
        }]
      },
      mp3Button: {
        text: 'MP3',
        build: [{
          label: 'Enable MP3 in Download List',
          type: 'checkbox',
          checked: (settings.EnableMP3 ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableMP3 = true;
            } else {
              settings.EnableMP3 = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'YouTube MP3 Services',
          type: 'multichoice',
          options: [
            {
              name: 'YouTube mp3',
              value: 'http://www.youtube-mp3.org/#v={videoid}',
              checked: (settings.MP3URL == 'http://www.youtube-mp3.org/#v={videoid}' ? true : false)
            }
          ],
          event: function(){
            if (this.type === "radio") {
              settings.MP3URL = this.parentNode.children[1].value;
            } else if (this.type === "text") {
              settings.MP3URL = this.value;
            }
            doSettingsChange();
            saveSettings();
          }
        }]
      },
      updateButton: {
        text: 'Update',
        build: [{
          label: 'Enable Auto Update',
          type: 'checkbox',
          checked: (settings.EnableUpdate ? true : false),
          event: function(){
            if (this.checked) {
              settings.EnableUpdate = true;
            } else {
              settings.EnableUpdate = false;
            }
            doSettingsChange();
            saveSettings();
          }
        }, {
          label: 'Update Interval',
          type: 'select',
          options: {
            'Always': '0',
            'Every Day': '86400000',
            'Every 2 Day': '172800000',
            'Every Week': '604800000',
            'Every 2 Week': '1209600000',
            'Every Month': '2592000000',
            'Never': '-1'
          },
          selected: settings.UpdateInterval,
          event: function(){
            settings.UpdateInterval = this.value;
            doSettingsChange();
            saveSettings();
          }
        }, {
          type: 'button',
          label: 'Check For New Update',
          children: [
            elements.settingsUpdateText = document.createElement("span")
          ],
          event: function(){
            elements.settingsCheckForUpdateButton.disabled = true;
            doUpdate(function(response){
              if (response.readyState === 4 && response.status === 200) {
                elements.settingsUpdateText.style.padding = "0 5px";
                if (hasNewUpdate(getUpdate(response.responseText))) {
                  elements.settingsUpdateText.style.color = "#080";
                  elements.settingsUpdateText.innerHTML = "Found new update, <a href=\"http://userscripts.org/scripts/show/114002\" target=\"_blank\">install</a> the new update.";
                } else {
                  elements.settingsUpdateText.style.color = "#000";
                  elements.settingsUpdateText.innerHTML = "No new updates.";
                  elements.settingsCheckForUpdateButton.disabled = false;
                  elements.settingsCheckForUpdateButton.innerHTML = "Check Again";
                }
              } else {
                elements.settingsUpdateText.style.color = "#800";
                elements.settingsUpdateText.innerHTML = "Couldn't contact server!";
                elements.settingsCheckForUpdateButton.disabled = false;
                elements.settingsCheckForUpdateButton.innerHTML = "Try Again";
              }
            }, function(){
              elements.settingsUpdateText.style.color = "#800";
              elements.settingsUpdateText.innerHTML = "Couldn't contact server!";
              elements.settingsCheckForUpdateButton.disabled = false;
              elements.settingsCheckForUpdateButton.innerHTML = "Try Again";
            });
          },
          register: function(elm){
            elements.settingsCheckForUpdateButton = elm;
          }
        }]
      },
      aboutButton: {
        text: 'About',
        build: [{
          type: 'div',
          html: '<h2>YouTube Center</h2>Copyright © 2011 - 2012 Jeppe Rune Mortensen (YePpHa). All Rights Reserved.<br /><br />If you have any problems, complains, questions or compliments you\'re welcome to contact me on my email.<br />Contact me: <a href="mailto:jepperm@gmail.com">jepperm@gmail.com</a><br /><br />Support me and this script by clicking this <a href="http://goo.gl/zjUin">link</a>.'
        }]
      }
    });
    
    elements.settingsInnerDiv.insertBefore(elements.group, elements.settingsInnerDiv.childNodes[0]);
    elements.settingsDiv.appendChild(elements.settingsInnerDiv);
    elements.main.appendChild(elements.settingsDiv);
  }
  
  function initUI() {
    elements.main = document.createElement("div");
    elements.main.style.margin = "0 0 5px 0";
    
    var b = [];
    for (var key in YouTubeStreamSortedByFormat) {
      b[b.length] = {
        text: "<b>"+getFormatType(key)+"</b>",
        className: "",
        style: "color:#666;font-size:0.9166em;padding-left:9px;"
      };
      for (var i = 0; i < YouTubeStreamSortedByFormat[key].length; i++) {
        if (is3D(YouTubeStreamSortedByFormat[key][i])) {
          b[b.length] = {
            text: getFormatTitle(YouTubeStreamSortedByFormat[key][i]),
            onclick: function() {
              downloadFile(this.getAttribute("itag"));
            },
            args: {
              itag: YouTubeStreamSortedByFormat[key][i].itag
            },
            register: function(elm) {
              if (!elements.downloadmenu3d) {
                elements.downloadmenu3d = [];
              }
              elements.downloadmenu3d[elements.downloadmenu3d.length] = elm;
            }
          };
        } else {
          b[b.length] = {
            text: getFormatTitle(YouTubeStreamSortedByFormat[key][i]),
            onclick: function() {
              downloadFile(this.getAttribute("itag"));
            },
            args: {
              itag: YouTubeStreamSortedByFormat[key][i].itag
            }
          };
        }
      }
    }
    b[b.length] = {
      text: "<b>MP3 (External Site)</b>",
      className: "",
      style: "color:#666;font-size:0.9166em;padding-left:9px;display:none",
      register: function(elm){
        elements.mp3title = elm;
      }
    };
    b[b.length] = {
      text: "Download from external site",
      onclick: function() {
        downloadMP3File();
      },
      style: "display:none",
      register: function(elm){
        elements.mp3link = elm;
      }
    };
    elements.downloadMenu = createYouTubeMenu(b);
    var a = getStreamOrQualityLower(settings.DownloadFormat, settings.DownloadQuality);
    elements.downloadButton = createYouTubeDoubleButton("Download " + getFormatTitle(a) + ", " + getFormatType(a.type.format), "<span class=\"yt-uix-button-content\">Download</span>", null, null, function(){
      downloadFileStreamQuality();
    }, "Download List", "<img class=\"yt-uix-button-arrow quimby_search_image\" src=\"//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif\" alt=\"\">", elements.downloadMenu, null, null);
    
    elements.repeatButton = createYouTubeButton("Toggle Repeat", "<img class=\"yt-uix-button-icon quimby_search_image\" src=\"//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif\" style=\"width:20px;height:18px;background:no-repeat url(//s.ytimg.com/yt/imgbin/www-master-vfl8ZHa_q.png) -303px -38px;\" alt=\"\" /><span class=\"yt-uix-button-content\">Repeat</span>", null, "yt-uix-button-toggle", function(){
      if (session.repeat) {
        session.repeat = false;
        setGlobalInformation("Repeat", 0);
        $RemoveClass(this, "yt-uix-button-toggled");
      } else {
        session.repeat = true;
        setGlobalInformation("Repeat", 1);
        $AddClass(this, "yt-uix-button-toggled");
      }
    });
    
    elements.ytbuttonswrapper = document.createElement("div");
    elements.ytelementLeft = document.createElement("div");
    elements.ytelementLeft.setAttribute("style", "float:left");
    elements.ytelementRight = document.createElement("div");
    elements.ytelementRight.setAttribute("style", "float:right");
    elements.ytelementClear = document.createElement("div");
    elements.ytelementClear.style.clear = "both";
    
    elements.ytbuttonswrapper.appendChild(elements.ytelementLeft);
    elements.ytbuttonswrapper.appendChild(elements.ytelementRight);
    elements.ytbuttonswrapper.appendChild(elements.ytelementClear);
    elements.main.appendChild(elements.ytbuttonswrapper);
    
    elements.ytelementRight.appendChild(elements.repeatButton);
    elements.ytelementRight.appendChild(document.createTextNode(" "));
    elements.ytelementRight.appendChild(elements.downloadButton);
    initSettingsUI();
    doSettingsChange();
    document.getElementById("watch-panel").insertBefore(elements.main, document.getElementById("watch-panel").firstChild);
  }

  function initGlobalInformation() {
    var elm = document.createElement("input");
    elm.id = "YouTubeCenterGlobalInformation";
    elm.style.position = "absolute";
    elm.style.top = "-100px";
    elm.style.left = "-100px";
    elm.style.width = "1px";
    elm.style.height = "1px";
    elm.style.border = "0";
    elm.style.margin = "0";
    elm.style.padding = "0";
    document.body.appendChild(elm);
  }

  function getGlobalInformation(key) {
    var g = $("YouTubeCenterGlobalInformation");
    if (g) {
      var value = g.value;
      var ca = value.split("&");
      for (var i = 0; i < ca.length; i++) {
        var c = ca[i].split("=");
        if (key == unescape(c[0])) {
          return unescape(c[1]);
        }
      }
    }
    return null;
  }

  function setGlobalInformation(key, value) {
    var g = $("YouTubeCenterGlobalInformation");
    if (g) {
      var v = getGlobalInformation(key);
      if (v) {
        g.value = g.value.replace(escape(key) + "=" + escape(v), escape(key) + "=" + escape(value));
      } else {
        if (g.value == "") {
          g.value = escape(key) + "=" + escape(value);
        } else {
          g.value += "&" + escape(key) + "=" + escape(value);
        }
      }
    }
  }

  function jsonReplacer(key, value) {
    if (typeof value === 'number' && !isFinite(value)) {
      return String(value);
    }
    return value;
  }

  function getTitle() {
    var meta = document.getElementsByTagName("meta");
    for (var i = 0; i < meta.length; i++) {
      if (meta[i].getAttribute("name") == "title") {
        return meta[i].getAttribute("content");
      }
    }
  }

  function init() {
    loadSettings();
    initGlobalInformation();
    setGlobalInformation("Settings", JSON.stringify(settings, jsonReplacer));
    setGlobalInformation("AutoResolution", settings.EnableAutoResolution);
    setGlobalInformation("PreventAutoPlay", settings.PreventAutoPlay);
    setGlobalInformation("PreventAutoBuffering", settings.PreventAutoBuffering);
    
    YouTubeVideoTitle = getTitle();
    var a = document.getElementsByTagName("a");
    for (var i = 0; i < a.length; i++) {
      if (a[i].getAttribute("rel") == "author") {
        YouTubeVideoAuthor = a[i].textContent;
        break;
      }
    }
    YouTubeVariables = getYouTubeVariables();
    if (!feather) {
      setGlobalInformation("YouTubeVariables", JSON.stringify(YouTubeVariables, jsonReplacer));
      YouTubeStreamInformation = parseYouTubeFormats(YouTubeVariables);
      YouTubeStreamSortedByFormat = splitYouTubeInformationByFormat(YouTubeStreamInformation);
      initUI();
      
      if (settings.AutoActivateRepeat) {
        session.repeat = true;
        setGlobalInformation("Repeat", 1);
        $AddClass(elements.repeatButton, "yt-uix-button-toggled");
      } else {
        setGlobalInformation("Repeat", 0);
      }
      
      if (settings.AutoExpandDescription) {
        $RemoveClass(document.getElementById("watch-description"), "yt-uix-expander-collapsed");
      }
      
      initYouTubePageInjection();
      setGlobalInformation("EnableDocumentShortcuts", (settings.EnableDocumentShortcuts ? "true" : "false"));
      checkForUpdates();
    } else { // Feather
      var elm = document.getElementById("vo");
      if (elm) {
        var s = document.createElement("span");
        s.style.padding = "0 5px";
        s.innerHTML = "YouTube Center: Feather isn't supported! - ";
        var a = document.createElement("a");
        a.href = "javascript:void(0);";
        a.addEventListener("click", function(){
          GM_xmlhttpRequest({
            method: "GET",
            url: "/feather_beta",
            onload: function(response){
              var d = "toggle_feather=true&session_token=" + response.responseText.match(/'XSRF_TOKEN': '(.*?)'/)[1];
              GM_xmlhttpRequest({
                method: "POST",
                data: d,
                url: "/feather_beta",
                headers: {
                  "Content-Type": "application/x-www-form-urlencoded"
                },
                onload: function(response){
                  window.location.reload();
                }
              });
            }
          });
          var parent = this.parentNode;
          parent.removeChild(this);
          parent.appendChild(document.createTextNode("Loading..."));
        }, false);
        a.innerHTML = "Turn Off";
        s.appendChild(a);
        elm.appendChild(s);
        console.log("YouTube Center: Feather isn't supported.");
      } else {
        console.error("YouTube Center: Couldn't fetch data from YouTube page - Be sure you have the lastest version.");
      }
    }
    if (settings.PlayerSize == 'small' || settings.PlayerSize == 'large') {
      document.getElementById("watch-headline-container").scrollIntoView();
    }
  }

  function initYouTubePageInjection() {
    inject(function(){
      var settings = eval('(' + getGlobalInformation("Settings") + ')');
      var YouTubeVariables = eval('(' + getGlobalInformation("YouTubeVariables") + ')');
      var ytLevel = {'highres': 5, 'hd1080': 4, 'hd720': 3, 'large': 2, 'medium': 1, 'small': 0};
      var callbakcs = [];
      var currentTime = 0;
      function init() {
        addCSS(".ytcenterfill{height:auto!important;}");
        if (settings.RemoveAdsPlayer && YouTubeVariables.args.ad_host) {
          var a = [
            'ad3_module', 'ad_channel_code_instream', 'ad_channel_code_overlay', 'ad_eurl',
            'ad_flags', 'ad_host', 'ad_host_tier', 'ad_logging_flag', 'ad_preroll', 'ad_slots',
            'ad_tag', 'ad_video_pub_id'
          ];
          for (var i = 0; i < a.length; i++) {
            if (!YouTubeVariables.args[a[i]]) continue;
            delete YouTubeVariables.args[a[i]];
          }
          if (YouTubeVariables.args.csi_page_type == "watch5ad")
            YouTubeVariables.args.csi_page_type = "watch5";
          yt.playerConfig = YouTubeVariables;
        }
        if (getYouTubePlayer().getCurrentTime) { // Properly not used
          currentTime = getYouTubePlayer().getCurrentTime();
        }
        if (settings.AutoHideBar) {
          if (settings.AutoHideBar == 0) {
            YouTubeVariables.args.autohide = 0;
          } else if (settings.AutoHideBar == 1) {
            YouTubeVariables.args.autohide = 1;
          } else if (settings.AutoHideBar == 2) {
            YouTubeVariables.args.autohide = 2;
          } else if (settings.AutoHideBar == 3) {
            YouTubeVariables.args.autohide = 3;
          }
          yt.playerConfig = YouTubeVariables;
        }
        /* Deletes variables which bugs the script */
        var variablesToDelete = [
          'ratings', 'ratings_preroll', 'ratings_module', 'ratings3_module'
        ];
        for (var i = 0; i < variablesToDelete.length; i++) {
          if (!YouTubeVariables.args[variablesToDelete[i]]) continue;
          delete YouTubeVariables.args[variablesToDelete[i]];
        }
        
        
        yt.setConfig({'PLAYER_REFERENCE': yt.player.update('watch-player', yt.playerConfig, true, gYouTubePlayerReady)});
        
        addRepeat();
        initShortcutsInDocument();
        if (getGlobalInformation("AutoResolution") === "true") {
          initAutoResolution();
        }
        if (getGlobalInformation("PreventAutoPlay") === "true") {
          if (getGlobalInformation("PreventAutoBuffering") === "true") {
            stopVideo();
          } else {
            pauseVideo();
          }
        } else if (getGlobalInformation("PreventAutoBuffering") === "true") {
          stopVideo();
        }
      }
      function addCSS(rule) {
        document.styleSheets[0].insertRule(rule, 0);
      }
      function seekToCurrentTime() {
        if (currentTime == 0) return;
        if (getYouTubePlayer().seekTo) {
          getYouTubePlayer().seekTo(currentTime);
        } else {
          setTimeout(seekToCurrentTime, 100);
        }
      }
      function pauseVideo() {
        if (getYouTubePlayer().pauseVideo) {
          getYouTubePlayer().pauseVideo();
        } else {
          setTimeout(pauseVideo, 100);
        }
      }
      function stopVideo() {
        if (getYouTubePlayer().stopVideo) {
          getYouTubePlayer().stopVideo();
        } else {
          setTimeout(stopVideo, 100);
        }
      }
      
      function getYouTubePlayer() {
        return yt.getConfig('PLAYER_REFERENCE');
      }
      function initShortcutsInDocument(e) {
        document.addEventListener("keydown", function(e){
          if (getGlobalInformation("EnableDocumentShortcuts") === "true") {
            e = e || window.event;
            if (document.activeElement.tagName.toLowerCase() === "input" || document.activeElement.tagName.toLowerCase() === "textarea") return;
            var player = getYouTubePlayer();
            if (!player.playVideo && !player.pauseVideo && !player.getPlayerState && !player.seekTo && !player.getDuration) return;
            switch (e.keyCode) {
              case 32: // Space
                var state = player.getPlayerState();
                if (state == -1 || state == 0 || state == 2 || state == 5) {
                  player.playVideo();
                } else {
                  player.pauseVideo();
                }
                break;
              case 37: // Left Arrow
                player.seekTo(player.getCurrentTime()-10, true);
                break;
              case 39: // Right Arrow
                player.seekTo(player.getCurrentTime()+10, true);
                break;
              case 35: // End
                player.seekTo(player.getDuration(), true);
                break;
              case 36: // Home
                player.seekTo(0, true);
                break;
              case 48: // 0
                player.seekTo(0, true);
                break;
              case 49: // 1
                player.seekTo(0.1*player.getDuration(), true);
                break;
              case 50: // 2
                player.seekTo(0.2*player.getDuration(), true);
                break;
              case 51: // 3
                player.seekTo(0.3*player.getDuration(), true);
                break;
              case 52: // 4
                player.seekTo(0.4*player.getDuration(), true);
                break;
              case 53: // 5
                player.seekTo(0.5*player.getDuration(), true);
                break;
              case 54: // 6
                player.seekTo(0.6*player.getDuration(), true);
                break;
              case 55: // 7
                player.seekTo(0.7*player.getDuration(), true);
                break;
              case 56: // 8
                player.seekTo(0.8*player.getDuration(), true);
                break;
              case 57: // 9
                player.seekTo(0.9*player.getDuration(), true);
                break;
              default:
                return;
            }
            e.preventDefault();
          }
        }, false);
      }
      function initAutoResolution() {
        if (getYouTubePlayer().getAvailableQualityLevels && getYouTubePlayer().setPlaybackQuality) {
          var availableQualities = getYouTubePlayer().getAvailableQualityLevels();
          if (availableQualities.indexOf(settings.AutoResolution) != -1) {
            getYouTubePlayer().setPlaybackQuality(settings.AutoResolution);
          } else {
            if (settings.AutoResolution != "small") {
              availableQualities = availableQualities.sort(function(a, b){
                return ytLevel[b] - ytLevel[a];
              });
              for (var i = 0; i < availableQualities.length; i++) {
                if (ytLevel[availableQualities[i]] <= ytLevel[settings.AutoResolution]) {
                  getYouTubePlayer().setPlaybackQuality(availableQualities[i]);
                  break;
                }
              }
            } else {
              getYouTubePlayer().setPlaybackQuality("medium");
            }
          }
        } else {
          setTimeout(initAutoResolution, 100);
        }
      }
      
      function setPlayBackQualityForFlash() {
        var player = getYouTubePlayer();
        if (player.setPlaybackQuality) {
          player.setPlaybackQuality(settings.AutoResolution);
        } else {
          setTimeout(setPlayBackQualityForFlash, 100);
        }
      }
      
      function addRepeat() {
        var player = getYouTubePlayer();
        if (player.addEventListener) {
          window['YouTubeCenterOnStateChange'] = function(state){
            if (state != 0) return;
            var getGlobalInformation = function(key) {
              var g = document.getElementById("YouTubeCenterGlobalInformation");
              if (g) {
                var value = g.value;
                var ca = value.split("&");
                for (var i = 0; i < ca.length; i++) {
                  var c = ca[i].split("=");
                  if (key == unescape(c[0])) {
                    return unescape(c[1]);
                  }
                }
              }
              return null;
            }
            var doRepeat = function() {
              var repeat = getGlobalInformation("Repeat");
              if (!repeat) return false;
              return (repeat == 1 ? true : false);
            }
            if (doRepeat()) {
              yt.getConfig('PLAYER_REFERENCE').playVideo();
            }
          };
          player.addEventListener('onStateChange', 'YouTubeCenterOnStateChange');
        } else {
          setTimeout(addRepeat, 100);
        }
      }
      
      function JSONReplacer(key, value) {
        if (typeof value === 'number' && !isFinite(value)) {
          return String(value);
        }
        return value;
      }
      
      function getGlobalInformation(key) {
        var g = document.getElementById("YouTubeCenterGlobalInformation");
        if (g) {
          var value = g.value;
          var ca = value.split("&");
          for (var i = 0; i < ca.length; i++) {
            var c = ca[i].split("=");
            if (key == unescape(c[0])) {
              return unescape(c[1]);
            }
          }
        }
        return null;
      }

      function setGlobalInformation(key, value) {
        var g = document.getElementById("YouTubeCenterGlobalInformation");
        if (g) {
          var v = getGlobalInformation(key);
          if (v) {
            g.value = g.value.replace(escape(key) + "=" + escape(v), escape(key) + "=" + escape(value));
          } else {
            if (g.value == "") {
              g.value = escape(key) + "=" + escape(value);
            } else {
              g.value += "&" + escape(key) + "=" + escape(value);
            }
          }
        }
      }

      function doRepeat() {
        var repeat = getGlobalInformation("Repeat");
        if (!repeat) return false;
        return (repeat == 1 ? true : false);
      }
      
      function doRepeatHTML5() {
        var player = document.getElementsByTagName("video")[0];
        if (player) {
          player.addEventListener('ended', function(){
            if (doRepeat()) {
              this.play();
            }
          }, false);
        } else {
          setTimeout(doRepeatHTML5, 500);
        }
      }
      init();
    });
  }

  /* Utils */
  function $(id) {
    return document.getElementById(id);
  }
  
  function $ToggleClass(element, c) {
    if ($HasClass(element, c)) {
      $RemoveClass(element, c);
    } else {
      $AddClass(element, c);
    }
  }
  
  function $AddClass(element, c) {
    var classes = element.className.split(" ");
    for (var i = 0; i < classes.length; i++) {
      if (classes[i] == c) return false;
    }
    if (element.className == "") {
      element.className = c;
    } else {
      element.className += " " + c;
    }
    return true;
  }
  
  function $RemoveClass(element, c) {
    var removed = false;
    var classes = element.className.split(" ");
    var newClasses = "";
    
    for (var i = 0; i < classes.length; i++) {
      if (classes[i] != c) {
        if (newClasses != "") newClasses += " ";
        newClasses += classes[i];
      } else {
        removed = true;
      }
    }
    element.className = newClasses;
    return removed;
  }
  
  function $HasClass(element, c) {
    var classes = element.className.split(" ");
    for (var i = 0; i < classes.length; i++) {
      if (classes[i] === c)
        return true;
    }
    return false;
  }
  
  function inject(callback) {
    var script = document.createElement("script");
    script.textContent = "(" + callback.toString() + ")();";
    document.body.appendChild(script);
  }

  function getHTML(element) {
    if (element == document)
      return document.documentElement.innerHTML;
    var d = document.createElement("div");
    d.appendChild(element);
    return d.innerHTML;
  }

  function createCookie(name, value, days) {
    if (days) {
      var date = new Date();
      date.setTime(date.getTime() + (days*24*60*60*1000));
      var expires = "; expires="+date.toGMTString();
    } else {
      var expires = "";
    }
    document.cookie = name + "=" + value + expires + "; path=/";
  }

  function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) == ' ')
        c = c.substring(1, c.length);
      if (c.indexOf(nameEQ) == 0)
        return c.substring(nameEQ.length, c.length);
    }
    return null;
  }

  function eraseCookie(name) {
    createCookie(name, "", -1);
  }
  
  function loadSettings() {
    var s = readCookie(CookieSettings);
    if (s) {
      var ca = s.split("&");
      for (var i = 0; i < ca.length; i++) {
        var c = ca[i].split("=");
        var t = c[1].split("\\");
        if (t.length == 1) {
          settings[unescape(c[0])] = unescape(c[1]); break;
        } else {
          switch (t[0]) {
            case 'null': settings[unescape(c[0])] = null;break;
            case 'undefined': settings[unescape(c[0])] = undefined; break;
            case 'string': settings[unescape(c[0])] = unescape(t[1]); break;
            case 'number': settings[unescape(c[0])] = Number(unescape(t[1])); break;
            case 'boolean': settings[unescape(c[0])] = (unescape(t[1]) === 'true'); break;
            case 'array': settings[unescape(c[0])] = eval('(' + unescape(t[1]) + ')'); break;
            case 'object': settings[unescape(c[0])] = eval('(' + unescape(t[1]) + ')'); break;
            case 'function': settings[unescape(c[0])] = eval('(' + unescape(t[1]) + ')'); break;
            case 'regexp': settings[unescape(c[0])] = new RegExp(unescape(t[1])); break;
            case 'date': settings[unescape(c[0])] = new Date(unescape(t[1])); break;
            case 'error': settings[unescape(c[0])] = new Error(unescape(t[1])); break;
            default: settings[unescape(c[0])] = unescape(t[1]); break;
          }
        }
      }
    }
  }

  function saveSettings() {
    var toType = function(obj) {
      return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
    }
    var s = "";
    var i = 0;
    for (var key in settings) {
      if (i > 0) s += "&"
      var t = toType(settings[key]);
      s += escape(key) + "=" + t + "\\";
      switch (t) {
        case 'null': s += "null";break;
        case 'undefined': s += "undefined";break;
        case 'string': s += escape(settings[key]); break;
        case 'number': s += escape(settings[key]); break;
        case 'boolean': s += escape(settings[key]); break;
        case 'array': s += escape(JSON.stringify(settings[key], jsonReplacer)); break;
        case 'object': s += escape(escape(settings[key])); break;
        case 'function': s += escape(settings[key].toString()); break;
        case 'regexp': s += escape(settings[key]); break;
        case 'date': s += escape(settings[key]); break;
        case 'error': s += escape(settings[key]); break;
        default: s += escape(settings[key]); break;
      }
      i++;
    }
    createCookie(CookieSettings, s, 30*24);
  }
  
  function checkForUpdates() {
    if (settings.EnableUpdate) {
      var t = (new Date).getTime();
      if (t-parseInt(settings.LastUpdateCheck, 10) >= settings.UpdateInterval) {
        settings.LastUpdateCheck = t;
        doUpdate(function(response){
          if (response.readyState === 4 && response.status === 200) {
            if (hasNewUpdate(getUpdate(response.responseText))) {
              elements.updateNote.innerHTML = "";
              var link = document.createElement("a");
              link.href = "http://userscripts.org/scripts/show/114002";
              link.target = "_blank";
              link.appendChild(document.createTextNode("Install"));
              elements.updateNote.appendChild(link);
              elements.updateNote.appendChild(document.createTextNode(" the new update."));
            }
          }
        });
        saveSettings();
      }
    }
  }
  
  function getUpdate(text) {
    return parseInt(/^\/\/ @updateVersion\s+([0-9]+)$/m.exec(text)[1], 10);
  }
  
  function hasNewUpdate(newVersion) {
    if (newVersion && newVersion > updateVersion) {
      return true;
    }
    return false;
  }
  
  function doUpdate(callback, error) {
    GM_xmlhttpRequest({
      method: "GET",
      url: "http://userscripts.org/scripts/source/114002.meta.js",
      headers: {
        "Content-Type": "text/plain"
      },
      onload: callback,
      onerror: error
    });
  }

  /* YouTube Utils */
  function getYouTubeVariables() {
    var html = getHTML(document);
    var startTag = "yt.playerConfig = {";
    var endTag = "};";
    
    var i = html.search(startTag);
    if (i == -1) {
      feather = true;
      return {};
    } else {
      html = html.substring(i+startTag.length);
      i = html.search(endTag);
      html = html.substring(0, i);
      return eval('({' + html + '})');
    }
  }

  function parseYouTubeFmtList(list) {
    var parts = list.split(",");
    var collection = [];
    for (var i = 0; i < parts.length; i++) {
      var a = parts[i].split("/");
      collection[collection.length] = {
        itag: a[0],
        dimension: a[1],
        unknown1: a[2],
        unknown2: a[3],
        unknown3: a[4]
      };
    }
    return collection;
  }

  function parseYouTubeStreamList(list) {
    var parts = list.split(",");
    var collection = [];
    for (var i = 0; i < parts.length; i++) {
      var coll = {};
      var args = parts[i].split("&");
      for (var j = 0; j < args.length; j++) {
        var keys = args[j].split("=");
        coll[keys[0]] = unescape(keys[1]);
      }
      if (coll.type.match(/[0-9a-zA-Z]+\/[0-9a-zA-Z]+;\+(.*)/)) {
        var tmp = coll.type.split(";");
        
        var ff = tmp[0];
        var fe = "";
        if (tmp.length > 2) {
          var t = "";
          for (var j = 1; j < tmp.length; j++) {
            t += tmp[j];
          }
          fe = t;
        } else {
          fe = tmp[1];
        }
        coll.type = {
          format: ff,
          extra: fe
        };
      } else {
        coll.type = {
          format: coll.type,
          extra: ""
        };
      }
      collection[collection.length] = coll;
    }
    return collection;
  }

  function parseYouTubeFormats(yt) {
    var fmtList = parseYouTubeFmtList(yt.args.fmt_list);
    var streamList = parseYouTubeStreamList(yt.args.url_encoded_fmt_stream_map);
    var collection = [];
    for (var i = 0; i < streamList.length; i++) {
      var fl = null;
      for (var j = 0; j < fmtList.length; j++) {
        if (streamList[i].itag != fmtList[j].itag) continue;
        fl = fmtList[j];
        break;
      }
      if (fl == null) {
        collection[collection.length] = streamList[i];
      } else {
        var coll = streamList[i];
        coll.dimension = fl.dimension;
        coll.unknown1 = fl.unknown1;
        coll.unknown2 = fl.unknown2;
        coll.unknown3 = fl.unknown3;
        collection[collection.length] = coll;
      }
    }
    return collection;
  }

  function createYouTubeButton(title, html, menu, classNames, onclick) {
    var button = document.createElement("button");
    button.innerHTML = html;
    if (menu) {
      button.appendChild(menu);
    }
    button.type = "button";
    button.className = "yt-uix-tooltip-reverse yt-uix-button yt-uix-tooltip yt-uix-button-default" + (classNames ? " " + classNames : "");
    button.title = title;
    button.setAttribute("aria-pressed", "false");
    button.setAttribute("onclick", ";return false;");
    button.setAttribute("role", "button");
    button.setAttribute("data-tooltip-text", title);
    if (onclick) {
      button.addEventListener("click", onclick, false);
    }
    return button;
  }

  function createYouTubeDoubleButton(title, html, menu, classNames, onclick, title2, html2, menu2, classNames2, onclick2) {
    var spn = document.createElement("span");
    spn.setAttribute("dir", "ltr");
    spn.className = "yt-uix-button-group watch show-label";
    
    var button = document.createElement("button");
    button.innerHTML = html;
    if (menu) {
      button.appendChild(menu);
    }
    button.type = "button";
    button.className = "start yt-uix-tooltip-reverse yt-uix-button yt-uix-tooltip yt-uix-button-default" + (classNames ? " " + classNames : "");
    button.title = title;
    button.setAttribute("aria-pressed", "false");
    button.setAttribute("onclick", ";return false;");
    button.setAttribute("role", "button");
    button.setAttribute("data-tooltip-text", title);
    if (onclick) {
      button.addEventListener("click", onclick, false);
    }
    
    spn.appendChild(button);
    
    var button2 = document.createElement("button");
    button2.innerHTML = html2;
    if (menu2) {
      button2.appendChild(menu2);
    }
    button2.type = "button";
    button2.className = "end yt-uix-tooltip-reverse yt-uix-button yt-uix-tooltip yt-uix-button-empty yt-uix-button-default" + (classNames2 ? " " + classNames2 : "");
    button2.title = title2;
    button2.setAttribute("aria-pressed", "false");
    button2.setAttribute("onclick", ";return false;");
    button2.setAttribute("role", "button");
    button2.setAttribute("data-tooltip-text", title2);
    if (onclick2) {
      button2.addEventListener("click", onclick2, false);
    }
    
    spn.appendChild(button2);
    
    return spn;
  }

  function createYouTubeMenu(build) {
    var menu = document.createElement("div");
    menu.className = "yt-uix-button-menu hid";
    for (var i = 0; i < build.length; i++) {
      var item = document.createElement("span");
      item.className = (build[i].className || build[i].className == "" ? build[i].className : "yt-uix-button-menu-item");
      if (build[i].style) {
        item.setAttribute("style", build[i].style);
      }
      item.setAttribute("onclick", ";return false;");
      item.innerHTML = build[i].text;
      if (build[i].onclick) {
        item.addEventListener("click", build[i].onclick, false);
      }
      if (build[i].args) {
        for (var key in build[i].args) {
          item.setAttribute(key, build[i].args[key]);
        }
      }
      if (build[i].register) {
        build[i].register(item);
      }
      menu.appendChild(item);
    }
    return menu;
  }

  function splitYouTubeInformationByFormat(informations) {
    var collection = {};
    for (var i = 0; i < informations.length; i++) {
      if (!collection[informations[i].type.format]) {
        collection[informations[i].type.format] = [];
      }
      var l = collection[informations[i].type.format].length;
      collection[informations[i].type.format][l] = informations[i];
    }
    return collection;
  }

  /* Initializing */
  init();
  /* END OF DOCUMENT */
})();

0 comments:

Post a Comment