stream4chan

Turn those beastly 4chan threads in an easy to use, easy to watch stream of content, given to you in a gallery-like format. Batteries not included

  1. // ==UserScript==
  2. // @name stream4chan
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.3
  5. // @description Turn those beastly 4chan threads in an easy to use, easy to watch stream of content, given to you in a gallery-like format. Batteries not included
  6. // @author Lauchlan105
  7. // @match http://boards.4chan.org/*/thread/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. //////////////////
  12. // # Settings # //
  13. //////////////////
  14.  
  15. //////////////////
  16. // # Settings # //
  17. //////////////////
  18.  
  19. /*
  20.  
  21. fix resizing (again)
  22. remove (S) for shuffle
  23. change settings font color
  24. make video size change with gallery
  25. prevent arrow from being pushed outside
  26.  
  27. */
  28.  
  29. var settingsArray = [
  30. //Loop whole thread
  31. true,
  32.  
  33. //Play automatically
  34. true,
  35.  
  36. //Shuffle on startup
  37. false,
  38.  
  39. //Play Webms
  40. true,
  41.  
  42. //Show Webm controls
  43. true,
  44.  
  45. //Play webm sound
  46. true,
  47.  
  48. //Play Gifs
  49. true,
  50.  
  51. //Gif duration (Seconds)
  52. 3,
  53.  
  54. //Play Images
  55. true,
  56.  
  57. //Image duration (Seconds)
  58. 3,
  59.  
  60. //Open Stream4chan on startup
  61. true,
  62.  
  63. //Override Individual media?
  64. true,
  65.  
  66. //Select Media randomly
  67. false
  68. ];
  69.  
  70. ///////////////////
  71. // # Variables # //
  72. ///////////////////
  73.  
  74. //Placeholder variables
  75. var globalTimeout;
  76. var webm;
  77. var gif;
  78. var png;
  79. var jpg;
  80. var SOT;
  81. var EOT;
  82. var noneSelected;
  83. var allContent;
  84. var usedContent;
  85. var currentContent;
  86.  
  87. ////////////////////////////
  88. // # Media Object Model # //
  89. ////////////////////////////
  90.  
  91. class Media{
  92. constructor(thumb, source, id){
  93.  
  94. // local scope variable for object access via video/thumbnail elements
  95. var obj = this;
  96.  
  97. this.position = 0;
  98. this.playcount = 0;
  99. this.prevMedia = noneSelected;
  100. this.nextMedia = noneSelected;
  101.  
  102. this.id = id === undefined ? "" : id;
  103.  
  104. this.thumb = document.createElement('img');
  105. this.thumb.src = thumb;
  106. this.thumb.setAttribute('class','sfc-slide-preview');
  107.  
  108. this.type = mediaType(source);
  109.  
  110. this.resizeTimeout = setTimeout(function(){}, 0);
  111.  
  112. //Handles deleted files/invalid media
  113. if(this.type === undefined || this.type === null){
  114.  
  115. //Force null for simpler conditionals
  116. this.type = null;
  117.  
  118. console.log('Media could not be made. One or more of the following are invalid...');
  119. console.log(' Given Arguments:');
  120. console.log(' thumb: ' + thumb);
  121. console.log(' source: ' + source);
  122. console.log(' id: ' + id);
  123. console.log('');
  124. console.log(' All functions will be assigned placeholders');
  125.  
  126. function placeHolderFunction(calledFunction){
  127. el_stage.innerHTML = calledFunction + ' cannot be called on comment with id ' + this.id;
  128. console.log(calledFunction + ' cannot be called on object with id ' + this.id);
  129. return;
  130. }
  131.  
  132. this.unhighlight = function(){ placeHolderFunction('unhiglight'); };
  133. this.highlight = function(){ placeHolderFunction('highlight'); };
  134. this.deselect = function(){ placeHolderFunction('deselect'); };
  135. this.resize = function(){ placeHolderFunction('resize'); };
  136. this.select = function(){ placeHolderFunction('select'); };
  137. this.pause = function(){ placeHolderFunction('pause'); };
  138. this.play = function(){ placeHolderFunction('play'); };
  139. return false;
  140. }
  141.  
  142. if(this.type === webm){
  143. this.media = document.createElement('video');
  144.  
  145. this.media.setAttribute("id", "sfc-webm");
  146.  
  147. this.media.setAttribute("controls","");
  148.  
  149. this.media.setAttribute("loop","");
  150. this.media.loop = true;
  151.  
  152. this.media.setAttribute("autoplay","");
  153. this.media.autoplay = false;
  154.  
  155. this.media.setAttribute("preload","");
  156. this.media.preload = "none";
  157.  
  158. }else if(this.type === png || this.type === gif || this.type === jpg){
  159. this.media = document.createElement('img');
  160. this.media.setAttribute("id","sfc-img");
  161. this.media.alt = source;
  162. }
  163.  
  164. this.media.setAttribute("class", "sfc-media");
  165. this.media.src = source;
  166.  
  167.  
  168. ///////////////////
  169. //MEDIA FUNCTIONS//
  170. ///////////////////
  171. this.play = function(){
  172.  
  173. if(obj !== SOT && obj !== EOT && obj !== noneSelected){
  174. if(obj.type == webm){
  175. obj.media.volume = op_playSound.checked ? obj.media.volume : 0;
  176. obj.media.play();
  177. }else if(obj.type == gif || obj.type == jpg || obj.type == png){
  178. obj.media.src = obj.media.alt;
  179. updateAutoplay();
  180. }
  181.  
  182. }
  183. };
  184.  
  185. this.pause = function(){
  186.  
  187. clearTimeout(globalTimeout);
  188. if(obj.type == webm){
  189. obj.media.pause();
  190. }else if(obj.type == gif || obj.type == jpg || obj.type == png){
  191. obj.media.src = obj.thumb.src;
  192. }
  193. };
  194.  
  195. ///////////////////////
  196. //THUMBNAIL FUNCTIONS//
  197. ///////////////////////
  198.  
  199. var highlight = function(){
  200.  
  201. obj.thumb.style.border = "2px solid gainsboro";
  202.  
  203. /*
  204.  
  205. //Scroll into view
  206. var pc = document.getElementById('pc' + id.substr(id.lastIndexOf('p') + 1));
  207. pc.scrollIntoView();
  208.  
  209. //Set container style to mimic focused content
  210. pc.style.background = '#f0e0d6';
  211. pc.style.border = '1px solid #D99F91!important';
  212.  
  213. */
  214. };
  215.  
  216. var unhighlight = function(){
  217.  
  218. obj.thumb.style.border = "2px solid transparent";
  219.  
  220. /*
  221.  
  222. //Remove focused content styles
  223. var pc = document.getElementById('pc' + id.substr(id.lastIndexOf('p') + 1));
  224. pc.style.background = '#F0C0B0!important';
  225. pc.style.border = '1px solid #D9BFB7';
  226.  
  227. */
  228. };
  229.  
  230. ///////////////////////////
  231. //MISCELLANEOUS FUNCTIONS//
  232. ///////////////////////////
  233.  
  234. this.select = function(){
  235.  
  236. obj.playcount++;
  237.  
  238. //Deselect active content
  239. if(currentContent !== null){
  240. currentContent.deselect();
  241. }
  242.  
  243. //Set currentContent to this object
  244. currentContent = obj;
  245.  
  246. //Highlight thumbnail border
  247. highlight();
  248.  
  249. //Add media to stage
  250. obj.media.controls = op_controls.checked;
  251. el_stage.appendChild(obj.media);
  252.  
  253. //Play Media
  254. obj.play();
  255.  
  256. //resize media
  257. obj.resize();
  258.  
  259. //Update auto playing
  260. updateAutoplay();
  261.  
  262. //Moves gallery so selected object is centered
  263. obj.slideGallery();
  264. //Update counter in top right
  265. updateCounter();
  266.  
  267. //Assign and load neighbouring media
  268. obj.getNeighbours();
  269.  
  270. if(obj.prevMedia.type === webm){
  271. obj.prevMedia.media.load();
  272. }else if(obj.nextMedia.type === webm){
  273. obj.nextMedia.media.load();
  274. }
  275. };
  276.  
  277. this.deselect = function(){
  278. obj.pause();
  279. obj.media.currentTime = 0;
  280. unhighlight();
  281. el_stage.innerHTML = "";
  282. currentContent = null;
  283.  
  284. //Reset prev and next media
  285. obj.prevMedia = noneSelected;
  286. obj.nextMedia = noneSelected;
  287. };
  288.  
  289. this.thumb.onclick = function(){
  290. if(obj.thumb.style.border == "2px solid gainsboro"){
  291. obj.deselect();
  292. }else{
  293. obj.select();
  294. }
  295. };
  296.  
  297. this.resize = function(){
  298.  
  299. clearTimeout(obj.resizeTimeout);
  300.  
  301. //Recursive resize function
  302. //repeats every {interval} seconds
  303. //if interval is undefined or < 0.01, default to 0.01
  304. function execResize(interval){
  305.  
  306. //if interval is below 0.01, set to minimum 0.01
  307. if(interval){
  308. if(interval < 0.05)
  309. interval = 0.05;
  310. }
  311.  
  312. if(isShown(el_sfc)){
  313. if(obj === currentContent){
  314. var setByWidth = true;
  315.  
  316. console.log('resizing');
  317.  
  318. //Set to max width
  319. obj.media.style.width = window.innerWidth - (el_stagePrev.clientWidth + el_stageNext.clientWidth) + 'px';
  320. obj.media.style.height = 'auto';
  321.  
  322. //if media height exceeds the stage height
  323. if(obj.media.clientHeight > el_stage.clientHeight){
  324. //Set to max height instead
  325. obj.media.style.height = el_stage.clientHeight + 'px';
  326. obj.media.style.width = 'auto';
  327. setByWidth = false;
  328. }
  329.  
  330. if(setByWidth){
  331. // if full width, set height padding
  332. obj.media.style.marginLeft = '0px';
  333. var difHeight = (el_stage.clientHeight - obj.media.clientHeight)/2;
  334. var topMarg = (difHeight) - ( difHeight%1 ); //Minus any decimals
  335. obj.media.style.marginTop = topMarg + 'px';
  336. }else{
  337. // if full height, set width padding
  338. obj.media.style.marginTop = '0px';
  339. var difWidth = (el_stage.clientWidth - obj.media.clientWidth)/2;
  340. var leftMarg = (difWidth) - ( difWidth%1 ); //Minus any decimals
  341. obj.media.style.marginLeft = leftMarg + 'px';
  342. }
  343.  
  344. if(interval){
  345. obj.resizeTimeout = setTimeout(function(){
  346. execResize(interval);
  347. }, interval*1000);
  348. }else{
  349. return;
  350. }
  351. }else{
  352. clearTimeout(obj.resizeTimeout);
  353. }
  354. }else{
  355. clearTimeout(obj.resizeTimeout);
  356. }
  357. return;
  358. }
  359.  
  360. //Continue resizing every 1 second till video ends
  361. execResize(0.2);
  362.  
  363. };
  364.  
  365. this.slideGallery = function(){
  366.  
  367. //resets slider transform
  368. el_internalSlider.style.transform = '';
  369.  
  370. //distance from the left of the browser to the middle of the thumbnail
  371. var middleOfThumb = getPageTopLeft(obj.thumb).left + (obj.thumb.clientWidth*1.5);
  372.  
  373. //The distance between left side of browser and vertical middle of browser
  374. var middleOfWindow = window.innerWidth/2;
  375.  
  376. //Displacement from middle
  377. var displacement = middleOfWindow - middleOfThumb;
  378.  
  379. //Include the current position of the internal slider
  380. var crntSliderValue = getPageTopLeft(el_internalSlider).left;
  381. displacement += crntSliderValue;
  382.  
  383. //Apply change
  384. el_internalSlider.style.transform = 'translateX( ' + displacement + 'px)';
  385.  
  386. }
  387.  
  388. this.previous = function(){
  389. obj.prevMedia.select();
  390. }
  391.  
  392. this.next = function(){
  393. obj.nextMedia.select();
  394. }
  395.  
  396. this.getNeighbours = function(){
  397. if(!canPlay(obj)){
  398. //If the current object isn't in usedContent -> prev = last in used content, next = first in used content
  399. obj.prevMedia = usedContent[usedContent.length - 1].select();
  400. obj.nextMedia = usedContent[0].select();
  401. }else if(op_random.checked){
  402. obj.prevMedia = getRandom();
  403. obj.nextMedia = getRandom(obj.prevMedia);
  404. }else if(op_loopAll.checked){
  405. obj.prevMedia = obj.position === 0 ? usedContent[usedContent.length - 1] : usedContent[obj.position - 1];
  406. obj.nextMedia = obj.position === usedContent.length - 1 ? usedContent[0] : usedContent[obj.position + 1];
  407. }else if(!op_loopAll.checked){
  408. obj.prevMedia = obj.position === 0 ? obj : usedContent[obj.position - 1];
  409. obj.nextMedia = obj.position === usedContent.length - 1 ? obj : usedContent[obj.position + 1];
  410. }
  411. }
  412.  
  413. this.media.getObj = function(){ return obj; };
  414. this.thumb.getObj = function(){ return obj; };
  415.  
  416. return true;
  417. }
  418. }
  419.  
  420. //////////////
  421. // # Main # //
  422. //////////////
  423.  
  424. (function(){
  425.  
  426. //Removes default padding
  427. document.body.style.padding = "0px";
  428.  
  429. insertElements();
  430. initVars();
  431. initPlaceholders();
  432. startInteractions();
  433. startEventListeners();
  434. var validSettings = applyDefaulSettings();
  435. if( !validSettings.valid ){
  436. console.log(validSettings.valid);
  437. for(var i = 0; i < validSettings.messages.length; i++){
  438. console.log(validSettings.messages[i]);
  439. el_stage.innerHTML = '<h1>' + validSettings.messages[i] + '</h1>';
  440. }
  441. return;
  442. }
  443.  
  444. //Show and hide gallery to force load thumbnails
  445. //without this .select() does not work until gallery is shown
  446. showGallery(true);
  447. showGallery(false);
  448.  
  449. magicMouse(false);
  450. sfc.style.display = "none"; //Force hide SFC
  451. showSFC(false); //Force styles to be ready for fade in
  452.  
  453. //If shuffled on start up -> shuffle
  454. if(settingsArray[2]){
  455. shuffle(true);
  456. }
  457.  
  458. //If open on startup is selected -> open on startup
  459. if(settingsArray[10]){
  460. showSFC(true);
  461. usedContent[0].select();
  462. }
  463. })();
  464.  
  465. /////////////////////////////////
  466. // # Initial Setup Functions # //
  467. /////////////////////////////////
  468.  
  469. //Insert html for buttons and modal
  470. function insertElements(){
  471.  
  472. //Start Button
  473. var btn1 = '[<a id="sfc-start" href="#">start</a>]';
  474. var btn2 = '[<a id="sfc-resume" href="#">resume</a>]';
  475.  
  476. //Add span to nav
  477. var nav = document.getElementsByClassName('navLinks desktop');
  478. for(var i = 0; i < nav.length; i++){
  479. var span = document.createElement('span');
  480. span.innerHTML = btn1 + " " + btn2;
  481.  
  482. span.className = 'sfc-nav';
  483. span.style.display = nav[i].style.display;
  484.  
  485. nav[i].parentNode.insertBefore(span, nav[i]);
  486. nav[i].parentNode.insertBefore(document.getElementById('op'), nav[i]);
  487. }
  488.  
  489. var html = '<!-- SETTINGS --> <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet"> <div id="sfc-settings-column"> <div id="sfc-settings-row"> <div id="sfc-settings-panel"> Settings <div id="sfc-settings-exit"></div> <!-- GENERAL SETTINGS --> <div> <p class="sfc-settings-title"> General </p> <div id="sfc-option"> <input id="stream4chan-loopAll" class="SFC-input" type="checkbox"> Loop whole thread (L) </div> <div id="sfc-option"> <input id="stream4chan-auto" class="SFC-input" type="checkbox"> Play Automatically (A) </div> <div id="sfc-option"> <input id="stream4chan-random" class="SFC-input" type="checkbox"> Random (R) </div> <div id="sfc-option"> <input id="stream4chan-shuffle" class="SFC-input" type="button" value=" Shuffle "> (S) </div> </div> <!-- WEBM SETTINGS --> <div> <p class="sfc-settings-title"> Webm </p> <div id="sfc-option"> <input id="stream4chan-webms" class="SFC-input" type="checkbox"> Play Webms (W) </div> <div id="sfc-option"> <input id="stream4chan-controls" class="SFC-input" type="checkbox"> Show Controls (C) </div> <div id="sfc-option"> <input id="stream4chan-playSound" class="SFC-input" type="checkbox"> Play sound (S) </div> </div> <!-- GIF SETTINGS --> <div> <p class="sfc-settings-title"> Gif </p> <div id="sfc-option"> <input id="stream4chan-gifs" class="SFC-input" type="checkbox"> Play Gifs (G) </div> <div id="sfc-option"> <input id="stream4chan-gif-duration" class="SFC-input" type="number" min="1" max="60" value="3" step="1"> Gif Duration (up/down) </div> </div> <!-- IMG SETTINGS --> <div> <p class="sfc-settings-title"> Images </p> <div id="sfc-option"> <input id="stream4chan-imgs" class="SFC-input" type="checkbox"> Play Images (I) </div> <div id="sfc-option"> <input id="stream4chan-img-duration" class="SFC-input" type="number" min="1" max="60" value="3" step="1"> Image Duration (Shift + up/down) </div> </div> </div> </div> </div> <!-- Main --> <div id="sfc-main"> <div id="sfc-main-prev" class="sfc-util prev"></div> <div id="sfc-stage"> </div> <div id="sfc-utility"> <div id="sfc-main-settings" class="sfc-util settings"></div> <div id="sfc-main-gallery" class="sfc-util gallery"></div> </div> <div id="sfc-counter"> <p id="sfc-counter-first">1</p> / <p id="sfc-counter-second">2</p> </div> <div id="sfc-main-next" class="sfc-util next"></div> </div> <!-- Gallery Slider --> <div id="sfc-gallery"> <div id="sfc-gallery-prev" class="sfc-util prev"></div> <div id="sfc-slider"> <div id="sfc-slider-internal"> </div> </div> <div id="sfc-gallery-next" class="sfc-util next"></div> </div> <!-- Hidden Cursor Overlay --> <span id="sfc-magicMouse"> </span>';
  490. // var html = '<!-- SETTINGS --> <div id="sfc-settings-column"><div id="sfc-settings-row"> <div id="sfc-settings-panel"> Settings <div id="sfc-settings-exit"></div> <!-- GENERAL SETTINGS --> <div> <p class="sfc-settings-title"> General </p> <div id="sfc-option"> <input id="stream4chan-loopAll" class="SFC-input" type="checkbox"> Loop whole thread (L) </div> <div id="sfc-option"> <input id="stream4chan-auto" class="SFC-input" type="checkbox"> Play Automatically (A) </div> <div id="sfc-option"> <input id="stream4chan-random" class="SFC-input" type="checkbox"> Random (R) </div> <div id="sfc-option"> <input id="stream4chan-shuffle" class="SFC-input" type="button" value=" Shuffle "> (S) </div> </div> <!-- WEBM SETTINGS --> <div> <p class="sfc-settings-title"> Webm </p> <div id="sfc-option"> <input id="stream4chan-webms" class="SFC-input" type="checkbox"> Play Webms (W) </div> <div id="sfc-option"> <input id="stream4chan-controls" class="SFC-input" type="checkbox"> Show Controls (C) </div> <div id="sfc-option"> <input id="stream4chan-playSound" class="SFC-input" type="checkbox"> Play sound (S) </div> </div> <!-- GIF SETTINGS --> <div> <p class="sfc-settings-title"> Gif </p> <div id="sfc-option"> <input id="stream4chan-gifs" class="SFC-input" type="checkbox"> Play Gifs (G) </div> <div id="sfc-option"> <input id="stream4chan-gif-duration" class="SFC-input" type="number" min="1" max="60" value="3" step="1"> Gif Duration (up/down) </div> </div> <!-- IMG SETTINGS --> <div> <p class="sfc-settings-title"> Images </p> <div id="sfc-option"> <input id="stream4chan-imgs" class="SFC-input" type="checkbox"> Play Images (I) </div> <div id="sfc-option"> <input id="stream4chan-img-duration" class="SFC-input" type="number" min="1" max="60" value="3" step="1"> Image Duration (Shift + up/down) </div> </div> </div> </div> </div> <!-- Main --> <div id="sfc-main"> <div id="sfc-main-prev" class="sfc-util prev"></div> <div id="sfc-stage"> </div> <div id="sfc-utility"> <div id="sfc-main-settings" class="sfc-util settings"></div> <div id="sfc-main-gallery" class="sfc-util gallery"></div> </div> <div id="sfc-main-next" class="sfc-util next"></div> </div> <!-- Gallery Slider --> <div id="sfc-gallery"> <div id="sfc-gallery-prev" class="sfc-util prev"></div> <div id="sfc-slider"> <div id="sfc-slider-internal"> </div> </div> <div id="sfc-gallery-next" class="sfc-util next"></div> </div>';
  491. // var css = '<!-- CSS --> <style> body, div, img, a, span, html, p{ margin: 0px; border: 0px; padding: 0px; } .sfc-nav{ height: auto; width: auto; } #sfc{ opacity: 1; -moz-transition: opacity 0.50s ease-in-out; -webkit-transition: opacity 0.50s ease-in-out; -o-transition: opacity 0.50s ease-in-out; -ms-transition: opacity 0.50s ease-in-out; transition: opacity 0.50s ease-in-out; } #sfc-main { width: 100%; height: 100%; z-index: 500; position: fixed; top: 0; display: flex; flex-flow: row; background-color: rgba(0,0,0,0.9 ); -webkit-box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); -moz-box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); } #sfc-gallery{ width: 100%; height: auto; z-index: 1000; position: fixed; bottom: 0; display: flex; flex-flow: row; background-color: rgba(0,0,0,0.35); transform: translateY(100%); -webkit-transition: transform 0.4s ease-in-out 0.05s; transition: transform 0.4s ease-in-out 0.05s; } #sfc-settings-column{ height: 100vh; z-index: 1500; top: 0; display: none; flex-flow: column; position: fixed; background-color: rgba(0,0,0,0.7); font-family: "PT Sans", sans-serif; font-size: 16pt; } #sfc-settings-row{ width: 100vw; display: flex; flex-flow: row; flex: 1 1 auto; } #sfc-settings-panel{ margin: auto; padding: 20px; flex: 0 1 auto; background-color: rgba(255,255,255,0.8); } .sfc-settings-title{ margin-top: 7%; text-decoration: underline; } #sfc-settings-exit { width: 20px; height: 20px; opacity: 0.3; float: right; background-image: url("http://www.myiconfinder.com/uploads/iconsets/4c515d45f6a8c4fe16e448a692a9370d.png"); background-size: contain; -webkit-transition: opacity 0.2s linear 0.05s, visibility 0s; transition: opacity 0.2s linear 0.05s, visibility 0s; } #sfc-settings-exit:hover{ opacity: 1; } #sfc-stage, #sfc-slider{ flex: 1 1 auto; margin: 5px; } #sfc-slider-internal{ width: 0px; } #sfc-utility{ position: fixed; bottom: 0; -webkit-transition: transform 0.4s ease-in-out 0.05s; transition: transform 0.4s ease-in-out 0.05s; } .sfc-util{ opacity: 0.1; width: 3vw; height: 3vw; background-color: rgba(255,255,255,0.6); background-clip: content-box; border-radius: 50%; background-size: 1.5vw; background-repeat: no-repeat; background-position:center; margin: auto; padding: 5px; -webkit-transition: opacity 0.2s linear 0.05s, visibility 0s; transition: opacity 0.2s linear 0.05s, visibility 0s; } .sfc-util:hover{ opacity: 1; } .gallery, .settings{ background-size: 2.2vw; } .gallery{ background-image: url("https://maxcdn.icons8.com/Android_L/PNG/24/Photo_Video/gallery-24.png"); } .settings{ background-image: url("https://maxcdn.icons8.com/Android_L/PNG/24/Very_Basic/settings-24.png"); } .prev, .next { flex: 0 0 3vw; background-image: url("http://www.dsetechnology.co.uk/images/disclose-arrow.png"); } .prev{ transform: rotate(180deg); } .sfc-slide-preview{ height: 6vh; width: auto; border: 2px solid transparent; -webkit-transition: border 0.2s linear 0.05s, visibility 0s; transition: border 0.2s linear 0.05s, visibility 0s; } .sfc-slide-preview:hover { border: 2px solid white; } </style> ';
  492. var css = '<style> <!-- CSS --> <style> body, div, img, a, span, html, p{ margin: 0px; border: 0px; padding: 0px; } #sfc-magicMouse { z-index: 1000; position: fixed; top: 0px; height: 100vh; width: 100vw; cursor: none; } #sfc-counter { min-height: ; position: fixed; top: 0px; right: 0px; color: rgba(255,255,255,0.3); font-family: \'Montserrat\', sans-serif; } #sfc-counter p { display: inline; } .sfc-nav{ height: auto; width: auto; } #sfc{ opacity: 1; -moz-transition: opacity 0.50s ease-in-out; -webkit-transition: opacity 0.50s ease-in-out; -o-transition: opacity 0.50s ease-in-out; -ms-transition: opacity 0.50s ease-in-out; transition: opacity 0.50s ease-in-out; } #sfc-main { width: 100%; height: 100%; z-index: 500; position: fixed; top: 0; display: flex; flex-flow: row; background-color: rgba(0,0,0,0.9 ); -webkit-box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); -moz-box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); box-shadow: inset 0px 0px 300px 28px rgba(0,0,0,1); } #sfc-gallery{ width: 100%; height: auto; z-index: 1000; position: fixed; bottom: 0; display: flex; flex-flow: row; background-color:rgba(0,0,0,0.35); transform: translateY(100%); -webkit-transition: transform 0.4s ease-in-out 0.05s; transition: transform 0.4s ease-in-out 0.05s; } #sfc-settings-column{ height: 100vh; z-index: 1500; top: 0; display: none; flex-flow: column; position: fixed; background-color: rgba(0,0,0,0.7); font-family: "PT Sans", sans-serif; font-size: 16pt; } #sfc-settings-row{ width: 100vw; display: flex; flex-flow: row; flex: 1 1 auto; } #sfc-settings-panel{ margin: auto; padding: 20px; flex: 0 1 auto; background-color: rgba(255,255,255,0.8); } .sfc-settings-title{ margin-top: 7%; text-decoration: underline; } #sfc-settings-exit { width: 20px; height: 20px; opacity: 0.3; float: right; background-image: url("http://www.myiconfinder.com/uploads/iconsets/4c515d45f6a8c4fe16e448a692a9370d.png"); background-size: contain; -webkit-transition: opacity 0.2s linear 0.05s, visibility 0s; transition: opacity 0.2s linear 0.05s, visibility 0s; } #sfc-settings-exit:hover{ opacity: 1; } #sfc-stage, #sfc-slider{ flex: 1 1 auto; margin: 5px; } #sfc-slider-internal{ width: 0px; -webkit-transition: transform 0.4s ease-in-out 0.05s; transition: transform 0.4s ease-in-out 0.05s; } #sfc-utility{ position: fixed; bottom: 0; -webkit-transition: transform 0.4s ease-in-out 0.05s; transition: transform 0.4s ease-in-out 0.05s; } .sfc-util{ opacity: 0.1; width: 3vw; height: 3vw; background-color: rgba(255,255,255,0.6); background-clip: content-box; border-radius: 50%; background-size: 1.5vw; background-repeat: no-repeat; background-position:center; margin: auto; padding: 5px; -webkit-transition: opacity 0.2s linear 0.05s, visibility 0s; transition: opacity 0.2s linear 0.05s, visibility 0s; } .sfc-util:hover{ opacity: 1; } .gallery, .settings{ background-size: 2.2vw; } .gallery{ background-image: url("https://maxcdn.icons8.com/Android_L/PNG/24/Photo_Video/gallery-24.png"); } .settings{ background-image: url("https://maxcdn.icons8.com/Android_L/PNG/24/Very_Basic/settings-24.png"); } .prev, .next { flex: 0 0 3vw; background-image: url("http://www.dsetechnology.co.uk/images/disclose-arrow.png"); } .prev{ transform: rotate(180deg); } .sfc-slide-preview{ height: 6vh; width: auto; border: 2px solid transparent; -webkit-transition: border 0.2s linear 0.05s, visibility 0s; transition: border 0.2s linear 0.05s, visibility 0s; } .sfc-slide-preview:hover { border: 2px solid white; } </style>';
  493.  
  494. var sfc = document.createElement('div');
  495. sfc.setAttribute('id','sfc');
  496. sfc.innerHTML = html + css;
  497.  
  498. var target = document.getElementsByClassName('thread');
  499. for(i = 0; i < target.length; i++){
  500. target[i].prepend(sfc);
  501. }
  502. }
  503.  
  504. //Create and initialize global variables for easy access to HTML elements
  505. function initVars(){
  506.  
  507. //Custom function to find elements while
  508. //alerting console of errors in case of null || undefined
  509. function getEl(elName){
  510. var temp = document.getElementById(elName);
  511. if(temp === null || temp === undefined){
  512. temp = document.getElementsByClassName(elName)[0];
  513. if(temp === null || temp === undefined){
  514. console.log('### ERROR ###');
  515. console.log('initVars: getEl(\'' + elName +'\') returned... ');
  516. console.log(temp);
  517. }
  518. }
  519. return temp;
  520. }
  521.  
  522. //Main Page
  523. el_startBtn = getEl('sfc-start');
  524. el_resumeBtn = getEl('sfc-resume');
  525.  
  526. //Modal
  527. el_sfc = getEl('sfc');
  528. el_magicMouse = getEl('sfc-magicMouse');
  529.  
  530. //Stage Area
  531. el_stage = getEl('sfc-stage');
  532. el_stagePrev = getEl('sfc-main-prev');
  533. el_stageNext = getEl('sfc-main-next');
  534.  
  535. //Utility buttons
  536. el_util = getEl("sfc-utility");
  537. el_galleryBtn = getEl("sfc-main-gallery");
  538. el_settingsBtn = getEl("sfc-main-settings");
  539. el_counter_first = getEl("sfc-counter-first");
  540. el_counter_second = getEl("sfc-counter-second");
  541.  
  542. //Gallery Area
  543. el_gallery = getEl("sfc-gallery");
  544. el_slider = getEl('sfc-slider');
  545. el_internalSlider = getEl('sfc-slider-internal');
  546. el_internalSlider.style.transform = "translateX(0px)";
  547. el_sliderPrev = getEl('sfc-gallery-prev');
  548. el_sliderNext = getEl('sfc-gallery-next');
  549.  
  550. //Settings and option area
  551. el_settings = getEl("sfc-settings-column");
  552. el_settingsExit = getEl("sfc-settings-exit");
  553. op_loopAll = getEl('stream4chan-loopAll');
  554. op_auto = getEl('stream4chan-auto');
  555. op_random = getEl('stream4chan-random');
  556. op_shuffle = getEl('stream4chan-shuffle');
  557.  
  558. op_webms = getEl('stream4chan-webms');
  559. op_controls = getEl('stream4chan-controls');
  560. op_playSound = getEl('stream4chan-playSound');
  561.  
  562. op_gifs = getEl('stream4chan-gifs');
  563. op_gif_duration = getEl('stream4chan-gif-duration');
  564.  
  565. op_imgs = getEl('stream4chan-imgs');
  566. op_img_duration = getEl('stream4chan-img-duration');
  567. }
  568.  
  569. //Inititialize values to placeholder variables
  570. function initPlaceholders(){
  571. //Type placeholders. Less quotations in code
  572. webm = 'webm';
  573. gif = 'gif';
  574. png = 'png';
  575. jpg ='jpg';
  576.  
  577. //Start of thread
  578. //Object based placeholder for the beginning of the thread (used when loopAll is unchecked)
  579. SOT = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
  580. SOT.media.src = "https://dummyimage.com/1920x1080/000000/ffffff.png&text=Start+of+thread";
  581. SOT.thumb.src = "https://dummyimage.com/480x270/000000/ffffff.png&text=Start+of+thread";
  582. SOT.type = "SOT";
  583.  
  584. //End of thread
  585. //Object based placeholder for the end of the thread (used when loopAll is unchecked)
  586. EOT = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
  587. EOT.media.src = "https://dummyimage.com/1920x1080/000000/ffffff.png&text=End+of+thread";
  588. EOT.thumb.src = "https://dummyimage.com/480x270/000000/ffffff.png&text=End+of+thread";
  589. EOT.type = "EOT";
  590.  
  591. //Object based placeholder for when there is no applicable media found or nothing is selected
  592. noneSelected = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
  593. noneSelected.media.src = "https://dummyimage.com/1920x1080/000000/ffffff.png&text=No+Media+Selected";
  594. noneSelected.thumb.src = "https://dummyimage.com/480x270/000000/ffffff.png&text=No+Media+Selected";
  595.  
  596. allContent = getContent();
  597. usedContent = getUsedContent();
  598. currentContent = noneSelected;
  599. }
  600.  
  601. //Links keyboard controls with functions and updates sfc accordinglys
  602. function startEventListeners(){
  603.  
  604. window.onresize = function(){
  605. // updateGallery();
  606. currentContent.resize();
  607. currentContent.slideGallery();
  608. }
  609.  
  610. op_loopAll.onchange = updateGallery;
  611. op_controls.onchange = function(){
  612. if(currentContent.type == webm){
  613. currentContent.media.controls = op_controls.checked;
  614. }
  615. };
  616.  
  617. op_playSound.onchange = function(){
  618. if(currentContent.type == webm){
  619. currentContent.media.volume = op_playSound.checked ? 1 : 0;
  620. }
  621. };
  622.  
  623. op_webms.onchange = updateGallery;
  624. op_gifs.onchange = updateGallery;
  625. op_imgs.onchange = updateGallery;
  626. op_auto.onchange = updateAutoplay;
  627. op_shuffle.onclick = shuffle;
  628.  
  629. el_stagePrev.onclick = previous;
  630. el_stageNext.onclick = next;
  631.  
  632. document.onkeydown = function(event){
  633. switch(event.keyCode){
  634.  
  635. //Space Key
  636. case 32:
  637. if(currentContent !== null){
  638. //if paused
  639. if(currentContent.type == webm){
  640. if(currentContent.media.paused){
  641. currentContent.play();
  642. }else{
  643. currentContent.pause();
  644. }
  645. }else{
  646. if(currentContent.media.src == currentContent.thumb.src){
  647. currentContent.play();
  648. }else{
  649. currentContent.pause();
  650. }
  651. }
  652. }
  653. break;
  654.  
  655. //Enter Key
  656. case 13:
  657. if(event.ctrlKey)
  658. return;
  659.  
  660. if(!isShown(el_sfc)){
  661.  
  662. if(event.altKey){
  663. el_resumeBtn.onclick();
  664. }else{
  665. el_startBtn.onclick();
  666. }
  667.  
  668. }
  669. break;
  670.  
  671. //Esc Key
  672. case 27:
  673. if(isShown(el_settings)){
  674. showSettings(false);
  675. }else if(isShown(el_gallery)){
  676. showGallery(false);
  677. }else if(isShown(el_sfc)){
  678. showSFC(false);
  679. }
  680. break;
  681.  
  682. //Left arrow Key
  683. case 37:
  684. if(!event.altKey)
  685. previous();
  686. break;
  687.  
  688. //Right arrow Key
  689. case 39:
  690. if(!event.altKey)
  691. next();
  692. break;
  693.  
  694. //Up arrow Key
  695. case 38:
  696. if(event.shiftKey){
  697. op_img_duration.value++;
  698. }else{
  699. op_gif_duration.value++;
  700. }
  701. break;
  702.  
  703. //Down arrow Key
  704. case 40:
  705. if(event.shiftKey){
  706. op_img_duration.value--;
  707. }else{
  708. op_gif_duration.value--;
  709. }
  710. break;
  711.  
  712. //L Key
  713. case 76:
  714. op_loopAll.checked = !op_loopAll.checked;
  715. op_loopAll.onchange();
  716. break;
  717.  
  718. //A Key
  719. case 65:
  720. op_auto.checked = !op_auto.checked;
  721. op_auto.onchange();
  722. break;
  723.  
  724. //R Key
  725. case 82:
  726. if(!event.ctrlKey)
  727. op_random.checked = !op_random.checked;
  728. break;
  729.  
  730. //Q Key
  731. case 81:
  732. break;
  733.  
  734. //S Key
  735. case 83:
  736. op_playSound.checked = !op_playSound.checked;
  737. op_playSound.onchange();
  738. break;
  739.  
  740. //W Key
  741. case 87:
  742. op_webms.checked = !op_webms.checked;
  743. op_webms.onchange();
  744. break;
  745.  
  746. //C Key
  747. case 67:
  748. op_controls.checked = !op_controls.checked;
  749. op_controls.onchange();
  750. break;
  751.  
  752. //G Key
  753. case 71:
  754. op_gifs.checked = !op_gifs.checked;
  755. op_gifs.onchange();
  756. break;
  757.  
  758. //I Key
  759. case 73:
  760. op_imgs.checked = !op_imgs.checked;
  761. op_imgs.onchange();
  762. break;
  763.  
  764. //Backspace
  765. case 8:
  766. settingsArray[11] = !settingsArray[11];
  767. break;
  768.  
  769. //Print what was typed into console
  770. default:
  771. var temp = "";
  772. if(event.shiftKey){
  773. temp += "Shift + ";
  774. }
  775.  
  776. if(event.altKey){
  777. temp += "Alt + ";
  778. }
  779.  
  780. if(event.ctrlKey){
  781. temp += "Ctrl + ";
  782. }
  783.  
  784. temp += event.keyCode;
  785.  
  786. console.log(temp);
  787. }
  788. }
  789.  
  790. el_gallery.onwheel = function(event){
  791.  
  792. //This number, when used with
  793. // if(event.wheelDelta > 0 ){
  794. // }else{
  795. // console.log('scrolling down');
  796. // el_internalSlider.style.transform = 'translateX(' + (getPageTopLeft(el_internalSlider).left) + 'px)';
  797. // }
  798. }
  799.  
  800. el_stage.onclick = function(e) {
  801. if (e.target === this){
  802. currentContent.pause();
  803. showSFC(false);
  804. return;
  805. }
  806. };
  807. }
  808.  
  809. //////////////////////////////////////////////
  810. // # SFC Control and Animation Functions # //
  811. //////////////////////////////////////////////
  812.  
  813. //Links functions with page controllers
  814. //eg: making gallery button show/hide gallery
  815. function startInteractions(){
  816.  
  817. //Apply functionality: click start to show modal and play first media item
  818. el_startBtn.onclick = function(){
  819. showSFC(true);
  820. if(op_auto.checked){
  821. usedContent[0].select();
  822. }
  823. };
  824.  
  825. el_resumeBtn.onclick = function(){
  826. showSFC(true);
  827. currentContent.resize();
  828. currentContent.play();
  829. updateAutoplay();
  830. };
  831.  
  832. //Apply functionality: click gallery button to show/hide gallery
  833. el_gallery.style.transform = "translateY(100%)";
  834. el_galleryBtn.onclick = showGallery;
  835.  
  836. //Apply functionality: click settings button to show settings
  837. //Click exit button to exit settings
  838. el_settingsBtn.onclick = function(){ showSettings(true); };
  839. el_settingsExit.onclick = function(){ showSettings(false); };
  840. }
  841.  
  842. //Applies default settings
  843. // • Default settings are on line 5
  844. function applyDefaulSettings(){
  845.  
  846. var messages = Array();
  847.  
  848. //Loop whole thread
  849. if(settingsArray[0] !== true && settingsArray[0] !== false){
  850. messages.push("Loop whole thread");
  851. }else{
  852. op_loopAll.checked = settingsArray[0];
  853. }
  854.  
  855. //Play automatically
  856. if(settingsArray[1] !== true && settingsArray[1] !== false){
  857. messages.push("Play automatically");
  858. }else{
  859. op_auto.checked = settingsArray[1];
  860. }
  861.  
  862. //Shuffle on startup
  863. if(settingsArray[2] !== true && settingsArray[2] !== false){
  864. messages.push("Shuffle on startup");
  865. }else{
  866. op_shuffle.value = settingsArray[2] ? "Unshuffle" : "Shuffle";
  867. }
  868.  
  869. //Play Webms
  870. if(settingsArray[3] !== true && settingsArray[3] !== false){
  871. messages.push("Play Webms");
  872. }else{
  873. op_webms.checked = settingsArray[3];
  874. }
  875.  
  876. //Show Webm controls
  877. if(settingsArray[4] !== true && settingsArray[4] !== false){
  878. messages.push("Show Webm controls");
  879. }else{
  880. op_controls.checked = settingsArray[4];
  881. }
  882.  
  883. //Play webm sound
  884. if(settingsArray[5] !== true && settingsArray[5] !== false){
  885. messages.push("Play webm sound");
  886. }else{
  887. op_playSound.checked = settingsArray[5];
  888. }
  889.  
  890. //Play Gifs
  891. if(settingsArray[6] !== true && settingsArray[6] !== false){
  892. messages.push("Play Gifs");
  893. }else{
  894. op_gifs.checked = settingsArray[6];
  895. }
  896.  
  897. //Gif duration (Seconds)
  898. if( isNaN(settingsArray[7]) ){
  899. messages.push("Gif duration (Seconds)");
  900. }else{
  901. op_gif_duration.value = settingsArray[7];
  902. }
  903.  
  904. //Play Images
  905. if(settingsArray[8] !== true && settingsArray[8] !== false){
  906. messages.push("Play Images");
  907. }else{
  908. op_imgs.checked = settingsArray[8];
  909. }
  910.  
  911. //Image duration (Seconds)
  912. if( isNaN(settingsArray[9]) ){
  913. messages.push("Image duration (Seconds)");
  914. }else{
  915. op_img_duration.value = settingsArray[9];
  916. }
  917. //Open Stream4chan on startup
  918. if(settingsArray[10] !== true && settingsArray[10] !== false){
  919. messages.push("Open Stream4chan on startup");
  920. }
  921.  
  922. //Override Individual Media
  923. if(settingsArray[11] !== true && settingsArray[11] !== false){
  924. messages.push("Override Individual Media");
  925. }
  926.  
  927. //Select Media randomly
  928. if(settingsArray[12] !== true && settingsArray[12] !== false){
  929. messages.push("Select Media Randomly");
  930. }else{
  931. op_random.checked = settingsArray[12];
  932. }
  933. return {
  934. valid: (messages.length <= 0),
  935. messages: messages
  936. };
  937. }
  938.  
  939. //Toggles showing the modal
  940. function showSFC(bool){
  941.  
  942. function show(){
  943. document.body.style.overflow = "hidden";
  944. el_sfc.style.display = "block";
  945. setTimeout(function(){
  946. el_sfc.style.opacity = 1;
  947. magicMouse(true);
  948. }, 40);
  949. return true;
  950. }
  951.  
  952. function hide(){
  953. showGallery(false);
  954. showSettings(false);
  955. currentContent.pause();
  956. el_sfc.style.opacity = 0;
  957. el_sfc.addEventListener("transitionend", function() {
  958. if(el_sfc.style.opacity == 0){
  959. el_sfc.style.display = "none";
  960. el_sfc.removeEventListener("transitionend", function(){}, false);
  961. document.body.style.overflow = "auto";
  962. showMouse(false);
  963. }
  964. }, false);
  965.  
  966. return true;
  967. }
  968.  
  969. if(bool === true){
  970. show();
  971. }else if (bool === false){
  972. hide();
  973. }else if (isShown(el_sfc)){
  974. show();
  975. }else{
  976. hide();
  977. }
  978.  
  979. return false;
  980. }
  981.  
  982. //Toggles showing the gallery
  983. function showGallery(bool){
  984.  
  985. function show(){
  986.  
  987. //Sets internal gallery slider to appropriate width
  988. //'if' statements causes this to only fire once
  989. updateGallery();
  990.  
  991. el_gallery.style.transform = "translateY(0px)";
  992. el_util.style.transform = "translateY(-" + el_gallery.clientHeight + "px)";
  993.  
  994. return true;
  995. }
  996.  
  997. function hide(){
  998. el_gallery.style.transform = "translateY(100%)";
  999. el_util.style.transform = "translateY(0)";
  1000. return true;
  1001. }
  1002.  
  1003. if(bool === true){
  1004. show();
  1005. }else if(bool === false){
  1006. hide();
  1007. }else if(el_gallery.style.transform == "translateY(100%)"){
  1008. show();
  1009. }else{
  1010. hide();
  1011. }
  1012.  
  1013. return false;
  1014. }
  1015.  
  1016. //Toggles showing the settings
  1017. function showSettings(bool){
  1018.  
  1019. function show(){
  1020. el_settings.style.display = "flex";
  1021. return true;
  1022. }
  1023.  
  1024. function hide(){
  1025. el_settings.style.display = "none";
  1026. return true;
  1027. }
  1028.  
  1029. if(bool === true){
  1030. show();
  1031. }else if(bool === false){
  1032. hide();
  1033. }else if(el_settings.style.display == "none" || el_settings.style.display === ""){
  1034. show();
  1035. }else{
  1036. hide();
  1037. }
  1038.  
  1039. return false;
  1040. }
  1041.  
  1042. //Parse through el_sfc, el_settings or el_gallery
  1043. //Return boolean indicating it's state
  1044. function isShown(el){
  1045. if(el === el_sfc){
  1046. return !(el_sfc.style.display == "none");
  1047. }
  1048.  
  1049. if(el === el_settings){
  1050. return !(el_settings.style.display == "none" || el_settings.style.display === "");
  1051. }
  1052.  
  1053. if(el === el_gallery){
  1054. return !(el_gallery.style.transform == "translateY(100%)");
  1055. }
  1056. }
  1057.  
  1058. /////////////////////////////////////////
  1059. // # Media and Media Array Functions # //
  1060. /////////////////////////////////////////
  1061.  
  1062. function previous(){
  1063.  
  1064. currentContent.previous();
  1065. }
  1066.  
  1067. function next(){
  1068.  
  1069. currentContent.next();
  1070. }
  1071.  
  1072. //Updates usedContent array, populates gallery and re-adjusts width
  1073. function updateGallery(contentToUse){
  1074.  
  1075. //Update contents of usedContent array
  1076. var validParams = contentToUse !== null && contentToUse !== undefined;
  1077. usedContent = validParams ? contentToUse : getUsedContent();
  1078.  
  1079. //Recalculate the previous and next media
  1080. currentContent.getNeighbours();
  1081.  
  1082. //Change currentContent to closest valid content
  1083. if(currentContent !== noneSelected && currentContent !== null){
  1084.  
  1085. if(!canPlay(currentContent)){
  1086.  
  1087. var newContent = null;
  1088. var crntContentFound = false;
  1089.  
  1090. //Find currentContent in allContent array and set newContent to next valid content
  1091. for(var i = 0; i <= allContent.length; i++){
  1092.  
  1093. //Keep i within bounds if settingsArray[0] (loop whole thread)
  1094. i = settingsArray[0] ? i%allContent.length : i;
  1095.  
  1096. //Terminates loop if out of bounds
  1097. if( i === allContent.length)
  1098. break;
  1099.  
  1100. //If crntContent is reached again, nothing was found
  1101. if(crntContentFound && allContent[i] === currentContent)
  1102. break;
  1103.  
  1104. //If content matches, crntContent has been found
  1105. if(allContent[i] === currentContent)
  1106. crntContentFound = true;
  1107.  
  1108. //If playable content has been found after crntcontent, save to newContent
  1109. if(crntContentFound && canPlay(allContent[i])){
  1110. newContent = allContent[i];
  1111. break;
  1112. }
  1113. }
  1114. //If newContent is null, change to noneSelected
  1115. newContent = newContent === null ? noneSelected : newContent;
  1116.  
  1117. newContent.select();
  1118. }
  1119. }
  1120.  
  1121. //Clear contents and width of internalSlider
  1122. el_internalSlider.innerHTML = "";
  1123. el_internalSlider.style.width = "-1px";
  1124.  
  1125. //Add all thumbnails to internalSlider
  1126. for(var i = 0; i < usedContent.length; i++){
  1127. el_internalSlider.appendChild(usedContent[i].thumb);
  1128. el_internalSlider.style.width = (el_internalSlider.clientWidth + (usedContent[i].thumb.clientWidth*1.2) ) + "px";
  1129. }
  1130.  
  1131. //Scroll seleceted media into gallery view again
  1132. currentContent.slideGallery();
  1133.  
  1134. updateCounter();
  1135. //Trigger height calculations without changing gallery state
  1136. // showGallery(isShown(el_gallery));
  1137. }
  1138.  
  1139. //Returns media type when given source
  1140. function mediaType(input){
  1141. if(input === undefined){
  1142. console.log('Error: mediaType input argument was undefined');
  1143. }else if(input === null){
  1144. console.log('Error: mediaType input argument was null');
  1145. }else{
  1146. var temp = input.toString();
  1147.  
  1148. temp = temp.substr(temp.lastIndexOf('.') + 1);
  1149.  
  1150. if(temp == webm) return webm;
  1151. if(temp == gif) return gif;
  1152. if(temp == png) return png;
  1153. if(temp == jpg) return jpg;
  1154. }
  1155.  
  1156. //Last Resort
  1157. return null;
  1158. }
  1159.  
  1160. //Returns if current user settings permits the playing of parsed object
  1161. function canPlay(mediaObj){
  1162. var objType = mediaObj.type;
  1163.  
  1164. var canPlay = false;
  1165.  
  1166. //Return 'checked' value of checkbox corresponding to the object type
  1167. switch(objType){
  1168.  
  1169. case webm:
  1170. return op_webms.checked;
  1171. case gif:
  1172. return op_gifs.checked;
  1173. case png:
  1174. case jpg:
  1175. return op_imgs.checked;
  1176. case "SOT":
  1177. case "EOT":
  1178. return !op_loopAll.checked;
  1179. break;
  1180. default:
  1181. return false;
  1182. }
  1183.  
  1184. // return (objType == webm && op_webms.checked) || (objType == gif && op_gifs.checked) || ( (objType == png || objType == jpg) && op_imgs.checked ) || (objType == "SOT" && !op_loopAll.checked) || (objType == "EOT" && !op_loopAll.checked);
  1185. }
  1186.  
  1187. //Applies autoplay based on user settings
  1188. function updateAutoplay(){
  1189.  
  1190. //Clear timeout to avoid timeout overlaps and
  1191. //unwanted function calls
  1192. clearTimeout(globalTimeout);
  1193.  
  1194. if(currentContent.type == webm){
  1195.  
  1196. //Loop media (incase auto is not turned on)
  1197. currentContent.media.loop = true;
  1198.  
  1199. //If it is turned on, set to false and await end of video
  1200. if(op_auto.checked){
  1201. currentContent.media.loop = false;
  1202. currentContent.media.onended = next;
  1203. }
  1204. }else if(currentContent.type == gif){
  1205.  
  1206. //If auto is checked apply according timeout
  1207. if(op_auto.checked){
  1208. globalTimeout = setTimeout(next, op_gif_duration.value*1000);
  1209. }
  1210. }else if(currentContent.type == png || currentContent.type == jpg){
  1211.  
  1212. //If auto is checked apply according timeout
  1213. if(op_auto.checked){
  1214. globalTimeout = setTimeout(next, op_img_duration.value*1000);
  1215. }
  1216. }
  1217. }
  1218.  
  1219. //Returns array of ALL elemnts. Including SOT, EOT and noneSelected
  1220. //Also sets the onclick method for the thumbnail in the default thread
  1221. function getContent(){
  1222.  
  1223. var temp = [];
  1224.  
  1225. var elements = document.getElementsByClassName('fileThumb');
  1226.  
  1227. //Pushes 'start of thread' placeholder
  1228. temp.push(SOT);
  1229.  
  1230. //Loops over all media elements in thread
  1231. //and pushes them to temp array
  1232. for(var i = 0; i < elements.length; i++){
  1233.  
  1234. var vidSrc = elements[i].href;
  1235. var imgSrc = elements[i].getElementsByTagName('img')[0].src;
  1236. var id = elements[i].parentNode.parentNode.id;
  1237.  
  1238. var x = new Media(imgSrc, vidSrc, id);
  1239.  
  1240. function playThis(){
  1241. showSFC(true);
  1242. x.select();
  1243. }
  1244.  
  1245. temp.push(x);
  1246.  
  1247. //Change clicking the video element to open SFC
  1248. var href = elements[i].getElementsByTagName('img')[0].parentNode;
  1249. href.setAttribute('oldHref', href.href);
  1250. href.oldHref = href.href;
  1251. href.setAttribute('imgSrc', imgSrc);
  1252. href.imgSrc = imgSrc;
  1253. href.href = imgSrc;
  1254. href.onclick = function(event){
  1255. inThreadClick(event);
  1256. };
  1257. }
  1258.  
  1259. //Pushes 'end of thread' placeholder
  1260. temp.push(EOT);
  1261.  
  1262. return temp;
  1263. }
  1264.  
  1265. //Returns all media permitted to play by user settings
  1266. function getUsedContent(){
  1267. var temp = [];
  1268. var count = 0;
  1269.  
  1270. for(var i = 0; i < allContent.length; i++){
  1271. if(canPlay(allContent[i])){
  1272. temp.push(allContent[i]);
  1273. temp[count].position = count;
  1274. count++;
  1275. }
  1276. }
  1277. return temp;
  1278. }
  1279.  
  1280. //Shuffles the usedContent array
  1281. function shuffle(){
  1282.  
  1283. //Direction is shuffle/unshuffle
  1284. var shuffled = op_shuffle.value === "Unshuffled";
  1285.  
  1286. if(!shuffled){
  1287. var newUsedContent = [];
  1288. var max, min = 0;
  1289.  
  1290. //Push a random index from the usedContent into a new array
  1291. while(usedContent.length > 0){
  1292.  
  1293. //Get random index
  1294. max = usedContent.length - 1;
  1295. var rand = Math.floor(Math.random() * (max - min + 1)) + min;
  1296.  
  1297. //remove one from old and add to new
  1298. newUsedContent.push(usedContent.splice(rand, 1)[0]);
  1299.  
  1300. //Update positions
  1301. newUsedContent[newUsedContent.length - 1].position = newUsedContent.length - 1;
  1302.  
  1303. }
  1304. //Overwrite the old and update gallery
  1305. usedContent = newUsedContent;
  1306. updateGallery(usedContent);
  1307.  
  1308. op_shuffle.value = "Unshuffle";
  1309.  
  1310. }else {
  1311. updateGallery();
  1312. op_shuffle.value = "Shuffle";
  1313. }
  1314. }
  1315.  
  1316. function getRandom(exclude){
  1317.  
  1318. //Array of media with lowest view count
  1319. var lowestPlayCount = [];
  1320.  
  1321. //Fill array with least viewed content
  1322. for(var i = 0; i < usedContent.length; i++){
  1323.  
  1324. //If placeholder media -> continue
  1325. if(usedContent[i] === SOT || usedContent[i] === EOT || usedContent[i] === exclude){
  1326. continue;
  1327. }
  1328.  
  1329. //If array is empty -> add current media
  1330. if(lowestPlayCount.length === 0){
  1331. lowestPlayCount.push(usedContent[i]);
  1332. continue;
  1333. }
  1334.  
  1335. //If new lowest found -> erase and push
  1336. if(usedContent[i].playcount < lowestPlayCount[0].playcount){
  1337. lowestPlayCount = [];
  1338. lowestPlayCount.push(usedContent[i]);
  1339. continue;
  1340. }
  1341.  
  1342. //If matching view count -> push
  1343. if(usedContent[i].playcount === lowestPlayCount[0].playcount){
  1344. lowestPlayCount.push(usedContent[i]);
  1345. continue;
  1346. }
  1347. }
  1348.  
  1349. //Select random media from array
  1350. var min = 0;
  1351. var max = lowestPlayCount.length - 1;
  1352. var rand = Math.floor(Math.random() * (max - min + 1)) + min;
  1353. console.log("Random");
  1354. console.log(lowestPlayCount);
  1355. console.log("");
  1356. return lowestPlayCount[rand];
  1357. }
  1358.  
  1359. function updateCounter(){
  1360. el_counter_first.innerHTML = currentContent.position + 1;
  1361. el_counter_second.innerHTML = usedContent.length;
  1362. }
  1363.  
  1364.  
  1365. ///////////////////////
  1366. // # Miscellaneous # //
  1367. ///////////////////////
  1368.  
  1369. //This function is called when a thumbnail in the thread is clicked
  1370. function inThreadClick(event, videoHref){
  1371. if(event.target === null)
  1372. return;
  1373.  
  1374. if(settingsArray[11]){
  1375.  
  1376. var hrefContainer = event.target.parentNode;
  1377. var currentID = hrefContainer.parentNode.parentNode.id;
  1378.  
  1379. setTimeout( function(){
  1380. hrefContainer.parentNode.classList.remove("image-expanded");
  1381. }, 0.0001);
  1382.  
  1383. for(var j = 0; j < allContent.length; j++){
  1384. if(allContent[j].id === currentID){
  1385. showSFC(true);
  1386. allContent[j].select();
  1387. }
  1388. }
  1389. }else{
  1390. event.target.href = event.target.oldHref;
  1391. event.target.click();
  1392. }
  1393. }
  1394.  
  1395. function magicMouse(bool){
  1396.  
  1397. var timer = setTimeout(function(){},0);
  1398.  
  1399. var showMouse = function(){
  1400. console.log("showing");
  1401. el_magicMouse.style.display = "none";
  1402. }
  1403.  
  1404. var hideMouse = function(){
  1405. console.log("hiding");
  1406. el_magicMouse.style.display = "block";
  1407. document.body.style.cursor = "auto";
  1408. }
  1409.  
  1410. var resetTimer = function(){
  1411. console.log("resetting");
  1412.  
  1413. showMouse();
  1414. clearTimeout(timer);
  1415. timer = setTimeout(hideMouse, 500);
  1416. }
  1417.  
  1418. if(bool === true){
  1419. document.body.onmousemove = resetTimer;
  1420. }else{
  1421. document.body.onmousemove = null;
  1422. }
  1423. }
  1424.  
  1425. function getPageTopLeft(el) {
  1426. var rect = el.getBoundingClientRect();
  1427. var docEl = document.documentElement;
  1428. return {
  1429. left: rect.left + (window.pageXOffset || docEl.scrollLeft || 0),
  1430. top: rect.top + (window.pageYOffset || docEl.scrollTop || 0)
  1431. };
  1432. }
  1433.  
  1434. //Convert css style strings to integer values (top, right, bottom and left)
  1435. function getCSSValues(styleStr){
  1436.  
  1437. var values = [];
  1438.  
  1439. do{
  1440. //Find match
  1441. var match = styleStr.match("\\d+[a-zA-Z]{2}");
  1442. var foundString = match[0];
  1443.  
  1444. //Remove all characters before and including the match
  1445. styleStr = styleStr.substr( match.index + foundString.length, styleStr.length - 1);
  1446.  
  1447. values.push(parseInt(foundString, 10));
  1448.  
  1449. }while(styleStr.length > 3); //If below 3 than it's not a valid style value
  1450.  
  1451. return values;
  1452. }