// ==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 + ' </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' ? ' - ' + 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:' Ctrl', value:false, subfield:true, }, loopKeysAlt:{ type:'checkbox', label:' Alt', value:true, subfield:true, }, loopKeysShift:{ type:'checkbox', label:' Shift', value:false, subfield:true, }, loopKeysKey:{ type:'text', label:' 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 = " "; 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; \ } \ " );
Mozilla add on,User script,Grease Monkey Script, greasemonkey userscripts, updater userscripts mafia wars userscripts mafia wars autoplayer userscripts mafia wars wall userscripts scripts userscripts travian greasemonkey greasemonkey download greasemonkey facebook greasemonkey tutorial greasemonkey youtube greasemonkey travian greasemonkey chrome greasemonkey mafia wars greasemonkey mafia wars autoplayer
Saturday, October 29, 2011
Better Loopy for YouTube
Subscribe to:
Post Comments (Atom)
0 comments:
Post a Comment