// ==UserScript==
// @name stream4chan
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Click the button to stream all webms in a 4chan thread
// @author Lauchlan105
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require http://code.jquery.com/jquery-latest.min.js
// @match http://boards.4chan.org/*/thread/*
// @grant none
// @downloadURL none
// ==/UserScript==
/////////////////////////////////////
//////////////Settings///////////////
/////////////////////////////////////
settingsArray = [
//Start Stream4Chan Automatically
true,
//Debug on startup
false,
//Gif duration increment value
0.25,
//Gif duration (seconds)
3,
//Automatically play next video
true,
//Play Webms
true,
//Play Gifs
true,
//Play thread on repeat
true,
//Start thread in random order
false,
//img to use for loading gif
'https://raw.githubusercontent.com/gabrielgio/FChan/master/4chan.png'
];
/////////////////////////////////////
//////////////Settings///////////////
/////////////////////////////////////
//Main
(function() {
createElements();
initElements();
startEvents();
if(autoStart){ start(); }
})();
function createElements(){
//Using Settings Array Variables
var z = 0; //Keeps count of array option correspondence to make adding options easier
autoStart = settingsArray[z++];
debugOn = settingsArray[z++];
countStep = settingsArray[z++];
var durationDefault = settingsArray[z++];
var autoPlayDefault = settingsArray[z++] ? 'checked' : '';
var playWebmsDefault = settingsArray[z++] ? 'checked' : '';
var playGifsDefault = settingsArray[z++] ? 'checked' : '';
var loopThreadDefault = settingsArray[z++] ? 'checked' : '';
var randomDefault = settingsArray[z++] ? 'checked' : '';
//Loading Spinner
spinner = document.createElement("img");
spinner.setAttribute("src",settingsArray[z++]);
spinner.setAttribute("id","stream4chan-spinner");
spinner.setAttribute("class","SFC-loading");
//Arrays
hiddenPrints = [];
hrefs = [];
backup = [];
preload(hrefs);
//Settings Header
var modalSettings_auto = ' Play Automatically';
var modalSettings_webms = ' Play Webms';
var modalSettings_gifs = ' Play Gifs';
var modalSettings_loopAll = ' Loop whole thread';
var modalSettings_shuffle = '';
var modalSettings_random = ' Random';
var modalSettings_duration = ' Gif Duration (Seconds)';
var modalSettings_exit = '
';
var modalSettings = '' + modalSettings_auto + modalSettings_webms + modalSettings_gifs + modalSettings_loopAll + modalSettings_shuffle + modalSettings_random + modalSettings_duration + modalSettings_exit + '
';
//Content Table
var modalContent = '';
var modalContent_Left = ' | ';
var modalContent_Right = ' | ';
var modalContent_Mid = '' + modalSettings + modalContent + ' | ';
var modalTable = '' + modalContent_Left + modalContent_Mid + modalContent_Right + '
';
//Main Div
var startBtn = '';
var resumeBtn = '';
var modalMain = '' + modalTable + '
';
//CSS
var settingsCSS = '.SFC-settings { opacity: 0.35; display: inline-block; list-style-type: none; width:100%; top: 0; height: auto; margin: 0em; padding-top: 0.5em; color:#9d9393; }';
var hoverCSS = '.SFC-settings:hover { opacity: 1; } .SFC-settings:last-child:hover { opacity: 0; }';
var settingsInputCSS = '.SFC-input { margin: 0em 0em 0em 1.5em; padding: 0em; }';
var settingsExitCSS = '.SFC-exit { float: right; height: 28px; width: 28px; background-image: url("https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/VisualEditor_-_Icon_-_Close_-_white.svg/2000px-VisualEditor_-_Icon_-_Close_-_white.svg.png"); background-size: contain; }';
var AllSettingsCSS = settingsCSS + hoverCSS + settingsInputCSS + settingsExitCSS;
//table
var tableCSS = '.SFC_table { max-height: 100vh; min-height: 100vh; max-width: 100vw; min-width: 100vw; } ';
var tableRowCSS = '.SFC_table_row { vertical-align: top; }';
var arrowCSS = '.SFC_arrow { width: 15px; padding: 15px; height: 100%; background-image: url("http://www.dsetechnology.co.uk/images/disclose-arrow.png"); background-repeat: no-repeat; background-position: center; background-color: rgba(255, 255, 255, 0); background-size: contain; }';
var leftCSS = '.SFC_th_left { opacity: 0.14; transform: rotate(180deg); }' + '.SFC_th_left:hover { opacity: 1; background-color:rgba(255, 255, 255, 0.6); }';
var rightCSS = '.SFC_th_right { opacity: 0.14; }' + '.SFC_th_right:hover { opacity: 1; background-color:rgba(255, 255, 255, 0.6); }';
var midCSS = '.SFC_th_mid { height: 100vh; }';
var allTableCSS = tableCSS + tableRowCSS + arrowCSS + leftCSS + rightCSS + midCSS;
var mediaCSS = '.SFC-media { width: 100%; }';
var contentCSS = '.SFC-content { cursor: default; display: block; }';
var modalCSS = ' .SFC-modal { display: none; height: 100vh; width: 100vw; position: fixed; z-index: 1; left: 0; top: 0; background-color: rgba(0,0,0,0.88); -webkit-box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75); -moz-box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75); box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75);}';
var loadingCSS = '.SFC-loading { margin-top: 100%idth: 200px; height: 200px; animation-name: load; animation-duration: 3s; animation-iteration-count: infinite;animation-timing-function: ease-in-out;}' +
'@keyframes load { 0%{ transform: rotate(0deg); } 80%{ transform: rotate(360deg); } 100%{ transform: rotate(360deg); } }';
var allModalCSS = loadingCSS + mediaCSS + contentCSS + modalCSS;
var allCSS = '';
//Add start and resume buttons
var nav = document.getElementsByClassName('navLinks desktop');
for(var i = 0; i < nav.length; i++){
debug('adding button to nav ' + i);
var span = document.createElement('span');
span.innerHTML = startBtn + resumeBtn;
span.className = 'stream4chan-start';
span.style.display = nav[i].style.display;
nav[i].parentNode.insertBefore(span, nav[i]);
nav[i].parentNode.insertBefore(document.getElementById('op'), nav[i]);
}
//add the modal to start of thread
var target = document.getElementsByClassName('thread');
for(i = 0; i < target.length; i++){
target[i].innerHTML = (modalMain + allCSS) + target[i].innerHTML;
}
}
function initElements(){
modal = document.getElementById('stream4chan-modal');
if(!modal){ debug('Modal not found!'); }
table = document.getElementById('stream4chan-table');
if(!table){ debug('Table not found!'); }
settings = document.getElementById('stream4chan-settings');
if(!settings){ debug('Settings not found!'); }
content = document.getElementById('stream4chan-content');
if(!content){ debug('Content not found!'); }
//Interactable Elements
prevButton = document.getElementById('stream4chan-prev');
if(!prevButton){ debug('Previous Button not found!'); }
nextButton = document.getElementById('stream4chan-next');
if(!nextButton){ debug('Next Button not found!'); }
startButton = document.getElementById('stream4chan-start');
if(!startButton){ debug('Start Button could not be found!');}
resumeButton = document.getElementById('stream4chan-resume');
if(!resumeButton){ debug('Resume Button could not be found!');}
autoplay = document.getElementById('stream4chan-auto?');
if(!autoplay){ debug('Autoplay checkbox not found!');}
playWebms = document.getElementById('stream4chan-webms?');
if(!playWebms){ debug('Player Webms input not found!');}
playGifs = document.getElementById('stream4chan-gifs?');
if(!playGifs){ debug('Player Gifs input not found!');}
loop = document.getElementById('stream4chan-loopAll?');
if(!loop){ debug('"loopAll" could not be found!'); }
random = document.getElementById('stream4chan-random?');
if(!random){ debug('"random" could not be found!'); }
shuffle = document.getElementById('stream4chan-shuffle?');
if(!shuffle){ debug('"shuffle" could not be found!'); }
duration = document.getElementById('stream4chan-duration?');
if(!duration){ debug('Gif duration input not found!');}
exit = document.getElementById('SFC-exit');
if(!exit){ debug('Exit not found!');}
currentVideo = 'start';
currentTime = 0;
currentTimeout = setTimeout(0);
modalOn = false;
shuffled = false;
}
function startEvents(){
//Window/Document Events
magicMouse();
window.onresize = function(){ setDimensions(contentExists()); };
window.onclick = function(event){if (event.target.id == 'stream4chan-content'){ stop(); }};
//Key Down Events
window.onkeydown = function(event){
event.target.blur();
if(!toggleDebug(event)){
//up arrow
if(event.keyCode == 38 && modalOn){
duration.stepUp();
}
//Down arrow
if(event.keyCode == 40 && modalOn){
duration.stepDown();
}
if (event.keyCode == 13 && event.shiftKey){
if(!modalOn){
start(currentVideo, currentTime);
}
}else if(event.keyCode == 13){
if(!modalOn){
start();
}
}
}
};
//Key Up Events
window.onkeyup = function(event){
event.target.blur();
if(modalOn){
//left arrow
if(event.keyCode == 37){
previousVideo();
}
//right arrow
if(event.keyCode == 39){
nextVideo();
}
//Escape key
if(event.keyCode == 27){
stop();
}
//W key
if(event.keyCode == 87){
playWebms.checked = !playWebms.checked;
}
//G key
if(event.keyCode == 71){
playGifs.checked = !playGifs.checked;
}
//A key
if(event.keyCode == 65){
autoplay.checked = !autoplay.checked;
applyAutoplay();
}
//L key
if(event.keyCode == 76){
loopAll.checked = !loopAll.checked;
}
//R key
if(event.keyCode == 82){
random.checked = !random.checked;
if(random.checked){
shuffleArr();
}else{
unshuffleArr();
}
}
//S key
if(event.keyCode == 83){
if(shuffle.value === ' shuffle '){
shuffleArr();
}else{
unshuffleArr();
}
}
//Space key
if(event.keyCode == 32){
if(contentExists('webm')){
if(hrefs[currentVideo].paused){
hrefs[currentVideo].play();
}else{
hrefs[currentVideo].pause();
}
}
}
}
};
//Page Element Events
exit.onclick = function(){ stop(); };
startButton.onclick = function(){ start(); };
resumeButton.onclick = function(){ start(currentVideo, currentTime); };
autoplay.onclick = function(){ applyAutoplay(); };
random.onclick = function(){
if(random.checked){
shuffleArr();
}else{
unshuffleArr();
}
};
prevButton.onclick = function(){ previousVideo(); };
nextButton.onclick = function(){ nextVideo(); };
shuffle.onclick = function(){
if(shuffle.value === ' unshuffle '){
unshuffleArr();
}else{
shuffleArr();
}
};
}
function displayModal(state){
//if state toggles modal if a state is not specified
if(state === true){
debug('Show Modal');
modalOn = state;
modal.style.display = 'block';
//removes scoll bar
document.body.style.overflow = 'hidden';
}else if(state === false){
debug('Hide Modal');
modalOn = state;
modal.style.display = 'none';
//removes scoll bar
document.body.style.overflow = 'scroll';
}else{
if(!modalOn){
debug('Show Modal');
modal.style.display = 'block';
//removes scoll bar
document.body.style.overflow = 'hidden';
}else{
debug('Hide Modal');
modal.style.display = 'none';
//removes scoll bar
document.body.style.overflow = 'scroll';
}
modalOn = !modalOn;
}
}
function start(crntVid, crntTime){
var newStart = (!crntVid && (crntVid !== 0));
currentVideo = !newStart ? crntVid-1 : 'start';
currentTime = crntTime ? crntTime : 0;
//resume if starting from the beginning
if(newStart){ unshuffleArr(); }
displayModal(true);
content.innerHTML = '';
content.appendChild(spinner);
//If document is loaded
if (document.readyState === "complete") {
nextVideo();
}else{
debug('page still loading');
window.onload = function(){
debug('page loaded');
if(modalOn){
nextVideo();
}
};
}
}
function stop(){
if(contentExists()){
currentTime = contentExists('webm') ? contentExists('webm').currentTime : 0;
pauseContent();
}
content.innerHTML = '';
displayModal(false);
}
function nextVideo(){
debug('Function: nextVideo');
pauseContent();
if(currentVideo === 'start'){ currentVideo = -1; }
//While the href can't be played
do{
currentVideo++;
if(loop.checked){
currentVideo = currentVideo % hrefs.length;
}
var temp = noContentToDisplay();
if(temp){
stop();
displayModal(true);
content.innerHTML = temp;
return;
}
}while(!canPlay(hrefs[currentVideo]));
//Play specified video/img element
playContent(hrefs[currentVideo]);
}
function previousVideo(){
debug('Function: previousVideo');
pauseContent();
if(currentVideo === 'start'){ currentVideo = 0; }
//While the href can't be played
do{
currentVideo--;
if(loop.checked){
if(currentVideo === -1){ currentVideo = hrefs.length -1;}
}
var temp = noContentToDisplay();
if(temp){
stop();
displayModal(true);
content.innerHTML = temp;
return;
}
}while(!canPlay(hrefs[currentVideo]));
//Play specified video/img element
playContent(hrefs[currentVideo]);
}
function playContent(currentContent){
debug('Function: playContent');
if(random.checked){
shuffleArr();
}
content.innerHTML = '';
content.appendChild(spinner);
//Play specified video/img element
//If its a webm, play when loaded, otherwise play immediately
if(getFileExt(currentContent.src) === 'webm' ){
if(currentContent.canStart){
showContent();
//add events for next video
applyAutoplay();
}else{
currentContent.oncanplaythrough = function(){
x.oncanplaythrough = function(){ this.canStart = true; };
showContent();
//add events for next video
applyAutoplay();
};
}
}else{
showContent();
//add events for next video
applyAutoplay();
}
}
function showContent(){
content.innerHTML = '';
content.appendChild(hrefs[currentVideo]);
setDimensions(contentExists());
if(contentExists('webm')){
contentExists('webm').currentTime = currentTime;
currentTime = 0;
contentExists('webm').play();
}
}
function pauseContent(){
debug('Function: pauseContent');
var x = contentExists('webm');
if(x){
x.removeEventListener('ended', nextVideo, false);
x.loop = true;
x.pause();
}
clearTimeout(currentTimeout);
content.appendChild(spinner);
}
function canPlay(currentContent){
return ( (playGifs.checked && (getFileExt(currentContent.src) === 'gif')) || (playWebms.checked && (getFileExt(currentContent.src) === 'webm')) );
}
function getFileExt(input){
temp = input.toString();
return temp.substr(temp.lastIndexOf('.') + 1);
}
function preload(arr){
debug('Function: preload');
var temp = document.getElementsByClassName('fileThumb');
var x = null;
//Iterates over number of gif/webms in a thread
for (i = 0; i < temp.length; i++){
debug(' intitializing element: ' + temp[i]);
//If (gif){
//} else if (webm){
//} else {}
if(getFileExt(temp[i]) == 'gif'){
x = document.createElement('img');
x.setAttribute("id", "stream4chan-gif");
}else if (getFileExt(temp[i]) == 'webm'){
x = document.createElement('video');
x.setAttribute("id", "stream4chan-webm");
x.setAttribute("controls","");
x.setAttribute("loop","");
x.loop = true;
x.setAttribute("autoplay","");
x.autoplay = false;
x.setAttribute("ended","");
x.ended = false;
x.setAttribute("preload", "");
x.preload = 'auto';
x.setAttribute("canStart", "false");
x.canStart = false;
x.oncanplaythrough = function(){ this.canStart = true; };
}
x.setAttribute("src",temp[i]);
x.setAttribute("style", "height: 100%; width: auto;");
arr.push(x);
backup.push(x);
}
}
function debug(text){
if(debugOn){
console.log(text);
}else{
hiddenPrints.push(text);
}
}
function toggleDebug(e) {
var evtobj = window.event? event : e;
if (!e || evtobj.keyCode == 68 && evtobj.ctrlKey && evtobj.altKey && evtobj.shiftKey){
debug('Function: toggleDebug');
if(debugOn){
console.log('##Debug Off##');
debugOn = false;
}else{
console.log('##Debug On##');
debugOn = true;
for(var i = 0; i < hiddenPrints.length; i++){
debug(hiddenPrints[i]);
}
hiddenPrints = [];
}
return true;
}
return false;
}
function setDimensions(e){
debug('Function: setDimensions');
content.style.height = window.innerHeight - (settings.offsetHeight*2) + 'px';
if(e){
e.style.height = '100%';
e.style.width = 'auto';
if(e.offsetWidth > (window.innerWidth - (prevButton.offsetWidth + nextButton.offsetWidth))){
debug(' Content was wider');
e.style.height = 'auto';
e.style.width = '100%';
}
e.style.marginTop = ((content.offsetHeight - e.offsetHeight)/2) + 'px';
e.style.marginBottom = ((content.offsetHeight - e.offsetHeight)/2) + 'px';
}
}
function contentExists(type){
debug('Function: contentExists');
if(type){
return document.getElementById('stream4chan-' + type);
}else{
var div = document.getElementById('stream4chan-webm');
if(!div){ div = document.getElementById('stream4chan-gif'); }
return div;
}
}
function thereAre(type){
debug('Function: thereAre');
if(type){
for(i = 0; i < hrefs.length; i++){
if(getFileExt(hrefs[i].src) === type){
return true;
}
}
}
debug(' Error: argument not passed to function');
return false;
}
function noContentToDisplay(){
debug('Function: noContentToDisplay');
var text = false;
//End and display modal if nothing can play
if(!playGifs.checked && !playWebms.checked){
text = 'Nothing selected to play';
}
//End and display modal if nothing can play
if(!thereAre('gif') && (playGifs.checked && !playWebms.checked)){
text = 'No gifs!';
}
//End and display modal if nothing can play
if(!thereAre('webm') && (!playGifs.checked && playWebms.checked)){
text = 'No Webms!';
}
//End and display modal if nothing can play
if(!thereAre('gif') && !thereAre('webm')){
text = 'No Gifs or Webs';
}
//End and display modal if out of range
if( 0 > currentVideo || currentVideo > hrefs.length-1){
text = 'End of Thread';
if(currentVideo < 0){ text = 'Start of thread'; }
}
if(text){ debug(' Returning: '+ text); }
return text;
}
function applyAutoplay(){
debug('Function: applyAutoplay');
var x = contentExists();
debug(' ' + getFileExt(x.src) + ' event listener is ' + autoplay.checked);
clearTimeout();
if(getFileExt(x.src) === 'webm'){
x.loop = !autoplay.checked;
x.removeEventListener('ended', nextVideo, false);
if(autoplay.checked){
x.addEventListener('ended', nextVideo, false);
}
}
if(getFileExt(x.src) === 'gif' && autoplay.checked){
currentTimeout = setTimeout(nextVideo, duration.value*1000);
}
}
function shuffleArr() {
debug('Function: shuffleArr');
var j, x, i;
for (i = hrefs.length; i; i--) {
j = Math.floor(Math.random() * i);
x = hrefs[i - 1];
hrefs[i - 1] = hrefs[j];
hrefs[j] = x;
}
shuffled = true;
shuffle.value = ' unshuffle ';
}
function unshuffleArr() {
debug('Function: unshuffleArr');
hrefs = backup.slice();
if(contentExists()){
for(var i = 0; i < hrefs.length; i++){
if(hrefs[i].src === contentExists().src){
currentVideo = i;
break;
}
}
}
shuffled = false;
shuffle.value = ' shuffle ';
}
function magicMouse() {
var mouseTimer = null, cursorVisible = true;
document.onmousemove = function() {
if (mouseTimer) {
window.clearTimeout(mouseTimer);
}
if (!cursorVisible) {
debug('adding cursor');
content.style.cursor = 'auto';
cursorVisible = true;
}
mouseTimer = window.setTimeout(function(){
debug('removing cursor');
mouseTimer = null;
content.style.cursor = 'none';
cursorVisible = false;
}, 1500);
};
}