Guest User

Download YouTube Videos as MP4 version: 1.5.16 June 20, 2013

a guest
Jun 24th, 2013
353
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name Download YouTube Videos as MP4
  3. // @description Adds a button that lets you download YouTube videos.
  4. // @namespace http://googlesystem.blogspot.com
  5. // @include http://www.youtube.com/watch?*
  6. // @include https://www.youtube.com/watch?*
  7. // @match http://www.youtube.com/watch?*
  8. // @match https://www.youtube.com/watch?*
  9. // @homepageURL http://userscripts.org/scripts/show/25105
  10. // @updateURL https://userscripts.org/scripts/source/25105.meta.js
  11. // @author Gantt
  12. // @version 1.5.16
  13. // @date 2013-06-20
  14. // @license MIT License
  15. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAB3RJTUUH2wMOCgIoGUYEAQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAGSUExURfi/JO/v797e3sbGxq2traWlpZSUlJycnNbW1oyEhIRaWow5OZQhIZwYGKUQEKUICK0ICJQxMYxKSoxzc4x7e4RCQpQYGKUAAK0AALUAAL0AAK0QEIxra5QpKa0YGIxSUsYAAKUhIZR7e87Ozr0ICJRSUr29vYxjY6U5OaUpKa0hIb21tZwAALUICO/Ozu/GxqUxMZSEhLUYGO/W1r0YGKVCQpQQEL0pKffe3vfW1pxra5Q5OcZCQvfn585CQr2trZx7e8ZSUs5SUq05Oc5jY9ZjY84AAKWMjM5zc957e60pKdaMjOelpbWcnLWUlLVCQsYYGMYICNbOzpQICMYhIbV7e5xaWt6cnPfv79bGxt6lpe+9vc5KSs6lpb0xMc6EhM69vbUxMbUhIb1aWs61tcZaWuecnMYxMb1KSsZjY96UlNa1td7W1r17e9a9vZwQEN6trb1jY8YQENZra+fOzr1zc85aWufe3t6MjMY5OdZaWt61tdZ7e+/n5+e9vc6MjMZra+/e3ue1tdalpd7GxrUpKalL4aAAAAABdFJOUwBA5thmAAACxklEQVR42uXX/1/SQBgH8NuAoEQ2ijgbBivJLznBAiUUKiyJSgOVAk0tKZKw75mRRt/7v4MBY8ezjW39Vs8v8rqHz/u1jbvbidC/XL8KmcpOqVT6nSjXjooGw8WfFd+QWGfE4oLbtbr++PdMOy0BDYLjEj/0xevfWIyVAI7b/aIj/9WHsRrA8Yf9bqSexVgD4Lic9kWE/LgPwPGfNfJHDO4P8Iuq+S2M9QD8oUp+nxEAcFCtfgIA/14x/9ElAKDQbNQAwN9VAiYEABy0OgsAWAnB/
  16. AcBAtVWawkAfJ4CD0BQADZavYcQgI9h3CCQjpD5PcEgwG+SwLRhIL0vz78SjAPEU3hrHODfyX4I6rUJIP0G3oExoNwFXpoB+HwXmDEFpF9IwKA5YK+Tp9fMAdUOsC6YA553gKcmgdTfAhV1oMQqADndQDmJ0AZLAsFnCIV3VYDHJLAjDkZKciAaFz/lCeBJB1glgXBrNLndBWLJ9uZGAI+keTBLANL8SnWAzWRniAC2pG+6lQF0hfjTqCIBrEvjDwiggFSLuIUoLY0vEwAbUcsnc/LlnO02HGvEz+hXEeJ5Yj+4L2vNkxOJDSnlQzliIq2synr3embiUBjmw0FyU83KX04Ob+9aAK/Ppd5deZloz4HFlCHzt3sX0x2a6LcvQb4ab8r7i+DVdqvnCq/D5ZzqdhfAcr5B9wD0PNwPEu0ZnLwK9oPgNfCQJ2fhhhITJ3E8BjeUOXA+QNQlBh5xLjemVCgKjzgzNIJFjWF4yJoKhafgIWt6VHGmjgR0HvMuTipPdWQJ6AImbBRSE8aY/sC4er5xFx5vHyB4YRRpFWUf0AL4c+dHkHZRFo9TDeB9Aa3Llwjr8FlFwB+wO/rHm0VbPae9mPini/O5h/XGxatw2I6fGHAOuhiGZVxO98lTdgutP94yaIvVdqxZdpvFYTT9X9UfqQQlTXlm8wkAAAAASUVORK5CYII=
  17. // ==/UserScript==
  18.  
  19. (function () {
  20.   var FORMAT_LABEL={'18':'MP4 360p','22':'MP4 720p (HD)','34':'FLV 360p','35':'FLV 480p','37':'MP4 1080p (HD)','38':'MP4 4K (HD)','43':'WebM 360p','44':'WebM 480p','45':'WebM 720p (HD)','46':'WebM 1080p (HD)'};
  21.   var FORMAT_TYPE={'18':'mp4','22':'mp4','34':'flv','35':'flv','37':'mp4','38':'mp4','43':'webm','44':'webm','45':'webm','46':'webm'};
  22.   var FORMAT_ORDER=['18','34','43','35','44','22','45','37','46','38'];
  23.   var FORMAT_RULE={'flv':'max','mp4':'all','webm':'none'};
  24.   // all=display all versions, max=only highest quality version, none=no version  
  25.   // the default settings show all MP4 videos, the highest quality FLV and no WebM
  26.  var BUTTON_TEXT={'ar':'تنزيل','cs':'Stáhnout','de':'Herunterladen','en':'Download','es':'Descargar','fr':'Télécharger','it':'Scarica','ja':'ダウンロード','pl':'Pobierz','pt':'Baixar','ro':'Descărcați','ru':'Скачать','tr':'İndir','zh':'下载'};
  27.   var BUTTON_TOOLTIP={'ar':'تنزيل هذا الفيديو','cs':'Stáhnout toto video','de':'Dieses Video herunterladen','en':'Download this video','es':'Descargar este vídeo','fr':'Télécharger cette vidéo','it':'Scarica questo video','ja':'このビデオをダウンロードする','pl':'Pobierz plik wideo','pt':'Baixar este vídeo','ro':'Descărcați acest videoclip','ru':'Скачать это видео','tr': 'Bu videoyu indir','zh':'下载此视频'};
  28.   var RANDOM=Math.floor(Math.random()*1234567890);
  29.   var CONTAINER_ID='download-youtube-video'+RANDOM;
  30.   var LISTITEM_ID='download-youtube-video-fmt'+RANDOM;
  31.   var BUTTON_ID='download-youtube-video-button'+RANDOM;
  32.   var DEBUG_ID='download-youtube-video-debug-info';
  33.    
  34.   var videoID, videoTicket, videoFormats, isFeather=false;
  35.   window.addEventListener('DOMContentLoaded', run, false);
  36.  
  37. function run() {
  38.  
  39.   isFeather=document.getElementById('p')!=null && document.getElementById('vo')!=null;
  40.  
  41.   var language=document.documentElement.getAttribute('lang');
  42.   var footer=document.getElementById('footer-links');
  43.   if (footer==null && isFeather) {
  44.     footer=document.getElementById('ft');
  45.   }
  46.   if (language=='' && footer && /\/ja\/|\.co\.jp/.test(footer.innerHTML)) {
  47.     language='ja'; // fix YouTube bug
  48.   }
  49.   var textDirection='left';
  50.   if (document.body.getAttribute('dir')=='rtl') {
  51.     textDirection='right';
  52.   }
  53.  
  54.   if (/^bg|ca|cs|el|es|eu|fi|fil|fr|gl|hr|hu|it|ml|ms|ro|ru|sl|sk|sr|sw|ta|te|uk|ur|vi|zu$/.test(language)) { // fix international UI
  55.     var likeButton=document.getElementById('watch-like');
  56.     if (likeButton) {
  57.       var spanElements=likeButton.getElementsByClassName('yt-uix-button-content');
  58.       if (spanElements) {
  59.         spanElements[0].style.display='none'; // hide like text
  60.       }
  61.     }
  62.     var marginPixels=10;
  63.     if (/^bg|hr|ml|ms$/.test(language)) {
  64.       marginPixels=3;
  65.     }
  66.     injectCSS('#watch7-secondary-actions .yt-uix-button{margin-'+textDirection+':'+marginPixels+'px!important}');
  67.   }
  68.        
  69.   // obtain video ID, temporary ticket, formats map  
  70.  
  71.   var config=null, args;
  72.   var usw=getUnsafeWindow();
  73.   if (usw.ytplayer && usw.ytplayer.config) {
  74.     config=usw.ytplayer.config;
  75.   }
  76.   if (config && config.args) {
  77.     args=config.args;
  78.   }
  79.   if (args) {
  80.     videoID=args['video_id'];
  81.     videoTicket=args['t'];
  82.     videoFormats=args['url_encoded_fmt_stream_map'];
  83.   }
  84.   debug('DYVAM - Info: Standard mode. videoID '+(videoID?videoID:'none')+'; ');
  85.        
  86.   if (videoID==null || videoTicket==null) { // Feather Flash
  87.     var videoPlayer=null;
  88.     if (isFeather) {
  89.       videoPlayer=document.getElementById('p');
  90.     }
  91.     if (videoPlayer) {
  92.       var flashValues=videoPlayer.innerHTML;
  93.       var videoIDMatches=flashValues.match(/(?:"|\&)video_id=([^(\&|$)]+)/);
  94.       videoID=(videoIDMatches)?videoIDMatches[1]:null;
  95.       var videoTicketMatches=flashValues.match(/(?:"|\&)t=([^(\&|$)]+)/);
  96.       videoTicket=(videoTicketMatches)?videoTicketMatches[1]:null;
  97.       var videoFormatsMatches=flashValues.match(/(?:"|\&)url_encoded_fmt_stream_map=([^(\&|$)]+)/);
  98.       videoFormats=(videoFormatsMatches)?videoFormatsMatches[1]:null;
  99.       debug('DYVAM - Info: Feather Flash mode. videoID '+(videoID?videoID:'none')+'; ');
  100.     }  
  101.   }    
  102.      
  103.   if (videoID==null || videoTicket==null) { // everything else
  104.     var bodyContent=document.body.innerHTML;  
  105.     var videoIDMatches=bodyContent.match(/\"video_id\":\s*\"([^\"]+)\"/);
  106.     videoID=(videoIDMatches)?videoIDMatches[1]:null;
  107.     var videoTicketMatches=bodyContent.match(/\"t\":\s*\"([^\"]+)\"/);
  108.     videoTicket=(videoTicketMatches)?videoTicketMatches[1]:null;
  109.     var videoFormatsMatches=bodyContent.match(/\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
  110.     videoFormats=(videoFormatsMatches)?videoFormatsMatches[1]:null;
  111.     debug('DYVAM - Info: Brute mode. videoID '+(videoID?videoID:'none')+'; ');
  112.   }
  113.  
  114.   debug('DYVAM - Info: url '+window.location.href+'; useragent '+window.navigator.userAgent+'; feather '+isFeather);  
  115.  
  116.   if (videoID==null || videoTicket==null || videoFormats==null || videoID.length==0 || videoTicket.length==0 || videoFormats.length==0) {
  117.    debug('DYVAM - Error: No config information found. YouTube must have changed the code.');
  118.    return;
  119.   }
  120.  
  121.    // video title
  122.   var videoTitle=document.title || 'video';
  123.   if (isFeather) {
  124.     videoTitle=videoTitle.replace(/^YouTube\s*\-\s*/i,'');
  125.   } else {
  126.     videoTitle=videoTitle.replace(/\s*\-\s*YouTube$/i,'');
  127.   }
  128.   videoTitle=videoTitle.replace(/[#"\?:\*]/g,'').replace(/[&\|\\\/]/g,'_').replace(/'/g,'\'').replace(/^\s+|\s+$/g,'').replace(/\.+$/g,'');
  129.                        
  130.   // parse the formats map
  131.   var sep1='%2C', sep2='%26', sep3='%3D';
  132.   if (videoFormats.indexOf(',')>-1) {
  133.     sep1=',';
  134.     sep2=(videoFormats.indexOf('&')>-1)?'&':'\\u0026';
  135.     sep3='=';
  136.   }  
  137.  
  138.   var videoURL=new Array();
  139.   var videoFormatsGroup=videoFormats.split(sep1);
  140.   for (var i=0;i<videoFormatsGroup.length;i++) {
  141.     var videoFormatsElem=videoFormatsGroup[i].split(sep2);
  142.     var videoFormatsPair=new Array();
  143.     for (var j=0;j<videoFormatsElem.length;j++) {
  144.       var pair=videoFormatsElem[j].split(sep3);
  145.       if (pair.length==2) {
  146.         videoFormatsPair[pair[0]]=pair[1];
  147.       }
  148.     }
  149.     if (videoFormatsPair['url']==null) continue;
  150.     url=unescape(unescape(videoFormatsPair['url'])).replace(/\\\//g,'/').replace(/\\u0026/g,'&');
  151.     if (videoFormatsPair['itag']==null) continue;
  152.     itag=videoFormatsPair['itag'];
  153.     if (videoFormatsPair['sig']) {
  154.       url=url+'&signature='+videoFormatsPair['sig'];
  155.     } else if (videoFormatsPair['s']) {
  156.       var sig=videoFormatsPair['s'];      
  157.       function swap(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c;return a};
  158.       if (sig.length==88) {      
  159.         var sigA=sig.split("");
  160.         sigA=sigA.slice(2);sigA=swap(sigA,1);sigA=swap(sigA,10);
  161.         sigA=sigA.reverse();sigA=sigA.slice(2);sigA=swap(sigA,23);
  162.         sigA=sigA.slice(3);sigA=swap(sigA,15);sigA=swap(sigA,34);
  163.         sig=sigA.join("");
  164.         url=url+'&signature='+sig;
  165.       } else if (sig.length==87) {
  166.         var sigA=sig.substr(44,40).split('').reverse().join('');
  167.         var sigB=sig.substr(3,40).split('').reverse().join('');
  168.         sig=sigA.substr(21,1)+sigA.substr(1,20)+sigA.substr(0,1)+sigA.substr(22,9)+
  169.         sig.substr(0,1)+sigA.substr(32,8)+sig.substr(43,1)+sigB;
  170.         url=url+'&signature='+sig;
  171.       } else if (sig.length==86) {
  172.         sig=sig.substr(2,15)+sig.substr(0,1)+sig.substr(18,23)+sig.substr(79,1)+
  173.         sig.substr(42,1)+sig.substr(43,36)+sig.substr(82,1)+sig.substr(80,2)+sig.substr(41,1);
  174.         url=url+'&signature='+sig;
  175.       } else if (sig.length==85) {
  176.         var sigA=sig.substr(44,40).split('').reverse().join('');
  177.         var sigB=sig.substr(3,40).split('').reverse().join('');
  178.         sig=sigA.substr(7,1)+sigA.substr(1,6)+sigA.substr(0,1)+sigA.substr(8,15)+sig.substr(0,1)+
  179.         sigA.substr(24,9)+sig.substr(1,1)+sigA.substr(34,6)+sig.substr(43,1)+sigB;
  180.         url=url+'&signature='+sig;                        
  181.       } else if (sig.length==84) {
  182.         var sigA=sig.substr(44,40).split('').reverse().join('');
  183.         var sigB=sig.substr(3,40).split('').reverse().join('');
  184.         sig=sigA+sig.substr(43,1)+sigB.substr(0,6)+sig.substr(2,1)+sigB.substr(7,9)+
  185.         sigB.substr(39,1)+sigB.substr(17,22)+sigB.substr(16,1);
  186.         url=url+'&signature='+sig;                        
  187.       } else if (sig.length==83) {
  188.         var sigA=sig.substr(43,40).split('').reverse().join('');
  189.         var sigB=sig.substr(2,40).split('').reverse().join('');
  190.         sig=sigA.substr(30,1)+sigA.substr(1,26)+sigB.substr(39,1)+
  191.         sigA.substr(28,2)+sigA.substr(0,1)+sigA.substr(31,9)+sig.substr(42,1)+
  192.         sigB.substr(0,5)+sigA.substr(27,1)+sigB.substr(6,33)+sigB.substr(5,1);
  193.         url=url+'&signature='+sig;        
  194.       } else if (sig.length==82) {
  195.         var sigA=sig.substr(34,48).split('').reverse().join('');
  196.         var sigB=sig.substr(0,33).split('').reverse().join('');
  197.         sig=sigA.substr(45,1)+sigA.substr(2,12)+sigA.substr(0,1)+sigA.substr(15,26)+
  198.         sig.substr(33,1)+sigA.substr(42,1)+sigA.substr(43,1)+sigA.substr(44,1)+
  199.         sigA.substr(41,1)+sigA.substr(46,1)+sigB.substr(32,1)+sigA.substr(14,1)+
  200.         sigB.substr(0,32)+sigA.substr(47,1);
  201.         url=url+'&signature='+sig;
  202.       }
  203.     }
  204.     if (url.toLowerCase().indexOf('http')==0) { // validate URL
  205.       videoURL[itag]=url+'&title='+videoTitle;    
  206.     }
  207.   }
  208.  
  209.   var showFormat=new Array();
  210.   for (var category in FORMAT_RULE) {
  211.     var rule=FORMAT_RULE[category];
  212.     for (var index in FORMAT_TYPE){
  213.       if (FORMAT_TYPE[index]==category) {
  214.         showFormat[index]=(rule=='all');
  215.       }
  216.     }
  217.     if (rule=='max') {
  218.       for (var i=FORMAT_ORDER.length-1;i>=0;i--) {
  219.         var format=FORMAT_ORDER[i];
  220.         if (FORMAT_TYPE[format]==category && videoURL[format]!=undefined) {
  221.           showFormat[format]=true;
  222.           break;
  223.         }
  224.       }
  225.     }
  226.   }
  227.  
  228.   var downloadCodeList=[];
  229.   for (var i=0;i<FORMAT_ORDER.length;i++) {
  230.     var format=FORMAT_ORDER[i];
  231.     if (videoURL[format]!=undefined && FORMAT_LABEL[format]!=undefined && showFormat[format]) {
  232.       downloadCodeList.push({url:videoURL[format],format:format,label:FORMAT_LABEL[format]});
  233.       debug('DYVAM - Info: itag'+format+' url:'+videoURL[format]);
  234.     }
  235.   }
  236.   if (downloadCodeList.length==0) {
  237.     debug('DYVAM - Error: No download URL found. Probably YouTube uses encrypted streams.');
  238.     return; // no format
  239.   }
  240.  
  241.   // find parent container
  242.   var parentElement=document.getElementById('watch7-action-buttons');
  243.   if (parentElement==null && isFeather) {  
  244.       parentElement=document.getElementById('vo');
  245.   }
  246.   if (parentElement==null) {
  247.     debug('DYVAM - No container for adding the download button. YouTube must have changed the code.');
  248.     return;
  249.   }
  250.  
  251.   // get button labels
  252.   var buttonText=(BUTTON_TEXT[language])?BUTTON_TEXT[language]:BUTTON_TEXT['en'];
  253.   var buttonLabel=(BUTTON_TOOLTIP[language])?BUTTON_TOOLTIP[language]:BUTTON_TOOLTIP['en'];
  254.    
  255.   if (isFeather) {
  256.       var button=document.createElement('button');
  257.       button.setAttribute('id', BUTTON_ID);
  258.       button.setAttribute('class', 'b');
  259.       button.setAttribute('style', 'margin-'+textDirection+':10px;padding:4px 6px;');
  260.       button.setAttribute('loop', '0');
  261.       var span=document.createElement('span');
  262.       button.appendChild(span);
  263.       button.appendChild(document.createTextNode(buttonText));
  264.       var flag=document.getElementById('fl');
  265.       if (flag) {
  266.         parentElement.insertBefore(button, flag);
  267.       } else {
  268.         parentElement.appendChild(button);
  269.       }
  270.       var downloadButton=document.getElementById(BUTTON_ID);
  271.       addEvent(downloadButton, 'click', downloadVideo);
  272.       return;
  273.   }
  274.  
  275.   // generate download code
  276.   var mainSpan=document.createElement('span');
  277.   var spanButton=document.createElement('span');
  278.   spanButton.setAttribute('class', 'yt-uix-button-content');
  279.   spanButton.appendChild(document.createTextNode(buttonText+' '));
  280.   mainSpan.appendChild(spanButton);
  281.   var imgButton=document.createElement('img');
  282.   imgButton.setAttribute('class', 'yt-uix-button-arrow');
  283.   imgButton.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
  284.   mainSpan.appendChild(imgButton);
  285.   var listItems=document.createElement('ol');
  286.   listItems.setAttribute('style', 'display:none;');
  287.   listItems.setAttribute('class', 'yt-uix-button-menu');
  288.   for (var i=0;i<downloadCodeList.length;i++) {
  289.     var listItem=document.createElement('li');
  290.     var listLink=document.createElement('a');
  291.     listLink.setAttribute('style', 'text-decoration:none;');
  292.     listLink.setAttribute('href', downloadCodeList[i].url);
  293.     var listSpan=document.createElement('span');
  294.     listSpan.setAttribute('class', 'yt-uix-button-menu-item');
  295.     listSpan.setAttribute('loop', i+'');
  296.     listSpan.setAttribute('id', LISTITEM_ID+downloadCodeList[i].format);
  297.     listSpan.appendChild(document.createTextNode(downloadCodeList[i].label));
  298.     listLink.appendChild(listSpan);
  299.     listItem.appendChild(listLink);
  300.     listItems.appendChild(listItem);    
  301.   }
  302.   mainSpan.appendChild(listItems);
  303.   var buttonElement=document.createElement('button');
  304.   buttonElement.setAttribute('id', BUTTON_ID);
  305.   buttonElement.setAttribute('class', 'yt-uix-button yt-uix-tooltip yt-uix-button-empty yt-uix-button-text');
  306.   buttonElement.setAttribute('style', 'margin-top:4px; margin-left:'+((textDirection=='left')?5:10)+'px;');
  307.   buttonElement.setAttribute('data-tooltip-text', buttonLabel);
  308.   addEvent(buttonElement, 'click', function(){return false;});
  309.   buttonElement.setAttribute('type', 'button');
  310.   buttonElement.setAttribute('role', 'button');
  311.   buttonElement.appendChild(mainSpan);
  312.                                            
  313.   // add the button
  314.   var containerSpan=document.createElement('span');
  315.   containerSpan.setAttribute('id', CONTAINER_ID);      
  316.   containerSpan.appendChild(document.createTextNode(' '));
  317.   containerSpan.appendChild(buttonElement);
  318.   parentElement.appendChild(containerSpan);
  319.    
  320.   for (var i=0;i<downloadCodeList.length;i++) {
  321.     var downloadFMT=document.getElementById(LISTITEM_ID+downloadCodeList[i].format);
  322.     addEvent(downloadFMT, 'click', downloadVideo);
  323.   }
  324.  
  325.   function downloadVideo(e) {
  326.     var e=e||window.event; // window.event for IE<9
  327.     var elem=e.target||e.srcElement; // e.srcElement for IE<9
  328.     e.returnValue=false;    
  329.     if (e.preventDefault) {
  330.       e.preventDefault();
  331.     }
  332.     window.location.href=downloadCodeList[elem.getAttribute('loop')].url;
  333.     return false;
  334.   }
  335.  
  336.   function injectCSS(code) {
  337.     var style=document.createElement('style');
  338.     style.type='text/css';
  339.     style.appendChild(document.createTextNode(code));
  340.     document.getElementsByTagName('head')[0].appendChild(style);
  341.   }
  342.  
  343.   function addEvent(elem, eventName, eventFunction) {
  344.     if (elem.addEventListener) {
  345.       elem.addEventListener(eventName, eventFunction, false);
  346.     } else if (elem.attachEvent) { // IE<9
  347.       elem.attachEvent('on'+eventName, eventFunction);
  348.     }
  349.   }
  350.  
  351.   function getUnsafeWindow() {
  352.     var usw=(typeof(this.unsafeWindow)=='undefined')?window:this.unsafeWindow; // Opera, Firefox, TamperMonkey
  353.     if (typeof(unsafeWindow)!='undefined' && unsafeWindow===window) { // built-in Chrome
  354.       var divElem = document.createElement('div');
  355.       divElem.setAttribute('onclick', 'return window;');
  356.       usw=divElem.onclick(); // from https://github.com/greasemonkey/greasemonkey/issues/1614
  357.     }
  358.     return usw;
  359.   }
  360.  
  361.   function debug(str) {
  362.     var debugElem=document.getElementById(DEBUG_ID);
  363.     if (debugElem==null) {
  364.       debugElem=document.createElement('div');
  365.       debugElem.setAttribute('id', DEBUG_ID);
  366.       debugElem.setAttribute('style', 'display:none;');
  367.       document.body.appendChild(debugElem);
  368.     }
  369.     debugElem.appendChild(document.createTextNode(str+' '));
  370.   }
  371.      
  372.   }
  373.  
  374. })();
Add Comment
Please, Sign In to add comment