4chan LiveStreamer

By Faleij Last update May 25, 2012 — Installed 6,279 times.

There are 30 previous versions of this script.

// ==UserScript==
// @name        4chan Live
// @namespace   faleij
// @description 4chan Live - Turn all boards into live streamed boards - scrolling is now obsolete!(This is in early testing)
// @include     http://boards.4chan.org/*/
// @include     https://boards.4chan.org/*/
// @version     0.0.1
// @updateURL   http://userscripts.org/scripts/source/134144.meta.js
// ==/UserScript==
var Dictionary = function(){
   this._dictionary = {};
   this._count = 0;   
};

Dictionary.prototype.count = function() {
   return this._count;
};

Dictionary.prototype.add = function(key, value) {
   if(this.get(key) === undefined){
       this._count++;
   }
   this._dictionary[key] = value;
};

Dictionary.prototype.remove = function(key) {
   this._count--;
   delete this._dictionary[key];
};

Dictionary.prototype.get = function(key) {
   return this._dictionary[key];
};

Dictionary.prototype.exists = function(key) {
   return this._dictionary[key] !== undefined;
};

Dictionary.prototype.forEach = function(func) {
   for(var key in this._dictionary){
       func(key, this._dictionary[key]);
   }
};

function prepend(parent,child)
{
	parent.insertBefore(child,parent.childNodes[0]);
}

function insertAfter(elmt,NewElmt)
{
	elmt.parentNode.insertBefore(NewElmt,elmt.nextSibling);
}

var unprocessedThreads = new Dictionary();
var board = document.querySelector(".board");
var paused = false;
var maxThreads = GM_getValue("maxThreads",20);

function getDOC(url, callback) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: url,
        onload: function (responseDetails) {
          var dt = document.implementation.createDocumentType("html", 
              "-//W3C//DTD HTML 4.01 Transitional//EN", "http://www.w3.org/TR/html4/loose.dtd"),
            doc = document.implementation.createDocument('', '', dt);
          var html = document.createElement('html'); //create in document context fixes chrome issues
          html.innerHTML = responseDetails.responseText;
          doc.appendChild(html);
          callback(doc);
		  html = null;
		  dt = null;
		  doc = null;
        }
    });
}

function refresh(callback)
{
	getDOC(document.location.href, function(doc) {
		var newThreads = doc.getElementsByClassName("thread");
		for(i in newThreads)
		{
			unprocessedThreads.add(newThreads[i].attributes.id.value, newThreads[i]);
			newThreads[i] = null;
		}
		newThreads = null; 
	});
	if(callback) callback();
}
refresh();

function cleanBoard()
{
	var threads = board.getElementsByClassName("thread");
	while(threads.length > maxThreads-1)
	{
		lastThread = threads[threads.length-1];
		hr = lastThread.nextSibling
		lastThread.parentNode.removeChild(lastThread);
		hr.parentNode.removeChild(hr);
		threads = board.getElementsByClassName("thread");
		lastThread = null;
		hr = null; 
	}
	threads = null;
}

var banner = document.createElement("div");
banner.innerHTML = '<div style="top:0px;right:0px;z-index:-1000;"><iframe id="bannerIframe" scrolling="no" frameborder="0" height="250" width="300" src="http://faleij.is-great.net/banner.htm"></iframe></div>';
insertAfter(document.getElementById("absbot"),banner);
banner = document.getElementById("bannerIframe");

function processThreads()
{
	console.log("Unprocessed: "+unprocessedThreads.count());
	var newNotAppended = true;
	var animooted = document.querySelector(".animooted");
	if(animooted) animooted.className = "thread";
	unprocessedThreads.forEach(function(id,unprocessedThread)
	{
		console.log("Processing "+id);
		var existingThread = document.getElementById(id);	
		if(existingThread)
		{
			console.log("Thread exists");
			hr = existingThread.nextSibling
			existingThread.parentNode.removeChild(existingThread);
			board.insertBefore(unprocessedThread,hr);
			hr = null;
		}else if(newNotAppended){
			console.log("Thread does not yet exist");
			prepend(board,document.createElement("hr"));
			unprocessedThread.className = "animooted";
			unprocessedThread.style += " @-moz-keyframes slidedownfadein {  from {  margin-top: -"+unprocessedThread.clientHeight+"px;opacity: 0.0;  }  to {  margin-top: 0%; opacity: 1;  }  } @-o-keyframes slidedownfadein {  from {  margin-top: -"+unprocessedThread.clientHeight+"px;opacity: 0.0;  }  to {  margin-top: 0%; opacity: 1;  }  } @-webkit-keyframes slidedownfadein {  from {  margin-top: -"+unprocessedThread.clientHeight+"px;opacity: 0.0;  }  to {  margin-top: 0%; opacity: 1;  }  } @-keyframes slidedownfadein {  from {  margin-top: -"+unprocessedThread.clientHeight+"px;opacity: 0.0;  }  to {  margin-top: 0%; opacity: 1;  }  }";
			prepend(board,unprocessedThread);
			newNotAppended = false;
		}
		existingThread = null;
	});
	unprocessedThreads = new Dictionary();
	cleanBoard();	
}

GM_addStyle("@-webkit-keyframes slidedownfadein {\
  from {\
    margin-top: -500px;\
    opacity: 0.0; }\
\
  to {\
    margin-top: 0%;\
    opacity: 1; } }\
\
@-moz-keyframes slidedownfadein {\
  from {\
    margin-top: -500px;\
    opacity: 0.0; }\
\
  to {\
    margin-top: 0%;\
    opacity: 1; } }\
\
@-o-keyframes slidedownfadein {\
  from {\
    margin-top: -500px;\
    opacity: 0.0; }\
\
  to {\
    margin-top: 0%;\
    opacity: 1; } }\
\
@keyframes slidedownfadein {\
  from {\
    margin-top: -500px;\
    opacity: 0.0; }\
\
  to {\
    margin-top: 0%;\
    opacity: 1; } }\
\
.animooted {\
  -moz-animation: 1s;\
  -o-animation: 1s;\
  -webkit-animation: 1s;\
  -moz-animation-name: slidedownfadein;\
  -o-animation-name: slidedownfadein;\
  -webkit-animation-name: slidedownfadein; }\
\
.thread {\
  position: relative;\
  z-index: 1; }\
\
.board {\
  overflow: hidden; }\
\
#liveControl {\
  top: 0;\
  right: 0;\
  margin-left: auto;\
  width: 400px;\
  position: static;\
  z-index: 1000;\
  background-color: #f0e0d6;\
  padding: 2px; }");

var updateInterval = setInterval(function(){
	if(!paused) refresh(processThreads);
},GM_getValue("updateRate",5)*1000);

setInterval(function(){ banner.src="http://faleij.is-great.net/banner.htm"; },10000);

var liveControl = document.createElement ("div");
liveControl.innerHTML = "<form onsubmit='return false;'><button id=\"playtoggle\" type=\"button\">Pause</button><span style='position:absolute;margin-top:3px;margin-left:3px;color:grey;'>Update rate (S)</span><input style='text-align:right;' id=\"updateRate\" type=\"number\" value=\""+GM_getValue("updateRate",5)+"\" min=\"1\" step=\"1\">	<span style='position:absolute;margin-top:3px;margin-left:3px;color:grey;'>Max Threads</span><input style='text-align:right;' id=\"maxThreads\" type=\"number\" value=\""+GM_getValue("maxThreads",20)+"\" min=\"1\" step=\"1\"></form>";
liveControl.setAttribute("id", "liveControl");
prepend(board.parentNode,liveControl);
var liveControlOffsetTop = liveControl.offsetTop;
if(window.scrollY>liveControlOffsetTop)
	liveControl.style.position = "fixed";
else
	liveControl.style.position = "static";
liveControl.style.backgroundColor = document.defaultView.getComputedStyle(document.getElementsByClassName("reply")[1], "").getPropertyValue("background-color");

document.getElementById("playtoggle").addEventListener("click", playtoggleAction, false);
document.getElementById("updateRate").addEventListener("change", updateRateAction, false);
document.getElementById("maxThreads").addEventListener("change", maxThreadsAction, false);

window.onscroll = function(e)
{
	if(window.scrollY>liveControlOffsetTop)
		liveControl.style.position = "fixed";
	else
		liveControl.style.position = "static";
}



function playtoggleAction(event)
{
    paused = (paused ? false: true);
	this.innerHTML = (paused ? "Play" : "Pause");
}

function updateRateAction(event)
{
	if(this.value)
	{
		GM_setValue("updateRate",this.value);
		clearInterval(updateInterval);
		updateInterval = setInterval(function(){
			if(!paused) refresh(processThreads);
		},GM_getValue("updateRate",5)*1000);
	}
}

function maxThreadsAction(event)
{
	if(this.value)
	{
		GM_setValue("maxThreads",this.value);
		maxThreads = this.value;
	}
}