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

目前為 2017-06-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         stream4chan
// @namespace    http://tampermonkey.net/
// @version      4.2
// @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
// @author       Lauchlan105
// @match        http://boards.4chan.org/*/thread/*
// @grant        none
// ==/UserScript==

//////////////////
// # Settings # //
//////////////////

var settingsArray = [
    
    //Loop whole thread
    true,

    //Play automatically
    true,

    //Randomize on startup
    false,

    //Play Webms
    true,

    //Show Webm controls
    true,

    //Play webm sound
    true,

    //Play Gifs
    true,

    //Gif duration (Seconds)
    3,

    //Play Images
    true,

    //Image duration (Seconds)
    3,

    //Open Stream4chan on startup
    true,

    //Override Individual media?
    true
];

///////////////////
// # Variables # //
///////////////////

//Placeholder variables
var globalTimeout;
var webm;
var gif;
var png;
var jpg;
var SOT;
var EOT;
var noneSelected;
var allContent;
var usedContent;
var currentContent;

////////////////////////////
// # Media Object Model # //
////////////////////////////

class Media{
    constructor(thumb, source, id){

    	// local scope variable for object access via video/thumbnail elements
        var obj = this;

        this.position = 0;

        this.id = id === undefined ? "" : id;

        this.thumb = document.createElement('img');
        this.thumb.src = thumb;
        this.thumb.setAttribute('class','sfc-slide-preview');

        this.type = mediaType(source);

        this.resizeTimeout = setTimeout(function(){}, 0);

        //Handles deleted files/invalid media
        if(this.type === undefined || this.type === null){

        	//Force null for simpler conditionals
        	this.type = null;

        	console.log('Media could not be made. One or more of the following are invalid...');
        	console.log('	Given Arguments:');
        	console.log('		thumb: ' + thumb);
        	console.log('		source: ' + source);
        	console.log('		id: ' + id);
        	console.log('');
        	console.log('	All functions will be assigned placeholders');

        	function placeHolderFunction(calledFunction){
        		console.log('This object is not valid and');
        		console.log(calledFunction + ' cannot be called in this object');
        		return;
    		}

			this.unhighlight = function(){ placeHolderFunction('unhiglight'); };
			this.highlight   = function(){ placeHolderFunction('highlight'); };
       	    this.deselect    = function(){ placeHolderFunction('deselect'); };
       	   	this.resize      = function(){ placeHolderFunction('resize'); };
       	    this.select      = function(){ placeHolderFunction('select'); };
			this.pause       = function(){ placeHolderFunction('pause'); };
     	    this.play        = function(){ placeHolderFunction('play'); };
			
        	return false;
        }

    	if(this.type === webm){
            this.media = document.createElement('video');

            this.media.setAttribute("id", "sfc-webm");

            this.media.setAttribute("controls","");

            this.media.setAttribute("loop","");
            this.media.loop = true;

            this.media.setAttribute("autoplay","");
            this.media.autoplay = false;

            this.media.setAttribute("preload","");
            this.media.preload = "none";

        }else if(this.type === png || this.type === gif || this.type === jpg){
            this.media = document.createElement('img');
            this.media.setAttribute("id","sfc-img");
            this.media.alt = source;
        }

    	this.media.setAttribute("class", "sfc-media");
    	this.media.src = source;


        ///////////////////
        //MEDIA FUNCTIONS//
        ///////////////////
    	this.play = function(){

        	if(obj !== SOT && obj !== EOT && obj !== noneSelected){
        		if(obj.type == webm){
	                obj.media.volume = op_playSound.checked ? obj.media.volume : 0;
	                obj.media.play();
	            }else if(obj.type == gif || obj.type == jpg || obj.type == png){
                    obj.media.src = obj.media.alt;
                    updateAutoplay();
                }

	            //Load neighbouring media
	            var prevMedia = obj.position === 0 ? usedContent[usedContent.length - 1] : usedContent[obj.position - 1];
	            var nextMedia = obj.position === usedContent.length - 1 ? usedContent[0] : usedContent[obj.position + 1];
	            if(prevMedia.type === webm){
	                prevMedia.media.load();
	            }else if(nextMedia.type === webm){
	                nextMedia.media.load();
	            }
        	}
        };

        this.pause = function(){

            clearTimeout(globalTimeout);
            if(obj.type == webm){
                obj.media.pause();
            }else if(obj.type == gif || obj.type == jpg || obj.type == png){
                obj.media.src = obj.thumb.src;
            }
        };

        ///////////////////////
        //THUMBNAIL FUNCTIONS//
        ///////////////////////

        var highlight = function(){

            obj.thumb.style.border = "2px solid gainsboro";

            /*

			//Scroll into view
	        var pc = document.getElementById('pc' + id.substr(id.lastIndexOf('p') + 1));
  	 		pc.scrollIntoView();

			//Set container style to mimic focused content 
  			pc.style.background = '#f0e0d6';
    		pc.style.border = '1px solid #D99F91!important';

    		*/
        };

        var unhighlight = function(){

            obj.thumb.style.border = "2px solid transparent";

			/*

			//Remove focused content styles           
 			var pc = document.getElementById('pc' + id.substr(id.lastIndexOf('p') + 1));
  			pc.style.background = '#F0C0B0!important';
    		pc.style.border = '1px solid #D9BFB7';

    		*/
        };

        ///////////////////////////
        //MISCELLANEOUS FUNCTIONS//
        ///////////////////////////

        this.select = function(){

            //Deselect active content
            if(currentContent !== null){
                currentContent.deselect();
            }

            //Set currentContent to this object
            currentContent = obj;

            //Highlight thumbnail border
            highlight();

            //Add media to stage
            obj.media.controls = op_controls.checked;
            el_stage.appendChild(obj.media);

            //Play Media
            obj.play();

            //resize media
            obj.resize();

            //Update auto playing
            updateAutoplay();

            //Moves gallery so selected object is centered
            obj.slideGallery();
            
        };


        this.deselect = function(){

            obj.pause();
            obj.media.currentTime = 0;
            unhighlight();
            el_stage.innerHTML = "";
            currentContent = null;
        };

        this.thumb.onclick = function(){
        	if(obj.thumb.style.border == "2px solid gainsboro"){
        		obj.deselect();
        	}else{
        		obj.select();
        	}
        };

        this.resize = function(){

            clearTimeout(obj.resizeTimeout);

        	//Recursive resize function
        	//repeats every {interval} seconds
        	//if interval is undefined or < 0.01, default to 0.01
        	function execResize(interval){

        		//if interval is below 0.01, set to minimum 0.01
        		if(interval){
        			if(interval < 0.05)
        				interval = 0.05;
        		}

                if(isShown(el_sfc)){
                    if(obj === currentContent){
                        var setByWidth = true;

                        console.log('resizing');

                        //Set to max width
                        obj.media.style.width = window.innerWidth - (el_stagePrev.clientWidth + el_stageNext.clientWidth) + 'px';
                        obj.media.style.height = 'auto';

                        //if media height exceeds the stage height
                        if(obj.media.clientHeight > el_stage.clientHeight){
                            //Set to max height instead
                            obj.media.style.height = el_stage.clientHeight + 'px';
                            obj.media.style.width = 'auto';
                            setByWidth = false;
                        }

                        if(setByWidth){
                            // if full width, set height padding
                            obj.media.style.marginLeft = '0px';
                            var difHeight = (el_stage.clientHeight - obj.media.clientHeight)/2;
                            var topMarg = (difHeight) - ( difHeight%1 ); //Minus any decimals
                            obj.media.style.marginTop = topMarg  + 'px';
                        }else{
                            // if full height, set width padding
                            obj.media.style.marginTop = '0px';
                            var difWidth = (el_stage.clientWidth - obj.media.clientWidth)/2;
                            var leftMarg = (difWidth) - ( difWidth%1 ); //Minus any decimals
                            obj.media.style.marginLeft = leftMarg  + 'px';
                        }

                        if(interval){
                            obj.resizeTimeout = setTimeout(function(){
                                execResize(interval);
                            }, interval*1000);
                        }else{
                            return;
                        }
                    }else{
                        clearTimeout(obj.resizeTimeout);
                    }  
                }else{
                    clearTimeout(obj.resizeTimeout);
                }
        		
        		return;
        	}

        	//Continue resizing every 1 second till video ends
        	execResize(0.2);

        };

        this.slideGallery = function(){

            //resets slider transform
            el_internalSlider.style.transform = '';

            //distance from the left of the browser to the middle of the thumbnail
            var middleOfThumb = getPageTopLeft(obj.thumb).left + (obj.thumb.clientWidth/2);

            //The distance between left side of browser and vertical middle of browser
            var middleOfWindow = window.innerWidth/2;

            //Distance to middle
            var distance = middleOfThumb - middleOfWindow;
            var distance = distance > 0 ? distance*-1 : distance + ((-1*distance)*2);
            el_internalSlider.style.transform = 'translateX( ' + distance + 'px)';

        }

        this.media.getObj = function(){ return obj; };
        this.thumb.getObj = function(){ return obj; };


        return true;
    }
}

//////////////
// # Main # //
//////////////

(function(){

    insertElements();
    initVars();
    initPlaceholders();
    startInteractions();
    startEventListeners();
    
    var validSettings = applyDefaulSettings();
    if( !validSettings.valid ){
        for(var i = 0; i < validSettings.messages.length; i++){
            el_stage.innerHTML = '<h1>' + validSettings.messages[i] + '</h1>';    
        }
        return;
    }

    //Show and hide gallery to force load thumbnails
    //without this .select() does not work until gallery is shown
    showGallery(true);
    showGallery(false);

    sfc.style.display = "none"; //Force hide SFC
    showSFC(false);             //Force styles to be ready for fade in 

    //If open on startup is selected -> open on startup
    if(settingsArray[10]){
        showSFC(true);
        usedContent[0].select();
    }
})();

/////////////////////////////////
// # Initial Setup Functions # //
/////////////////////////////////

//Insert html for buttons and modal
function insertElements(){

    //Start Button
    var btn1 = '[<a id="sfc-start" href="#">start</a>]';
    var btn2 = '[<a id="sfc-resume" href="#">resume</a>]';

    //Add span to nav
    var nav = document.getElementsByClassName('navLinks desktop');
    for(var i = 0; i < nav.length; i++){
        var span = document.createElement('span');
        span.innerHTML = btn1 + " " + btn2;

        span.className = 'sfc-nav';
        span.style.display = nav[i].style.display;

        nav[i].parentNode.insertBefore(span, nav[i]);
        nav[i].parentNode.insertBefore(document.getElementById('op'), nav[i]);
    }

    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>';
    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> ';

    var sfc = document.createElement('div');
    sfc.setAttribute('id','sfc');
    sfc.innerHTML = html + css;

    var target = document.getElementsByClassName('thread');
    for(i = 0; i < target.length; i++){
        target[i].prepend(sfc);
    }
}

//Create and initialize global variables for easy access to HTML elements
function initVars(){

    //Custom function to find elements while 
        //alerting console of errors in case of null || undefined
    function getEl(elName){
        var temp = document.getElementById(elName);
        if(temp === null || temp === undefined){
            temp = document.getElementsByClassName(elName)[0];
            if(temp === null || temp === undefined){
                console.log('### ERROR ###');
                console.log('initVars: getEl(\'' + elName +'\') returned... ');
                console.log(temp);
            }
        }
        return temp;
    }

    //Main Page
    el_startBtn = getEl('sfc-start');
    el_resumeBtn = getEl('sfc-resume');

    //Modal
    el_sfc = getEl('sfc');

    //Stage Area
    el_stage = getEl('sfc-stage');
    el_stagePrev = getEl('sfc-main-prev');
    el_stageNext = getEl('sfc-main-next');

    //Utility buttons
    el_util = getEl("sfc-utility");
    el_galleryBtn = getEl("sfc-main-gallery");
    el_settingsBtn = getEl("sfc-main-settings");

    //Gallery Area
    el_gallery = getEl("sfc-gallery");
    el_slider = getEl('sfc-slider');
    el_internalSlider = getEl('sfc-slider-internal');
    el_sliderPrev = getEl('sfc-gallery-prev');
    el_sliderNext = getEl('sfc-gallery-next');

    //Settings and option area
    el_settings = getEl("sfc-settings-column");
    el_settingsExit = getEl("sfc-settings-exit");
    op_loopAll = getEl('stream4chan-loopAll');
    op_auto = getEl('stream4chan-auto');
    op_random = getEl('stream4chan-random');
    op_shuffle = getEl('stream4chan-shuffle');

    op_webms = getEl('stream4chan-webms');
    op_controls = getEl('stream4chan-controls');
    op_playSound = getEl('stream4chan-playSound');

    op_gifs = getEl('stream4chan-gifs');
    op_gif_duration = getEl('stream4chan-gif-duration');

    op_imgs = getEl('stream4chan-imgs');
    op_img_duration = getEl('stream4chan-img-duration');
}

//Inititialize values to placeholder variables
function initPlaceholders(){
    //Type placeholders. Less quotations in code
    webm = 'webm';
    gif = 'gif';
    png = 'png';
    jpg ='jpg';

    //Start of thread
    //Object based placeholder for the beginning of the thread (used when loopAll is unchecked)
    SOT = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
    SOT.media.src = ""; //https://dummyimage.com/1920x1080/000000/ffffff.png&text=Start+of+thread"; 
    SOT.thumb.src = ""; //https://dummyimage.com/480x270/000000/ffffff.png&text=Start+of+thread";
    SOT.type = "SOT";

    //End of thread
    //Object based placeholder for the end of the thread (used when loopAll is unchecked)
    EOT = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
    EOT.media.src = ""; //https://dummyimage.com/1920x1080/000000/ffffff.png&text=End+of+thread
    EOT.thumb.src = ""; //https://dummyimage.com/480x270/000000/ffffff.png&text=End+of+thread
    EOT.type = "EOT";

    //Object based placeholder for when there is no applicable media found or nothing is selected
    noneSelected = new Media("https://dummyimage.com/1920x1080/000000/ffffff.png","https://dummyimage.com/1920x1080/000000/ffffff.png");
    noneSelected.media.src = "https://dummyimage.com/1920x1080/000000/ffffff.png&text=No+Media+Selected";
    noneSelected.thumb.src = "https://dummyimage.com/480x270/000000/ffffff.png&text=No+Media+Selected";

    allContent = getContent();
    usedContent = getUsedContent();
    currentContent = noneSelected;
}

//////////////////////////////////////////////
// # SFC Control  and Animation Functions # //
//////////////////////////////////////////////

//Links functions with page controllers
//eg: making gallery button show/hide gallery
function startInteractions(){

    //Apply functionality: click start to show modal and play first media item
    el_startBtn.onclick = function(){
        showSFC(true);
        if(op_auto.checked){
            usedContent[0].select();
        }
    };

    el_resumeBtn.onclick = function(){
        showSFC(true);
        currentContent.resize();
        currentContent.play();
        updateAutoplay();
    };

    //Apply functionality: click gallery button to show/hide gallery
    el_gallery.style.transform = "translateY(100%)";
    el_galleryBtn.onclick = showGallery;

    //Apply functionality: click settings button to show settings
        //Click exit button to exit settings
    el_settingsBtn.onclick = function(){ showSettings(true); };
    el_settingsExit.onclick = function(){ showSettings(false); };
}

//Applies default settings
//  • Default settings are on line 5
function applyDefaulSettings(){

    var validSettings = true;
    var messages = Array();

    //Loop whole thread
    if(settingsArray[0] !== true && settingsArray[0] !== false){
        messages.push("Loop whole thread");
        validSettings = false;
    }else{
        op_loopAll.checked = settingsArray[0];
    }

    //Play automatically
    if(settingsArray[1] !== true && settingsArray[1] !== false){
        messages.push("Play automatically");
        validSettings = false;
    }else{
        op_auto.checked = settingsArray[1];       
    }

    //Randomize on startup
    if(settingsArray[2] !== true && settingsArray[2] !== false){
        messages.push("Randomize on startup");
        validSettings = false;
    }else{
        op_random.checked = settingsArray[2];       
    }

    //Play Webms
    if(settingsArray[3] !== true && settingsArray[3] !== false){
        messages.push("Play Webms");
        validSettings = false;
    }else{
        op_webms.checked = settingsArray[3];       
    }

    //Show Webm controls
    if(settingsArray[4] !== true && settingsArray[4] !== false){
        messages.push("Show Webm controls");
        validSettings = false;
    }else{
        op_controls.checked = settingsArray[4];      
    }

    //Play webm sound
    if(settingsArray[5] !== true && settingsArray[5] !== false){
        messages.push("Play webm sound");
        validSettings = false;
    }else{
        op_playSound.checked = settingsArray[5];       
    }

    //Play Gifs
    if(settingsArray[6] !== true && settingsArray[6] !== false){
        messages.push("Play Gifs");
        validSettings = false;
    }else{
        op_gifs.checked = settingsArray[6];
    }

    //Gif duration (Seconds)
    if( isNaN(settingsArray[7]) ){
        messages.push("Gif duration (Seconds)");
        validSettings = false;
    }else{
        op_gif_duration.value = settingsArray[7];
    }

    //Play Images
    if(settingsArray[8] !== true && settingsArray[8] !== false){
        messages.push("Play Images");
        validSettings = false;
    }else{
        op_imgs.checked = settingsArray[8];
    }

    //Image duration (Seconds)
    if( isNaN(settingsArray[9]) ){
        messages.push("Image duration (Seconds)");
        validSettings = false;
    }else{
        op_img_duration.value = settingsArray[9];
    }
    
    //Open Stream4chan on startup
    if(settingsArray[10] !== true && settingsArray[10] !== false){
        messages.push("Open Stream4chan on startup");
        validSettings = false;
    }
    
    return {
        valid: validSettings,
        messages: messages
    };
}

//Toggles showing the modal
function showSFC(bool){

    function show(){
        document.body.style.overflow = "hidden";
        el_sfc.style.display = "block";
        setTimeout(function(){
            el_sfc.style.opacity = 1;
        }, 40);
        return true;
    }

    function hide(){
        showGallery(false);
        showSettings(false);
        currentContent.pause();
        el_sfc.style.opacity = 0;
        el_sfc.addEventListener("transitionend", function() {
            if(el_sfc.style.opacity == 0){
                el_sfc.style.display = "none";
                el_sfc.removeEventListener("transitionend", function(){}, false);
                document.body.style.overflow = "scroll";
            }
        }, false);
        return true;
    }

    if(bool === true){
        show();
    }else if (bool === false){
        hide();
    }else if (isShown(el_sfc)){
        show();
    }else{
        hide();
    }

    return false;
}

//Toggles showing the gallery
function showGallery(bool){
    

    function show(){

        //Sets internal gallery slider to appropriate width
        //'if' statements causes this to only fire once
        if(el_internalSlider.style.width == ""){
            updateGallery();
        }

        el_gallery.style.transform = "translateY(0px)";
        el_util.style.transform = "translateY(-" + el_gallery.clientHeight + "px)";

        return true;
    }

    function hide(){
        el_gallery.style.transform = "translateY(100%)";
        el_util.style.transform = "translateY(0)";
        return true;
    }

    if(bool === true){
        show();
    }else if(bool === false){
        hide();
    }else if(el_gallery.style.transform == "translateY(100%)"){
        show();
    }else{
        hide();
    }

    return false;
}

//Toggles showing the settings
function showSettings(bool){

    function show(){
        el_settings.style.display = "flex";
        return true;
    }

    function hide(){
        el_settings.style.display = "none";
        return true;
    }

    if(bool === true){
        show();
    }else if(bool === false){
        hide();
    }else if(el_settings.style.display == "none" || el_settings.style.display === ""){
        show();
    }else{
        hide();
    }

    return false;
}

//Parse through el_sfc, el_settings or el_gallery
//Return boolean indicating it's state
function isShown(el){
    if(el === el_sfc){
        return !(el_sfc.style.display == "none");
    }

    if(el === el_settings){
        return !(el_settings.style.display == "none" || el_settings.style.display === "");
    }

    if(el === el_gallery){
        return !(el_gallery.style.transform == "translateY(100%)");
    }
}

/////////////////////////////////////////
// # Media and Media Array Functions # //
/////////////////////////////////////////

//Updates usedContent array, populates gallery and readjusts width
function updateGallery(){
    //Update contents of usedContent array
    usedContent = getUsedContent();

    //Change currentContent to closest valid content
    if(currentContent !== noneSelected && currentContent !== null){
        if(!canPlay(currentContent)){

            var newContent = null;

            //If usedContent actually has something in it
            if(usedContent.length > 0){
                var a = 0;
                var b = 0;

                //Find currentContent in allContent array
                if(currentContent !== noneSelected){
                    for(var i = 0; i < allContent.length; i++){
                        if(allContent[i] === currentContent){
                            a = i;
                            b = i;
                        }
                    }
                }
                

                //Begin searching in both directions for playable media
                //starting from currentContent
                do{
                    a++;
                    b--;

                    //If a is above range set to start
                    if(a >= allContent.length){
                        a = 0;
                    }

                    //If b is below range set to end
                    if(b < 0){
                        b = allContent.length - 1;
                    }

                    if(canPlay(allContent[a])){
                    	//if can play a --> play a
                        newContent = allContent[a]; 

                    }else if(canPlay(allContent[b])){
                    	// if can play b --> play b
                        newContent = allContent[b];

                    }else if(a == b){
                    	// if circled around to beginning --> noneSelected (no content found)
                        newContent = noneSelected;

                    }
                }while(newContent === null);
                newContent = newContent;
            }else{
                newContent = noneSelected;
            }
            newContent.select();
        }
    }

    //Clear contents and width of internalSlider
    el_internalSlider.innerHTML = "";
    el_internalSlider.style.width = "-1px";

    //Add all thumbnails to internalSlider
    for(var i = 0; i < usedContent.length; i++){
        el_internalSlider.appendChild(usedContent[i].thumb);
        el_internalSlider.style.width = (el_internalSlider.clientWidth + (usedContent[i].thumb.clientWidth*1.2) ) + "px";
    }

    //Trigger height calculations without changing gallery state
    showGallery(isShown(el_gallery));
    
}

//Play next valid media item
function next(){

    if(op_loopAll.checked){

        //If at last position play first item
        if(currentContent.position === usedContent.length-1){
            usedContent[0].select();
        }else{
        	if(usedContent[currentContent.position + 1] !== undefined){
        		usedContent[currentContent.position + 1].select();	
        	}
        }

    }else{

        //If its not the last item --> play next, else do nothing
        if(currentContent.position !== usedContent.length-1){
            usedContent[currentContent.position + 1].select();
        }
    }
}

//Play previous valid media item
function previous(){
    if(op_loopAll.checked){

        //If at first position play last item
        //else play previous
        if(currentContent.position === 0){
            usedContent[usedContent.length - 1].select();
        }else{
            if(usedContent[currentContent.position - 1] !== undefined){
        		usedContent[currentContent.position - 1].select();	
        	}
        }

    }else{

        //If its not the last item --> play next, else do nothing
        if(currentContent.position !== 0){
            usedContent[currentContent.position - 1].select();
        }
    }
}

//Returns media type when given source
function mediaType(input){
	if(input === undefined){
		console.log('Error: mediaType input argument was undefined');
	}else if(input === null){
		console.log('Error: mediaType input argument was null');
	}else{
		var temp = input.toString();

	    temp = temp.substr(temp.lastIndexOf('.') + 1);

	    if(temp == webm) return webm;
	    if(temp == gif)   return gif;
	    if(temp == png)  return png;
	    if(temp == jpg)  return jpg;
	}

	//Last Resort    
    return null;
}

//Returns if current user settings permits the playing of parsed object
function canPlay(mediaObj){
    var objType = mediaObj.type;
    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);
}

//Applies autoplay based on user settings
function updateAutoplay(){

    //Clear timeout to avoid timeout overlaps and
    //unwanted function calls
    clearTimeout(globalTimeout);


    if(currentContent.type == webm){

        //Loop media (incase auto is not turned on)
        currentContent.media.loop = true;

        //If it is turned on, set to false and await end of video
        if(op_auto.checked){
            currentContent.media.loop = false;
            currentContent.media.onended = next;
        }
    }else if(currentContent.type == gif){

        //If auto is checked apply according timeout
        if(op_auto.checked){
            globalTimeout = setTimeout(next, op_gif_duration.value*1000);
        }
    }else if(currentContent.type == png || currentContent.type == jpg){

        //If auto is checked apply according timeout
        if(op_auto.checked){
            globalTimeout = setTimeout(next, op_img_duration.value*1000);
        }
    }
}

//Returns array of ALL elemnts. Including SOT, EOT and noneSelected
//Also sets the onclick method for the thumbnail in the default thread
function getContent(){

    var temp = [];

    var elements = document.getElementsByClassName('fileThumb');

    //Pushes 'start of thread' placeholder
    temp.push(SOT);

    //Loops over all media elements in thread
    //and pushes them to temp array
    for(var i = 0; i < elements.length; i++){

        var vidSrc = elements[i].href;
        var imgSrc = elements[i].getElementsByTagName('img')[0].src;
        var id = elements[i].parentNode.parentNode.id;

        var x = new Media(imgSrc, vidSrc, id);

        function playThis(){
        	showSFC(true);
        	x.select();
        }

        //Change clicking the video element to open SFC
        if(settingsArray[11]){
        	elements[i].getElementsByTagName('img')[0].parentNode.href = imgSrc;

	        elements[i].getElementsByTagName('img')[0].parentNode.onclick = function(event){

	        	var hrefContainer = event.target.parentNode;
	        	var currentID = hrefContainer.parentNode.parentNode.id;

	        	setTimeout( function(){
	        		hrefContainer.parentNode.classList.remove("image-expanded");	
	        	}, 0.0001);

	        	for(var j = 0; j < allContent.length; j++){
	        		if(allContent[j].id === currentID){
	        			showSFC(true);
	        			allContent[j].select();
	        		}
	        	}
	        	
	        }	
        }

        temp.push(x);
    }

    //Pushes 'end of thread' placeholder
    temp.push(EOT);

    return temp;
}

//Returns all media permitted to play by user settings
function getUsedContent(){
    var temp = [];
    var count = 0;

    for(var i = 0; i < allContent.length; i++){
        if(canPlay(allContent[i])){
            temp.push(allContent[i]);
            temp[count].position = count;
            count++;
        }
    }
    return temp;
}

///////////////////////
// # Miscellaneous # //
///////////////////////

function startEventListeners(){

    window.onresize = function(){
        updateGallery();
        currentContent.resize();
        currentContent.slideGallery();
    }

    op_loopAll.onchange = updateGallery;
    
    op_controls.onchange = function(){
        if(currentContent.type == webm){
        	currentContent.media.controls = op_controls.checked;
        }
    };

    op_playSound.onchange = function(){
        if(currentContent.type == webm){
            currentContent.media.volume = op_playSound.checked ? 1 : 0;
        }
    };

    op_webms.onchange = updateGallery;
    op_gifs.onchange = updateGallery;
    op_imgs.onchange = updateGallery;
    op_auto.onchange = updateAutoplay;

    el_stagePrev.onclick = previous;
    el_stageNext.onclick = next;

    document.onkeydown = function(event){
        switch(event.keyCode){

            //Space Key
            case 32:
                if(currentContent !== null){
                        //if paused
                        if(currentContent.type == webm){
                            if(currentContent.media.paused){
                                currentContent.play();
                            }else{
                                currentContent.pause();
                            }
                        }else{
                            if(currentContent.media.src == currentContent.thumb.src){
                                currentContent.play();
                            }else{
                                currentContent.pause();
                            }
                        }
                        
                }
                break;

            //Enter Key
            case 13:
                if(event.ctrlKey)
                    return;

                if(!isShown(el_sfc)){

                    if(event.altKey){
                        el_resumeBtn.onclick();                        
                    }else{
                        el_startBtn.onclick();
                    }

                }
                break;    

            //Esc Key
            case 27:
                if(isShown(el_settings)){
                    showSettings(false);
                }else if(isShown(el_gallery)){
                    showGallery(false);
                }else if(isShown(el_sfc)){
                    showSFC(false);
                }
                break;

            //Left arrow Key
            case 37:
                if(!event.altKey)
                    previous();
                break;

            //Right arrow Key
            case 39:
                if(!event.altKey)
                    next();
                break;

            //Up arrow Key
            case 38:
            	if(event.shiftKey){
            		op_img_duration.value++;
            	}else{
            		op_gif_duration.value++;
            	}
                break;

            //Down arrow Key
            case 40:
                if(event.shiftKey){
            		op_img_duration.value--;
            	}else{
            		op_gif_duration.value--;
            	}
                break;

            //L Key
            case 76:
                op_loopAll.checked = !op_loopAll.checked;
                op_loopAll.onchange();
                break;

             //A Key
            case 65:
                op_auto.checked = !op_auto.checked;
                op_auto.onchange();
                break;

            //R Key
            case 82:
                op_random.checked = !op_random.checked;
                op_random.onchange();
                break;

            //Q Key
            case 81:
                //op_shuffle.onclick();
                break;

            //S Key
            case 83:
                op_playSound.checked = !op_playSound.checked;
                op_playSound.onchange();
                break;

            //W Key
            case 87:
                op_webms.checked = !op_webms.checked;
                op_webms.onchange();
                break;

            //C Key
            case 67:
                op_controls.checked = !op_controls.checked;
                op_controls.onchange();
                break;

            //G Key
            case 71:
                op_gifs.checked = !op_gifs.checked;
                op_gifs.onchange();
                break;

            //I Key
            case 73:
                op_imgs.checked = !op_imgs.checked;
                op_imgs.onchange();
                break;

            //Print what was typed into console
            default:
            	var temp = "";
            	
            	if(event.shiftKey){
            		temp += "Shift + ";
            	}

            	if(event.altKey){
            		temp += "Alt + ";
            	}

            	if(event.ctrlKey){
            		temp += "Ctrl + ";
            	}

            	temp += event.keyCode;

                console.log(temp);
        }
    }

    el_gallery.onwheel = function(event){

        //This number, when used with 
        // if(event.wheelDelta > 0 ){
                        
        // }else{
        //     console.log('scrolling down');
        //     el_internalSlider.style.transform = 'translateX(' + (getPageTopLeft(el_internalSlider).left) + 'px)';    
        // }
        
    }

    el_stage.onclick = function(e) {
        if (e.target === this){
            currentContent.pause();
            showSFC(false);
            return;
        }
    };

}

function getPageTopLeft(el) {
    var rect = el.getBoundingClientRect();
    var docEl = document.documentElement;
    return {
        left: rect.left + (window.pageXOffset || docEl.scrollLeft || 0),
        top: rect.top + (window.pageYOffset || docEl.scrollTop || 0)    
    };
}