Saturday, October 29, 2011

Better Loopy for YouTube


// ==UserScript==
// @name           YouTube_Better_Loopy
// @namespace      PI
// @description    Loop songs within time duration play all videos on a page(by //                 extra script)
// @include        http://*.youtube.com/watch?v*
// @include        http://youtube.com/watch?v*
// @include        http://*.youtube.com/watch_popup?v*
// @include        http://youtube.com/watch_popup?v*
// @include        http://*.youtube.com/watch?NR*
// @version        3.5.8.0
// @credit         CDM, and the supporter(s) of the original script 'Loopy for YouTube'; PhasmaExMachina for his Updater and Options Dialog
// @require     http://userscripts.org/scripts/source/91400.user.js
// @notify         true
// ==/UserScript==


var version = '3.5.8.0';
var debug = false;

//Dont run in/from frames. 
if(window.top != window.self)
 return;


//******** Default Settings - can be changed. ******//
var extensionEnabled = true;
var nextVideosEnabled = true;
var timedLoopEnabled = true;
var newButtonStyle = true;
var buttonLess = false;
var enableKeyboardShortcuts = true;
var loopKeysCtrl = false;
var loopKeysAlt = true;
var loopKeysShift = false;
var loopKeysKey = 'L';
var loopByDefault = true;
//******** Default Settings section over. ******//


//******** Utility methods. Don't change if you don't understand what you are doing*********//
var isChrome = (navigator.userAgent.toLowerCase().indexOf('chrome') >= 0);
var isFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') >= 0);
log = debug ? (isChrome ? function(message){console.log(message);} : (isFirefox ? GM_log : function(){}) ) : function() {}
var url = new String(window.location.href).toLowerCase();

var GM_KEY_PREFIX = "GM_KEY_";
var MAX_DAYS = 5000;
function GM_GlobalSetValue(key, val)
{
    var gmFound = false;
    try
    {
        if(GM_setValue && isFirefox) // to increase the problems, Chrome defines the method and says "not supported".
        {
            GM_setValue(key, val);
            gmFound = true;
        }
    }
    catch(ex)
    {
        //I hate you Google Chrome.
    }
   
    if(!gmFound)
    {
        //work around using cookies.
        createCookie(GM_KEY_PREFIX + key, val, MAX_DAYS);
    }
}

function GM_GlobalGetValue(key, defaultValue)
{
    var returnValue = defaultValue;
    var gmFound = false;
    try
    {
        if(GM_getValue && isFirefox)
        {
            returnValue = GM_getValue(key, defaultValue);
            gmFound = true;
        }
    }
    catch(ex)
    {
  //do nothing
    }
    if(!gmFound)
    {
  var cookieTry = readCookie(GM_KEY_PREFIX + key);
  if(cookieTry)
  {
   returnValue = cookieTry;
   if(returnValue == "false") //most probably, we wanted this. 
    returnValue = false;
   else if(returnValue == "true")
    returnValue = true;
  }
    }

    return returnValue;
}

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 getChildrenByXPath(currentNode, xpath, CallBack)
{
 var returnArray = new Array(); 
 var nodesSnapshot = document.evaluate(xpath, currentNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

 for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ )
  returnArray.push(CallBack ? CallBack(nodesSnapshot.snapshotItem(i)) : nodesSnapshot.snapshotItem(i));

 return returnArray;
}

function findPosX(obj)
{
 var curleft = 0;
 if(obj.offsetParent)
  while(1) 
  {
   
    curleft += obj.offsetLeft;
    if(!obj.offsetParent)
   break;
    obj = obj.offsetParent;
  }
 else if(obj.x)
  curleft += obj.x;
 return curleft;
}

function findPosY(obj)
{
 var curtop = 0;
 if(obj.offsetParent)
  while(1)
  {
    curtop += obj.offsetTop;
    if(!obj.offsetParent)
   break;
    obj = obj.offsetParent;
  }
 else if(obj.y)
  curtop += obj.y;
 return curtop;
}

function clickObject(obj)
{
 if(!obj)
  return;
    var clickEvent = window.document.createEvent("MouseEvent");
    clickEvent.initEvent("click", true, false);
    obj.dispatchEvent(clickEvent);
}
  
//***************************************************************************


//
var isPopup = url.indexOf('watch_popup') >= 0;
var wasPlaying = false;

//*********** Check for Updates - Only for Firefox. ************** //
function onVersionCheck(remoteVersion)
{
 log("*********Version returned: " + remoteVersion + "*************");
}
if(typeof ScriptUpdater != 'undefined' && isFirefox)
{
 ScriptUpdater.check(57971, version, onVersionCheck);
}




//***************** Configuration Settings - Many thanks to PhasmaExMachina *********************************** // 
//***************** http://userscripts.org/scripts/show/62718 ************************************************* //
//***************** (There are some changes on top of his original script to suit my needs )******************* //
Config = {
 data:null,
 callback:null,
 tempOptions:{},
 footerHtml:'<span style="font-size:.9em;">Note: You may need to refresh the page to see changes.</span>',
 reloadOnSave:false,
 init:function(settings) {
  Config.settings = settings;
 },
 preloadData:function() {
  Config.data = {};
  Config.settings = typeof(Config.tabs) != 'undefined' ? Config.tabs : Config.settings; 
  for(var tabName in Config.settings) {
   if(typeof(Config.settings[tabName].fields) == "object") {
    var fields = Config.settings[tabName].fields
    for(var fieldName in fields) {
     Config.data[fieldName] = Config.get(fieldName);
     
    }
   }
  }
 },
 close:function(saved) {
  document.body.removeChild(Config.$('ConfigBodyWrapper'));
  document.body.removeChild(Config.$('ConfigMask'));
  window.removeEventListener('keyup', Config.keyUpHandler, true);
  if(typeof(Config.callback) == 'function')
   Config.callback(saved);
 },
 show:function(callback) {
  Config.tempOptions = {};
  Config.settings = typeof(Config.settings) != 'undefined' ? Config.settings : Config.tabs;
  Config.callback = typeof(callback) == 'function' ? callback : null;
  if(typeof(Config.styleDrawn) == 'undefined') {    // apply styling
   GM_addStyle("\
     #ConfigMask { position:absolute; width:100%; top:0; left:0; height:100%; background-color:#000; opacity:.7; z-index:9000; } \
     #ConfigBody * { border:none; font-size:12px; color:#333; font-weight:normal !important; margin:0 !important; padding:0 !important; background:none; text-decoration:none; font-family:Helvetica Neue,Arial,Helvetica,sans-serif; line-height:1.2em; } \
     #ConfigBody { width:400px; margin:auto !important; top:0px; right: 0px; position:fixed; text-align:left; background:#f9f9f9; border:1px outset #333; padding:0 !important; font-family:Arial; font-size:14px; border-radius:5px; -moz-border-radius:5px; cursor:default; z-index:9010; color:#333; padding-bottom:1em !important; } \
     #ConfigBody a { text-decoration:underline; color:#000099 !important; } \
     #ConfigBody strong, #ConfigContentBox strong { font-weight:bold !important; } \
     #ConfigBody h1 { font-size:13px; font-weight:bold !important; padding:.5em !important; border-bottom:1px solid #333; background-color:#999; margin-bottom:.75em !important; } \
     #ConfigBody h2 { font-weight:bold; margin:.5em 1em !important; } \
     #ConfigBody h1 { font-size:13px; font-weight:bold; color:#fff; text-decoration:none; } \
     #ConfigBody h1 a:hover { text-decoration:underline; } \
     #ConfigBody li { list-style-type:circle; } \
     #ConfigBody p { font-size:12px; font-weight:normal; margin-bottom:1em !important; } \
     #ConfigContentPadding { margin:0 1em !important; }\
     #ConfigTabs { margin-top:20px !important; }\
     #ConfigTabs span { border:1px solid #666; border-radius:5px 5px 0 0; -moz-border-radius:5px 5px 0 0; padding: 2px 10px !important; position:relative; top:-2px; background-color:#ddd; cursor:pointer; }\
     #ConfigTabs span:hover { background-color:#eee; }\
     #ConfigTabs span.active { background-color:#F9F9F9; top:-1px; border-bottom:none; padding-top:3px !important; font-weight:bold; cursor:inherit; }\
     #ConfigTabs span.active:hover { background-color:#F9F9F9; }\
     #ConfigContentBox { border:1px inset #666; padding:1.5em 1em 1em !important; max-height:300px; overflow:auto; }\
     #ConfigContentBox table { width:auto !important; }\
     #ConfigContentBox td { font-weight:normal; }\
     #ConfigContentBox input { border:1px inset #666 !important; }\
     #ConfigContentBox td.fieldLabel { text-align:left !important; padding-right:.5em !important;font-weight:bold !important; }\
     #ConfigContentBox td.subFieldLabel { text-align:left !important; padding-right:.5em !important; }\
     #ConfigContentBox td select { border:1px inset #666; }\
     #ConfigHistory { margin:0 1em 1em 1em !important; max-height:150px; overflow-y:auto; border:1px inset #999; padding:0 1em 1em !important; width:448px; } \
     #ConfigHistory ul { margin-left:2em !important; } \
     #ConfigClose { float:right; cursor:pointer; height:14px; opacity:.5; } \
     #ConfigClose:hover { opacity:.9; } \
     #ConfigFooter { padding:1.5em 1em 0 !important; } \
     #ConfigFooter input { border:1px outset #666; padding:3px 5px 5px 20px !important; background:no-repeat 4px center #eee; border-radius:3px; -moz-border-radius:3px; cursor:pointer; width:80px; float:right; margin-left:.5em !important; } \
     #ConfigFooter input:hover { background-color:#f9f9f9; } \
     #ConfigFooter select { border:1px inset #666; }\
     #ConfigContentBox #ConfigFieldTable { width:auto !important; margin-left:2em !important; }\
     #ConfigContentBox #ConfigFieldTable td { padding-bottom:.5em !important; }"
    );
   if(typeof(Config.css) != 'undefined')     // apply user specified styles if set
    GM_addStyle(Config.css);
   Config.styleDrawn = true;
  }
  // declare and apply config background mask
  var noticeBg = document.createElement('div');       
  noticeBg.id = "ConfigMask";
  noticeBg.style.height = (unsafeWindow.scrollMaxY + unsafeWindow.innerHeight) + 'px';
  document.body.appendChild(noticeBg);
  // declare and apply config window
  var noticeWrapper = document.createElement('div');
   noticeWrapper.setAttribute('style', 'position:absolute; width:100%; top:0; left:0; z-index:9010; max-width:auto; min-width:auto; max-height:auto; min-height:auto;');
   noticeWrapper.id = "ConfigBodyWrapper";
    var notice = document.createElement('div');
    notice.id = "ConfigBody";
    var html = '<h1>\
        <img src="' + Config.icons.config + '" align="absmiddle" style="margin-top:-2px;"/>\
        ' + (typeof(Config.scriptName) == 'string' ? Config.scriptName + ' - ' : '') + ' Settings\
       </h1>\
       <div id="ConfigContentPadding">\
        <div id="ConfigTabs">';
    // draw tabls
    var i = 0;
    var firstTabId = "";
    for(var label in Config.settings) {
     var id = 'configTab' + label.replace(/\s/g, '_');
     html += '<span id="' + id + '">' + label + '</span>';
     firstTabId = i == 0 ? id : firstTabId;
     i++;
    }
    html += '</div><div id="ConfigContentBox">';   
    html += '</div>';   
    html += '</div>';   
    html += '<div id="ConfigFooter">\
       <input type="button" id="ConfigCancelButton" value="Cancel" style="background-image:url(' + Config.icons.close + ')"/>\
       <input type="button" id="ConfigCloseButton" value="Save" style="background-image:url(' + Config.icons.save + ')"/>\
       ' + Config.footerHtml + '\
      </div>';
    notice.innerHTML = html;;
   noticeWrapper.appendChild(notice);
  document.body.appendChild(noticeWrapper);
  // add tab change listeners
  for(var label in Config.settings) {
   var id = 'configTab' + label.replace(/\s/g, '_');
   Config.$(id).addEventListener('click', function() { Config.activateTab(this.id); }, false);
  }
  // add escape key press and other listener
  Config.activateTab(firstTabId);
  window.addEventListener('keyup', Config.keyUpHandler, true);
  Config.$('ConfigCloseButton').addEventListener('click', function() {
   Config.save();
   Config.close(true);
   if(Config.reloadOnSave)
    document.location = '';
  }, true);
  Config.$('ConfigCancelButton').addEventListener('click', function() { Config.close(false) }, true);
  if(Config.onclick)
   Config.$('ConfigBody').addEventListener('click',Config.onclick, false);
 },
 //-------------------------------- "private" methods -----------------------------------------
 activateTab:function(id) {
  // deactivate current tab
  var elems = Config.$('ConfigTabs').getElementsByTagName('span');
  for(var i = 0; i < elems.length; i++) {
   elems[i].className = ''; 
  }
  // set current tab
  Config.$(id).className = 'active';
  var key = id.replace(/^configTab/, '').replace(/_/g, ' ');
  var fields = Config.settings[key].fields;
  var html = typeof(Config.settings[key].html) == 'string' ? Config.settings[key].html : '';
  html += '<table cellpadding="0" cellspacing="0" border="0" id="ConfigFieldTable">';
  for(var fieldName in fields) {
   var field = fields[fieldName];
   var type = typeof(field.type) != 'string' ? 'html' : field.type;
   var tip = typeof(field.tip) == 'string' ? field.tip : '';
   var classUsed = (field.subfield == true) ? 'subFieldLabel' : 'fieldLabel';
   var disabledString = (field.disabled == true) ? ' disabled="disabled" ' : '';
   if(type != 'html')
    html += '<tr title="' + tip + '"><td colspan="' + (type == 'html' ? '2' : '1') + '" class="'+classUsed+'">' +
     (typeof(field.label) == 'string' ? field.label : '') + '</td><td>';
   else
    html += '<tr>';
   switch(type) {
    case 'select':
     html += '<select id="configInput_' + fieldName + '" '+ disabledString +'>';
     if(typeof(field.options) == 'undefined')
      alert('Options Error: ' + fieldName + ' of type "select" is missing the "options" property');
     else {
      for(var text in field.options) {
       var val = field.options[text];
       html += '<option value="' + val + '"' + (Config.get(fieldName) == val ? ' selected' : '') + '>' + text + ' &nbsp;</option>';
      }
     }
     html += '</select>';
     break;
    case 'password':
    case 'text':
     var width = typeof(fields[fieldName].width) != 'undefined' ? (fields[fieldName].width.toString().match(/px/) ? fields[fieldName].width : fields[fieldName].width + 'px') : false;
     var maxLengthString = typeof(field.maxLength) != 'string'? "" : ' maxLength = "' + field.maxLength + '" ';
     html += '<input '+ maxLengthString + 'id="configInput_' + fieldName + '" value="' + Config.get(fieldName) + '" style="' + (width ? 'width:' + width + ';' : '') + '" type="' + type + '"'+ disabledString +'/>';
     break;
    case 'checkbox':
     html += '<input id="configInput_' + fieldName + '" type="checkbox" style="position:relative; top:2px;"' + (Config.get(fieldName) ? 'checked' : '' ) + ''+ disabledString +'/>';
     break;
    case 'html':
     html += '<td colspan="2">' + field.value + '</td>';
     break;
   }
   if(type != 'html') {
    html += '<i>';
    html += typeof(fields[fieldName].text) == 'string' ? ' &nbsp; - ' + fields[fieldName].text : '';
    html += '</i></td></tr>';
   } else
    html += '</tr>';
  }
  html += '</table>';
  // add check for updates
  if(id == "configTabAbout" && typeof(ScriptUpdater) == 'object' && typeof(ScriptUpdater.scriptId) != 'undefined') {
   html += '<p><br/><a href="javascript:void(0)" id="ConfigCheckUpdatesLink">Check for updates</a></p>';
  }
  Config.$('ConfigContentBox').innerHTML = html;
  // add event listeners
  for(var fieldName in fields) {
   switch(fields[fieldName].type) {
    case 'checkbox': 
     Config.$('configInput_' + fieldName).addEventListener('change', function() {
      Config.tempOptions[this.id.toString().match(/configInput_(.+)$/)[1]] = this.checked;// ? '1' : '0';
     }, false);
     break;
    case 'select': 
     Config.$('configInput_' + fieldName).addEventListener('change', function() {
      Config.tempOptions[this.id.toString().match(/configInput_(.+)$/)[1]] = this.value;                  
     }, false);
     break;
    case 'password':
    case 'text':
     Config.$('configInput_' + fieldName).addEventListener('keyup', function() {
      Config.tempOptions[this.id.toString().match(/configInput_(.+)$/)[1]] = this.value;                  
     }, false);
     break;
   }
  }
  if(id == "configTabAbout" && typeof(ScriptUpdater) == 'object' && typeof(ScriptUpdater.scriptId) != 'undefined') {
   $('#ConfigCheckUpdatesLink')[0].addEventListener('click', function() { ScriptUpdater.forceNotice(ScriptUpdater.scriptId, ScriptUpdater.scriptCurrentVersion); }, false);
  }
 },
 keyUpHandler:function (e) {
  if(e.keyCode == 27) { Config.close(false); }
 },
 icons:{
  install:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALZSURBVBgZBcFLiFVlAADg7zzuPLzjzDjOMINMitIie5gF+UAkIZSgRQuXLZIWrY021dYIggJdJURElJsoqlWRYA9GshGFCNQeOjoTk6bjeOd5zzn/f07flzRNA459ObcHJ3cM9+1fq2prVa2qa+uh7mAZ9xCxiAV8iu9zgDqEvU9ODOx//dkxALBa1kNrZT202I2TZcVyEd28t+Lb66uHcTwHqEMYH+xJwNyDqJUk8oQsp7eV2tqbytJUK+OpyX5bhtojH07Pv58CxKoabOeEmuUy0al4UNDp0umysM5/KxG8eWbW/u1tj4+2xnKAWFUjG3tSqwWr3ShNEzmyjDQjk8gSaiRxyYUbiy7PduZzgFiW40P9mc56sFY00rSRpaQxkaVkGlmGJnNnqXDq7N9LOJYDhLLcNj7Y0uk2AjRkMZE2iGQaeZOqG2IrCmXY/s1rB+6nALEstk0M9VotG0lKliRSpEjw+YUjPjq3RxkKoSjEsoiQwvMnvusXQ09vK1VGUg1qjVrUqDWKUJoc3emVj3dbWeuEUJZLkEMoyrF2u0+aUEPD19OHNXVQ1kEZgy2bHrZzYq/l7qr766/m3VC0ub+SQyyLDXm7R56SpYlYJ0JdOvzYy2JTi3VUa8x35jwxecBKue7S7E+dXW+nI/nB42dGcWLPI1vdXmrcvBO1++iGUmxqtxb+UtVBqCtVrCwVy3Y/dNBKtZb+OjO1kMeyfA4vXLo6Y3E9t1I0qtjo6goxGB/cKtRRbGr/dmaNDEy4PHfe+etTd8vgSB6r6ukXD+3qf+ulfQDg6OnCJ7+8p6xL3VDaMfqofTuOuHhryrk/fl4tokPz7zRX8lhVM7fvdXx29qrhgX7Dg32G271OHv3dxg09entSvXnqmXcHJGm/6Ru/ad89dmrm9AdXIK9D+GLq4rXJqYvXtmEzNmMTNmGor6fV6utr6YxWfvjzR0P/vDGTh7GvAP4H2uh1wse2x/0AAAAASUVORK5CYII%3D",
  config:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALvSURBVBgZBcFNaNUFAADw3//jbe/t6d6cc2/kUpeXsEgUsSSiKIzAQxDdvCgdulgagmBXLx4K7BgRWamnOgSDIj3EusRangwlbVvOyba25tvH23v/z36/oCxLcOr7uaO48sxA9Vg7LbTTQloUtrKihXUsI8cqVvAtfo4Biix78eDItmPnX90FADaTotFOisZqJx9NUta7udnlDT/+vXkc52KAIsua/T0BmHuSqwSBOCCK6a2E9vSGojBUiTg0WvNUoz74xeTjT0OAPE376zFZwXoSaKU86dLq0OqwssXSRg4uXn/o2Fjd80OVXTFAnqaD23tCm102O7kwDMSIIsKISCAKKBDka36bXnX7YetxDJAnSbNRi7S2Mu1uKQxLUUiYB6KQSCmKUEYW17o+u/lgDadigCxJ9jb7K1qdUgYlUR4IS+RsPfhFliaeGzkhr+SyJBv74aOX/wsB8qS7d6TRazMpBSFREAjWH0lmflV21lR7e/T19fl3acmbAw+9MzT7CQRlWXrr0k+1OArb3104bvKfVKEE6fSEffv2mZ+f12w2hWFodnbW6Oio8fFxRVHUY8i6ya56vSoMKKAkCAi279bpdCwvL5uYmFCr1Rw4cEC73Vav1786c+ZMO4Q86fbFCnFIFAYEoY17tzSiTcPDw+7fv+/1kxe9e/q8R/PzRkZG7N+///Tly5fL+JVz14dw6eizeyyslWYXc/UqnVZLFEWazabh4WG1Kv19lGVgfX3d3Nyc6elpcZ4kb+DEH3dnrG7FNrqlNC8V2UEjG/MGBxeMjY2ZHP/aVFDa8/RuKysr7ty58yUuxHmaHn77tRdqH598CQDkJde+mcKAhYUFRw4f1Ol0zMzMaDQa8F6tVns/ztN0ZmG55drNuwa21Qz0Vw3UezXqvQYGh1y9etUHH5419fukxcVFy2XTrVufl1mW3bxx40YeHDp5ZQjnsBc7sRM7sAONak+lUq1WHKrds7S05M/yyF84efva2Sn4HxcNUm7wsX3qAAAAAElFTkSuQmCC",
  close:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg%3D%3D",
  uso:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQCAYAAAAiYZ4HAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh9JREFUeNp0krmLWnEQxyf7zLoajyIWXojIxkK0EiIGCRamCKQwEdIIgYQoQSR/wLY2goVVJGCa1BaL2liKBESFiOJFiMRb1xMVRbx+mfdA0RwDA4/3m+Mz3xmAf9hDNJ/P9zWXy935/f7A5eXlFfzPRCKROBgMfqvX62S5XBLabDbbh8M76zRYKpUqvF5vyGw2P+bz+cBisWCz2cB2u33wV2WFQvEoFArlW60WmUwmZLVakdFoRNxu9xd8Fp51UKlUWmS91ev11zweD5AZMAFmsxkgWhpDpsfKarVaE4lEqpVKhUynU4a73++TcrlMarUa6Xa7G7vd/u4QT93c3HzmcrlPSqUSiMVihrvX68F6vYZsNkvPcOFyuV5Uq9VuoVD4ztrv91wOhwMCgQAGgwEsFguYz+eMSyQSkMvlwGazqUAg8KnRaHSo4XA4Q9leYRdmHrpyJpMBehaDwQBCoRB2ux2gapRSqbymsP2PTqezsFqtz+6hpVIpprLRaGTw8BcgBVOo2WyOj8NbLJaP+Xx+k0gkCL00xGNEoJ2WOZlMznQ6nfVsFyaT6X273d4eAmkfj8ckHo+PNRrNSzrm4jRBq9XysDWF18Cg0OzpdPrO6XS+QRVvz6oj0nOch25NYrEYgxEOhxsymezpadyxA8p5HxUDXBTgSUA0Gv3pcDheI2LiNIE6fOAN/cKkK9RdUSwWkx6P5y0mZv+8ud8CDABidDMA4Sb2JAAAAABJRU5ErkJggg%3D%3D",
  save:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAH+SURBVBgZBcE9i11VGAbQtc/sO0OCkqhghEREAwpWAWUg8aMVf4KFaJEqQtAipTZWViKiCGOh2Ap2gmJhlSIWFsFOxUK0EsUM3pl79n4f12qHb3z3Fh7D83gC95GOJsDe0ixLk5Qq/+xv/Lw9Xd+78/HLX3Y8fXTr2nWapy4eCFKxG7Fby97SnDlYtMbxthyfzHO//nl85fNvfvnk8MbX5xa8IHx1518Vkrj54Q+qQms2vVmWZjdiu5ZR2rT01166/NCZg/2PFjwSVMU6yjoC1oq+x6Y3VbHdlXWExPd379nf7Nmejv2Os6OC2O4KLK0RNn3RNCdr2Z5GJSpU4o+/TkhaJ30mEk5HwNuvX7Hpi76wzvjvtIwqVUSkyjqmpHS0mki8+9mPWmuWxqYvGkbFGCUAOH/+QevYI9GFSqmaHr5wkUYTAlGhqiRRiaqiNes6SOkwJwnQEqBRRRJEgkRLJGVdm6R0GLMQENE0EkmkSkQSVVMqopyuIaUTs0J455VLAAAAAODW0U/GiKT0pTWziEj44PZ1AAAAcPPqkTmH3QiJrlEVDXDt0qsAAAAAapa5BqUnyaw0Am7//gUAAAB49tEXzTmtM5KkV/y2G/X4M5fPao03n/sUAAAAwIX7y5yBv9vhjW/fT/IkuSp5gJKElKRISYoUiSRIyD1tufs/IXxui20QsKIAAAAASUVORK5CYII%3D",
 },
 getField:function(key) {
  Config.settings = typeof(Config.tabs) != 'undefined' ? Config.tabs : Config.settings;
  for(var tabName in Config.settings) {
   if(typeof(Config.settings[tabName].fields) == "object") {
    var fields = Config.settings[tabName].fields
    for(var fieldName in fields)
     if(fieldName == key)
      return fields[fieldName];
   }
  }
  return false;
 },
 get:function(key) {
  if(Config.data != null && typeof(Config.data[key]) != 'undefined')
   return Config.data[key];
  else {
   var field = Config.getField(key);
   key = typeof(Config.prefix) == 'string' ? Config.prefix + key : key;
   switch(field.type) {
    case 'checkbox':
     if(typeof(field.value) == 'undefined' || !field.value)
      return GM_GlobalGetValue(key, false);
     else 
      return GM_GlobalGetValue(key, field.value);
     break;
    case 'select':
    case 'password':
    case 'text':
     return GM_GlobalGetValue(key, (typeof(field.value) != 'undefined' ?  field.value : ''));
     break;
    default: 
     return 'not found';
   } 
  }
 },
 save:function() { 
  for(var x in Config.tempOptions)
   Config.set(x, Config.tempOptions[x]);
  Config.tempOptions = {};
 },
 set:function(key, value) {
  key = typeof(Config.prefix) == 'string' ? Config.prefix + key : key; 
  GM_GlobalSetValue(key, value);
 },
 $:function(id) {
  return document.getElementById(id);
 },
};

onClickButtonLess = function(e)
{
 var prefix = 'configInput_';
 if(!e)
  return;
 var node = e.target;
 
 if(node.tagName.toLowerCase() != 'input' || node.getAttribute('type').toLowerCase() != 'checkbox')
  return;
  
 function $x(id){ return document.getElementById(prefix + id);}
 
 function click(obj, toCheck)
 {
  var evt = document.createEvent("MouseEvents");
  evt.initEvent("click", true, true);
  if(obj.checked != toCheck)
   obj.dispatchEvent(evt);
 }
 
 if(node.checked)
 {
  if(node.id == prefix + 'buttonLess')
  {
   click($x('timedLoopEnabled'), false);
   // $x('nextVideosEnabled').checked = false;
   // $x('loopByDefault').checked = true;
   click($x('nextVideosEnabled'), false);
   click($x('loopByDefault'), true);
   
  }
  else if(node.id == prefix + 'timedLoopEnabled' || node.id == prefix + 'nextVideosEnabled')
   click($x('buttonLess'), false);
 }
}

Config.scriptName = "Better Loopy for YouTube";
Config.onclick = onClickButtonLess;

Config.tabs = {
 "Features":{
  html:'<p>Set your feature preferences here.</p>',
  fields:{
   timedLoopEnabled:{
    type:'checkbox',
    label:'Enable Timed Loop',
    tip:'Check if you want to loop videos within a time duration.',
    value:true,
   },
   nextVideosEnabled:{
    type:'checkbox',
    label:'Enable playing all videos on a page',
    tip:'Check if you want to press the Play All bookmark button on any page to play all videos one by one. ',
    value:true,
   },
   buttonLess:{
    type:'checkbox',
    label:'No UI, just plain loop',
    tip:'If checked, none of the buttons/links/text boxes etc. will be shown. You can still loop through keyboard shortcuts',
    value:false,
   },
   loopByDefault:{
    type:'checkbox',
    label:'Loop all videos by default',
    value:false,
    tip:'Check if you automatically want to loop all videos you watch.',
   },
  }
 },
 "Keyboard Shortcuts":{
  html:'<p>Set your keyboard shortcut preferences here.</p>',
  fields:{
   enableKeyboardShortcuts:{
    type:'checkbox',
    label:'Enable Keyboard shortcuts',
    tip:'Check if keyboard shortcuts should be enabled.',
    value:true,
   },
   EnterToFocus:{
    type:'checkbox',
    label:'Pressing enter focuses on Start Time',
    tip:'Check if you want to press the "Play All" button on any page to play all the video one by one. ',
    value:true,
   },
   LoopKeys:{
    type:'html',
    value:'<strong>Toogle Loop shortcut keys:</strong>',
   },
   loopKeysCtrl:{
    type:'checkbox',
    label:'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ctrl',
    value:false,
    subfield:true,
   },
   loopKeysAlt:{
    type:'checkbox',
    label:'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Alt',
    value:true,
    subfield:true,    
   },
   loopKeysShift:{
    type:'checkbox',
    label:'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Shift',
    value:false,
    subfield:true,
   },   
   loopKeysKey:{
    type:'text',
    label:'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Key (A-Z only)',
    value:'L',
    subfield:true,
    disabled:false,
    width:'50',
    maxLength:'1',
   },
  }
 }, 
 "About":{
  html:'<p><a href="http://userscripts.org/scripts/show/57971"><span style="font:15px !important;font-weight:bold;color:gray">Better Loopy for YouTube</span></a> by <a href="http://userscripts.org/users/piyushsoni">Piyush Soni</a><br/> version '+ version +' <br/><br/><a href="http://userscripts.org/scripts/source/57971.user.js" >Install latest version</a><iframe src="http://piyushsoni.com/feed/betterloopy.html" scrolling="no" height="70px" width="100%"/>',
 }
};

//Read config settings
function readConfigIntoBetterLoopy()
{
 log('came inside readConfig.');
 timedLoopEnabled = Config.get('timedLoopEnabled');
 nextVideosEnabled = Config.get('nextVideosEnabled');
 buttonLess = Config.get('buttonLess');
 enableKeyboardShortcuts = Config.get('enableKeyboardShortcuts');
 loopKeysCtrl = Config.get('loopKeysCtrl');
 loopKeysAlt = Config.get('loopKeysAlt');
 loopKeysShift = Config.get('loopKeysShift');
 loopKeysKey = Config.get('loopKeysKey').toUpperCase();
 loopByDefault = Config.get('loopByDefault');
 extensionEnabled = timedLoopEnabled || nextVideosEnabled;
 log('timedloop: ' + timedLoopEnabled);
 log('nextvid: ' + nextVideosEnabled);
 log('buttonLess: ' + buttonLess);
 log('loopKeysCtrl: ' + loopKeysCtrl);
 log('loopKeysShift: ' + loopKeysShift);
 log('loopKeysAlt: ' + loopKeysAlt);
 log('loopKeysKey: ' + loopKeysKey);
 log('loopByDefault: ' + loopByDefault);
 log('extensionEnabled: ' + extensionEnabled);
 log('enableKeyboardShortcuts: ' + enableKeyboardShortcuts);
 // log(' :' + );
 
}

readConfigIntoBetterLoopy();

//Make settings accessible. 
if(isFirefox)
 GM_registerMenuCommand('Better Loopy for YouTube Settings', showSettings);

isCosmicPanda = ($('masthead-nav') != null);
var linksSection = $('masthead-sections') || $('masthead-nav');
if(linksSection)
{
 var settingsLink = create('a',[['href','#'],['click', function() {showSettings();}],['innerHTML','Better Loopy'],['class', 'split end ']]);
 
 splitLink = $('.split end', linksSection);
 // log("split link : " + splitLink);
 if(splitLink)
 {
  // splitLink.setAttribute('style','margin-right:0px !important');
  splitLink.removeAttribute('class');
  linksSection.insertBefore(settingsLink, splitLink.nextSibling);
 }
 else
  linksSection.appendChild(settingsLink); 
}

function showSettings()
{
 if($('ConfigBody'))
  Config.close(false);
 else
 {
  if(ytObj && ytObj.getPlayerState() == "1")
  {
   ytObj.pauseVideo();
   wasPlaying = true;
  }
  Config.show(onSettingsDone);
 }
}


//************Keyboard shortcuts section ******************************
if(enableKeyboardShortcuts)
 window.addEventListener('keyup', handleKeyPress, false);
function handleKeyPress(e)
{
 var shiftPressed = e.shiftKey;
 var altPressed = e.altKey;
 var ctrlPressed = e.ctrlKey;
 var node = e.target;
 
 //Ok. Once again, test adding this comment. 
 //log("Key: " + e.keyCode + ", Key Modifiers: " + (ctrlPressed ? " ctrl " : " ") + (altPressed ? "alt " : " ") + (shiftPressed ? "shift" : ""));
 
 switch(e.keyCode)
 {
  case 13://Enter
   if(ytObj && (node.id == 'txtStartTime' || node.id == 'txtEndTime'))
   {
    time = getMinuteSecondsTime(ytObj.getCurrentTime());
    node.value = time;
   }
   else 
   {
    if(node.id == 'idTextBoxDivSendLoopLink')
    {
     clickObject($('buttonSendLinkOK'));
    }
    else if(node.tagName.toUpperCase() == 'HTML' && $('txtStartTime')) $('txtStartTime').focus();
   }
  break;
  default:
   var incorrectKeys = (loopKeysShift != shiftPressed) || (loopKeysAlt != altPressed) || (loopKeysCtrl != ctrlPressed);
   if(e.keyCode == loopKeysKey.charCodeAt(0) && !incorrectKeys)
   {
    LoopyOnOff();
    goToStart();
    e.stopPropagation();
    e.preventDefault();    
   }
   else if(e.keyCode == 'M'.charCodeAt(0) && !incorrectKeys)
   {
    showSettings();
    e.stopPropagation();
    e.preventDefault();
   }
   else if(e.keyCode == 'N'.charCodeAt(0) && !incorrectKeys)
   {
    playNext();
    e.stopPropagation();
    e.preventDefault();
   }
   else if(e.keyCode == 'P'.charCodeAt(0) && !incorrectKeys)
   {
    togglePlay();
    e.stopPropagation();
    e.preventDefault();
   }   
 }
}

function onSettingsDone(saved)
{
 if(wasPlaying)
 {
  ytObj.playVideo();
  wasPlaying = false;
 }
 if(saved)
 {
  //readConfigIntoBetterLoopy();
  //location.reload();
 }
}

var ytLoop = false;
var ytPlayList;
var ytPLIndex;
var loopCounter = 0;
var maxLoopLimit = -1;
var eventRegistered = false;
var elementsAdded = false;
var eventRegistrationRetries = 0;
var ytPlayer = null, ytObj = null;
var divBetterLoopyActions, buttonActions;
var actionsPanelOn = false;
var divSendLoopLink; 
var sharePanelEventAdded = false;
var isCosmicPanda = false;

function /*class*/ RememberedSong(iVideoID, iStartTime, iEndTime)
{
 this.videoID = iVideoID;
 this.startTime = iStartTime;
 this.endTime = iEndTime;
}

// var arrRememberedSongs = new Array();
// var song1 = new RememberedSong('test1', '1:23','4:45');
// var song2 = new RememberedSong('test2', '0:23','3:45');
// arrRememberedSongs.push(song1);
// arrRememberedSongs.push(song2);
// // alert(uneval(arrRememberedSongs));
// GM_GlobalSetValue('RememberString', uneval(arrRememberedSongs));
// var arr2 = eval(GM_GlobalGetValue('RememberString', ('({})')));
// alert(arr2[1].videoID);

var APPEND_MODE_NONE = 0;
var APPEND_MODE_END = 1;
var APPEND_MODE_BEGINNING = 2;
function create(elementType, attributes, appendMode, parent)
{
 var newEl = document.createElement(elementType);
 for(i in attributes)
 {
  if(attributes[i][0] == 'innerHTML')
   newEl.innerHTML = attributes[i][1];
  else if(attributes[i][0] == 'click')
   newEl.addEventListener('click', attributes[i][1], false);
  else
   newEl.setAttribute(attributes[i][0], attributes[i][1]);
 }
 if(!appendMode || appendMode == APPEND_MODE_NONE)
  return newEl;
 if(appendMode == APPEND_MODE_END)
  parent.appendChild(newEl);
 else if(appendMode == APPEND_MODE_BEGINNING)
  parent.insertBefore(newEl, parent.firstChild);
 
 return newEl;
}

function $(identifier, altParent, returnAll)
{
    var obj = null, objs = null;
    doc = altParent ? altParent : document;
    if(identifier.indexOf(".") == 0)
    {
        identifier = identifier.replace(".","");
        objs = doc.getElementsByClassName(identifier);
    }
    else if(identifier.indexOf("<") == 0)
    {
        identifier = identifier.replace("<","").replace(">","");
        objs = doc.getElementsByTagName(identifier);
    }
    else
    {
        obj = doc.getElementById(identifier);
        if(!obj)
            objs = doc.getElementsByName(identifier);
    }
    if(objs && objs.length > 0)
    {
        if(returnAll)
            return objs;
        else
            return objs[0];
    }   
    else
        return obj;
}

if(buttonLess)
 extensionEnabled = false;
if(!extensionEnabled) 
 nextVideosEnabled = false;

loopy = create('div', [['id','eLoopy']]);
a = create('label', [['class','LoopyOff'],['title','Enable auto replay'],['innerHTML','Loop'],['id','eOnOff'],['click', function () {LoopyOnOff(); return false;}]]);

if (window.location.href.toLowerCase().indexOf("feature=playlist") > 0) 
{
 if(a)
  a.innerHTML = "Loop PlayList";

 urlArgs = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
 for(var i = 0; i < urlArgs.length; i++) 
 {
  arg = urlArgs[i].split('=');
  if (arg[0].toLowerCase() == "p") 
  {
   ytPlayList = arg[1];
  } else if (arg[0].toLowerCase() == "index") 
  {
   ytPLIndex = parseInt(arg[1], 10)+1;
  }
 }

 if(ytPlayList == getCookie("LoopyPL")) 
 {
  a.title = "Disable auto replay";
  a.setAttribute("class", "LoopyOn");
  ytLoop = true;
 }
}

if(!buttonLess)
 loopy.appendChild(a);
window.setTimeout(function() { initLoopy(); }, 500);

function initLoopy()
{
 if (!elementsAdded && !buttonLess)
 {
  var piExtension = create('div',[['id','newDiv']]);
  
  var blankSpan = create('span',[['width','100px']]);
  blankSpan.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
  
  var buttonSet = create('input', [['type','button'],['value','Loop'],['class','NewLoopyOff yt-uix-tooltip-reverse yt-uix-button yt-uix-tooltip'], ['id','loopSet'], ['title','Enable auto replay'],['tabindex','4'],['style','vertical-align:top !important'],['click', function() {LoopyOnOff(); goToStart();}]]);
  
  if(nextVideosEnabled && !$('txtVideos'))
  {
   var textel = document.createTextNode("Next videos: ");
   var spaceEl = document.createTextNode("  ");
   var inputbox = create('input', [['id', 'txtVideos'],['value', getQuerystring('nextVideos')],['class','TextBox']]);
   var linkNext = create('a', [['style','cursor:pointer']]);
   linkNext.addEventListener('click', playNext, false);
   linkNext.innerHTML = "Next >";  
   piExtension.appendChild(textel);
   piExtension.appendChild(inputbox);
   piExtension.appendChild(spaceEl);
   piExtension.appendChild(linkNext);
  }
  
  //See if the URL contains custom time interval to loop. 
  var urlStartTime = getQuerystring("loopStart");
  var urlEndTime = getQuerystring("loopEnd");
  var customTime = false;
  if(urlStartTime.length > 0 || urlEndTime.length > 0)
  {
   customTime = true;
   //Enable timed loop, at least for this session. 
   timedLoopEnabled = true;   
  }
  
  //For looping between a start and end time:
  var extraDiv = null; 
  if(timedLoopEnabled && !$('txtStartTime'))
  {
   if(nextVideosEnabled)
    piExtension.appendChild(blankSpan);
   var loopStart = document.createTextNode("   Loop Start time: ");
   inputStartTime = create('input', [['id','txtStartTime'],['size','5'],['tabindex','2'],['class','TextBox'],['title','Enter time in mm:ss format or press Enter key to set current video time']]);
   
   var loopEnd = document.createTextNode(" End Time: ");
   inputEndTime = create('input', [['id','txtEndTime'],['size','5'],['tabindex','3'],['class','TextBox'],['title','Enter time in mm:ss format or press Enter key to set current video time']]);
   inputEndTime.addEventListener('blur', goToStart, false);

   piExtension.appendChild(loopStart);
   piExtension.appendChild(inputStartTime);
   piExtension.appendChild(loopEnd);
   piExtension.appendChild(inputEndTime);
   piExtension.appendChild(document.createTextNode(' '));
   piExtension.appendChild(buttonSet);
  }
  
  if(urlStartTime.length > 0 && inputStartTime && getProperTime(urlStartTime) > 0)
  {
   inputStartTime.value = urlStartTime;
  }
  
  if(urlEndTime.length > 0 && inputEndTime && getProperTime(urlEndTime) > 0)
  {
   inputEndTime.value = urlEndTime;   
  }
  
  var watchPanelDiv = $("watch-panel") || $(".watch-panel-section watch-panel-divided-top", document);
  if(watchPanelDiv)
  {
   if(nextVideosEnabled != timedLoopEnabled) //one of them is false, and not both.
   {
    piExtension.setAttribute('style','float:right');
    extraDiv = create('div',[['id','idExtraDiv'],['style','overflow:hidden']]);
    extraDiv.appendChild(piExtension);
   }
   
   //Remove the top padding from the watch-panel div, which makes the 'Loopy' button detached from the YouTube player. 
   watchPanelDiv.setAttribute("style","padding-top:0px");
   
   if(extensionEnabled)
   {
    piExtension.appendChild(buttonSet);
    SetupActionsUI(piExtension);
    
    //Add extension to watch panel.     
    watchPanelDiv.insertBefore((extraDiv ? extraDiv : piExtension), watchPanelDiv.firstChild);
    buttonSet.setAttribute('class','Thin ' + buttonSet.getAttribute('class'));
   }
   else if(newButtonStyle)
   {
    var watchActionsRight = $("watch-actions-right") || $("watch-actions");
    watchActionsRight.appendChild(buttonSet);
    
   }
   if(!newButtonStyle)
    watchPanelDiv.insertBefore(loopy, watchPanelDiv.firstChild);
  }
  else
  {
   //last resort
   watchPlayerDivID = "watch-player";
   watchPlayerDiv = $(watchPlayerDivID);
   if(isPopup)
   {
    if(!watchPlayerDiv)
     watchPlayerDiv = $("watch-player-div");
    if(watchPlayerDiv)
     watchPlayerDiv = watchPlayerDiv.parentNode;
   }
   if(watchPlayerDiv)
   {
    if(!extensionEnabled)
    {
     if(newButtonStyle)
      watchPlayerDiv.appendChild(buttonSet);
     else
      watchPlayerDiv.appendChild(loopy);
    }
    else
     watchPlayerDiv.appendChild(piExtension);
   }
  }
  
  elementsAdded = true;
 }
 
 
 ytPlayer = $("movie_player");
 if(!ytPlayer && isPopup)
  ytPlayer = $("video-player");// in case of pop up. 
 
 try
 {
  if(!eventRegistered)
  {
   ytObj = ytPlayer.wrappedJSObject || ytPlayer;
   ytObj.addEventListener("onStateChange", 'onPlayerStateChange');
   eventRegistered = true;
   log('registered on retry number ' + eventRegistrationRetries);
  }
  
  //Play All Videos/Loop by default. 
  if(ytObj && !ytLoop && ((extensionEnabled && nextVideosEnabled && inputbox && inputbox.value != "") || loopByDefault || customTime))
  {
   //Make it automatically on since there are next videos to be played or the user wants it that way. 
   LoopyOnOff();
   log('Loop is on by default');
  }
  
  //Run according to the custom time. 
  if(ytObj && customTime && eventRegistered)
  {
   // var startTime = getProperTime(inputStartTime.value);
   // ytObj.seekTo(startTime, true);
   var callBack = unsafeWindow.onPlayerStateChange || onPlayerStateChange;
   callBack(0);
  }
 
 }
 catch(ex)
 {
  if(ex.toString().indexOf('Not enough arguments') < 0)
   log('exception! ' +  ex.toString());
 }
 
 if(!eventRegistered && eventRegistrationRetries < 5)
  window.setTimeout(function() { initLoopy(); }, 500 + (eventRegistrationRetries++)*500);
}

function SetupActionsUI(addToElement)
{
 //Create settings dropdown button:
 //buttonActions class when active : yt-uix-button-active
 buttonActions = create('button', [['class','yt-uix-button-masked yt-uix-tooltip-reverse yt-uix-button yt-uix-tooltip Thin'],['aria-activedescendant',''],['aria-haspopup','true'],['aria-expanded','false'],['aria-pressed','false'],['role','button'],['onclick',';return false;'],['type','button'],['style','vertical-align:top !important;'],['data-tooltip-title','Better Loopy Actions'],['click', OnActionsClick],['id','idButtonActions']], APPEND_MODE_END, addToElement);
 
 var imgActions = create('img', [['class','yt-uix-button-arrow'],['alt','Actions'],['src','//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif']], APPEND_MODE_END, buttonActions);
 
 //Create Settings dropdown. 
 divBetterLoopyActions = create('div', [['id','idDivBetterLoopyActions'],['class','yt-uix-button-menu watch'], ['style','display:none;font-size:12px']], APPEND_MODE_END, addToElement);
 
 var divInsideActions = create('div',[['id','idInnerActions']], APPEND_MODE_END, divBetterLoopyActions);
 
 //Add , 'Remember Loop For Video'
 var listActions = CreateMenuList('listInnerActions', ['Settings', 'Set Repeat Limit', 'Send Looped Link'], divInsideActions); 
 
 divSendLoopLink = create('div', [['id','idDivSendLoopLink'],['innerHTML','<br/>Copy and send the following link for the looped part: <br/>'],['style','display:none; position:absolute; z-index:9000;top:300;left:500;width:500px; height: 80px; background-color:grey;color:#FFFFFF;text-align:center;vertical-align:center;']], APPEND_MODE_END, addToElement);
 var sendLinkTextBox = create('input', [['id','idTextBoxDivSendLoopLink'],['type','text'],['style','vertical-align:center'],['size','80']], APPEND_MODE_END, divSendLoopLink);
 divSendLoopLink.innerHTML = divSendLoopLink.innerHTML + "<br/>";
 var okButton = create('input',[['id','buttonSendLinkOK'],['type','button'],['value','OK'],['style','vertical-align:bottom'],['click',function () {divSendLoopLink.style.display='none';}]], APPEND_MODE_END, divSendLoopLink);
 
 // var watchActionsShare = $('watch-actions-share');
 // var longLinkNodes = getChildrenByXPath(watchActionsShare, "//input[contains(text(), 'Long link')]");
 // if(longLinkNodes.length > 0)
  // OnSharePanelReady();
 // else if(!sharePanelEventAdded)
 // {
  // //wait for it to be ready. 
  // sharePanelEventAdded = true;
  // watchActionsShare.addEventListener('DOMNodeInserted', OnSharePanelReady, false);
 // }
 
 //Set event on body click. 
 document.body.addEventListener('click', bodyClick, true);
}

function CreateMenuList(idList, arrListItemTexts, containerElement)
{
 var listActions = create('ul', [['id','listInnerActions'], ['class','addto-menu'],['style','text-align: left;']], APPEND_MODE_END, containerElement);
 var i;
 for(i = 0; i < arrListItemTexts.length; ++i)
 {
  var listItem = create('li', null, APPEND_MODE_END, listActions);
  var spanId = 'ActionItem' + arrListItemTexts[i].replace(/ /g,"");
  var spanItem = create('span',[['class','yt-uix-button-menu-item addto-item LeftAligned'],['innerHTML',arrListItemTexts[i]],['click', function() {OnActionItemClick(this);}],['id',spanId]], APPEND_MODE_END, listItem);
 }
}

unsafeWindow.onPlayerStateChange = function(newState)
{
 log('Player state changed to ' + newState + ', ytLoop is ' + ytLoop);
 if (ytLoop && newState == "0")
 {
  if (typeof ytPlayList != "undefined") 
  {
   if (ytPLIndex == $("playlistVideoCount_PL").innerHTML) 
   {
    var url = $("playlistRow_PL_0").getElementsByTagName("a")[0].href + "&playnext=1";
    window.setTimeout(function() { window.location = url}, 60);
   }
  } 
  else 
  {
   //If any video IDs are there in the next Videos list, try to play them first. 
   if(!playNext())
   {
    if(maxLoopLimit > 0 && loopCounter >= maxLoopLimit)
    {
     //Max loop limit set. We don't want to loop anymore. 
     LoopyOnOff();
     return;
    }
    window.setTimeout(function() { ytObj.playVideo(); }, 60);
    if(isChrome)
    {
     ytObj.playVideo();
     setTimeout(function() { goToStart(); }, 900);
    }
    ++loopCounter;
    if(!newButtonStyle && a)
     a.title = "Video looped " + loopCounter + " time(s). " + "Disable auto replay";
    buttonSet = $('loopSet');
    if(buttonSet)
    {
     buttonSet.setAttribute('title', "Video looped " + loopCounter + " time(s). " + "Disable auto replay");
     buttonSet.setAttribute('data-tooltip', "Video looped " + loopCounter + " time(s). " + "Disable auto replay");
    }
    if(timedLoopEnabled)
    {
     var videoDuration = ytObj.getDuration();
     var startTime = getProperTime($("txtStartTime").value);
     //If startTime box is empty, move to start of video.
     if(startTime < 0)
      startTime = 0;
     ytObj.seekTo(startTime, true);

     var endTime = getProperTime($("txtEndTime").value);
     if(endTime > startTime && endTime < videoDuration)
     {
      setTimeout(function() { goToStart(); }, 900);
     }
    }
   }
  }
 }
}

function goToStart()
{
 log('checking');
 if(!ytObj || !ytLoop)
  return;
 endTimeBox = $("txtEndTime");
 if(isFirefox && (!endTimeBox || endTimeBox.value == ""))
  return;
 var endTime = getProperTime(endTimeBox ? endTimeBox.value : "");
 if(endTime < 0)
  endTime = ytObj.getDuration();
 
 var currentTime = ytObj.getCurrentTime();
 if(currentTime >= endTime)
 {
  var callBack = unsafeWindow.onPlayerStateChange || onPlayerStateChange;
  callBack(0);
 }
 else
  setTimeout(function() { goToStart(); }, 900);
}

LoopyOnOff = function() 
{
 log(ytLoop ? 'disabling loop' : 'enabling loop');
 var newClass, oldClass, newTitle;
 if (ytLoop) 
 {
  ytLoop = false;  
  if (typeof ytPlayList != "undefined") setCookie("LoopyPL", null);
  newTitle = "Enable auto replay";
  oldClass = "LoopyOn";
  newClass = "LoopyOff";
 }
 else 
 {
  ytLoop = true; 
  if (typeof ytPlayList != "undefined") setCookie("LoopyPL", ytPlayList);
  newTitle = "Disable auto replay";
  oldClass = "LoopyOff";
  newClass = "LoopyOn";
 }
 
 buttonSet = $("loopSet");
 if(buttonSet)
 {
  buttonSet.setAttribute('title', newTitle);
  buttonSet.setAttribute('data-tooltip', newTitle);
  currentStyle = buttonSet.getAttribute('class');
  buttonSet.setAttribute('class', currentStyle.replace("New" + oldClass,"New" + newClass));
  //buttonSet.focus();
 }
 if(!newButtonStyle)
 {
  oldOnOff = $("eOnOff");
  if(oldOnOff)
  {
   oldOnOff.title = newTitle;
   oldOnOff.setAttribute("class", newClass);
  }
 }
 if(ytLoop && isChrome) // How many times I have to say that I hate you Google Chrome. 
  goToStart();
}

bodyClick = function(e)
{
 if(e.target.id != 'idButtonActions')
  HideActions();
}

OnActionsClick = function()
{
 if(actionsPanelOn)
  HideActions();
 else
  ShowActions();
}

function ShowActions()
{
 if(!actionsPanelOn)
 {  
  actionsPanelOn = true;
  var x = findPosX(buttonActions);
  var y = findPosY(buttonActions);
  var oneOffsetX = buttonActions.offsetLeft;
  var oneOffsetY = buttonActions.offsetTop;
  // var rect = buttonActions.getBoundingClientRect();
  // x = rect.left - page.scrollLeft;
  // y = rect.top - page.scrollTop;
  
  isCosmicPanda = ($('masthead-nav') != null);
  
  divBetterLoopyActions.style.display = 'block';
  var width = divBetterLoopyActions.offsetWidth;
  
  if(isCosmicPanda)
  {
   divBetterLoopyActions.style.left = (oneOffsetX + buttonActions.offsetWidth - width) + "px";
   divBetterLoopyActions.style.top = (oneOffsetY + buttonActions.offsetHeight) + "px";
  }
  else
  {
   divBetterLoopyActions.style.left = (x + buttonActions.offsetWidth - width) + "px";
   divBetterLoopyActions.style.top = (y + buttonActions.offsetHeight) + "px";
  }
  
  var currentClass = buttonActions.getAttribute('class');
  buttonActions.setAttribute('class', currentClass + ' yt-uix-button-active');
 }
}

function HideActions()
{
 if(actionsPanelOn)
 {
  actionsPanelOn = false;
  divBetterLoopyActions.style.display = 'none';
  
  var currentClass = buttonActions.getAttribute('class');
  buttonActions.setAttribute('class', currentClass.replace(' yt-uix-button-active',''));
 }
}

function OnActionItemClick(obj)
{
 HideActions();
 var id = obj.id;
 if(id == 'ActionItemSettings')
 {
  showSettings();
 }
 else if(id == 'ActionItemSendLoopedLink')
 {
  // var shareButton = $("watch-share");
  // if(shareButton)
   // clickObject(shareButton);

  var textBox = $('idTextBoxDivSendLoopLink');
  if(textBox)
  {
   var loopString = "";
   //textBox.value = window.location.replace("#","");
   var txtStartTime = $('txtStartTime');
   var txtEndTime = $('txtEndTime');
   
   if(txtStartTime && txtStartTime.value != "")
    loopString += "&loopStart="+txtStartTime.value;
   if(txtEndTime && txtEndTime.value != "")
    loopString += "&loopEnd="+txtEndTime.value;
   if(loopString.length == 0)
   {
    alert('You are not looping a part of the video and can therefore send the whole link. :)');
    return;
   }
   
   textBox.value = window.location.href.replace("#","") + loopString;
   // textBox.focus();
   // textBox.select(); //why doesn't it work? 
   window.setTimeout(function() {textBox.focus();},0);
   window.setTimeout(function() {textBox.select();},0); //Why does it work? 
  }
  
  divSendLoopLink.style.display = 'block';
  
 }
 else if(id == 'ActionItemSetRepeatLimit')
 {
  limit = prompt("Enter the number of times the video should be looped");
  maxLoopLimit = parseInt(limit, 10);
  if(isNaN(maxLoopLimit))
  {
   maxLoopLimit = -1;
   return;
  }
 }
 else if(id == 'ActionItemRememberLoopForVideo')
 {
  var videoID = getQuerystring('v');
  var txtStartTime = $('txtStartTime');
  var txtEndTime = $('txtEndTime');
  var rememberString = videoID + ",";
  if(txtStartTime && txtStartTime.value != "")
   rememberString += "loopStart," + txtStartTime.value + ",";
  if(txtEndTime && txtEndTime.value != "")
   rememberString += "loopEnd," + txtEndTime.value + ",";
  if(rememberString.length > 0)
  {
   var lastRemember = GM_GlobalGetValue("RememberString","");
   GM_GlobalSetValue("RememberString", lastRemember + "," + rememberString + ";");
   // alert('Done!');
  }
 }
}

function OnSharePanelReady()
{
 var watchActionsShare = $('watch-actions-share');
 // var longLinkNodes = getChildrenByXPath(watchActionsShare, "//input[contains(text(), 'Long link')]");
 // if(sharePanelEventAdded)
  // watchActionsShare.removeEventListener('DOMNodeInserted', OnSharePanelReady, false);
 var sharePanelURL = getChildrenByXPath(watchActionsShare, "//input[@class='share-panel-url']");
 divSendLoopLink.style.display = 'block';
}

function getCookie(name)
{
 var results = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
 if (results) 
 {
  return unescape(results[2]);
 } else {
  return null;
 }
}

function getProperTime(stringTime)
{
 if(trim(stringTime, " ") == "")
  return -1;
 var timeInSeconds = 0;
 var index = stringTime.indexOf(':');
 if(index < 0)
  timeInSeconds = parseInt(stringTime, 10);
 else
 {
  var time = stringTime.split(":");
  timeInSeconds = parseInt(time[0]*60, 10) + parseInt(time[1], 10);
 }
 if(isNaN(timeInSeconds))
  return 0;
 else 
  return timeInSeconds;
}

function getMinuteSecondsTime(seconds)
{
 totalSeconds = Math.round(seconds);
 seconds = totalSeconds % 60;
 if(seconds < 10) seconds = "0" + seconds; 
 minutes = (totalSeconds - seconds)/60;
 return new String(minutes + ":" + seconds);
}

function setCookie(name, value) 
{
 document.cookie = name + "=" + escape(value);
}

function trim(str, chars) 
{
 return ltrim(rtrim(str, chars), chars);
}
 
function ltrim(str, chars) 
{
 chars = chars || "\\s";
 return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}
 
function rtrim(str, chars) 
{
 chars = chars || "\\s";
 return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
} 

function getQuerystring(key, default_)
{
  if (default_==null) default_="";
  key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
  var qs = regex.exec(window.location.href);
  if(qs == null)
 return default_;
  else
 return qs[1];
}

function playNext()
{
 if(!extensionEnabled || !nextVideosEnabled)
  return false;
  
 log('Playing next video');
  
 var retVal = false;
 var el = $('txtVideos');
 if(el && el.value != "")
 {
  var index = el.value.indexOf(";");
  var videoID = el.value;
  if(index>0)
  {
   videoID = videoID.substring(0,index);
   el.value = el.value.substring(index+1);
  }
  else
   el.value = "";
  watchString = isPopup ? "watch_popup" : "watch";
  var nextLocation = "http://www.youtube.com/"+ watchString +"?v="+videoID;
  if(el.value.length > 1)
   nextLocation += "&nextVideos="+el.value;
  window.location = nextLocation;
  retVal = true;
 }
 return retVal;
}

function togglePlay()
{
 if(ytObj)
 {
  state = ytObj.getPlayerState();
  if(state == "1")
   ytObj.pauseVideo();
  else if(state == "2")
   ytObj.playVideo();
 }
}

if (typeof GM_addStyle == "undefined") 
{
 GM_addStyle = function(text) {
  var head = document.getElementsByTagName("head")[0];
  style = create('style',[['type','text/css']]);
  style.textContent = text;
  head.appendChild(style);
 }
}

GM_addStyle("      \
 #eLoopy {     \
  width: 28px;    \
  margin-left: auto;   \
  text-align: center;   \
  background: #EFEFEF;   \
  border-left: #B1B1B1 1px solid;  \
  border-right: #B1B1B1 1px solid; \
  border-bottom: #B1B1B1 1px solid; \
  padding: 1px 4px 1px 4px;  \
  margin-bottom: 5px; }   \
 #newDiv {     \
  margin-left: auto;   \
  color:grey;   \
  text-align: center;   \
  font-size: 11px;   \
  vertical-align:middle;   \
  background:-moz-linear-gradient(center top , #FFFFFF, #dfdfdf) repeat scroll 0 0 #F6F6F6;  \
  background: #F6F6F6 -webkit-gradient(linear, 0% 0%, 0% 100%, from(white), to(#dfdfdf));   \
  border-left: #B1B1B1 1px solid;  \
  border-right: #B1B1B1 1px solid; \
  border-bottom: #B1B1B1 1px solid; \
  border-top: #B1B1B1 1px solid;  \
  padding: 2px 4px 2px 4px;  \
  border-radius: 3px;    \
  -moz-border-radius: 3px; \
  height:20px        \
  }   \
 #eOnOff {     \
  font-weight: bold;   \
  font-size: 11px;   \
  text-decoration: none;   \
  -moz-user-select: none;   \
  -khtml-user-select: none;  \
  user-select: none; }   \
 .LoopyOff {     \
  color: grey !important; }  \
 .LoopyOff:hover {    \
  color: black !important; }  \
 .LoopyOn {     \
  color: crimson !important; } \
 .NewLoopyOff{      \
  color:gray;      \
  font-weight:bold;    \
 }         \
 .NewLoopyOn{      \
  color:rgb(191,0,65);   \
  font-weight:bold;    \
 }         \
 .Thin{      \
  height:1.72em;      \
 }         \
 .LeftAligned{      \
  margin-left: 0;      \
 }         \
 .TextBox{      \
  height:16px;      \
  background-color:white;    \
  border:1px solid lightgray;     \
  color:black;    \
  vertical-align:center;   \
  border-radius: 3px;    \
  -moz-border-radius: 3px; \
 }         \
 "
);

0 comments:

Post a Comment