4chan \ IB Simple Web Media Player

Simple Web Media Player for 4chan and other imageboards.

当前为 2022-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 4chan \ IB Simple Web Media Player
  3. // @description Simple Web Media Player for 4chan and other imageboards.
  4. // @namespace LabMember-001
  5. // @author https://github.com/LabMember-001
  6. // @license GPLv3
  7. // @version 1.2
  8.  
  9. // @grant none
  10. // @run-at document-end
  11.  
  12. // @match https://*.4chan.org/*
  13. // @match https://*.4channel.org/*
  14. // @match https://*.smuglo.li/*
  15. // @match https://*.smugloli.net/*
  16. // @match https://*.kissu.moe/*
  17. // @match https://*.2kind.moe/*
  18. // @match https://*.1chan.net/*
  19. // @match https://*.otterchat.net/*
  20. // @match https://*.fatchan.org/*
  21. // @match https://*.7chan.org/*
  22. // @match https://*.420chan.org/*
  23. // @match https://*.anon.cafe/*
  24.  
  25. // @match https://*.archived.moe/*
  26. // @match https://*.desuarchive.org/*
  27. // @match https://*.4plebs.org/*
  28. // @match https://*.warosu.org/*
  29.  
  30. // ==/UserScript==
  31.  
  32. // Most of the sites above are to test scripts.
  33.  
  34. console.log('Loading IB Media Player.');
  35.  
  36. /* Repository: */
  37. /* https://github.com/LabMember-001/Simple-Web-Media-Player */
  38. /* https://okabe.moe/projects/simplewebmediaplayer */
  39. /* */
  40. /* */
  41. /* */
  42.  
  43. /* == BOARD SUPPORT ==
  44. // Script will alter behavior on different imageboard backends
  45. // When adding custom board scripts, alter board behavior on bottom of the script. You'll need some knowledge on JS.
  46. // In most cases, simply adding an @match to the top of this script to turn it on for that domain will work
  47. // as it captures all link presses and checks for webm.
  48. // If you want more compatability, check the issue tracker for existing requests and if there are none, you may request help.
  49. */
  50.  
  51. /* == SCRIPT CONFIGURATION ==
  52. // To define a board as a precoded backend type (vichan, tinyib, lynxchan, etc) simply
  53. // add a regex for that domain in the swmpBoards category below these comments.
  54. */
  55.  
  56. /* == TO DO ==
  57. //
  58. // High priority
  59. // - Resizing
  60. //
  61. // Medium Priority
  62. // - Loading poster for large files
  63. // - Make window move stop if you release mouseup outside of the screen.
  64. // - Automatic backend script detection.
  65. //
  66. // Low Priority
  67. // - Title length
  68. // - Fix support for archived.moe redirection urls.
  69. // - Fix the "&" from player.php in vichan title.
  70. // - Fix titles on some backends. (Mostly just vichan+4chan taking original filename right now).
  71. */
  72.  
  73. /* == Current Script Support ==
  74. // Yotsuba (4chan)
  75. // Tinyboard/Vichan/Infinity
  76. // Fuuka
  77. // TinyIB
  78. // Wakaba
  79. // FoolFuuka (Not archived.moe's redirects, but 4plebs is fine).
  80. // Kusaba
  81. */
  82.  
  83. /* == Script Specific Bugs ==
  84. // - Vichan shows original title when clicked on video thumb, but not link.
  85. // - Doesnt grab titles on Fuuka, TinyIB, Wakaba, FoolFuuka
  86. //
  87. */
  88.  
  89. /*
  90. // == Missing Script Support ==
  91. // Some Kusaba sites have issues loading video when clicked on thumbnail. Links typically work. 7chan is fine.
  92. // Phutaba has a slight bug with videos which shows file information when hovering over player depending on which link you click.
  93. // Some of the bigger Lynxchan sites are very modified and have no thumbnail click support.
  94. // jschan and vichan will not properly work if not added to the board specific types. Should be fixable for me later.
  95. // InfinityNext does not work. Don't care.
  96. */
  97.  
  98. // Get domain
  99. var currentUrl = window.location.href;
  100.  
  101. // Board Types - probably temporary.
  102. var swmpBoards = {
  103. fourchan: // For title support, otherwise works well without adding here.
  104. ['https:\/\/*..*(4chan|4channel).org\/*'],
  105. vichan: // For Title and thumbnail click support. Not great without due to thumbnails.
  106. ['https:\/\/*..*(smuglo.li|smugloli.net)\/*',
  107. 'https:\/\/*..*2kind.moe\/*',
  108. 'https:\/\/*..*kissu.moe\/*'], // Also works on new UI (lolnoty) as long as it's in here.
  109. tinyib: // Title support not written, otherwise works well without adding here.
  110. ['https:\/\/*..*1chan.net\/*'],
  111. wakaba: // Title support not written, otherwise works well without adding here.
  112. ['https:\/\/*..*otterchat.net\/*'],
  113. lynxchan: // Title support not written, otherwise works well without adding here.
  114. ['https:\/\/*..*anon.cafe\/*'],
  115. jschan: // Title support not written. Jschan installations *must* be added here to work properly.
  116. ['https:\/\/*..*fatchan.org\/*']
  117. };
  118.  
  119. // regex to check the backend script of current domain
  120. var backendScript = [];
  121. Object.keys(swmpBoards).forEach(script => {
  122. swmpBoards[script].forEach(regex => {
  123. if (currentUrl.match(regex)) {
  124. backendScript = script;
  125. }
  126. });
  127. });
  128.  
  129. console.log(backendScript);
  130.  
  131.  
  132. // This configuration variable can be overwritten wherever you want later
  133. // on as you wish or add your own site variables for user configuration.
  134. var swmpConfig = {
  135. autoplay: 'true', // Autoplay media when launched by SWMP.
  136. loop: 'true', // Loop media when launched by SWMP.
  137. windowed: 'true',
  138. positionTop: '100',
  139. positionOffset: '100',
  140. positionSide: 'right',
  141. volume: 60,
  142. muted: 'false',
  143. theme: 'default', //Default theme
  144. themes: //All themes
  145. [
  146. ['default', 'MPC Light'],
  147. ['dark', 'MPC Dark'],
  148. ['kurisu', 'Kurisumasu'],
  149. ['bluemoon', 'Blue Moon'],
  150. ['modernity', 'Modernity']
  151. ],
  152. files: 'avi|mpeg|mpg|ogv|mp4|webm|flv|wav|mp3|m4a|mp2|ogg|flac',
  153. allowMultiple: 'false'
  154. };
  155.  
  156. // Initialize site configuration. As of now it's on a per site basis and not using GM values, I like different designs.
  157. if (localStorage.swmpVolume == undefined) {
  158. localStorage.swmpVolume = swmpConfig.volume;
  159. } else {
  160. swmpConfig.volume = localStorage.swmpVolume;
  161. }
  162.  
  163. if (localStorage.swmpTheme == undefined) {
  164. localStorage.swmpTheme = swmpConfig.theme;
  165. } else {
  166. swmpConfig.theme = localStorage.swmpTheme;
  167. }
  168.  
  169. if (localStorage.swmpAutoplay == undefined) {
  170. localStorage.swmpAutoplay = swmpConfig.autoplay;
  171. } else {
  172. swmpConfig.autoplay = localStorage.swmpAutoplay;
  173. }
  174.  
  175. if (localStorage.swmpLoop == undefined) {
  176. localStorage.swmpLoop = swmpConfig.loop;
  177. } else {
  178. swmpConfig.loop = localStorage.swmpLoop;
  179. }
  180.  
  181. if (localStorage.swmpAllowMultiple == undefined) {
  182. localStorage.swmpAllowMultiple = swmpConfig.allowMultiple;
  183. } else {
  184. swmpConfig.allowMultiple = localStorage.swmpAllowMultiple;
  185. }
  186.  
  187. if (localStorage.swmpWindowed == undefined) {
  188. localStorage.swmpWindowed = swmpConfig.windowed;
  189. } else {
  190. swmpConfig.windowed = localStorage.swmpWindowed;
  191. }
  192.  
  193. if (localStorage.swmpMuted == undefined) {
  194. localStorage.swmpMuted = swmpConfig.muted;
  195. } else {
  196. swmpConfig.muted = localStorage.swmpMuted;
  197. }
  198.  
  199.  
  200. // Add style to head when DOM is loaded.
  201.  
  202. if (!document.getElementById('swmp-stylesheet')) { // Don't bother injecting style on demo page.
  203. var swmpStyle = document.createElement('style');
  204. swmpStyle.setAttribute('id', 'swmp-stylesheet');
  205. swmpStyle.innerHTML = `.swmp *{background:0 0;border:0;outline:0;margin:0;padding:0;line-height:1;height:unset;width:unset;font-family:-apple-system,BlinkMacSystemFont,URW Gothic,MS PGothic,Helvetica,sans-serif;font-size:11pt;text-indent:4px;letter-spacing:1px;color:var(--swmp-text-color)}div.swmp{--swmp-background:#e6e6e6;--swmp-container-border:#000;--swmp-container-box-shadow:none;--swmp-player-container-background:#000;--swmp-controls-background:var(--swmp-background);--swmp-text-color:#000;--swmp-button-background:var(--swmp-background);--swmp-button-mask-color:#000;--swmp-seek-height:30px;--swmp-seek-offset:10px;--swmp-seek-background:var(--swmp-controls-background);--swmp-seek-progress-color:lightgrey;--swmp-seek-border-left:darkgray;--swmp-seek-border-top:darkgray;--swmp-seek-border-right:#fff;--swmp-seek-border-bottom:#fff;--swmp-range-thumb-color:var(--swmp-controls-background);--swmp-range-thumb-border-left:#fff;--swmp-range-thumb-border-top:#fff;--swmp-range-thumb-border-right:#000;--swmp-range-thumb-border-bottom:#000;--swmp-btn-border-left:#fff;--swmp-btn-border-top:#fff;--swmp-btn-border-right:#000;--swmp-btn-border-bottom:#000;--swmp-btn-border-left-active:#000;--swmp-btn-border-top-active:#000;--swmp-btn-border-right-active:#fff;--swmp-btn-border-bottom-active:#fff}div.swmp.swmp-theme-dark,div.swmp.swmp-theme-dark *{--swmp-background:#333;--swmp-container-border:#000;--swmp-controls-background:var(--swmp-background);--swmp-text-color:#888;--swmp-button-background:var(--swmp-background);--swmp-button-mask-color:#888;--swmp-seek-height:30px;--swmp-seek-offset:10px;--swmp-seek-progress-color:#555;--swmp-seek-border-left:#1a1a1a;--swmp-seek-border-top:#1a1a1a;--swmp-seek-border-right:#464646;--swmp-seek-border-bottom:#464646;--swmp-range-thumb-color:var(--swmp-background);--swmp-range-thumb-border-left:#777;--swmp-range-thumb-border-top:#777;--swmp-range-thumb-border-right:#000;--swmp-range-thumb-border-bottom:#000;--swmp-btn-border-left:#777;--swmp-btn-border-top:#777;--swmp-btn-border-right:#000;--swmp-btn-border-bottom:#000;--swmp-btn-border-left-active:#000;--swmp-btn-border-top-active:#000;--swmp-btn-border-right-active:#777;--swmp-btn-border-bottom-active:#777}div.swmp.swmp-theme-kurisu,div.swmp.swmp-theme-kurisu *{--swmp-background:#a44242;--swmp-text-color:#fff;--swmp-button-mask-color:#fff}div.swmp.swmp-theme-bluemoon,div.swmp.swmp-theme-bluemoon *{--swmp-background:#272d37;--swmp-text-color:#eee;--swmp-button-mask-color:#d3d3d3;--swmp-controls-background:#49525D;--swmp-btn-border-left:#DDDDDD;--swmp-btn-border-top:#DDDDDD;--swmp-range-thumb-border-top:#ddd;--swmp-range-thumb-color:#272d37}div.swmp.swmp-container{position:relative;display:inline-flex;flex-direction:column;padding:2px;background:var(--swmp-background);font-family:-apple-system,BlinkMacSystemFont,URW Gothic,MS PGothic,Helvetica,sans-serif;font-size:11pt;text-indent:4px;letter-spacing:1px;color:var(--swmp-text-color);border:1px solid var(--swmp-container-border);line-height:1;z-index:1;overflow:hidden;min-width:320px;width:auto;outline:0;box-shadow:var(--swmp-container-box-shadow)}div.swmp.swmp-window.swmp-window-container{display:flex;justify-content:space-between;padding-bottom:2px}div.swmp.swmp-fullscreen div.swmp-settings.swmp-settings-container,div.swmp.swmp-fullscreen div.swmp-window.swmp-window-container{display:none}div.swmp.swmp-minimized div.swmp-player-container{display:none}div.swmp.swmp-fullscreen div.swmp-player-container{display:block}span.swmp.swmp-window.swmp-window-titlebar{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;position:relative;margin:auto;cursor:move;width:100%;text-align:center;display:block}span.swmp.swmp-window.swmp-window-title{display:block;max-width:260px;text-overflow:ellipsis;overflow:hidden;margin:auto;line-height:1.5;margin-bottom:-2px}span.swmp.swmp-window.swmp-window-buttons-contain{display:flex;flex:0 1 auto}div.swmp .swmp-player-container{display:flex;height:100%;background:var(--swmp-player-container-background)}div.swmp.swmp-container.swmp-audio{display:block}div.swmp.swmp-container.swmp-fullscreen{background:#000;position:unset!important;width:100%;height:auto}div.swmp.swmp-container.swmp-fullscreen iframe,div.swmp.swmp-container.swmp-fullscreen video{width:100%;height:100%;max-width:100%;max-height:100%}div.swmp video{background:#000;max-width:500px;max-height:500px;width:auto;height:auto;margin:auto}div.swmp iframe{background:#000;min-width:420px;min-height:236px;max-width:500px;max-height:500px;width:auto;height:auto;margin:auto;pointer-events:none}div.swmp audio{min-width:320px;min-height:40px}div.swmp.swmp-controls{bottom:0;left:0;background:var(--swmp-controls-background);width:100%;display:flex;flex-direction:column}div.swmp.swmp-fullscreen div.swmp.swmp-controls{position:absolute;opacity:0;transition:opacity .5s 1s ease-out}div.swmp.swmp-fullscreen div.swmp.swmp-controls:hover{opacity:1;position:absolute;transition:none}div.swmp.swmp-controls span.swmp-seek-container{display:flex;width:calc(100% - 6px);height:var(--swmp-seek-height);margin:auto}div.swmp.swmp-controls span.swmp-seek-container input.swmp.swmp-seeker{width:calc(100% - var(--swmp-seek-offset));left:calc(var(--swmp-seek-offset)/ 2);-webkit-appearance:none;background:#0000;padding:0;margin:0;height:var(--swmp-seek-height);position:absolute;z-index:1;cursor:pointer;-webkit-margin-top:-14px}span.swmp input[type=range]::-webkit-slider-runnable-track{width:100%;height:6px;cursor:pointer;background:#0000;border-radius:0;border:1px solid #000;border-left-color:var(--swmp-seek-border-left);border-top-color:var(--swmp-seek-border-top);border-right-color:var(--swmp-seek-border-right);border-bottom-color:var(--swmp-seek-border-bottom)}span.swmp input[type=range]::-moz-range-track{width:100%;height:6px;cursor:pointer;background:#0000;border-radius:0;border:1px solid #000;border-left-color:var(--swmp-seek-border-left);border-top-color:var(--swmp-seek-border-top);border-right-color:var(--swmp-seek-border-right);border-bottom-color:var(--swmp-seek-border-bottom)}span.swmp progress::-webkit-progress-bar{background:#0000}span.swmp progress::-webkit-progress-value{background:var(--swmp-seek-progress-color)}span.swmp progress::-moz-progress-bar{background:var(--swmp-seek-progress-color)}span.swmp input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:4px solid var(--swmp-range-thumb-color);height:8px;width:2px;border-radius:0;padding:4px 3px;background:#0000;background:linear-gradient(180deg,var(--swmp-seek-border-top) 10%,var(--swmp-seek-background) 10%,var(--swmp-seek-background) 90%,var(--swmp-seek-border-bottom) 90%);cursor:pointer;margin-top:-6px;box-shadow:1px 1px 0 0 var(--swmp-range-thumb-border-bottom),-1px -1px 0 0 var(--swmp-range-thumb-border-top)}span.swmp input[type=range]::-moz-range-thumb{appearance:none;border:4px solid var(--swmp-range-thumb-color);height:8px;width:6px;border-radius:0;background:linear-gradient(180deg,var(--swmp-seek-border-top) 10%,var(--swmp-seek-background) 10%,var(--swmp-seek-background) 90%,var(--swmp-seek-border-bottom) 90%);cursor:pointer;box-shadow:1px 1px 0 0 var(--swmp-range-thumb-border-bottom),-1px -1px 0 0 var(--swmp-range-thumb-border-top)}span.swmp input[type=range]::-webkit-range-progress{height:6px;background-color:#0000}span.swmp input[type=range]::-moz-range-progress{height:6px;background-color:#0000}span.swmp input[type=range]{background:0 0;border:0;outline:0}span.swmp progress.swmp-volume{width:50px;position:absolute;height:6px;right:8px;border:none;bottom:7px;z-index:0;background:var(--swmp-seek-background)}span.swmp input.swmp-volume[type=range]{-webkit-appearance:none;background:#0000;padding:0;margin-left:2px;cursor:pointer;position:relative}span.swmp input.swmp-volume[type=range]::-webkit-slider-thumb{padding:4px 2px;width:2px}span.swmp input.swmp-volume[type=range]::-moz-range-thumb{padding:0 1px;width:1px}div.swmp.swmp-controls span.swmp-seek-container progress.swmp-progress{width:100%;height:6px;z-index:0;position:relative;background:var(--swmp-seek-background);bottom:-12px;border:none}span.swmp.swmp-row-bottom{display:flex;flex-direction:row;height:20px;margin-bottom:2px}div.swmp.swmp-fullscreen span.swmp.swmp-row-bottom{padding-bottom:5px}span.swmp.swmp-buttons-container{height:100%}select.swmp.swmp-selector{-webkit-appearance:none;appearance:none;background:var(--swmp-button-background);border:1px solid var(--swmp-text-color);color:var(--swmp-text-color);outline:0;border-radius:0;width:85px;overflow:hidden;text-overflow:ellipsis;letter-spacing:0;text-indent:0}label.swmp.swmp-settings{display:inline-flex;flex-direction:row-reverse}input.swmp.swmp-settings{margin:-2px 0 0 4px;border:1px solid var(--swmp-text-color);appearance:none;-webkit-appearance:none;outline:0;width:14px;height:14px;background:var(--swmp-controls-background)}input.swmp.swmp-settings:checked{outline:5px inset var(--swmp-text-color);outline-offset:-8px}button.swmp.swmp-button{min-width:26px;height:100%;padding:0 4px;display:inline-block;cursor:pointer;margin-left:2px;margin-right:2px;color:var(--swmp-button-mask-color);background:var(--swmp-button-background);border:1px solid;border-bottom-color:var(--swmp-btn-border-bottom);border-right-color:var(--swmp-btn-border-right);border-top-color:var(--swmp-btn-border-top);border-left-color:var(--swmp-btn-border-left);filter:unset}button.swmp.swmp-button span{display:block;width:16px;height:16px;image-rendering:pixelated;background-repeat:no-repeat;background-color:var(--swmp-button-mask-color);-webkit-mask-image:var(--image);mask-image:var(--image);-webkit-mask-size:16px;mask-size:16px;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}span.swmp.swmp-window button{height:20px;min-width:22px;width:22px}button.swmp.swmp-button.swmp-window-minimize span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAARElEQVRYR+3V0Q0AMAQFQPYfuv7apAPg4yzguQQZw5XD/UMAAgQIECCwTuA0fcc7+C8gAIFxgaYleG3W3QECBAgQaBcokVQGIRA6KiEAAAAASUVORK5CYII=');margin-top:2px;-webkit-mask-size:12px;mask-size:12px}button.swmp.swmp-button.swmp-window-close span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9UlEQVRoge2Y2woCMRBD5699dB79a7ViQfDWaZNMFybQR5NzVndhNatUKpUjxe/nctT+Vn59HsYItf+1nDFC7f9Ujhyh9v8qR4xQ+0fKV0bY/XYKDERHIvDtnGcEWBIyeIaEHB4pkQaPkEiHnwXxyc9QE/0mtrjyCgkZfA9SQg7fg5BIg+9ZkUiH75mR2Aa+xS0uwHyzC8Vt/ieULuG2fhOnSfgA3LYSDoBOk2DAyySi8O1RyXyzo8P3pEuswKdLIODTJJDwcgkGvFQiMhKBV/UPj0yXC/r/jiyXC/q/jsDKBf1vI/ByQf8jbtz/bdj9lUqlAs4N+1iFrUSwCpcAAAAASUVORK5CYII=');margin-top:2px;-webkit-mask-size:12px;mask-size:12px}button.swmp.swmp-button.swmp-playbutton span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAA8klEQVRoQ+3aMQ6DMAyF4XDycjCGVmrPBRmapVLkJLafn1GRMiHB/+EpEVtJfm3J+8vtAO86kb2uI8tkfidwfsM/WSA9QBsAPUQC0ENGAbSQWQAdZBVAA9ECwiFWgDCINQAO8QLAIN4AdwgK4AZBA8whUQAzSDRADWEBLEPYANMQVsAwhB0gQrIAupA/AHR60d2bs09APFRgBYjhbfJsgOFwNsB0OAtgOTwaoA6PApiFowHm4SiAW7g3wD3cCwALtwbAw60AYeFaQHj4KoAmfBZAFz4KoA2XAPThPcCz3njU9QJtFdWvud2vBuovgn5A+glcJSF8MQrukbIAAAAASUVORK5CYII=')}button.swmp.swmp-button.swmp-playbutton.swmp-playing span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAtUlEQVRoQ+2Y2w6AIAxD5f8/2kuCRgiJbdjDMMfnxUE5o9WyLf6Uxde//W4D+8SJuGKE9OqbhrxUFCGkFxt4qQ1CInpX2SMWCIGQwc1ILBACIRBqFQixd1HUkF4MMUMs8taVESVGupFGDZpACIROBTCy5tOsMhGiijiMIb2IEkQJkTeihCAUUUIQ6S4hShAliBKVAZwYJzbuTn7ufoiFExs04cRpndg4xRyl7uDlWPWE+aTbwAFy3FQxPpmarQAAAABJRU5ErkJggg==')}button.swmp.swmp-button.swmp-stopbutton span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAUUlEQVRYR+3Xuw0AIAwD0WQC2H9LJuDXICFqm+KywEWvc4b50tyP7x7oIpE6O221bgEeQAABBBBAAAEEEEAAAQTsAkW0jPYqei0jUf9k7ON0AGFsNSFlb3+JAAAAAElFTkSuQmCC')}.swmp.swmp-timer-container,button.swmp.swmp-button.swmp-fullscreen{margin-left:auto}button.swmp.swmp-button.swmp-fullscreen span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAP0lEQVQ4T2NkoBAwQvX/x2EOQXmCCghZgG4AjE/IYzAXMw4jAwj5Gac8sYFG0AB4qBLplOEYCwOfF4gMfExlADQ3GBE+X9RsAAAAAElFTkSuQmCC')}button.swmp.swmp-button.swmp-volume span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAG1BMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABp4cHsAAAACHRSTlMA1WZ/JZz0Rme5O54AAACGSURBVDjL5ZIxCoAwDEWjVXDs6OjUWXDxRl6hqyL2H9uqKKL5oKtmzIOXNvkiP6qc9LOSAAciAoiIAAcdRNEGmrtoBQb1TbSCFt3eK3CUSAr0KliUlQbEA1YFCTCqID47qMAAwztAVXS4x2RfffC8kusSw7O100Px0/Iw0PjwwPGI0lB/s2bRbW7duVgj2wAAAABJRU5ErkJggg==')}button.swmp.swmp-button.swmp-volume.swmp-min span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAAd0lEQVR42u2Xuw2AMAwFr41YhKmyVyp2YhZ6pwEJqJDgmRTv3PukV/gDxpjBKDRl+5mV0LWvbIRKUGjEXrJoZIIjGongHI1AcI3mc8E9mpeCeFwWWGCBBTqBfNj9MK4TFk7CykxZ+glnS8LhlXI6Akws/gCMGZAO8wpmVouK9vcAAAAASUVORK5CYII=')}button.swmp.swmp-button.swmp-volume.swmp-max span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAD1BMVEVHcEwAAAAAAAAAAAAAAADTrAj/AAAABXRSTlMA/4hD1KmiQHMAAACwSURBVDjL1ZPBDYMwDEV/nQzAIwwAVQdoNoD9l+qhgAyKW/VGfbHkp/wk9rf0//EI6sZ8Kqx5oj/U07IdgKcHlXfOcNAyVpCA4oU2oAqDF9pB9pcYK7hLAkYnBJKMUZqgc0IgaaJIFYp0Yw/JYJAy9CegBUZZA1SYlWA4gwxdExgUCWiA/mfQlgovj56bog/6loRN/Nz2w6CSG1Q82tgMoX1iw8UWDU0dr8HXxblwvAAGdxy1HX87LAAAAABJRU5ErkJggg==')}button.swmp.swmp-button.swmp-volume.swmp-mute span{--image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAHlBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC3KG9qAAAACnRSTlMACZko0v9e9AufnFf4OQAAAJdJREFUeJzlkrsNgDAMRCMhL8BO6WluBmZha2LZDvmdRA1uUHi+S/xJ6Uexk/9bJuAEMcIabBmQQNLknFBw2KERFyMFcEEFalROMAlCaUYFWKoLBDU81wUdkCobgGbHDT0Q/06gSOJJA3hqeKmgd7BX0TpY5V2v2ibO3bW2L+YRg5on6KOtRT9AzdIy2PrwheMrmi4GPhk3sCZY/9usUnQAAAAASUVORK5CYII=')}span.swmp.swmp-volume-container{display:flex;width:60px;height:100%;position:relative}span.swmp.swmp-volume-container input.swmp.swmp-volume.swmp-range{width:50px;vertical-align:top;position:relative}button.swmp.swmp-button:active{border-bottom-color:var(--swmp-btn-border-bottom-active);border-right-color:var(--swmp-btn-border-right-active);border-top-color:var(--swmp-btn-border-top-active);border-left-color:var(--swmp-btn-border-left-active)}span.swmp.swmp-timer-container{display:inline-table;margin-right:5px;cursor:default}span.swmp.swmp-timer-container span.swmp.swmp-time{display:table-cell;vertical-align:bottom}#quote-preview{z-index:1}div.swmp.swmp-theme-modernity,div.swmp.swmp-theme-modernity *{--swmp-background:#222;--swmp-container-border:#222;--swmp-text-color:#eee;--swmp-button-mask-color:#eee;--swmp-controls-background:#222;--swmp-btn-border-left:#0000;--swmp-btn-border-top:#0000;--swmp-btn-border-right:#0000;--swmp-btn-border-bottom:#0000;--swmp-btn-border-left-active:#0000;--swmp-btn-border-top-active:#0000;--swmp-btn-border-right-active:#0000;--swmp-btn-border-bottom-active:#0000;--swmp-seek-background:#444;--swmp-seek-progress-color:#bbb;--swmp-seek-border-left:#444;--swmp-seek-border-top:#444;--swmp-seek-border-right:#444;--swmp-seek-border-bottom:#444;--swmp-container-box-shadow:0px 0px 10px #000;font-family:Arial;font-weight:700}div.swmp.swmp-theme-modernity input[type=range]::-moz-range-thumb{border:none;height:8px;width:8px;background:#eee;box-shadow:none;border-radius:100%;padding:4px}div.swmp.swmp-theme-modernity input[type=range]::-webkit-slider-thumb{border:none;height:8px;width:8px;background:#eee;box-shadow:none;border-radius:100%;padding:4px}`;
  206. document.head.appendChild(swmpStyle);
  207. }
  208.  
  209. var youtubeIsLoaded = false;
  210. var gmwindow;
  211. if (typeof GM_info != 'undefined') {
  212. gmwindow = unsafeWindow;
  213. } else {
  214. gmwindow = window;
  215. }
  216.  
  217. // SWMP Code
  218.  
  219. class swmp {
  220.  
  221. constructor(obj) {
  222. this.name = 'Simple Web Media Player';
  223. if (obj.id == undefined) {
  224. this.id = this.uuid();
  225. } else {
  226. this.id = obj.id;
  227. }
  228.  
  229. this.type = obj.type; // "video" or "audio" player. Youtube too.
  230. this.mime = obj.mime; // MIME type for source, Example: "video/webm". Feed to enable checking for file support (webm not supported by iOS)
  231. this.url = obj.url; // File Location
  232. this.poster = obj.poster; // Optional Video Preview for when autoplay is off.
  233. this.autoplay = obj.autoplay; // Optional Autoplay
  234. this.loop = obj.loop; // Optional Loop
  235. this.windowed = obj.windowed; // Optional set false to disable windowed mode and place inline
  236. this.defaultVolume = parseInt(swmpConfig.volume);
  237.  
  238. if (obj.url == undefined) {
  239. console.log('No media given');
  240. return false;
  241. }
  242.  
  243. if (obj.windowed == undefined) {
  244. this.windowed = swmpConfig.windowed;
  245. }
  246.  
  247. if (obj.title == undefined) {
  248. this.title = this.getFileName(obj.url);
  249. } else {
  250. this.title = obj.title; // If Applicable, assign from existing parameters on IB.
  251. }
  252.  
  253. // Check URL for Type and Mime, also sets mime+type based on fileextension in url
  254. if (this.checkURL(this.url) == false) {
  255. return false;
  256. }
  257.  
  258. // Create Container
  259. this.container = document.createElement('div');
  260. this.container.setAttribute('class', 'swmp swmp-container');
  261. this.container.setAttribute('id', this.id);
  262. this.container.setAttribute('tabindex', '0');
  263.  
  264.  
  265. // Create Window Container
  266. if (this.windowed != 'false') {
  267. this.prepareWindow();
  268. }
  269.  
  270. // Prepare the media that's gonna play.
  271.  
  272. if (this.type == 'video' || this.type == 'audio') {
  273. // Check if browser can play formats, create audio or video tag and fill with source.
  274. this.preparePlayer();
  275. } else if (this.type == 'youtube') {
  276. // Load Youtube iframe.
  277. if (this.prepareYoutube() == false) {
  278. console.log("Error: YouTube");
  279. return false;
  280. }
  281. }
  282.  
  283. // Add Theme
  284. if (swmpConfig.theme != undefined) {
  285. this.container.classList.add(`swmp-theme-${swmpConfig.theme}`);
  286. }
  287.  
  288. if (this.type == 'video' || this.type == 'audio') {
  289. // Create buttons
  290. this.prepareSharedEvents();
  291. this.prepareControls();
  292. this.preparePlayerEvents();
  293. this.prepareSettings();
  294. } else if (this.type == 'youtube') {
  295. //this.prepareSharedEvents();
  296. //this.prepareYoutubeEvents();
  297. }
  298.  
  299. }
  300.  
  301. prepareWindow() {
  302. this.windowContainer = document.createElement('div');
  303. this.windowContainer.setAttribute('class', 'swmp swmp-window swmp-window-container');
  304. this.container.appendChild(this.windowContainer);
  305.  
  306. // Create Title/WindowDragbar and put inside Window Container
  307. this.windowTitlebar = document.createElement('span');
  308. this.windowTitlebar.setAttribute('class', 'swmp swmp-window swmp-window-titlebar');
  309.  
  310. this.windowTitle = document.createElement('span');
  311. this.windowTitle.setAttribute('class', 'swmp swmp-window swmp-window-title');
  312. if (this.title != undefined) {
  313. this.windowTitle.textContent = this.title;
  314. } else {
  315. this.windowTitle.textContent = this.url;
  316. }
  317.  
  318. this.windowTitlebar.appendChild(this.windowTitle);
  319. this.windowContainer.appendChild(this.windowTitlebar);
  320.  
  321. // Create Window Buttons Container
  322. this.windowButtonsContain = document.createElement('span');
  323. this.windowButtonsContain.setAttribute('class', 'swmp swmp-window swmp-window-buttons-contain');
  324. this.windowContainer.appendChild(this.windowButtonsContain);
  325.  
  326. // Create Minimize Button (Video only)
  327. if (this.type == 'video' || this.type == 'youtube') {
  328. this.windowMinimize = document.createElement('button');
  329. this.windowMinimize.setAttribute('class', 'swmp swmp-button swmp-window-minimize');
  330. this.windowMinimize.innerHTML = '<span></span>';
  331. this.windowMinimize.addEventListener('click', (event) => {
  332. event.preventDefault();
  333. if (this.container.classList.contains('swmp-minimized') ) {
  334. this.container.classList.remove('swmp-minimized');
  335. } else {
  336. this.container.classList.add('swmp-minimized');
  337. }
  338. });
  339. this.windowButtonsContain.appendChild(this.windowMinimize);
  340. }
  341.  
  342. // Create Close Button
  343. this.windowClose = document.createElement('button');
  344. this.windowClose.setAttribute('class', 'swmp swmp-button swmp-window-close');
  345. this.windowClose.innerHTML = '<span></span>';
  346. this.windowClose.addEventListener('click', (event) => {
  347. event.preventDefault();
  348. this.container.remove();
  349. });
  350. this.windowButtonsContain.appendChild(this.windowClose);
  351.  
  352. // Window Event
  353. this.makeDraggable(this.container);
  354.  
  355.  
  356. // Disable Default Context Menu on Titlebar
  357. this.windowTitlebar.addEventListener('contextmenu', function(evt) {
  358. evt.preventDefault();
  359. }, false);
  360. // Right Click Event
  361. this.windowTitlebar.addEventListener('mousedown', (event) => {
  362. event.preventDefault();
  363. switch (event.which) {
  364. case 3: //rightclick
  365. this.openSettings();
  366. break;
  367. }
  368. });
  369. }
  370.  
  371. prepareControls() {
  372. // Create and put Controls inside Container
  373. this.controls = document.createElement('div');
  374. this.controls.setAttribute('class', 'swmp swmp-controls');
  375. this.container.appendChild(this.controls);
  376.  
  377. // Disable Default Context Menu on Controls
  378. this.controls.addEventListener('contextmenu', function(evt) {
  379. evt.preventDefault();
  380. }, false);
  381. // Right Click Event
  382. this.controls.addEventListener('mousedown', (event) => {
  383. switch (event.which) {
  384. case 3: //rightclick
  385. this.openSettings();
  386. break;
  387. }
  388. });
  389.  
  390. // Create Progress/Seeker Container
  391. this.seekContain = document.createElement('span');
  392. this.seekContain.setAttribute('class', 'swmp swmp-seek-container');
  393. //this.seekContain.textContent = 'seek-cntnr';
  394. this.controls.appendChild(this.seekContain);
  395.  
  396. // Create Progress Bar
  397. this.progress = document.createElement('progress');
  398. this.progress.setAttribute('value', '0');
  399. this.progress.setAttribute('min', '0');
  400. this.progress.setAttribute('max', '1000');
  401. this.progress.setAttribute('step', '1');
  402. this.progress.setAttribute('class', 'swmp swmp-progress');
  403. this.seekContain.appendChild(this.progress);
  404.  
  405. // Create Seeker Input
  406. this.seeker = document.createElement('input');
  407. this.seeker.setAttribute('type', 'range');
  408. this.seeker.setAttribute('value', '0');
  409. this.seeker.setAttribute('min', '0');
  410. this.seeker.setAttribute('max', '1000');
  411. this.seeker.setAttribute('step', '1');
  412. this.seeker.setAttribute('class', 'swmp swmp-seeker');
  413. this.seekContain.appendChild(this.seeker);
  414.  
  415. // Create a Row Bottom Container
  416. this.bottomRow = document.createElement('span');
  417. this.bottomRow.setAttribute('class', 'swmp swmp-row-bottom');
  418. this.controls.appendChild(this.bottomRow);
  419.  
  420. // Create Buttons Container
  421. this.buttonsContain = document.createElement('span');
  422. this.buttonsContain.setAttribute('class', 'swmp swmp-buttons-container');
  423. this.bottomRow.appendChild(this.buttonsContain);
  424.  
  425. // Create Play/Pause Button and put inside Controls
  426. this.playbutton = document.createElement('button');
  427. this.playbutton.setAttribute('class', 'swmp swmp-button swmp-playbutton');
  428. this.playbutton.innerHTML = "<span></span>"; // ◀
  429. this.playbutton.addEventListener("click", event => {
  430. event.preventDefault();
  431.  
  432. if (this.type == 'video' || this.type == 'audio') {
  433. if (this.player.paused ) {
  434. this.player.play();
  435. } else {
  436. this.player.pause();
  437. }
  438. } else if (this.type == 'youtube') {
  439. this.togglePlay();
  440. }
  441. });
  442. this.buttonsContain.appendChild(this.playbutton);
  443.  
  444. // Create a Stop/Reload Button and put inside Controls
  445. this.stopbutton = document.createElement('button');
  446. this.stopbutton.setAttribute('class', 'swmp swmp-button swmp-stopbutton');
  447. this.stopbutton.innerHTML = "<span></span>"; // ■
  448. this.stopbutton.addEventListener("click", event => {
  449. event.preventDefault();
  450.  
  451. if (this.type == 'video' || this.type == 'audio') {
  452. this.player.pause();
  453. this.seeker.value = 0;
  454. this.seeker.setAttribute("value", 0);
  455. this.progress.value = 0;
  456. this.progress.setAttribute("value", 0);
  457. this.player.currentTime = 0;
  458. this.currentTimer.textContent = '00:00';
  459. } else if (this.type == 'youtube') {
  460. this.seeker.value = 0;
  461. this.seeker.setAttribute("value", 0);
  462. this.progress.value = 0;
  463. this.progress.setAttribute("value", 0);
  464. this.ytplayer.stopVideo();
  465. this.currentTimer.textContent = '00:00';
  466. }
  467. });
  468. this.buttonsContain.appendChild(this.stopbutton);
  469.  
  470. // Create a Volume Container
  471. this.volumeContain = document.createElement('span');
  472. this.volumeContain.setAttribute('class', 'swmp swmp-volume-container');
  473. this.bottomRow.appendChild(this.volumeContain);
  474.  
  475. // Create a Volume Button and put inside Buttons Container
  476. this.volumeButton = document.createElement('button');
  477. this.volumeButton.setAttribute('class', 'swmp swmp-button swmp-volume');
  478. this.volumeButton.innerHTML = "<span></span>";
  479. this.volumeButton.addEventListener("click", event => {
  480. event.preventDefault();
  481. this.toggleMute();
  482. });
  483. this.buttonsContain.appendChild(this.volumeButton);
  484.  
  485. // Create a Volume Progress and put inside Volume Container
  486. this.volumeProgress = document.createElement('progress');
  487. this.volumeProgress.setAttribute('value', this.defaultVolume);
  488. this.volumeProgress.setAttribute('min', '0');
  489. this.volumeProgress.setAttribute('max', '100');
  490. this.volumeProgress.setAttribute('step', '1');
  491. this.volumeProgress.setAttribute('class', 'swmp swmp-volume swmp-progress');
  492. this.volumeContain.appendChild(this.volumeProgress);
  493.  
  494. // Create a Volume Input and put inside Volume Container
  495. this.volumeRange = document.createElement('input');
  496. this.volumeRange.setAttribute('type', 'range');
  497. this.volumeRange.setAttribute('value', this.defaultVolume);
  498. this.volumeRange.setAttribute('min', '0');
  499. this.volumeRange.setAttribute('max', '100');
  500. this.volumeRange.setAttribute('step', '1');
  501. this.volumeRange.setAttribute('class', 'swmp swmp-volume swmp-range');
  502. this.volumeContain.appendChild(this.volumeRange);
  503.  
  504. // Create a Timer Container and put inside Controls
  505. this.timerContain = document.createElement('span');
  506. this.timerContain.setAttribute('class', 'swmp swmp-timer-container');
  507. this.bottomRow.appendChild(this.timerContain);
  508.  
  509. // Create a timerCurrent and put inside Timer Container
  510. this.currentTimer = document.createElement('span');
  511. this.currentTimer.setAttribute('class', 'swmp swmp-time swmp-current');
  512. this.currentTimer.textContent = '00:00';
  513. this.timerContain.appendChild(this.currentTimer);
  514.  
  515. // Add a separator between timer
  516. this.timerSeperator = document.createElement('span');
  517. this.timerSeperator.setAttribute('class', 'swmp swmp-time swmp-separator');
  518. this.timerSeperator.textContent = '/';
  519. this.timerContain.appendChild(this.timerSeperator);
  520.  
  521. // Create a totalTimer and put inside Timer Container
  522. this.totalTimer = document.createElement('span');
  523. this.totalTimer.setAttribute('class', 'swmp swmp-time swmp-total');
  524. this.totalTimer.textContent = '00:00';
  525. this.timerContain.appendChild(this.totalTimer);
  526.  
  527. // Create a Fullscreen Button and put inside Bottom Row Container
  528. if (this.type == 'video' || this.type == 'youtube') {
  529. this.fullscreenbutton = document.createElement('button');
  530. this.fullscreenbutton.setAttribute('class', 'swmp swmp-button swmp-fullscreen');
  531. this.fullscreenbutton.innerHTML = "<span></span>"; // ▣
  532. this.fullscreenbutton.addEventListener("click", event => {
  533. event.preventDefault();
  534. this.fullscreen(this.container);
  535. });
  536. this.bottomRow.appendChild(this.fullscreenbutton);
  537. }
  538. }
  539.  
  540. prepareSettings() {
  541.  
  542. this.openSettings = () => {
  543. // Settings Menu
  544.  
  545. if (this.container.querySelector('.swmp-settings-container') != null) {
  546. this.settingsContainer.remove();
  547. return false; //Already open
  548. }
  549.  
  550. this.settingsContainer = document.createElement('div');
  551. this.settingsContainer.setAttribute('class', 'swmp swmp-settings swmp-settings-container');
  552. this.settingsContainer.innerHTML = 'Settings:';
  553.  
  554.  
  555. this.br = document.createElement('br');
  556. this.settingsContainer.appendChild(this.br);
  557.  
  558. this.themeSelector = document.createElement('select');
  559. this.themeSelector.setAttribute('class', 'swmp swmp-selector swmp-settings swmp-theme-selector');
  560.  
  561. this.themes = '';
  562. swmpConfig.themes.forEach(theme => {
  563. if (localStorage.swmpTheme == theme[0]) {
  564. this.themeSelector.value = theme[0];
  565. this.themes += `<option value="${theme[0]}" selected>${theme[1]}</option>`;
  566. } else {
  567. this.themes += `<option value="${theme[0]}">${theme[1]}</option>`;
  568. }
  569. });
  570. this.themeSelector.innerHTML = this.themes;
  571.  
  572. this.themeSelector.onchange = (event) => {
  573. if (this.themeSelector.value == '') {
  574. return false;
  575. } else {
  576. this.removeClassByPrefix(this.container, 'swmp-theme-'); //regex remove [theme-*]
  577. this.container.classList.add(`swmp-theme-${this.themeSelector.value}`);
  578. swmpConfig.theme = this.themeSelector.value;
  579. localStorage.swmpTheme = this.themeSelector.value;
  580. }
  581. };
  582.  
  583. this.settingsContainer.appendChild(this.themeSelector);
  584.  
  585. // Autoplay Settings
  586. this.autoplayLabel = document.createElement('label');
  587. this.autoplayLabel.setAttribute('class', 'swmp swmp-settings swmp-label swmp-autoplay-label');
  588. this.autoplayLabel.textContent = 'Autoplay';
  589. this.autoplayCheck = document.createElement('input');
  590. this.autoplayCheck.setAttribute('class', 'swmp swmp-settings swmp-input swmp-autoplay-input');
  591. this.autoplayCheck.setAttribute('type', 'checkbox');
  592. if (localStorage.swmpAutoplay == 'true') {
  593. this.autoplayCheck.setAttribute('checked', 'checked');
  594. }
  595. this.autoplayCheck.addEventListener('change', (event) => {
  596. if (this.autoplayCheck.checked == true) {
  597. this.autoplayCheck.setAttribute('checked', 'checked');
  598. localStorage.swmpAutoplay = 'true';
  599. swmpConfig.autoplay = 'true';
  600. } else {
  601. this.autoplayCheck.removeAttribute('checked');
  602. localStorage.swmpAutoplay = 'false';
  603. swmpConfig.autoplay = 'false';
  604. }
  605. });
  606. this.autoplayLabel.appendChild(this.autoplayCheck);
  607. this.settingsContainer.appendChild(this.autoplayLabel);
  608.  
  609. // Loop Settings
  610. this.loopLabel = document.createElement('label');
  611. this.loopLabel.setAttribute('class', 'swmp swmp-settings swmp-label swmp-loop-label');
  612. this.loopLabel.textContent = 'Loop';
  613. this.loopCheck = document.createElement('input');
  614. this.loopCheck.setAttribute('class', 'swmp swmp-settings swmp-input swmp-loop-input');
  615. this.loopCheck.setAttribute('type', 'checkbox');
  616. if (localStorage.swmpLoop == 'true') {
  617. this.loopCheck.setAttribute('checked', 'checked');
  618. }
  619. this.loopCheck.addEventListener('change', (event) => {
  620. if (this.loopCheck.checked == true) {
  621. this.loopCheck.setAttribute('checked', 'checked');
  622. if (this.type == 'video' || this.type == 'audio') {
  623. this.player.setAttribute('loop', 'true');
  624. } else if (this.type == 'youtube') {
  625. // Youtube Loop
  626. }
  627. localStorage.swmpLoop = 'true';
  628. swmpConfig.loop = 'true';
  629. } else {
  630. this.loopCheck.removeAttribute('checked');
  631. if (this.type == 'video' || this.type == 'audio') {
  632. this.player.removeAttribute('loop');
  633. } else if (this.type == 'youtube') {
  634. // Youtube Loop
  635. }
  636. localStorage.swmpLoop = 'false';
  637. swmpConfig.loop = 'false';
  638. }
  639. });
  640. this.loopLabel.appendChild(this.loopCheck);
  641. this.settingsContainer.appendChild(this.loopLabel);
  642. // Multiple Players Settings
  643. this.multiLabel = document.createElement('label');
  644. this.multiLabel.setAttribute('class', 'swmp swmp-settings swmp-label swmp-multi-label');
  645. this.multiLabel.textContent = 'Multi';
  646. this.multiCheck = document.createElement('input');
  647. this.multiCheck.setAttribute('class', 'swmp swmp-settings swmp-input swmp-multi-input');
  648. this.multiCheck.setAttribute('type', 'checkbox');
  649. if (localStorage.swmpAllowMultiple == 'true') {
  650. this.multiCheck.setAttribute('checked', 'checked');
  651. }
  652. this.multiCheck.addEventListener('change', (event) => {
  653. if (this.multiCheck.checked == true) {
  654. this.multiCheck.setAttribute('checked', 'checked');
  655. localStorage.swmpAllowMultiple = 'true';
  656. swmpConfig.allowMultiple = 'true';
  657. } else {
  658. this.multiCheck.removeAttribute('checked');
  659. localStorage.swmpAllowMultiple = 'false';
  660. swmpConfig.allowMultiple = 'false';
  661. }
  662. });
  663. this.multiLabel.appendChild(this.multiCheck);
  664. this.settingsContainer.appendChild(this.multiLabel);
  665.  
  666.  
  667. this.container.appendChild(this.settingsContainer);
  668.  
  669. };
  670. }
  671.  
  672. preparePlayer() {
  673. // Create Player HTML5 Video or Audio format.
  674. if (this.type == 'video') {
  675. this.container.classList.add('swmp-video');
  676. this.player = document.createElement('video');
  677. this.player.setAttribute('class', 'swmp swmp-video swmp-player');
  678. if (this.poster != false && this.poster != undefined) {
  679. this.player.setAttribute('poster', this.poster);
  680. }
  681. } else if (this.type == 'audio') {
  682. this.container.classList.add('swmp-audio');
  683. this.player = document.createElement('audio');
  684. this.player.setAttribute('class', 'swmp swmp-audio swmp-player');
  685. } else {
  686. console.log(`SWMP Error: invalid type of ${this.type}.`);
  687. return false;
  688. }
  689.  
  690. // Check Format Support
  691. if (this.mime != undefined) {
  692. if (this.player.canPlayType(this.mime) == '') {
  693. this.closeError = document.createElement('span');
  694. this.closeError.innerHTML = `Your browser can't play this format: ${this.mime}`;
  695. this.container.addEventListener('click', (event) => {
  696. this.container.remove();
  697. });
  698. this.container.appendChild(this.closeError);
  699. return false;
  700. }
  701. }
  702.  
  703. // Preload metadata
  704. this.player.setAttribute('preload', 'metadata');
  705.  
  706. // Create Player Container and Put inside Container
  707. this.playerContainer = document.createElement('div');
  708. this.playerContainer.setAttribute('class', 'swmp swmp-player-container');
  709. this.container.appendChild(this.playerContainer);
  710. // Put Player inside Video Container
  711. this.playerContainer.appendChild(this.player);
  712.  
  713. // Create and put Source inside Player
  714. this.source = document.createElement('source');
  715. this.source.setAttribute('src', this.url);
  716. if (this.mime != undefined) {
  717. this.source.setAttribute('type', this.mime);
  718. }
  719. this.player.appendChild(this.source);
  720.  
  721. // Is Autoplay?
  722. if ( (swmpConfig.autoplay == true || swmpConfig.autoplay == 'true') && this.autoplay != false) {
  723. this.player.setAttribute('autoplay', true);
  724. }
  725.  
  726. // Is Loop?
  727. if ( (swmpConfig.loop == true || swmpConfig.loop == 'true') && this.loop != false) {
  728. this.player.setAttribute('loop', true);
  729. }
  730. }
  731.  
  732. prepareYoutube() {
  733.  
  734. console.log('SWMP: prepareYoutube() ');
  735.  
  736. if (youtubeIsLoaded == false) {
  737. // Include YouTube iframe API if not already added:
  738. if (!document.getElementById('swmp-youtube-api')) {
  739. console.log("SWMP: Loading YouTube API");
  740. var tag = document.createElement('script');
  741. tag.src = "https://www.youtube.com/iframe_api";
  742. tag.setAttribute('id', 'swmp-youtube-api');
  743. var firstScriptTag = document.getElementsByTagName('script')[0];
  744. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  745. }
  746. gmwindow.onYouTubeIframeAPIReady = (event) => {
  747. youtubeIsLoaded = true;
  748. console.log('SWMP: Firing YouTube Ready Event from inside PrepareYouTube()');
  749. this.makeYoutube();
  750. };
  751. } else {
  752. // API Already loaded, just make video.
  753. console.log('SWMP: YT API Already loaded, make vid');
  754. this.makeYoutube();
  755. }
  756. }
  757.  
  758. makeYoutube() {
  759. // Make new player
  760. console.log('SWMP: makeYoutube()');
  761. this.playerfirstrun = true;
  762. var self = this;
  763. this.onPlayerReady = (event) => {
  764. console.log('SWMP Youtube: onPlayerReady()');
  765. if (this.playerfirstrun == true) {
  766. this.playerfirstrun = false;
  767. this.playerContainer.appendChild(this.ytplayer.getIframe() );
  768. this.windowTitle.textContent = this.ytplayer.getVideoData().title;
  769. this.totalTimer.textContent = this.formatSeconds(this.ytplayer.getDuration() );
  770. this.ytplayer.getIframe().style.display = 'inherit';
  771.  
  772. this.ytplayer.setVolume(this.defaultVolume);
  773. this._volume = this.defaultVolume;
  774. /*if (swmpConfig.muted == 'true') {
  775. this.ytplayer.mute();
  776. this.volumeButton.classList.add('swmp-mute');
  777. } else {
  778. this.ytplayer.unMute();
  779. }*/
  780. this.ytplayer.unMute();
  781.  
  782. if (this.autoplay == 'true') {
  783. this.ytplayer.playVideo();
  784. this.playbutton.classList.add('swmp-playing');
  785. }
  786.  
  787.  
  788. }
  789.  
  790. window.addEventListener('message', function(event) {
  791. if (event.source === self.ytplayer.getIframe().contentWindow) {
  792. var data = JSON.parse(event.data);
  793. //console.log(data);
  794.  
  795. // Dispatch Volume and Mute Event -- These are sent together by iframe.
  796. if (data.event === "infoDelivery" && data.info && data.info.volume) {
  797. //console.log(data.info.volume);
  798. //console.log(data.info.muted);
  799. if (data.info.volume != self._volume) {
  800. //console.log(data.info.volume);
  801. self.updateVolume();
  802. }
  803. }
  804.  
  805. // Dispatch on Time Event
  806. if (data.event === "infoDelivery" && data.info && data.info.currentTime) {
  807. //console.log(data.info.currentTime);
  808. self.currentTimer.textContent = self.formatSeconds(data.info.currentTime);
  809. }
  810.  
  811. // Dispatch on Player state event
  812. if (data.event === "infoDelivery" && data.info && data.info.playerState) {
  813. //console.log(data.info.playerState);
  814. self.updatePlay();
  815. self.updateVolume();
  816. }
  817. }
  818. });
  819.  
  820. }
  821.  
  822. this.videogenerate = document.createElement('div');
  823. this.videogenerate.style.display = 'none';
  824. this.videogenerate.setAttribute('id', `ytplayer-${this.id}`);
  825. document.body.appendChild(this.videogenerate);
  826. this.ytplayer = new gmwindow.YT.Player(`ytplayer-${this.id}`, {
  827. height: '1080',
  828. width: '1920', // Allows quality to increase to 1080p. Can't be forced by API, need fast internet.
  829. videoId: this.videoid,
  830. playerVars: {
  831. 'playsinline': 1,
  832. 'autoplay': 1,
  833. 'loop': 1,
  834. //'origin': window.location.href,
  835. 'rel': 0,
  836. 'modestbranding': 1,
  837. 'controls': 0
  838. },
  839. events: {
  840. 'onReady': this.onPlayerReady
  841. }
  842. });
  843. console.log(this.ytplayer);
  844.  
  845. this.playerContainer = document.createElement('div');
  846. this.playerContainer.setAttribute('class', 'swmp-player-container');
  847.  
  848. this.container.appendChild(this.playerContainer);
  849.  
  850. this.prepareSettings();
  851. this.prepareControls();
  852. this.prepareSharedEvents();
  853. this.prepareYoutubeEvents();
  854.  
  855. }
  856.  
  857.  
  858. prepareYoutubeEvents() {
  859.  
  860. this.togglePlay = function() {
  861. if (this.ytplayer.getPlayerState() == 1) { // Playing
  862. this.ytplayer.pauseVideo();
  863. } else { // -1 unstarted, 0 ended, 1 playing, 2 Pause, 3 buffering, 5 video cued
  864. this.ytplayer.playVideo();
  865. }
  866. };
  867.  
  868. var timeInterval;
  869. this.updatePlay = () => {
  870.  
  871. this._playerState = this.ytplayer.getPlayerState();
  872.  
  873. if (this._playerState == 1) { // Playing
  874. this.playbutton.classList.add('swmp-playing');
  875. this.updateTime();
  876.  
  877. var self = this;
  878. var timeInterval = setInterval(function() {
  879. self._playerState = self.ytplayer.getPlayerState();
  880. if (self._playerState == 1) {
  881. self.updateTime();
  882. }
  883. if (self._playerState == 0) { //Ended
  884. self.updatePlay();
  885. }
  886. //console.log('time'); //still bugged on close unlike the other interval
  887. if (self._playerState != 1 || self.container == null || self.container == undefined || self.container == false) {
  888. clearInterval(timeInterval);
  889. timeInterval = null;
  890. }
  891. }, 80);
  892.  
  893. } else if (this._playerState == 0 || this._playerState == -1 || this._playerState == 2 ) { // -1 unstarted, 0 ended, 1 playing, 2 Pause, 3 buffering, 5 video cued
  894. this.playbutton.classList.remove('swmp-playing');
  895. }
  896.  
  897. if (this._playerState == 0 || this._playerState == -1 ) {
  898. this.progress.value = 0;
  899. this.progress.setAttribute('value', 0);
  900. this.seeker.value = 0;
  901. this.seeker.setAttribute('value', 0);
  902. }
  903.  
  904. if (this._playerState == 0) {
  905. console.log('uwah video loop');
  906. if (swmpConfig.loop == 'true') {
  907. this.ytplayer.seekTo(0);
  908. this.ytplayer.playVideo();
  909. }
  910. }
  911.  
  912. };
  913.  
  914. this.seeker.oninput = (event) => {
  915. //on mousedown temporary add a mute to avoid annoying seeking sounds?
  916. this.ytplayer.seekTo(Math.floor(this.ytplayer.getDuration() * this.seeker.value / this.seeker.max) );
  917. this.progress.value = this.seeker.value;
  918. this.progress.setAttribute('value', this.seeker.value);
  919. this.updatePlay();
  920. };
  921.  
  922. this.updateTime = () => {
  923. this.seeker.value = Math.floor(this.ytplayer.getCurrentTime() / this.ytplayer.getDuration() * this.seeker.max);
  924. this.seeker.setAttribute('value', this.seeker.value);
  925. this.progress.value = this.seeker.value;
  926. this.progress.setAttribute('value', this.seeker.value);
  927. };
  928.  
  929.  
  930. this.toggleMute = () => {
  931.  
  932. if (this.ytplayer.isMuted() == true ) {
  933. this.volumeButton.classList.remove('swmp-mute');
  934. this._volume = this.ytplayer.getVolume() ;
  935. this.ytplayer.unMute();
  936. this.ytplayer.setVolume(this._volume);
  937. this.volumeRange.setAttribute('value', this._volume );
  938. this.volumeRange.value = this._volume;
  939. this.volumeProgress.setAttribute('value', this._volume );
  940. this.volumeProgress.value = this._volume;
  941. swmpConfig.muted = 'false';
  942. localStorage.swmpMuted = 'false';
  943. } else {
  944. this.volumeButton.classList.add('swmp-mute');
  945. this.ytplayer.mute();
  946. this.volumeRange.setAttribute('value', 0 );
  947. this.volumeRange.value = 0;
  948. this.volumeProgress.setAttribute('value', 0 );
  949. this.volumeProgress.value = 0;
  950. swmpConfig.muted = 'true';
  951. localStorage.swmpMuted = 'true';
  952. }
  953. };
  954.  
  955. this.updateVolume = (firstrun = false) => {
  956. this.volumeButton.classList.remove('swmp-mute');
  957.  
  958. if (firstrun == true) {
  959. this.volumeRange.setAttribute('value', this.defaultVolume);
  960. this.volumeProgress.setAttribute('value', this.defaultVolume);
  961. this._volume = this.defaultVolume;
  962. this.volumeButton.classList.add('swmp-min');
  963. }
  964.  
  965. this._volume = this.ytplayer.getVolume();
  966.  
  967. this.volumeRange.setAttribute('value', this.volumeRange.value );
  968. this.volumeProgress.setAttribute('value', this.volumeRange.value );
  969. this.ytplayer.setVolume(this.volumeRange.value);
  970.  
  971. if (this._volume > 50) {
  972. this.volumeButton.classList.add('swmp-max');
  973. this.volumeButton.classList.remove('swmp-med');
  974. this.volumeButton.classList.remove('swmp-min');
  975. } else if (this._volume > 10) {
  976. this.volumeButton.classList.add('swmp-med');
  977. this.volumeButton.classList.remove('swmp-max');
  978. this.volumeButton.classList.remove('swmp-min');
  979. } else if (this._volume > 5) {
  980. this.volumeButton.classList.add('swmp-min');
  981. this.volumeButton.classList.remove('swmp-max');
  982. this.volumeButton.classList.remove('swmp-med');
  983. }
  984.  
  985. localStorage.swmpVolume = this.volumeRange.value;
  986. swmpConfig.volume = this.volumeRange.value;
  987. };
  988.  
  989. this.volumeRange.oninput = (event) => {
  990. this.updateVolume();
  991. };
  992.  
  993. }
  994.  
  995.  
  996.  
  997. prepareSharedEvents() {
  998.  
  999. this.playerContainer.addEventListener('click', event => {
  1000. this.togglePlay();
  1001. });
  1002.  
  1003. this.playerContainer.addEventListener('dblclick', event => {
  1004. this.fullscreen(this.container);
  1005. });
  1006.  
  1007. this.container.addEventListener('keydown', event => {
  1008. if (event.repeat) { return; } // Don't spam
  1009.  
  1010. switch(event.key) {
  1011. case ' ': //Space
  1012. this.togglePlay();
  1013. break;
  1014. case 'f':
  1015. this.fullscreen(this.container);
  1016. break;
  1017. case 'm':
  1018. this.toggleMute();
  1019. break;
  1020. case 'x':
  1021. this.container.remove();
  1022. break;
  1023. default:
  1024. return;
  1025. }
  1026.  
  1027. event.preventDefault();
  1028.  
  1029. }, true);
  1030.  
  1031. this.container.addEventListener('fullscreenchange', event => {
  1032. const fullscreenElement =
  1033. document.fullscreenElement ||
  1034. document.mozFullScreenElement ||
  1035. document.webkitFullscreenElement ||
  1036. document.msFullscreenElement;
  1037.  
  1038. if (!fullscreenElement) {
  1039. this.container.classList.remove('swmp-fullscreen');
  1040. } else {
  1041. this.container.classList.add('swmp-fullscreen');
  1042. }
  1043. });
  1044.  
  1045. this.fullscreen = (element) => {
  1046. const fullscreenElement =
  1047. document.fullscreenElement ||
  1048. document.mozFullScreenElement ||
  1049. document.webkitFullscreenElement ||
  1050. document.msFullscreenElement;
  1051.  
  1052. if (fullscreenElement) {
  1053. if (document.exitFullscreen) {
  1054. document.exitFullscreen();
  1055. } else if (document.mozCancelFullScreen) {
  1056. document.mozCancelFullScreen();
  1057. } else if (document.webkitExitFullscreen) {
  1058. document.webkitExitFullscreen(); //Safari sucks
  1059. } else if (document.msExitFullscreen) {
  1060. document.msExitFullscreen();
  1061. }
  1062. this.container.classList.remove('swmp-fullscreen');
  1063. } else {
  1064. if (element.requestFullscreen) {
  1065. element.requestFullscreen();
  1066. } else if (element.mozRequestFullScreen) {
  1067. element.mozRequestFullScreen();
  1068. } else if (element.webkitRequestFullscreen) {
  1069. element.webkitRequestFullscreen();
  1070. } else if (element.msRequestFullscreen) {
  1071. element.msRequestFullscreen();
  1072. }
  1073. this.container.classList.add('swmp-fullscreen');
  1074. }
  1075. };
  1076. }
  1077.  
  1078. preparePlayerEvents() {
  1079.  
  1080.  
  1081. this.player.onended = (event) => {
  1082. this.player.classList.remove('swmp-playing');
  1083. this.playbutton.classList.remove('swmp-playing');
  1084. this.seeker.value = 0;
  1085. this.progress.value = 0;
  1086. clearInterval(this.player.interval);
  1087. this.currentTimer.textContent = '00:00';
  1088. };
  1089.  
  1090. this.player.addEventListener('loadedmetadata', (event) => {
  1091. this.totalTimer.textContent = this.formatSeconds(this.player.duration);
  1092. });
  1093.  
  1094.  
  1095. this.player.onplay = (event) => {
  1096. this.player.classList.add('swmp-playing');
  1097. this.playbutton.classList.add('swmp-playing');
  1098. var self = this;
  1099. this.player.interval = window.setInterval(function(event) {
  1100. self.player.timeupdate();
  1101. }, 40);
  1102. };
  1103.  
  1104. this.player.onpause = (event) => {
  1105. this.player.classList.remove('swmp-playing');
  1106. this.playbutton.classList.remove('swmp-playing');
  1107. clearInterval(this.player.interval);
  1108. };
  1109.  
  1110. this.player.onerror = (event) => {
  1111. clearInterval(this.player.interval);
  1112. var _error = this.player.error.message;
  1113. this.container.innerHTMl = _error;
  1114. };
  1115.  
  1116. this.player.timeupdate = (event) => {
  1117. this.seeker.value = Math.floor(this.player.currentTime / this.player.duration * this.seeker.max);
  1118. this.seeker.setAttribute("value", this.seeker.value);
  1119. this.progress.value = this.seeker.value;
  1120. this.progress.setAttribute("value", this.seeker.value);
  1121. this.updateTimer();
  1122. };
  1123.  
  1124. this.seeker.oninput = (event) => {
  1125. //on mousedown temporary add a mute to avoid annoying seeking sounds?
  1126. this.player.currentTime = Math.floor(this.player.duration * this.seeker.value / this.seeker.max);
  1127. this.progress.value = this.seeker.value;
  1128. this.progress.setAttribute("value", this.seeker.value);
  1129. this.updateTimer();
  1130. };
  1131.  
  1132. this.updateTimer = () => {
  1133. this.currentTimer.textContent = this.formatSeconds(this.player.currentTime);
  1134. //this.totalTimer.textContent = this.formatSeconds(this.player.duration);
  1135. };
  1136.  
  1137. this.togglePlay = () => {
  1138. if (this.player.paused ) {
  1139. this.player.play();
  1140. } else {
  1141. this.player.pause();
  1142. }
  1143. };
  1144.  
  1145. this.toggleMute = () => {
  1146. if (this.player.muted) {
  1147. this.volumeButton.classList.remove('swmp-mute');
  1148. this.player.muted = false;
  1149. this._volume = this.player.volume;
  1150. this.volumeRange.setAttribute('value', this._volume * 100 );
  1151. this.volumeRange.value = this._volume * 100;
  1152. this.volumeProgress.setAttribute('value', this._volume * 100 );
  1153. this.volumeProgress.value = this._volume * 100;
  1154. swmpConfig.muted = 'false';
  1155. localStorage.swmpMuted = 'false';
  1156. } else {
  1157. this.volumeButton.classList.add('swmp-mute');
  1158. this.player.muted = true;
  1159. this.volumeRange.setAttribute('value', 0 );
  1160. this.volumeRange.value = 0;
  1161. this.volumeProgress.setAttribute('value', 0 );
  1162. this.volumeProgress.value = 0;
  1163. swmpConfig.muted = 'true';
  1164. localStorage.swmpMuted = 'true';
  1165. }
  1166. //console.log(this._volume);
  1167. };
  1168.  
  1169. this.updateVolume = (firstrun = false) => {
  1170.  
  1171. this.volumeButton.classList.remove('swmp-mute');
  1172.  
  1173. if (firstrun == true) {
  1174. this.volumeRange.setAttribute('value', this.defaultVolume);
  1175. this.volumeProgress.setAttribute('value', this.defaultVolume);
  1176. this.volumeButton.classList.add('swmp-min');
  1177. this._volume = this.defaultVolume;
  1178.  
  1179. }
  1180.  
  1181. if (this.player.muted) {
  1182. this.player.muted = false;
  1183. }
  1184.  
  1185. this.volumeRange.setAttribute('value', this.volumeRange.value );
  1186. this.volumeProgress.setAttribute('value', this.volumeRange.value );
  1187. this.player.volume = parseFloat(this.volumeRange.value / 100);
  1188. this._volume = this.player.volume;
  1189.  
  1190. if (this._volume > 0.50) {
  1191. this.volumeButton.classList.add('swmp-max');
  1192. this.volumeButton.classList.remove('swmp-med');
  1193. this.volumeButton.classList.remove('swmp-min');
  1194. } else if (this._volume > 0.10) {
  1195. this.volumeButton.classList.add('swmp-med');
  1196. this.volumeButton.classList.remove('swmp-max');
  1197. this.volumeButton.classList.remove('swmp-min');
  1198. } else if (this._volume > 0.05) {
  1199. this.volumeButton.classList.add('swmp-min');
  1200. this.volumeButton.classList.remove('swmp-max');
  1201. this.volumeButton.classList.remove('swmp-med');
  1202. }
  1203.  
  1204. //console.log(this._volume);
  1205.  
  1206. localStorage.swmpVolume = this.volumeRange.value;
  1207. swmpConfig.volume = this.volumeRange.value;
  1208. };
  1209.  
  1210. this.volumeRange.oninput = (event) => {
  1211. this.updateVolume();
  1212. };
  1213.  
  1214. this.player.addEventListener('volumechange', this.updateVolume() );
  1215. this.updateVolume(true); // First time Run
  1216. }
  1217.  
  1218. formatSeconds = (seconds) => {
  1219. this._sec_num = parseInt(seconds, 10);
  1220. this._hours = Math.floor(this._sec_num / 3600);
  1221. this._minutes = Math.floor(this._sec_num / 60) % 60;
  1222. this._seconds = this._sec_num % 60;
  1223.  
  1224. return [this._hours,this._minutes,this._seconds]
  1225. .map(v => v < 10 ? "0" + v : v)
  1226. .filter((v,i) => v !== "00" || i > 0)
  1227. .join(":");
  1228. }
  1229.  
  1230. removeClassByPrefix(el, prefix) {
  1231. let pattern = '(' + prefix + '(\\s|(-)?(\\w*)(\\s)?)).*?';
  1232. var regEx = new RegExp(pattern, 'g');
  1233. el.className = el.className.replace(regEx, '');
  1234. }
  1235.  
  1236. uuid() {
  1237. // Source: https://www.w3resource.com/javascript-exercises/javascript-math-exercise-23.php
  1238. var dt = new Date().getTime();
  1239. var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  1240. var r = (dt + Math.random()*16)%16 | 0;
  1241. dt = Math.floor(dt/16);
  1242. return (c=='x' ? r :(r&0x3|0x8)).toString(16);
  1243. });
  1244. return uuid;
  1245. }
  1246.  
  1247. getFileName(url) {
  1248. return url.split('/').pop().split('#')[0].split('?')[0];
  1249. }
  1250.  
  1251. checkURL(url) {
  1252.  
  1253. // if match youtube regex then do this and return
  1254.  
  1255. var regex = new RegExp("^(?:https?:)?//[^/]*(?:youtube(?:-nocookie)?\.com|youtu\.be|yewtu\.be).*[=/]([-\\w]{11})(?:\\?|=|&|$)", "gmi");
  1256. var result = regex.exec(url);
  1257.  
  1258. console.log(regex);
  1259. console.log(url);
  1260. console.log(result);
  1261.  
  1262. if (url.match(regex) ) {
  1263. console.log('SWMP: YouTube');
  1264. this.type = 'youtube';
  1265. this.videoid = result[1];
  1266. console.log(this.videoid);
  1267. return true;
  1268. }
  1269.  
  1270. console.log("SWMP: Video or Audio");
  1271. // Check filename in URL for Type / MIME.
  1272. this.fileExt = url.split(/[#?&]/)[0].split('.').pop().trim();
  1273. this.fileExt = url.split(/[#?&]/).slice(-2)[0].split('.').pop().trim();
  1274. console.log(this.fileExt);
  1275. this.fileExt = this.fileExt.toLowerCase();
  1276.  
  1277. switch (this.fileExt) {
  1278. //VIDEO
  1279. case 'avi':
  1280. this.type = 'video';
  1281. this.mime = 'video/x-msvideo';
  1282. break;
  1283. case 'mpeg':
  1284. this.type = 'video';
  1285. this.mime = 'video/mpeg';
  1286. break;
  1287. case 'mpg':
  1288. this.type = 'video';
  1289. this.mime = 'video/mpeg';
  1290. break;
  1291. case 'ogv':
  1292. this.type = 'video';
  1293. this.mime = 'video/ogg';
  1294. break;
  1295. case 'mp4':
  1296. this.type = 'video';
  1297. this.mime = 'video/mp4';
  1298. break;
  1299. case 'webm':
  1300. this.type = 'video';
  1301. this.mime = 'video/webm';
  1302. break;
  1303. case 'flv':
  1304. this.type = 'video';
  1305. this.mime = 'video/x-flv';
  1306. break;
  1307. //AUDIO
  1308. case 'wav':
  1309. this.type = 'audio';
  1310. this.mime = 'audio/x-wav';
  1311. break;
  1312. case 'mp3':
  1313. this.type = 'audio';
  1314. this.mime = 'audio/mpeg';
  1315. break;
  1316. case 'm4a':
  1317. this.type = 'audio';
  1318. this.mime = 'audio/mp4';
  1319. break;
  1320. case 'mp2':
  1321. this.type = 'audio';
  1322. this.mime = 'audio/mpeg';
  1323. break;
  1324. case 'ogg':
  1325. this.type = 'audio';
  1326. this.mime = 'audio/ogg';
  1327. break;
  1328. case 'flac':
  1329. this.type = 'audio';
  1330. this.mime = 'audio/flac';
  1331. break;
  1332. default:
  1333. console.log('No matching ext/mime');
  1334. console.log(this.fileExt);
  1335. return false;
  1336. }
  1337. return true; //If valid
  1338. }
  1339.  
  1340. makeDraggable (element){
  1341. var elm = this.windowTitlebar;
  1342.  
  1343. element.style.position = 'fixed';
  1344. element.style.top = swmpConfig.positionTop + 'px';
  1345. if (swmpConfig.positionSide == 'right') {
  1346. element.style.right = swmpConfig.positionOffset + 'px';
  1347. } else {
  1348. element.style.left = swmpConfig.positionOffset + 'px';
  1349. }
  1350.  
  1351. var isMouseDown = false,
  1352. mouseX,
  1353. mouseY,
  1354. elmTop,
  1355. elmLeft,
  1356. diffX,
  1357. newElmTop,
  1358. newElmLeft,
  1359. diffY,
  1360. rightBarrier,
  1361. bottomBarrier;
  1362.  
  1363. function mouseDown(e) {
  1364.  
  1365. if (e.which == '3') {
  1366. return false; // Right click
  1367. }
  1368.  
  1369. isMouseDown = true;
  1370.  
  1371. mouseX = e.clientX;
  1372. mouseY = e.clientY;
  1373.  
  1374. elmTop = element.offsetTop;
  1375. elmLeft = element.offsetLeft;
  1376. diffX = mouseX - elmLeft;
  1377. diffY = mouseY - elmTop;
  1378.  
  1379. }
  1380.  
  1381. function mouseUp() {
  1382. isMouseDown = false;
  1383. if (swmpConfig.positionSide == 'right') {
  1384. var currentleft = parseInt(element.style.left, 10);
  1385. element.style.left = "unset";
  1386. element.style.right = document.documentElement.clientWidth - currentleft - element.offsetWidth + "px";
  1387. }
  1388. }
  1389.  
  1390. function mouseMove(e) {
  1391.  
  1392. if (!isMouseDown) return;
  1393.  
  1394. if (e.which != '1') {
  1395. isMouseDown = false;
  1396. }
  1397. var newMouseX = e.clientX;
  1398. var newMouseY = e.clientY;
  1399.  
  1400. newElmTop = newMouseY - diffY;
  1401. newElmLeft = newMouseX - diffX;
  1402.  
  1403. rightBarrier = document.documentElement.clientWidth - element.offsetWidth;
  1404. bottomBarrier = window.innerHeight - element.offsetHeight;
  1405.  
  1406. if( ( newElmLeft < 0 ) || ( newElmTop < 0) || ( newElmLeft > rightBarrier ) || (newElmTop > bottomBarrier) ) {
  1407. if ( newElmLeft < 0 ) {
  1408. newElmLeft = 0;
  1409.  
  1410. }
  1411.  
  1412. if ( newElmTop < 0) {
  1413. newElmTop = 0;
  1414.  
  1415. }
  1416. if ( newElmLeft > rightBarrier ) {
  1417. newElmLeft = rightBarrier;
  1418.  
  1419. }
  1420. if (newElmTop > bottomBarrier) {
  1421. newElmTop = bottomBarrier;
  1422. }
  1423. }
  1424.  
  1425. element.style.top = newElmTop + "px";
  1426. if (swmpConfig.positionSide == 'right') {
  1427. element.style.left = newElmLeft + "px";
  1428. element.style.right = "unset";
  1429. } else {
  1430. element.style.left = newElmLeft + "px";
  1431. }
  1432.  
  1433. }
  1434.  
  1435. document.addEventListener('mousemove', mouseMove);
  1436. elm.addEventListener('mouseup', mouseUp);
  1437. elm.addEventListener('mousedown', mouseDown);
  1438. }
  1439.  
  1440. }
  1441.  
  1442. // Above this is SWMP class.
  1443.  
  1444.  
  1445. // Below this is the event listener that checks if what you are clicking on is a video or audio that should be put into SWMP.
  1446. // If you want to make site specific changes, either expand on it or just make a completely separate event listener below it.
  1447.  
  1448. document.querySelector('body').addEventListener('click', (event) => {
  1449. //console.log("Userscript - "+event.target.tagName);
  1450.  
  1451. // If there are no links in sight, do nothing.
  1452. if (event.target.tagName != 'A' && event.target.parentNode.tagName != 'A') {
  1453. return;
  1454. }
  1455. // Lets figure out which one is the link. Wont normally chain As inside As so... Sometimes image may be inside span inside A so lets do 3x.
  1456. var clicked = false;
  1457. if (event.target.tagName == 'A') {
  1458. clicked = event.target;
  1459. } else if (event.target.parentNode.tagName == 'A') {
  1460. clicked = event.target.parentNode;
  1461. } else if (event.target.parentNode.parentNode.tagName == 'A') {
  1462. clicked = event.target.parentNode.parentNode;
  1463. }
  1464. function getVideo() {
  1465. if (backendScript == '' || backendScript == null || backendScript == undefined || backendScript == 'fourchan') {
  1466. return clicked.getAttribute('href');
  1467. } else if (backendScript == 'vichan') {
  1468. //console.log("Userscript - "+clicked.parentNode.querySelectorAll('p.fileinfo a') );
  1469. var allthelinks = clicked.parentNode.querySelectorAll('p.fileinfo a'); // Prevent capturing the (Hide) link on some installs
  1470. var matchinglink = '';
  1471. let regex = new RegExp("\.("+swmpConfig.files+")+$", 'i');
  1472. for (var i = 0; i < allthelinks.length ; i++) {
  1473. if (allthelinks[i].getAttribute('href').match(regex) ) {
  1474. matchinglink = allthelinks[i];
  1475. }
  1476. }
  1477. if (matchinglink == '') { // Kissu New UI (no thanks, but here it is anyways) <- This should also return normal href if not video?
  1478. matchinglink = clicked;
  1479. }
  1480. //console.log("Userscript - "+matchinglink);
  1481. return matchinglink.getAttribute('href');
  1482. } else {
  1483. return clicked.getAttribute('href');
  1484. }
  1485. }
  1486. function getFileNameFromUrl(link) {
  1487. if (backendScript == '' || backendScript == null || backendScript == undefined || backendScript == 'fourchan') {
  1488. return link;
  1489. } else if (backendScript == 'vichan') {
  1490. return link;
  1491. } else {
  1492. return link;
  1493. }
  1494. }
  1495. function getTitle(link) {
  1496. if (backendScript == 'fourchan') {
  1497. if (link.parentNode.querySelector('div.fileText a').getAttribute('title') ) {
  1498. return link.parentNode.querySelector('div.fileText a').getAttribute('title');
  1499. } else {
  1500. return link.parentNode.querySelector('div.fileText a').innerText;
  1501. }
  1502.  
  1503. }
  1504. if (backendScript == 'vichan') { // https://regex101.com/r/HivDYB/2
  1505. let regex = new RegExp(`([^\/|^\=|^\n|^\&])+\.(${swmpConfig.files})([^\w]|$)`, 'gi'); // Tests for www.webmaster.com/test.webm domain too.
  1506. var newlink = link.match(regex); // Remove query behind *final filename* &title for old filename on vichan will stay.
  1507. newlink.forEach(string => function(string) {
  1508. string = string.replace('/(\?|\&)+/i', '');
  1509. return string;
  1510. } ); //remove remnants of queries that i couldnt catch with a good regex
  1511. return newlink.slice(-1)[0]; // return last in array, grabs t= title for vichan player.php links.
  1512. }
  1513. }
  1514.  
  1515. var clickedHref;
  1516. var clickedFileName;
  1517. var clickedTitle;
  1518. var anythingmatch;
  1519.  
  1520. function isYoutube(link) {
  1521. var regex = new RegExp("^(?:https?:)?//[^/]*(?:youtube(?:-nocookie)?\.com|youtu\.be|yewtu\.be).*[=/]([-\\w]{11})(?:\\?|=|&|$)", "gmi");
  1522. var result = regex.exec(link);
  1523.  
  1524. if (link.match(regex) != null ) {
  1525. anythingmatch = true;
  1526. console.log('Userscript - YouTube: '+result[1]); // Video ID
  1527. clickedTitle = 'YouTube: ' + result[1];
  1528. return true;
  1529. } else {
  1530. console.log('Userscript - Not YouTube');
  1531. return false;
  1532. }
  1533. }
  1534.  
  1535. // If youtube
  1536.  
  1537. if (isYoutube(clicked.getAttribute('href') ) ) {
  1538. console.log('Userscript - YouTube link clicked');
  1539. clickedHref = clicked.getAttribute('href');
  1540. //clickedTitle = 'Loading YouTube';
  1541. }
  1542.  
  1543. function isVideo(link) {
  1544. var regex = new RegExp(`\.(${swmpConfig.files})+$`, 'gi');
  1545. var result = regex.exec(link);
  1546.  
  1547. if (link.match(regex) != null) {
  1548. anythingmatch = true;
  1549. console.log('Userscript - Video/Audio match:' + result);
  1550. return true;
  1551. } else {
  1552. console.log('Userscript - Not Video/Audio');
  1553. return false;
  1554. }
  1555. }
  1556.  
  1557. //get video links
  1558. clickedHref = getVideo();
  1559.  
  1560. if (isVideo(clickedHref) ) {
  1561. //clickedHref = getVideo();
  1562. clickedFileName = getFileNameFromUrl(clicked.getAttribute('href') );
  1563. clickedTitle = false;
  1564. if (backendScript == '' || backendScript == null || backendScript == undefined) {
  1565. clickedTitle = decodeURI(clickedFileName); //Change with other scripts later if it doesn't already contain the title.
  1566. } else if (backendScript == 'fourchan') {
  1567. clickedTitle = decodeURI(getTitle(clicked) );
  1568. } else if (backendScript == 'vichan') {
  1569. clickedTitle = decodeURI(getTitle(clicked.getAttribute('href') ) );
  1570. } else {
  1571. clickedTitle = decodeURI(clickedFileName); //Change with other scripts later if it doesn't already contain the title.
  1572. }
  1573. console.log("Userscript - URL: " + clicked.getAttribute('href') );
  1574. console.log("Userscript - Filename: " + clickedFileName );
  1575. }
  1576.  
  1577. if (anythingmatch != true) {
  1578. return false;
  1579. } else {
  1580. // If video audio or youtube matched, prevent other events.
  1581. event.preventDefault();
  1582. event.stopPropagation();
  1583. }
  1584.  
  1585.  
  1586.  
  1587. // Okay, time to load the player.
  1588. var playerid = false;
  1589.  
  1590. if (swmpConfig.allowMultiple != 'false') {
  1591. playerid = `play-swmp-${clicked.getAttribute('href')}`;
  1592. } else {
  1593. playerid = 'play-swmp';
  1594. }
  1595.  
  1596. if (typeof(document.getElementById(playerid)) != 'undefined' && document.getElementById(playerid) != null) {
  1597. if (swmpConfig.allowMultiple != 'false') {
  1598. return false; //already exists, lets do nothing.
  1599. } else {
  1600. document.getElementById(playerid).remove();
  1601. //already exists, lets get rid of it.
  1602. }
  1603. }
  1604. //console.log(playerid+clickedHref+clickedTitle);
  1605. let newembed = new swmp({
  1606. id: playerid,
  1607. url: clickedHref,
  1608. title: clickedTitle
  1609. });
  1610. //console.log(backendScript);
  1611. if (backendScript == 'jschan') { // To preserve tab to select player and enable player keybinds like close/fullscreen/pause/mute.
  1612. clicked.parentNode.parentNode.appendChild(newembed.container);
  1613. } else {
  1614. clicked.parentNode.appendChild(newembed.container);
  1615. }
  1616.  
  1617.  
  1618. }, true); // Fuck other scripts.