video tabs

tabbed video sources on certain drama and anime sites

目前為 2016-04-05 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name video tabs
  3. // @namespace gnblizz
  4. // @description tabbed video sources on certain drama and anime sites
  5. // @version 1.04
  6. // @include http://www.animehere.com/*
  7. // @include http://www.animenova.org/*
  8. // @include http://www.animenova.tv/*
  9. // @include http://www.animeplus.tv/*
  10. // @include http://www.animetoon.eu/*
  11. // @include http://www.animetoon.org/*
  12. // @include http://www.animetoon.tv/*
  13. // @include http://animewow.eu/*
  14. // @include http://www.animewow.eu/*
  15. // @include http://www.animewow.org/*
  16. // @include http://dramago.com/*
  17. // @include http://www.dramago.com/*
  18. // @include http://www.dramagalaxy.com/*
  19. // @include http://www.dramagalaxy.eu/*
  20. // @include http://www.dramagalaxy.tv/*
  21. // @include http://www.gogoanime.com/*
  22. // @include http://www.goodanime.co/*
  23. // @include http://www.goodanime.eu/*
  24. // @include http://www.goodanime.net/*
  25. // @include http://www.gooddrama.net/*
  26. // @noframes
  27. // @run-at document-start
  28. // @grant none
  29. // @icon data:image/gif;base64,R0lGODlhMAAwAKECAAAAAICAgP///////yH5BAEKAAMALAAAAAAwADAAAALQnI+py+0Po5y02ouz3rz7D4biBJTmiabqyrbuC8fyHAf2jedpzuOvAAwKh6mhUfg7Hks3gHLpehptwIBTioxig0zrdStIgrslMFA8pCKp1oAZjXW6w/Mt/Nl2t8HeFl7o5QZgBagEYyawNxhUl7h4dlelFlZG+QVY6aglmIjjuKd50xla9RKI0mSCqaPJSMM0aEK4mhfbpSnTabM4WXrShtpHI6gqKvmKnCwns0tm2lOsLP3aUy08aK0zvc3d7b09Ei4+Tl5ufo6err7O3n5QAAA7
  30. // ==/UserScript==
  31. "use strict";
  32. var domain = document.domain.match(/(\w+)\.\w+$/)[1], darkTheme;
  33.  
  34. function TabSites() {
  35. switch(domain) {
  36. case 'gooddrama':
  37. return MakeTabs('html,body,#body,#header,#top_block,.info_block,.note span,#eps_blocks,#downloads_heading,#comments_heading,.reply,.info_box{color:#888;background-color:#111;height:auto}table[width="790"],#eps_blocks,.right_col,.info_block .ad{opacity:.6;}.note>font>span{background-color:inherit!important;}');
  38. case 'dramago':
  39. return MakeTabs('#menu-bar,.bar,#content,#top_block,#search-box-banner-inner,.info_block,#eps_blocks,.s_right_col #sidebar,#footer{background-color:#111;color:#aaa;border:1px solid #777}body{background-color:#444;background-blend-mode:color-burn;}.note span{background-color:#111!important;color:#aaa!important}');
  40. case 'dramagalaxy':
  41. return MakeTabs('#content,#top_block,#search-box-banner-inner,.info_block,#eps_blocks,.s_right_col #sidebar{background-color:#121B23;color:#aaa;border:1px solid #777}img[src$="2egfcQR.png"]{display:none}');
  42. case 'animenova':
  43. return MakeTabs('html,body,#body,#header,#comments_heading,.comment,.info_box{color:#555;background-color:#111;height:auto}br:empty,.note img{display:none;}.comment{border:0px}.right_col,table,.ad,.sc_ad,#g_promo,#upper_header{opacity:.6;}');
  44. case 'animeplus':
  45. return MakeTabs('html,body,#body,#header,.part,.note>span,.report_video,#comments_heading,.comment,.info_box{background-color:#000!important;color:#aaa!important;}a.report_video:hover{color:red!important;}.right_col,table,.ad,.sc_ad{opacity:.6;}#comments,.comment,#body,#streams{border:1px solid #555}');
  46. case 'animewow':
  47. return MakeTabs('#page,#body>.s_left_col,#body>.s_right_col,#search-box-banner-inner,#eps_blocks{color:#aaa;background-color:#111;}.vmargin,#top_block,.info_block{border:1px solid #aaa;background-color:#111;}.info_block *{color:#aaa!important}');
  48. case 'gogoanime':
  49. return MakeTabs2('.postcontent', '');
  50. case 'goodanime':
  51. return MakeTabs2('.postcontent', '.topad,#wrapper,.premiumdll,.postcontent,td[bgcolor]{background-color:#111;color:#aaa}#header,#footer,html,body{background-color:#000;color:#aaa}a h5,td[bgcolor] *{color:inherit!important;}');
  52. case 'animehere':
  53. SetStyle('#streams+div.playpage{margin-top:5px;}');
  54. return MakeTabs2('#playbox', '#html,body{background-color:#111;}body,.content,.cfix,.side-title{color:#ccc!important;background-color:#111!important;}.related li a{color:unset;}.tipbot,.sidebar,.banner{background-color:black;color:white;}.tipbot>*,.sidebar>*,.banner>*{opacity:.7;}.related,.like{opacity:.5;}body>footer{background:unset;}a{color: #036;}#stOverlay{display:block!important;z-index:0!important;}');
  55. default:
  56. return MakeTabs('');
  57. }
  58. }
  59.  
  60. function RemoveSomeIframes() {
  61. var streams = obj('#streams');
  62. if(streams) {
  63. var o, name, a=objs('IFRAME'), i=a.length;
  64. if(i) do {
  65. o = a[--i];
  66. if(!streams.contains(o)) {
  67. if(o.id)
  68. name = '#' + o.id;
  69. else {
  70. try {
  71. name = o.src.match(/\/\/(?:www\.)?([^/]+)/)[1];
  72. } catch(e) {
  73. name = 'unknown';
  74. }
  75. }
  76. var div = obj('+DIV'), btn = obj('+BUTTON|type=button|title='+o.getAttribute('src')+'|=show '+name+' content', div);
  77. if(o.parentNode.nodeName == 'TD') o = o.parentNode;
  78. btn.setAttribute('onclick', 'this.parentNode.innerHTML=decodeURIComponent("'+encodeURIComponent(o.outerHTML)+'");');
  79. o.parentNode.insertBefore(div, o);
  80. o.parentNode.removeChild(o);
  81. }
  82. } while(i);
  83. return true;
  84. }
  85. return false;
  86. }
  87.  
  88. function obj(name, parent) {
  89. if(!parent) parent = document;
  90. switch (name.charAt(0)) {
  91. case '#':
  92. return parent.getElementById(name.slice(1));
  93. case '.':
  94. return parent.getElementsByClassName(name.slice(1))[0];
  95. case '+':
  96. var a = name.split('|'); name = a.shift();
  97. var m = name.match(/^\+(\w+)/), node = document.createElement(m[1]);
  98. m = name.match(/\.\w+/); if(m) node.className = m[0].slice(1);
  99. m = name.match(/#\w+/); if(m) node.id = m[0].slice(1);
  100. while(a.length) {
  101. var name = a.shift(); m = name.search('=');
  102. switch(m) {
  103. case -1: node.setAttribute(name, name); break;
  104. case 0: node.innerHTML = name.slice(1); break;
  105. default: node.setAttribute(name.slice(0,m), name.slice(m+1)); break;
  106. }
  107. };
  108. if(parent != document) parent.appendChild(node);
  109. return node;
  110. }
  111. return parent.getElementsByTagName(name)[0];
  112. }
  113.  
  114. function objs(name, parent) {
  115. if(!parent) parent = document;
  116. if(name.charAt(0) == '.') return parent.getElementsByClassName(name.slice(1));
  117. return parent.getElementsByTagName(name);
  118. }
  119.  
  120. function SetStyle(style) {
  121. if(style)
  122. return obj('+STYLE|='+style, obj('HEAD'));
  123. }
  124.  
  125. function RemoveElement(node) {
  126. if(node) return node.parentNode.removeChild(node);
  127. }
  128.  
  129. function domainName(href) {
  130. var m = href.match(/\:\/\/(?:www\.|embed\.)?([^\/]+)/);
  131. return(m ? m[1] : 'unknown');
  132. }
  133.  
  134. function Remember(name, value) {
  135. if(value) localStorage.setItem(name, value);
  136. else localStorage.removeItem(name);
  137. }
  138.  
  139. function remembered(name) {
  140. return localStorage.getItem(name);
  141. }
  142.  
  143. function TabOnMouseUp(event) {
  144. if(!event.ctrlKey) switch(event.button) {
  145. case 1:
  146. break;
  147. case 0:
  148. TabSelect(this);
  149. default:
  150. return;
  151. }
  152. NewWindow(this);
  153. }
  154.  
  155. function NewWindow(o) {
  156. var m = decodeURI(o.dataset.content).match(/src="(\S+?)"/);
  157. console.log(m);
  158. if(m)
  159. window.open(m[1].replace(/&/g,'&'), '_newtab');
  160. }
  161.  
  162. function TabSelect(n) {
  163. var o, a;
  164. switch(typeof(n)) {
  165. case 'object':
  166. o = n || obj('#player_tab_0');
  167. break;
  168. case 'string':
  169. a = obj('#player_tabs').childNodes;
  170. for(o = 0;; o++) {
  171. if(o >= a.length) { n = 0; break; }
  172. if(a[o].innerHTML == n) { n = a[o].id.slice(-1); break; }
  173. }
  174. case 'number':
  175. o = obj('#player_tab_'+n) || obj('#player_tab_0');
  176. }
  177. if(o.getAttribute('class') != 'active_tab') {
  178. a = obj('.active_tab');
  179. if(a) a.removeAttribute('class');
  180. o.setAttribute('class', 'active_tab');
  181. obj('#tabplayer').innerHTML = decodeURI(o.dataset.content);
  182. Remember('preferedServer', o.innerHTML);
  183. }
  184. }
  185.  
  186. function MakeTabs(style) {
  187. if(obj('#player_tabs')) return true;
  188. var content = obj('#streams');
  189. if(content) {
  190. var va = objs('.vmargin', content), i=va.length;
  191. if(i) {
  192. var tabs = obj('+UL#player_tabs');
  193. do {
  194. var o = va[--i];
  195. var ifr = obj('IFRAME', o), tab = obj('+LI#player_tab_'+i), ttl;
  196. if(ifr) {
  197. var src = ifr.getAttribute('src');
  198. if(src.match(/[?&]/) == '&') ifr.setAttribute('src', src.replace('&', '?')); // bugfix
  199. if(!ifr.getAttribute('allowfullscreen')) ifr.setAttribute('allowfullscreen', 'true'); // enable fs for html5 video
  200. tab.textContent = domainName(src);
  201. ttl = 'span.playlist';
  202. } else {
  203. tab.textContent = '?';
  204. ttl = '.error_box';
  205. }
  206. ttl = o.querySelector(ttl); if(ttl) tab.title = ttl.textContent;
  207. tab.setAttribute('data-content', encodeURI(o.innerHTML));
  208. RemoveElement(o);
  209. tab.onmouseup = TabOnMouseUp;
  210. tabs.insertBefore(tab, tabs.firstChild);
  211. } while(i);
  212. obj('+LI|title=about videotabs|=ⓘ|style=float:right;padding:5px;', tabs).onclick = About;
  213. obj('+LI|title=dim the light|=✶|style=float:right;padding:5px;', tabs).onclick = Dimmer;
  214. if(style) {
  215. obj('+LI|title=toggle dark theme|=▣|style=float:right;padding:5px;', tabs).onclick = ToggleTheme;
  216. if(remembered('noDarkTheme')) darkTheme = style;
  217. else obj('+STYLE#darkTheme|='+style, obj('HEAD'));
  218. }
  219. obj('+LI|=&nbsp', tabs);
  220. content.insertBefore(tabs, va[0]);
  221. obj('+DIV#tabplayer', content);
  222. SetStyle('#player_tabs li{background-color:#393939;color:white;display:block;float:left;width:auto;padding:5px 10px;cursor:pointer;}#player_tabs li:hover{color:yellow;}#player_tabs li:last-child{cursor:auto;float:unset}#player_tabs .active_tab{background-color:#505050}#player_tabs .active_tab:hover{color:gray}a.report_video:link{color:#0047AB}html{height:unset;}\n');
  223. TabSelect(remembered('preferedServer'));
  224. Disclaimer();
  225. return true;
  226. }
  227. }
  228. return false;
  229. }
  230.  
  231. function Dimmer(tab) {
  232. function DimPart(desc) {
  233. var style = '', names = Object.getOwnPropertyNames(desc), name, value;
  234. for(name of names) {
  235. value = desc[name];
  236. if(typeof(value) == 'number') value += 'px';
  237. style += name.replace(/_/g, '-') + ':' + value + '; '
  238. }
  239. obj('+DIV|style=position:absolute;background-color:black;opacity:.85;z-index:1001;'+style, div);
  240. }
  241. //var po = /\b(?:animetoon|gogoanime|goodanime)\.\w+$/.test(document.domain) ? document.documentElement : obj('#page') || document.body,
  242. var po = document.documentElement,
  243. rc = document.querySelector('#tabplayer iframe').getBoundingClientRect(),
  244. rcp = po.getBoundingClientRect(),
  245. div = obj('+DIV#dimmer', document.body),
  246. above = rc.y-rcp.y,
  247. below = rcp.height - above - rc.height;
  248. DimPart({top:0, left:0, width:'100%', height:above, min_width:rcp.width});
  249. DimPart({top:above, left:0, width:'calc(50% - ' + (rcp.width/2 - rc.x) + 'px)', height:rc.height});
  250. DimPart({top:above, left:'calc(50% + ' + (rc.width - rcp.width/2 + rc.x) + 'px)', width:'calc(50% - ' + (rc.width + rc.x - rcp.width/2) + 'px)', height:rc.height});
  251. DimPart({top:above+rc.height, left:0, width:'100%', height:below, min_width:rcp.width, bottom:0});
  252. div.onclick = function(event) { div.parentNode.removeChild(div); };
  253. return div;
  254. }
  255.  
  256. function About() {
  257. var dlg = obj('+DIALOG#aboutvideotabs|open|style=position:fixed;top:20%;right:30%;z-index:2147483647;text-align:center;color:black;background-color:antiquewhite;border:7px ridge greenyellow;', Dimmer()), adr = ['mailto:gnblizz'];
  258. adr.push('@web.de?subject=videotabs%20at%20',document.domain);
  259. obj('+H1|=about videotabs', dlg);
  260. obj('+P|=<small>videotabs is public domain by <a href="'+adr.join('')+'" title="email the author directly">gnblizz</a>.</small>', dlg);
  261. obj('+BUTTON|type=button|=videotabs web page|title=info, code, feedback and stats of videotabs', obj('+A|href=https://greasyfork.org/en/scripts/11480-video-tabs|target=_newtab', dlg));
  262. }
  263.  
  264. function ToggleTheme() {
  265. if(darkTheme) {
  266. obj('+STYLE#darkTheme|='+darkTheme, obj('HEAD'));
  267. Remember('noDarkTheme', darkTheme = '');
  268. } else {
  269. var style = obj('#darkTheme');
  270. darkTheme = style.innerHTML;
  271. document.head.removeChild(style);
  272. Remember('noDarkTheme', 'noDarkTheme');
  273. }
  274. }
  275.  
  276. //gogoanime.com, goodanime.eu | .postcontent
  277. //animehere.com | #playbox
  278. function MakeTabs2(select, style) {
  279. var streams = obj(select), o;
  280. if(streams && obj('IFRAME', streams)) {
  281. streams.id = 'streams';
  282. for(o of streams.children) {
  283. if(o.nodeType == 1 && obj('IFRAME', o))
  284. o.className = 'vmargin';
  285. }
  286. return MakeTabs(style);
  287. }
  288. return false;
  289. }
  290.  
  291. function Disclaimer() {
  292. var o = obj('#footer');
  293. if(o && o.textContent.match(/(Copyright|©)/i))
  294. obj('+P|=<br>Disclaimer: Video materials used here are the property of their respective and rightful owners.', o);
  295. o = document.querySelector('.info_block .note>span.imp:last-of-type');
  296. if(o && o.textContent == '95%')
  297. o.textContent = '0.5%';// a slightly more realistic value
  298. }
  299.  
  300. /* I couldn't figure out, how the click got stolen. It doesn't happen with my FireFox configuration and with konqueror I'm just not familiar enough.
  301. function OpenWindowReplacement() { // this runs in external context
  302. var culprit = location.hostname, div = document.createElement('DIV'), where = document.mozFullScreenElement || document.body;
  303. console.log("yahvt: blocked unauthorized attempt to open a window by", culprit, open.caller, arguments);
  304. div.style = 'position:absolute;top:40px;left:50px;color:white;text-shadow:1px 1px black;z-index:2147483647;';
  305. div.textContent = 'yahvt: blocked unauthorized attempt to open a window by '+culprit+'.';
  306. if(where.tagName == 'VIDEO') where = where.parentNode;
  307. where.appendChild(div);
  308. window.setTimeout( function() { div.parentNode.removeChild(div); }, 10000);
  309. }
  310. function DenyOpenWindow() {
  311. obj('+SCRIPT#DenyOpenWindow', document.head).innerHTML = 'window.open='+
  312. OpenWindowReplacement.toSource().replace(/OpenWindowReplacement|"use strict";\n\n?|\/\/.*?$/gm,'')
  313. +';\n';
  314. }*/
  315.  
  316. function FixNovaBug() {
  317. var spn = obj('#full_notes');
  318. if(spn) {
  319. var m = spn.textContent.replace(/\n/gm,'<br>').match(/\u2026\smore(?:<br>)?(.*)\sless\sless/);//u2026 = '...'
  320. if(m) {
  321. spn.parentNode.innerHTML = m[1];
  322. console.log('description text fixed');
  323. return 1;
  324. }
  325. }
  326. }
  327.  
  328. SetStyle('iframe,.vmargin{display:none}').id = 'no_frames';
  329. document.addEventListener("DOMContentLoaded", function() {
  330. if(TabSites()) {
  331. RemoveSomeIframes();
  332. //DenyOpenWindow();
  333. window.setTimeout(function(){try{window.autoClose=-1;MHideBar();}catch(e){}},1999);
  334. } else {
  335. // auto expand description - it's foolish to hide the last few words of a sen...[expand]
  336. if(obj('#brief_notes') && !FixNovaBug()) SetStyle('#full_notes{display:inline!important;}#full_notes>a[href="#"],#brief_notes{display:none!important;}');
  337. // mark watched episodes red
  338. if(obj('#videos')) SetStyle('#videos a:visited{color:red;}');
  339. }
  340. RemoveElement(obj('#no_frames'));
  341. });
  342.  
  343. // public domain by gnblizz
  344. // contact me with my username + '@web.de'