cbox-videos

Open youtube links in a dialog in cbox chats

  1. // ==UserScript==
  2. // @author @leoncastro
  3. // @namespace https://github.com/leoncastro
  4. // @name cbox-videos
  5. // @version 0.01
  6. // @description Open youtube links in a dialog in cbox chats
  7. // @include https://my.cbox.ws/*
  8. // @include /(https?:)?//(www\d\.)cbox\.ws\/box/
  9. // @compatible firefox+greasemonkey
  10. // @compatible chrome+tampermonkey
  11. // @grant none
  12. // @run-at document-end
  13. // ==/UserScript==
  14. //
  15. (function($){
  16. // @part-name @desktop
  17. // @part-author https://github.com/leoncastro
  18. // @part-license Private use only
  19. // @part-description desktop with moveable and resizable dialogs
  20. // @part-version beta-0.3 (partial content)
  21. // <@desktop>
  22. var minW = 50, minH = 50, minX = 0, maxX = 9999, minY = 0, maxY = 9999,
  23. ks = '-', kh = '#', kp = '.', kc = ',', kn = '>',
  24. prefix_main = 'wdlg',
  25. selector_main = prefix_main + ks + 'main',
  26. selector_tool = prefix_main + ks + 'tool',
  27. selector_head = prefix_main + ks + 'head',
  28. selector_text = prefix_main + ks + 'text',
  29. selector_ctrl = prefix_main + ks + 'ctrl',
  30. selector_exit = prefix_main + ks + 'exit',
  31. selector_body = prefix_main + ks + 'body',
  32. selector_pane = prefix_main + ks + 'pane',
  33. selector_foot = prefix_main + ks + 'foot',
  34. selector_grip = prefix_main + ks + 'grip';
  35. var template_css = `
  36. <style id="###0###" type="text/css">
  37. .wdlg-tool{position:absolute;background-color:#F4F4F4;z-index:910;-moz-box-shadow:3px 3px 8px #818181;-webkit-box-shadow:3px 3px 8px #818181;box-shadow:3px 3px 8px #818181;cursor:default}
  38. .wdlg-tool.selected{z-index:911}
  39. .wdlg-tool>.wdlg-head{display:block;position:relative;height:24px;line-height:24px;padding:8px 32px 0 8px;cursor:move;color:black;background-color:darkgray;font-size:130%;font-weight:normal;overflow:hidden}
  40. .wdlg-tool.selected>.wdlg-head{background-color:white}
  41. .wdlg-tool>.wdlg-head>.wdlg-text{display:block;position:absolute;left:0;right:0;margin:0 32px 0 8px;overflow:hidden}
  42. .wdlg-tool>.wdlg-head>.wdlg-ctrl{display:block;position:absolute;top:0;right:0}
  43. .wdlg-tool>.wdlg-head>.wdlg-ctrl>div{display:block;float:right;width:32px;height:16px;padding:8px;text-align:center;cursor:pointer;color:white;filter:alpha(opacity=50);opacity:0.5;}
  44. .wdlg-tool>.wdlg-head>.wdlg-ctrl>div:hover{filter:alpha(opacity=100);opacity:1}
  45. .wdlg-tool>.wdlg-head>.wdlg-ctrl>.wdlg-exit:hover{background-color:red}
  46. .wdlg-tool>.wdlg-head>.wdlg-ctrl>.wdlg-exit:hover>svg>path{stroke:white}
  47. .wdlg-tool>.wdlg-body{display:block;position:relative;width:100%;margin:0;padding:0;overflow:hidden}
  48. .wdlg-tool>.wdlg-body>.wdlg-pane{display:block;position:relative;width:100%;height:100%;margin:0;padding:0;overflow:hidden}
  49. .wdlg-tool>.wdlg-foot{display:block;position:relative;padding-right:16px}
  50. .wdlg-tool>.wdlg-foot>.wdlg-grip{display:block;position:absolute;bottom:0;right:0;width:16px;height:16px;cursor:se-resize}
  51. </style>
  52. `;
  53. var template_dlg =`
  54. <div id="###1###" class="wdlg-tool" style="width:###2###;height:###3###;left:###4###;top:###5###;">
  55. <div class="wdlg-head">
  56. <div class="wdlg-text"></div>
  57. <div class="wdlg-ctrl">
  58. <div class="wdlg-exit">
  59. <svg width="16" height="16" overflow="hidden"><path d="M 2 2 L 13 13 M 13 2 L 2 13" stroke="#000" stroke-opacity="1" stroke-width="2" fill="none"></path></svg>
  60. </div>
  61. </div>
  62. </div>
  63. <div class="wdlg-body">
  64. <div class="wdlg-pane">
  65. ###0###
  66. </div>
  67. </div>
  68. <div class="wdlg-foot">
  69. <div class="wdlg-grip">
  70. <svg width="16" height="16" overflow="hidden"><path d="M 13 2 L 2 13 M 13 6 L 6 13 M 13 10 L 10 13 Z" stroke="#FFF" stroke-opacity="1" stroke-width="0.6" fill="none"></path></svg>
  71. </div>
  72. </div>
  73. </div>
  74. `;
  75. var template_frm = `<iframe frameborder="0" scrolling="no" width="100%" height="100%" src="###0###"></iframe>`;
  76. // Function call:
  77. function safeCall(o,a){if(o[a])o[a]();}
  78. // Node:
  79. function nodeCreate(o,n,c,t){t=t||'div';var e=document.createElement('div');if(n)e.id=n;if(c)e.className=c;if(o)o.appendChild(e);return e;}
  80. function nodeSelect(n,o){return'string'==typeof n?(o||document).querySelector(n):n;}
  81. function nodeInsert(n,t,o){n=nodeSelect(n,o);if(n)n.insertAdjacentHTML('beforeEnd',t);}
  82. function nodeRemove(n,o){n=nodeSelect(n,o);if(n){n.innerHTML='';if(n.parentNode)n.parentNode.removeChild(n);}}
  83. // Class:
  84. function classInsert(n,c){n=nodeSelect(n);if(n){n.className=(n.className?n.className+' ':'')+c;}}
  85. function classRemove(n,c){n=nodeSelect(n);if(n&&n.className){n.className=(' '+n.className+' ').split(' '+c+' ').join(' ').trim();}}
  86. // Event:
  87. function eventInsert(e,n,fn){n=nodeSelect(n);return n.addEventListener(e,fn,0);}
  88. function eventRemove(e,n,fn){n=nodeSelect(n);return n.removeEventListener(e,fn,0);}
  89. function eventCancel(e,c){e.returnValue=!1;safeCall(e,'preventDefault');if(c){e.keyCode=0;e.cancelBubble=!0;e.retainFocus=!0;safeCall(e,'stopPropagation');}}
  90. function eventFilter(e){return((e.which&&(e.which==1))||(e.button&&(e.button==1)));}
  91. // Helpers:
  92. function getFreeId(){var i=1,j;while(document.getElementById(j=(selector_main+ks+([1e4]+i).slice(-4))))i++;return j;}
  93. function getPixels(s){return s+'px';}
  94. function getToolX(n){return parseInt(n.style.left);}
  95. function getToolY(n){return parseInt(n.style.top);}
  96. function getToolW(n){return n.offsetWidth;}
  97. function getToolH(n){return n.offsetHeight;}
  98. // Template:
  99. function templateBuild()
  100. {
  101. var t = null;
  102. if(arguments.length)
  103. {
  104. t = arguments[0];
  105. for(var i = 1; i< arguments.length; i++)
  106. t = t.replace('###' + (i - 1) + '###', arguments[i]);
  107. }
  108. return t;
  109. }
  110. // Desktop:
  111. var desktop = function(n)
  112. {
  113. n = n || document.body;
  114. this.handleDesk = n;
  115. this.selectTool = null;
  116. this.selectItem = null;
  117. this.x = 0;
  118. this.y = 0;
  119. this.w = 0;
  120. this.h = 0;
  121. this.cx = 0;
  122. this.cy = 0;
  123. this.mx = 0;
  124. this.my = 0;
  125.  
  126. var it = this;
  127. eventInsert('mousedown', n, function(e){it.onMouseDn(e);});
  128. eventInsert('mousemove', n, function(e){it.onMouseMv(e);});
  129. eventInsert('mouseup' , n, function(e){it.onMouseUp(e);});
  130.  
  131. this.insertWindowHTML = function(html, opt, cx, cy, x, y)
  132. {
  133. var t = prefix_main + ks + 'style';
  134. if( !nodeSelect(kh + t) )
  135. nodeInsert(this.handleDesk, templateBuild(template_css, t));
  136.  
  137. var n = getFreeId();
  138. opt = opt || {};
  139. opt.title = opt.title || n;
  140. this.w = this.w || 320; // 320, 400, 480, 640, 800, 1024 // TODO
  141. this.h = this.h || 204; // 180, 224, 270, 360, 450, 576 // TODO
  142. cx = cx || opt.cx || opt.height || this.w;
  143. cy = cy || opt.cy || opt.width || this.h - 24;
  144. x = x || opt.x || opt.left || Math.random() * (getToolW(this.handleDesk) - cx);
  145. y = y || opt.y || opt.top || Math.random() * (getToolH(this.handleDesk) - cy);
  146.  
  147. nodeInsert(this.handleDesk, templateBuild(template_dlg, html, n,
  148. getPixels(cx), getPixels(24 + cy), getPixels(x), getPixels(y)));
  149. if(this.selectTool)
  150. this.selectLeave(1);
  151. this.selectEnter(nodeSelect(kh + n));
  152. var it = this,
  153. c = kh + n + kp + selector_tool + kn + kp + selector_head + kn +
  154. kp + selector_ctrl + kn + kp + selector_exit;
  155. if(nodeSelect(c))
  156. eventInsert('click', c, function(e){
  157. if(it.b)
  158. it.selectLeave(1);
  159. nodeRemove(kh + n);
  160. eventCancel(e, 1);
  161. });
  162. this.onMouseUp();
  163. return n;
  164. };
  165. this.insertWindowLink = function(link, opt, cx, cy, x, y)
  166. {
  167. var html = templateBuild(template_frm, link);
  168. opt = opt || {};
  169. opt.link = link;
  170. this.insertWindowHTML(html, opt, cx, cy, x, y);
  171. };
  172. this.deleteWindowTool = function(n)
  173. {
  174. nodeRemove(kh + n);
  175. };
  176. };
  177. var fn = desktop.prototype;
  178. // Mouse:
  179. fn.onMouseDn = function(e)
  180. {
  181. var o = e.target || e.srcElement, n = null, h = null;
  182. while(o)
  183. {
  184. if(o.className)
  185. {
  186. if( !h && (
  187. ~String.prototype.indexOf.call(o.className, selector_grip) ||
  188. ~String.prototype.indexOf.call(o.className, selector_head)) )
  189. h = o;
  190. if( ~String.prototype.indexOf.call(o.className, selector_tool) )
  191. {
  192. n = o;
  193. break;
  194. }
  195. }
  196. o = o.parentNode;
  197. }
  198. if(this.selectTool && (this.selectTool != n))
  199. {
  200. this.selectLeave(1);
  201. }
  202. if(n && (!this.selectTool || (n == this.selectTool)))
  203. {
  204. if(h)
  205. eventCancel(e);
  206. this.selectEnter(n);
  207. if(eventFilter(e))
  208. this.selectItem = h;
  209. }
  210. };
  211. fn.onMouseMv = function(e)
  212. {
  213. var mx = e.pageX || e.clientX + document.documentElement.scrollLeft;
  214. var my = e.pageY || e.clientY + document.documentElement.scrollTop;
  215. var x = mx - this.mx + this.cx;
  216. var y = my - this.my + this.cy;
  217. this.cx = this.cy = 0;
  218. this.mx = mx;
  219. this.my = my;
  220. if(!this.selectItem)
  221. return true;
  222. if(this.resizeStep(x, y))
  223. {
  224. if(this.selectTool)
  225. {
  226. var b = nodeSelect(kp + selector_body, this.selectTool);
  227. if(b)
  228. {
  229. b.style.width = getPixels(getToolW(this.selectTool));
  230. b.style.height = getPixels(getToolH(this.selectTool) -
  231. getToolH(nodeSelect(kp + selector_head, this.selectTool)));
  232. }
  233. }
  234. }
  235. else
  236. {
  237. var dX = x, dY = y;
  238. if(this.x + dX < minX)
  239. this.cx = (dX - (x = minX - this.x));
  240. else if(this.x + this.w + dX > maxX)
  241. this.cx = (dX - (x = maxX - this.x - this.w));
  242. if(this.y + dY < minY)
  243. this.cy = (dY - (y = minY - this.y));
  244. else if(this.y + this.h + dY > maxY)
  245. this.cy = (dY - (y = maxY - this.y - this.h));
  246. this.x += x;
  247. this.y += y;
  248. }
  249. this.selectTool.style.left = getPixels(this.x);
  250. this.selectTool.style.top = getPixels(this.y);
  251. this.selectTool.style.width = getPixels(this.w);
  252. this.selectTool.style.height = getPixels(this.h);
  253.  
  254. eventCancel(e);
  255. };
  256. fn.onMouseUp = function(e)
  257. {
  258. if(this.selectTool)
  259. {
  260. var b = nodeSelect(kp + selector_body, this.selectTool);
  261. if(b)
  262. {
  263. b.style.width = getPixels(getToolW(this.selectTool));
  264. b.style.height = getPixels(getToolH(this.selectTool) -
  265. getToolH(nodeSelect(kp + selector_head, this.selectTool)));
  266. }
  267. }
  268. this.selectLeave(0);
  269. };
  270. // Select:
  271. fn.selectEnter = function(n)
  272. {
  273. if(n && (n != this.selectTool))
  274. {
  275. this.resizeInit(n);
  276. this.x = getToolX(n);
  277. this.y = getToolY(n);
  278. this.w = getToolW(n);
  279. this.h = getToolH(n);
  280. }
  281. };
  282. fn.selectLeave = function(e)
  283. {
  284. if(e)
  285. this.resizeInit(null);
  286. this.selectItem = null;
  287. this.cx = 0;
  288. this.cy = 0;
  289. };
  290. // Resize:
  291. fn.resizeInit = function(n)
  292. {
  293. var s = 'selected';
  294. if(n)
  295. {
  296. this.selectTool = n;
  297. classInsert(this.selectTool, s);
  298. }
  299. else
  300. {
  301. classRemove(this.selectTool, s);
  302. this.selectTool = n;
  303. }
  304. };
  305. fn.resizeStep = function(x, y)
  306. {
  307. var h = '';
  308. if(this.selectItem && this.selectItem.className)
  309. {
  310. if(this.selectItem.className === selector_grip)
  311. h = 'br';
  312. }
  313. var dX = x, dY = y, r = 0;
  314. if(~h.indexOf('b'))
  315. {
  316. if(this.h + dY < minH)
  317. this.cy = (dY - (y = minH - this.h));
  318. else if(this.y + this.h + dY > maxY)
  319. this.cy = (dY - (y = maxY - this.y - this.h));
  320. this.h += y;
  321. r = 1;
  322. }
  323. if(~h.indexOf('r'))
  324. {
  325. if(this.w + dX < minW)
  326. this.cx = (dX - (x = minW - this.w));
  327. else if(this.x + this.w + dX > maxX)
  328. this.cx = (dX - (x = maxX - this.x - this.w));
  329. this.w += x;
  330. r = 1;
  331. }
  332. return r;
  333. };
  334. // </@desktop>
  335.  
  336. // <@cbox-videos>
  337. var yt_video = '//www.youtube.com/embed/###?wmode=opaque&autoplay=1&modestbranding=1&rel=0&showinfo=1';
  338. var yt_regex = /(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((?:\w|-){11})(?:\S+)?/i;
  339. var deskctrl = new desktop();
  340.  
  341. var addVideo = function(txt, x, y)
  342. {
  343. var yt_urlid = txt && txt.match(yt_regex);
  344. if( yt_urlid && (yt_urlid = yt_urlid[1]) )
  345. {
  346. deskctrl.insertWindowLink(yt_video.replace('###', yt_urlid), {}, null, null, x, y);
  347. return 1;
  348. }
  349. return 0;
  350. };
  351.  
  352. document.addEventListener('click', function(e){
  353. e = window.event ? event : e;
  354. var url, obj = window.event ? event.srcElement : e.target;
  355. if( (obj.tagName == 'A' && obj.className && ~obj.className.indexOf('autoLink') && (url = obj.href)) ||
  356. (obj.tagName == 'DIV' && obj.className && ~obj.className.indexOf('body') &&
  357. obj.parentNode && ~obj.parentNode.className.indexOf('msg') && obj.innerHTML &&
  358. (url = obj.innerHTML.match(yt_regex)) && (url = url[0])) )
  359. {
  360. if(addVideo(url, e.clientX, e.clientY))
  361. e.preventDefault();
  362. }
  363. });
  364. // </@cbox-videos>
  365.  
  366. })();