Last active
December 12, 2015 05:59
-
-
Save qqueue/4726278 to your computer and use it in GitHub Desktop.
html5chan commit 02c2a90a186a3c2727bd1c0eda5af4a211f9a017, jade vs no-jade vs jade with manual modifications. jade versions are technically a few commits behind but otherwise functional. All scripts can be installed by greasemonkey without overwriting each other, but make sure to enable only one at a time.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name html5chan-jade-nowith-noescape-profiled | |
// @namespace https://github.com/nami-doc/html5chan | |
// @description The Minority Report of 4chan userscripts | |
// | |
// @match *://boards.4chan.org/* | |
// @exclude *://boards.4chan.org/f/* | |
// @exclude *://boards.4chan.org/*/catalog | |
// @exclude *://boards.4chan.org/*/catalog/* | |
// @exclude *://boards.4chan.org/robots.txt | |
// | |
// @run-at document-start | |
// | |
// @grant none | |
// ==/UserScript== | |
(function(){ | |
"use strict"; | |
var | |
jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i<len;++i){var key=keys[i],val=obj[key];"boolean"==typeof val||null==val?val&&(terse?buf.push(key):buf.push(key+'="'+key+'"')):0==key.indexOf("data")&&"string"!=typeof val?buf.push(key+"='"+JSON.stringify(val)+"'"):"class"==key&&Array.isArray(val)?buf.push(key+'="'+(val.join(" "))+'"'):escaped&&escaped[key]?buf.push(key+'="'+(val)+'"'):buf.push(key+'="'+val+'"')}}return buf.join(" ")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); | |
var templates = {}; | |
templates.thread = function threadTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'id':(locals.id || "t" + (locals.thread.no) + ""), 'data-no':(locals.thread.no), "class": ('#classes ' + (locals.thread.className()) + '') }, {"id":true,"data-no":true,"class":true})); | |
buf.push('>'); | |
var __val__ = locals.thread.op.render('div', 'op') | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('<div class="thread-info">' + ((interp = (locals.thread.omitted && locals.thread.omitted.replies || 0) + locals.thread.replies.length) == null ? '' : interp) + ' replies and\n' + ((interp = (locals.thread.omitted && locals.thread.omitted.imageReplies || 0) + locals.thread.imageReplies.length) == null ? '' : interp) + ' images.'); | |
if ( locals.thread.preview) | |
{ | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.thread.url), "class": ('expand-link') }, {"href":true})); | |
buf.push('>Expand</a>'); | |
} | |
buf.push('</div><div class="replies">'); | |
// iterate thread.replies | |
;(function(){ | |
if ('number' == typeof locals.thread.replies.length) { | |
for (var $index = 0, $$l = locals.thread.replies.length; $index < $$l; $index++) { | |
var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.thread.replies) { | |
$$l++; var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
templates.board = function boardTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<nav id="toplinks" class="boardlinks">'); | |
var __val__ = board.nav | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</nav><header id="header"><a'); | |
buf.push(attrs({ 'id':('banner'), 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(board.banner), 'alt':('4chan::') }, {"src":true,"alt":true})); | |
buf.push('/></a><hgroup><h1 id="board-name"><a'); | |
buf.push(attrs({ 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = board.title | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a></h1><h2 id="board-subtitle">'); | |
var __val__ = board.subtitle | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</h2></hgroup></header>'); | |
if ( board.motd) | |
{ | |
buf.push('<div id="motd"><button id="hide-motd" type="button">Hide News</button><div id="message">'); | |
var __val__ = board.motd | |
buf.push(null == __val__ ? "" : __val__); | |
buf.push('</div></div>'); | |
} | |
buf.push('<div id="threads">'); | |
// iterate threads | |
;(function(){ | |
if ('number' == typeof locals.threads.length) { | |
for (var $index = 0, $$l = locals.threads.length; $index < $$l; $index++) { | |
var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.threads) { | |
$$l++; var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div>'); | |
if ( board.isBoard) | |
{ | |
buf.push('<ul id="pages">'); | |
if ( board.page > 0) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page - 1) }, {"href":true})); | |
buf.push('>previous</a></li>'); | |
} | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.url) }, {"href":true})); | |
buf.push('>0</a></li><li><a href="1">1</a></li><li><a href="2">2</a></li><li><a href="3">3</a></li><li><a href="4">4</a></li><li><a href="5">5</a></li><li><a href="6">6</a></li><li><a href="7">7</a></li><li><a href="8">8</a></li><li><a href="9">9</a></li><li><a href="10">10</a></li>'); | |
if ( board.page < 10) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page + 1) }, {"href":true})); | |
buf.push('>next</a></li>'); | |
} | |
buf.push('<li><a href="catalog">Catalog</a></li></ul>'); | |
} | |
if (!( board.locked)) | |
{ | |
buf.push('<div id="postform-wrapper"><form'); | |
buf.push(attrs({ 'id':('postform'), 'enctype':('multipart/form-data'), 'method':('POST'), 'action':('https://sys.4chan.org/' + (board.name) + '/post') }, {"enctype":true,"method":true,"action":true})); | |
buf.push('><input type="hidden" value="3145728" name="MAX_FILE_SIZE"/>'); | |
if ( board.threadId) | |
{ | |
buf.push('<input type="\" value="\" name="\"/>'); | |
} | |
buf.push('<input type="hidden" value="regist" name="mode"/><input'); | |
buf.push(attrs({ 'id':('password'), 'type':('hidden'), 'name':('pwd'), 'value':(board.password) }, {"type":true,"name":true,"value":true})); | |
buf.push('/><div id="fields"><input id="name" type="text" name="name" tabindex="10" placeholder="name#tripcode"/><input id="email" type="text" name="email" tabindex="10" placeholder="email"/><input id="subject" type="text" name="sub" tabindex="10" placeholder="subject"/><div id="comment-field"><textarea id="comment" name="com" rows="4" tabindex="10" placeholder="comment"></textarea></div><div id="captcha" style="display: none;"><a id="recaptcha_image" href="javascript:Recaptcha.reload()" title="Click for new captcha"></a><input id="recaptcha_response_field" type="text" name="recaptcha_response_field" tabindex="10" placeholder="captcha"/></div><div id="file-field"><input id="file" type="file" name="upfile" tabindex="10"/><label id="spoiler-field"><input type="checkbox" value="on" name="spoiler" tabindex="10"/>Spoiler?</label></div><div id="buttons"><button id="post" type="submit" tabindex="10" value="Submit">Post ' + ((interp = board.isThread ? 'Reply' : 'New Thread') == null ? '' : interp) + '</button>'); | |
if ( board.isThread) | |
{ | |
buf.push('<button id="sage" type="submit" name="email" value="sage" tabindex="10">Sage Reply</button>'); | |
} | |
buf.push('<span id="post-status"></span><progress id="progress" max="100" value="0" hidden=""></progress></div></div></form></div>'); | |
} | |
buf.push('<span id="updater"><span id="update-status"></span><button id="update-now">Update now</button></span>'); | |
return buf.join(""); | |
} | |
templates.post = function PostTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'data-no':(locals.post.no), 'data-idx':(locals.post.idx), 'id':(locals.id || "p" + (locals.post.no) + ""), "class": ('' + (locals.classes) + ' ' + (locals.post.className()) + '') }, {"data-no":true,"class":true,"data-idx":true,"id":true})); | |
buf.push('><h1 class="post-header ptdr"><button'); | |
buf.push(attrs({ 'type':('button'), 'value':(locals.post.no), "class": ('hide') }, {"type":true,"value":true})); | |
buf.push('>×</button><button'); | |
buf.push(attrs({ 'type':('submit'), 'form':('reportform'), 'name':('no'), 'value':(locals.post.no), "class": ('report') }, {"type":true,"form":true,"name":true,"value":true})); | |
buf.push('>!</button><a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('subject') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.subject | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a><a'); | |
buf.push(attrs({ 'href':(locals.post.email ? "href='mailto:' + (email) + ''" : ''), "class": ('name') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.name | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a><span class="tripcode">'); | |
var __val__ = locals.post.tripcode | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="capcode">'); | |
var __val__ = locals.post.capcode | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="posteruid">'); | |
var __val__ = locals.post.uid && "(ID: #{post.uid})" | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><time'); | |
buf.push(attrs({ 'pubdate':(true), 'datetime':(locals.post.time.toISOString()), 'title':(locals.post.time) }, {"datetime":true,"title":true})); | |
buf.push('>'); | |
var __val__ = locals.post.time.relativeTime() | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</time>'); | |
if ( locals.post.op && locals.post.thread.sticky) | |
{ | |
buf.push('<img alt="sticky" src="//static.4chan.org/image/sticky.gif"/>'); | |
} | |
if ( locals.post.op && locals.post.thread.closed) | |
{ | |
buf.push('<img alt="closed" src="//static.4chan.org/image/closed.gif"/>'); | |
} | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('permalink') }, {"href":true})); | |
buf.push('>No.<span class="no">'); | |
var __val__ = locals.post.no | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span></a></h1>'); | |
if ( locals.post.image) | |
{ | |
buf.push('<div class="fileinfo"><span class="filename">'); | |
var __val__ = locals.post.image.filename || '' | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="dimensions">'); | |
var __val__ = locals.post.image.width + "x" + locals.post.image.height | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="size">'); | |
var __val__ = locals.post.image.size | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><a'); | |
buf.push(attrs({ 'href':('http://iqdb.org/?url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>iqdb</a><a'); | |
buf.push(attrs({ 'href':('http://google.com/searchbyimage?image_url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>google</a><a'); | |
buf.push(attrs({ 'href':('http://regex.info/exif.cgi/exif.cgi?imgurl=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>exif</a><a'); | |
buf.push(attrs({ 'href':('http://archive.foolz.us/' + (board.name) + '/search/image/' + (encodeURIComponent(locals.post.image.md5)) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>foolz</a></div><a'); | |
buf.push(attrs({ 'target':('_blank'), 'href':(locals.post.image.url), 'data-width':(locals.post.image.width), 'data-height':(locals.post.image.height), "class": ('file') }, {"target":true,"href":true,"data-width":true,"data-height":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(locals.post.image.thumb.url), 'width':(locals.post.image.thumb.width), 'height':(locals.post.image.thumb.height), "class": ('thumb') }, {"src":true,"width":true,"height":true})); | |
buf.push('/></a>'); | |
} | |
if ( locals.post.deletedImage) | |
{ | |
buf.push('<img alt="File deleted." src="//static.4chan.org/image/filedeleted.gif" class="deleted-image"/>'); | |
} | |
buf.push('<div class="comment">'); | |
var __val__ = locals.post.comment | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</div><footer class="backlinks">'); | |
var __val__ = locals.post.backlinks() | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</footer></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
var out$ = typeof exports != 'undefined' && exports || this, split$ = ''.split, slice$ = [].slice; | |
(function(){ | |
var board, x$, ref$, page, that, onready, onupdate, onpostinsert, onbacklink; | |
console.group("html5chan"); | |
console.timeStamp("html5chan-init"); | |
console.time("init"); | |
console.time("interactive"); | |
out$.board = board = {}; | |
x$ = board; | |
ref$ = split$.call(window.location.pathname, '/'), x$.name = ref$[1], page = ref$[2], x$.threadNo = ref$[3]; | |
x$.isThread = !!x$.threadNo; | |
x$.isBoard = !x$.isThread; | |
x$.page = parseInt(page, 10) || 0; | |
x$.url = "//boards.4chan.org/" + x$.name + "/"; | |
x$.threadurl = x$.url + 'res/'; | |
x$.threadPath = "/" + x$.name + "/res/" + x$.threadNo; | |
x$.archive = (function(){ | |
switch (that = x$.name) { | |
case 'a': | |
case 'jp': | |
case 'm': | |
case 'tg': | |
case 'u': | |
case 'tv': | |
case 'v': | |
case 'vg': | |
return "http://archive.foolz.us/" + that + "/thread"; | |
case 'lit': | |
return "http://fuuka.warosu.org/" + that + "/thread"; | |
case 'diy': | |
case 'g': | |
case 'sci': | |
return "http://archive.installgentoo.net/" + that + "/thread"; | |
} | |
}()); | |
x$.ready = false; | |
if (/404/.test(document.title) && board.archive) { | |
if (that = /\d+/.exec(window.location.pathname)) { | |
window.location = board.archive + "/" + that[0]; | |
return; | |
} | |
} | |
out$.onready = onready = function(it){ | |
document.addEventListener('html5chan-ready', it); | |
}; | |
out$.onupdate = onupdate = function(it){ | |
document.addEventListener('html5chan-update', it); | |
}; | |
out$.onpostinsert = onpostinsert = function(it){ | |
document.addEventListener('html5chan-postinsert', it); | |
}; | |
out$.onbacklink = onbacklink = function(it){ | |
document.addEventListener('html5chan-backlink', it); | |
}; | |
}.call(this)); | |
(function(){ | |
var truncate; | |
out$.truncate = truncate = function(it, length){ | |
length == null && (length = 20); | |
if (it.length > length) { | |
return it.substring(0, length) + "..."; | |
} else { | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var delay, deadZone, tooltip; | |
delay = 200; | |
deadZone = 10; | |
out$.tooltip = tooltip = function(arg$){ | |
var show, hide; | |
show = arg$.show, hide = arg$.hide; | |
return function(e){ | |
var x, y, timeout, lastEvent, createTooltip, resetTimeout, removeTooltip, this$ = this; | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
createTooltip = function(){ | |
show.call(this$, lastEvent); | |
listen(this$).off('mousemove', resetTimeout).on('mousemove', removeTooltip); | |
}; | |
resetTimeout = function(e){ | |
clearTimeout(timeout); | |
timeout = setTimeout(createTooltip, delay); | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
}; | |
removeTooltip = function(arg$){ | |
var cx, cy; | |
cx = arg$.clientX, cy = arg$.clientY; | |
if (Math.abs(x - cx) > deadZone || Math.abs(y - cy) > deadZone) { | |
hide.apply(this, arguments); | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
} | |
}; | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).once('mouseout', function(){ | |
hide.apply(this, arguments); | |
clearTimeout(timeout); | |
listen(this).off('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
}); | |
}; | |
}; | |
}.call(this)); | |
(function(){ | |
var $, $$, L, ref$, mutationMacro, x$, classify, closest; | |
out$.$ = $ = function(it){ | |
return document.getElementById(it); | |
}; | |
out$.$$ = $$ = function(it){ | |
return document.querySelectorAll(it); | |
}; | |
out$.L = L = function(it){ | |
return document.createElement(it); | |
}; | |
(ref$ = Element.prototype).matchesSelector == null && (ref$.matchesSelector = Element.prototype.mozMatchesSelector); | |
mutationMacro = function(nodes){ | |
var node, i$, len$, n; | |
if (nodes.length === 1) { | |
return typeof nodes[0] === 'string' | |
? document.createTextNode(nodes[0]) | |
: nodes[0]; | |
} | |
node = document.createDocumentFragment(); | |
for (i$ = 0, len$ = nodes.length; i$ < len$; ++i$) { | |
n = nodes[i$]; | |
if (typeof n === 'string') { | |
n = document.createTextNode(n); | |
} | |
node.appendChild(n); | |
} | |
return node; | |
}; | |
x$ = Node.prototype; | |
x$.prepend == null && (x$.prepend = function(){ | |
this.insertBefore(mutationMacro(arguments), this.firstChild); | |
}); | |
x$.append == null && (x$.append = function(){ | |
this.appendChild(mutationMacro(arguments)); | |
}); | |
x$.before == null && (x$.before = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this); | |
}); | |
x$.after == null && (x$.after = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this.nextSibling); | |
}); | |
x$.replace == null && (x$.replace = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.replaceChild(mutationMacro(arguments), this); | |
}); | |
x$.remove == null && (x$.remove = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.removeChild(this); | |
}); | |
out$.classify = classify = (function(){ | |
classify.displayName = 'classify'; | |
var prototype = classify.prototype, constructor = classify; | |
function classify(els){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.els = els; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.add = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.add(it); | |
} | |
}; | |
prototype.remove = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.remove(it); | |
} | |
}; | |
prototype.toggle = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.toggle(it); | |
} | |
}; | |
return classify; | |
}()); | |
out$.closest = closest = function(selector, el){ | |
for (; el; el = el.parentElement) { | |
if (el.matchesSelector(selector)) { | |
return el; | |
} | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var onPosts; | |
out$.onPosts = onPosts = function(listenerSpec){ | |
onpostinsert(function(it){ | |
var selector, ref$, listeners, i$, x$, ref1$, len$, event, listener; | |
for (selector in ref$ = listenerSpec) { | |
listeners = ref$[selector]; | |
for (i$ = 0, len$ = (ref1$ = it.detail.post.querySelectorAll(selector)).length; i$ < len$; ++i$) { | |
x$ = ref1$[i$]; | |
for (event in listeners) { | |
listener = listeners[event]; | |
x$.addEventListener(event, listener); | |
} | |
} | |
} | |
}); | |
}; | |
}.call(this)); | |
(function(){ | |
var pluralize; | |
pluralize = function(number, unit){ | |
return Math.round(number) + " " + unit + (number >= 1.5 ? 's' : '') + " ago"; | |
}; | |
Date.prototype.relativeTime = function(){ | |
var days, diff, hours, minutes, seconds; | |
if ((days = (diff = Date.now() - this.getTime()) / 86400000) > 1) { | |
return pluralize(days, 'day'); | |
} else if ((hours = days * 24) > 1) { | |
return pluralize(hours, 'hour'); | |
} else if ((minutes = hours * 60) > 1) { | |
return pluralize(minutes, 'minute'); | |
} else if ((seconds = minutes * 60) >= 1) { | |
return pluralize(seconds, 'second'); | |
} else { | |
return 'from the future!'; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var listen; | |
out$.listen = listen = (function(){ | |
listen.displayName = 'listen'; | |
var prototype = listen.prototype, constructor = listen; | |
function listen(element){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.element = element; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.on = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, handler); | |
} | |
return this; | |
}; | |
prototype.once = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, (function(){ | |
function once(e){ | |
var target; | |
target = e.target; | |
this.removeEventListener(event, once); | |
return handler.apply(this, arguments); | |
} | |
return once; | |
}())); | |
} | |
return this; | |
}; | |
prototype.off = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.removeEventListener(event, handler); | |
} | |
return this; | |
}; | |
['on', 'once', 'off'].forEach(function(method){ | |
var original; | |
original = prototype[method]; | |
prototype[method] = function(event, handler){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = split$.call(event, ' ')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
original.call(this, x$, handler); | |
} | |
return this; | |
}; | |
}); | |
['click', 'mouseover', 'scroll'].forEach(function(e){ | |
prototype[e] = function(selector, handler){ | |
return this.on(e, selector, handler); | |
}; | |
}); | |
return listen; | |
}()); | |
}.call(this)); | |
(function(){ | |
var debounce, defer, repeat; | |
out$.debounce = debounce = function(delay, fn){ | |
var timeout; | |
return function(){ | |
var ctx, args; | |
ctx = this; | |
args = arguments; | |
clearTimeout(timeout); | |
timeout = setTimeout(function(){ | |
fn.apply(ctx, args); | |
}, delay); | |
}; | |
}; | |
out$.defer = defer = function(delay, fn){ | |
var args; | |
if (typeof delay === 'function') { | |
fn = delay; | |
delay = 4; | |
args = Array.prototype.slice.call(arguments, 2); | |
} else { | |
args = Array.prototype.slice.call(arguments, 1); | |
} | |
return setTimeout.apply(null, [fn, delay].concat(args)); | |
}; | |
out$.repeat = repeat = (function(){ | |
repeat.displayName = 'repeat'; | |
var prototype = repeat.prototype, constructor = repeat; | |
function repeat(delay, options, fn){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.delay = delay; | |
if (typeof options === 'function') { | |
fn = options; | |
options = {}; | |
} | |
this$.fn = fn; | |
this$.timeoutee = function(){ | |
this$.fn.apply(this$, arguments); | |
if (this$.auto) { | |
this$.timeout = this$.repeat(); | |
} | |
}; | |
this$.auto = options.auto != null ? options.auto : true; | |
if (options.start !== false) { | |
this$.start(); | |
} | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.stop = function(){ | |
clearTimeout(this.timeout); | |
}; | |
prototype.start = function(){ | |
var args; | |
args = slice$.call(arguments); | |
this.stop(); | |
this.timeout = setTimeout.apply(null, [this.timeoutee, this.delay].concat(args)); | |
}; | |
prototype.restart = prototype.start; | |
prototype.repeat = prototype.start; | |
return repeat; | |
}()); | |
}.call(this)); | |
(function(){ | |
var setter, getter, ref$; | |
setter = function(storage){ | |
return function(key, val){ | |
var obj, ref$; | |
if (val != null) { | |
obj = (ref$ = {}, ref$[key] = val, ref$); | |
} | |
for (key in ref$ = obj || key) { | |
val = ref$[key]; | |
storage.setItem("html5chan-" + key, JSON.stringify(val)); | |
} | |
}; | |
}; | |
getter = function(storage){ | |
return function(it){ | |
try { | |
return JSON.parse(storage.getItem("html5chan-" + it)); | |
} catch (e$) {} | |
}; | |
}; | |
ref$ = out$; | |
ref$.set = setter(localStorage); | |
ref$.get = getter(localStorage); | |
ref$.sset = setter(sessionStorage); | |
ref$.sget = getter(sessionStorage); | |
}.call(this)); | |
(function(){ | |
var Post; | |
out$.Post = Post = (function(){ | |
Post.displayName = 'Post'; | |
var prototype = Post.prototype, constructor = Post; | |
prototype.postprocess = function(){ | |
var that, i$, len$, link, quoted, backlinks, ref$; | |
if (that = this.comment.match(/>>\d+/g)) { | |
for (i$ = 0, len$ = that.length; i$ < len$; ++i$) { | |
link = that[i$]; | |
quoted = link.substring(8); | |
backlinks = (ref$ = Post.backlinks)[quoted] || (ref$[quoted] = {}); | |
if (!backlinks[this.no]) { | |
((ref$ = Post.newBacklinks)[quoted] || (ref$[quoted] = {}))[this.no] = true; | |
backlinks[this.no] = true; | |
} | |
} | |
} | |
return constructor[this.no] = this; | |
}; | |
prototype.backlinks = function(onlyNew, postEl){ | |
var html, backlinks, post, idx; | |
html = ""; | |
backlinks = onlyNew | |
? Post.newBacklinks | |
: Post.backlinks; | |
if (backlinks[this.no]) { | |
for (post in backlinks[this.no]) { | |
if (board.isThread) { | |
idx = Post[post].idx; | |
} else { | |
idx = post; | |
} | |
html += "<a href=\"#p" + post + "\" class=\"backlink quotelink\">«" + idx + "</a> "; | |
if (onlyNew) { | |
document.dispatchEvent(new CustomEvent('html5chan-backlink', { | |
detail: { | |
no: post, | |
post: postEl | |
} | |
})); | |
} | |
} | |
} | |
return html; | |
}; | |
prototype.className = function(){ | |
var c, that; | |
c = "post "; | |
if (this.image) { | |
c += 'imagepost '; | |
} | |
if (this.sage) { | |
c += 'sage '; | |
} | |
if (that = this.tripcode) { | |
c += "tripcoded " + that + " "; | |
} | |
if (this.capcode) { | |
c += this.capcode === "## Admin" ? 'admin ' : 'mod '; | |
} | |
if (that = this.uid) { | |
c += "uid " + that; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
classes == null && (classes = ''); | |
return templates.post({ | |
container: container, | |
classes: classes, | |
id: id, | |
post: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, wrapper; | |
classes == null && (classes = ''); | |
x$ = wrapper = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return wrapper.firstElementChild; | |
}; | |
Object.defineProperty(prototype, 'text', { | |
get: function(){ | |
var x$; | |
x$ = L('div'); | |
return x$.innerHTML = this.comment, x$.textContent; | |
}, | |
configurable: true, | |
enumerable: true | |
}); | |
constructor.backlinks = {}; | |
constructor.newBacklinks = {}; | |
constructor.tripcodes = {}; | |
constructor.uids = {}; | |
function Post(){} | |
return Post; | |
}()); | |
}.call(this)); | |
(function(){ | |
var Thread; | |
out$.Thread = Thread = (function(){ | |
Thread.displayName = 'Thread'; | |
var prototype = Thread.prototype, constructor = Thread; | |
prototype.postprocess = function(){ | |
var i$, ref$, len$, reply; | |
this.posts = [this.op].concat(this.replies); | |
this.imageReplies = []; | |
this.reply = {}; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (reply.image) { | |
this.imageReplies.push(reply); | |
} | |
this.reply[reply.no] = reply; | |
} | |
if (Thread[this.no]) { | |
this['new'] = []; | |
this.deleted = []; | |
for (i$ = 0, len$ = (ref$ = Thread[this.no].replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!this.reply[reply.no]) { | |
this.deleted.push(reply); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!Thread[this.no].reply[reply.no]) { | |
this['new'].push(reply); | |
} | |
} | |
} | |
return Thread[this.no] = this; | |
}; | |
prototype.className = function(){ | |
var c; | |
c = 'thread '; | |
if (this.sticky) { | |
c += 'sticky '; | |
} | |
if (this.locked) { | |
c += ' locked'; | |
} | |
if (this.preview) { | |
c += ' preview'; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
container == null && (container = 'article'); | |
classes == null && (classes = ''); | |
return templates.thread({ | |
container: container, | |
classes: classes, | |
id: id, | |
thread: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, d; | |
x$ = d = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return d.firstElementChild; | |
}; | |
function Thread(){} | |
return Thread; | |
}()); | |
}.call(this)); | |
(function(){ | |
var dimensionRegex, sizeRegex, filenameRegex, spoilerRegex, sageRegex, parseThread, parsePost, parser, thumbsBase, imagesBase, humanized, parseApiPost; | |
dimensionRegex = /(\d+)x(\d+)/; | |
sizeRegex = /[\d\.]+ [KM]?B/; | |
filenameRegex = /title="([^"]+)"/; | |
spoilerRegex = /^Spoiler Image/; | |
sageRegex = /^sage$/i; | |
parseThread = function(el){ | |
var x$, omitted; | |
x$ = new Thread; | |
x$.no = el.id.substring(1); | |
x$.url = board.threadurl + x$.no; | |
x$.preview = true; | |
if (omitted = el.querySelector('.summary')) { | |
x$.omitted = { | |
replies: parseInt(omitted.textContent.match(/\d+(?= posts?)/), 10) || 0, | |
imageReplies: parseInt(omitted.textContent.match(/\d+(?= image (?:replies|reply))/), 10) || 0 | |
}; | |
} | |
x$.sticky = el.querySelector('.stickyIcon') != null; | |
x$.closed = el.querySelector('.closedIcon') != null; | |
x$.op = parsePost.call(x$, el.querySelector('.op')); | |
x$.op.idx = 0; | |
x$.replies = Array.prototype.map.call(el.getElementsByClassName('reply'), parsePost, x$); | |
x$.postprocess(); | |
return x$; | |
}; | |
parsePost = function(el, idx){ | |
var thread, x$, ref$, that, img, thumb, info, dimensions; | |
thread = this; | |
x$ = new Post; | |
x$.idx = 1 + idx + (((ref$ = thread.omitted) != null ? ref$.replies : void 8) || 0); | |
x$.thread = thread; | |
x$.no = el.id.substring(1); | |
x$.url = (x$.op = el.classList.contains('op')) | |
? thread.url | |
: thread.url + "#p" + x$.no; | |
x$.time = new Date(parseInt(el.querySelector('.dateTime').dataset.utc, 10) * 1000); | |
x$.subject = el.querySelector('.postInfo.desktop .subject').innerHTML; | |
x$.name = el.querySelector('.name').innerHTML; | |
x$.tripcode = (ref$ = el.querySelector('.postertrip')) != null ? ref$.innerHTML : void 8; | |
x$.capcode = (ref$ = el.querySelector('.capcode')) != null ? ref$.innerHTML : void 8; | |
x$.email = (ref$ = el.querySelector('.useremail')) != null ? ref$.href.substring(7) : void 8; | |
if (that = x$.email) { | |
x$.sage = sageRegex.test(that); | |
} | |
x$.comment = parser.enhance(el.querySelector('.postMessage').innerHTML); | |
x$.uid = (ref$ = el.querySelector('.hand')) != null ? ref$.textContent : void 8; | |
if (img = el.querySelector('.fileThumb')) { | |
if (img.firstElementChild.alt === "File deleted.") { | |
x$.deletedImage = true; | |
} else { | |
thumb = img.firstElementChild; | |
info = el.querySelector('.fileInfo').innerHTML; | |
dimensions = dimensionRegex.exec(info); | |
x$.image = { | |
thumb: { | |
url: thumb.src, | |
width: parseInt(thumb.style.width, 10), | |
height: parseInt(thumb.style.height, 10) | |
}, | |
url: thumb.parentNode.href, | |
width: parseInt(dimensions[1], 10), | |
height: parseInt(dimensions[2], 10), | |
size: sizeRegex.exec(thumb.alt)[0], | |
filename: (ref$ = filenameRegex.exec(info)) != null ? ref$[1] : void 8, | |
md5: thumb.dataset.md5, | |
spoiler: spoilerRegex.test(thumb.alt) | |
}; | |
} | |
} | |
x$.postprocess(); | |
return x$; | |
}; | |
out$.parser = parser = { | |
board: function(document){ | |
var threads; | |
console.time("parse board"); | |
threads = Array.prototype.map.call(document.querySelectorAll('.thread'), parseThread); | |
console.timeEnd("parse board"); | |
return threads; | |
}, | |
thread: function(document){ | |
var thread; | |
console.time("parse thread"); | |
thread = parseThread(document.querySelector('.thread')); | |
console.timeEnd("parse thread"); | |
return thread; | |
}, | |
api: function(data){ | |
var op, x$, ref$; | |
op = data.posts[0]; | |
x$ = new Thread; | |
x$.no = op.no; | |
x$.url = board.threadurl + op.no; | |
x$.preview = !!op.omitted_posts; | |
x$.sticky = !!op.sticky; | |
x$.closed = !!op.closed; | |
ref$ = data.posts.map(parseApiPost, x$), x$['op'] = ref$[0], x$['replies'] = slice$.call(ref$, 1); | |
x$.postprocess(); | |
return x$; | |
} | |
}; | |
thumbsBase = "//thumbs.4chan.org/" + board.name + "/thumb/"; | |
imagesBase = "//images.4chan.org/" + board.name + "/src/"; | |
humanized = function(bytes){ | |
var kbytes; | |
if (bytes < 1024) { | |
return bytes + " B"; | |
} else if ((kbytes = Math.round(bytes / 1024)) < 1024) { | |
return kbytes + " KB"; | |
} else { | |
return (kbytes / 1024).toString().substring(0, 3) + " MB"; | |
} | |
}; | |
parseApiPost = function(data, i){ | |
var x$, that; | |
x$ = new Post; | |
x$.idx = i; | |
x$.thread = this; | |
x$.url = this.url; | |
x$.time = new Date(data.time * 1000); | |
x$.no = data.no; | |
x$.subject = data.sub; | |
x$.name = data.name; | |
x$.tripcode = data.trip; | |
x$.uid = data.id; | |
x$.capcode = data.capcode; | |
x$.email = data.email; | |
x$.sage = x$.email === 'sage'; | |
x$.comment = (that = data.com) ? parser.enhance(that) : ''; | |
x$.image = data.fsize ? { | |
thumb: { | |
url: thumbsBase + data.tim + 's.jpg', | |
width: data.tn_w, | |
height: data.tn_h | |
}, | |
url: imagesBase + "" + data.tim + data.ext, | |
width: data.w, | |
height: data.h, | |
size: humanized(data.fsize), | |
filename: data.filename + "" + data.ext, | |
md5: data.md5, | |
spoiler: !!data.spoiler | |
} : void 8; | |
x$.deletedImage = !!data.filedeleted; | |
x$.postprocess(); | |
return x$; | |
}; | |
}.call(this)); | |
(function(){ | |
parser.enhance = function(it){ | |
if (it.length === 0) { | |
return it; | |
} | |
return it.replace(/<wbr>/g, '').replace(/(?:https?:\/\/)?(?:www\.)?(youtu\.be\/([\w\-_]+)(\?[&=\w\-_;\#]*)?|youtube\.com\/watch\?([&=\w\-_;\.\?\#\%]*)v=([\w\-_]+)([&=\w\-\._;\?\#\%]*))/g, '<a href="https://$1" class="youtube" data-id="$2$5" data-params="$3$4$6" target="_blank"><img src="//img.youtube.com/vi/$2$5/2.jpg"></a>').replace(/\((https?:\/\/)([^<\s\)]+)\)/g, '(<a class="external" rel="noreferrer" href="$1$2" title="$1$2" target="_blank">$2</a>)').replace(/([^"']|^)(https?:\/\/)([^<\s]+)/g, '$1<a class="external" rel="noreferrer" href="$2$3" title="$2$3" target="_blank">$3</a>').replace(/(^|>|;|\s)([\w\.\-]+\.(?:com|net|org|eu|jp|us|co\.uk)(\/[^<\s]*)?(?=[\s<]|$))/g, '$1<a class="external" rel="noreferrer" href="http://$2" title="$2" target="_blank">$2</a>').replace(/<span\x20class="deadlink">>>(\d+)<\/span>/g, board.archivelink); | |
}; | |
board.archivelink = board.archive ? "<a href=\"" + board.archive + "/$1\" class=\"deadlink\">>>$1</a>" : '$&'; | |
}.call(this)); | |
(function(){ | |
var lastUpdate, unread, favicons, x$, y$, drawFavicon, updater, fade, fadeWhenVisible; | |
lastUpdate = new Date; | |
unread = 0; | |
favicons = { | |
sfw: (x$ = L('img'), x$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv8AAAD/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/AAAA/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfj/////////////w==', x$), | |
nsfw: (y$ = L('img'), y$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8AAAD/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/AAAA/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfjAAD//////////w==', y$) | |
}; | |
drawFavicon = debounce(200, function(){ | |
var ref$, x$, link, y$, z$; | |
if ((ref$ = $('favicon')) != null) { | |
ref$.remove(); | |
} | |
x$ = link = L('link'); | |
x$.id = 'favicon'; | |
x$.rel = 'icon'; | |
x$.type = 'image/x-icon'; | |
y$ = L('canvas'); | |
y$.width = 16; | |
y$.height = 16; | |
z$ = y$.getContext('2d'); | |
z$.drawImage(favicons[board.type], 0, 0); | |
if (unread > 0) { | |
z$.font = '8px monospace'; | |
z$.fillStyle = '#000'; | |
z$.strokeStyle = '#fff'; | |
z$.lineWidth = 4; | |
z$.textBaseline = 'bottom'; | |
z$.textAlign = 'right'; | |
z$.strokeText(unread, 16, 16); | |
z$.fillText(unread, 16, 16); | |
} | |
link.href = y$.toDataURL('image/png'); | |
document.head.appendChild(link); | |
}); | |
out$.updater = updater = { | |
update: function(){ | |
var x$; | |
updater.status.textContent = "Updating thread..."; | |
updater.button.disabled = true; | |
x$ = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board.name + "/res/" + board.thread.no + ".json"); | |
x$.setRequestHeader('If-Modified-Since', lastUpdate.toUTCString()); | |
listen(x$).on('load', function(){ | |
var lastModified, thread, i$, ref$, len$, post, backlinks, last; | |
if (this.status === 404) { | |
document.title += '(dead)'; | |
updater.status.textContent = "thread 404'd"; | |
return; | |
} | |
if (this.status === 304) { | |
updater.countdown.restart(); | |
return; | |
} | |
lastModified = new Date(this.getResponseHeader('Last-Modified')); | |
if (!(lastModified > lastUpdate)) { | |
updater.countdown.restart(); | |
return; | |
} | |
updater.status.textContent = "update detected, parsing"; | |
lastUpdate = lastModified; | |
thread = parser.api(JSON.parse(this.response)); | |
if (thread['new'].length > 0) { | |
$("t" + thread.no).lastElementChild.insertAdjacentHTML('beforeend', (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'new reply')); | |
} | |
return results$; | |
}()).join('')); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: $("p" + post.no) | |
} | |
})); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.thread .backlinks')).length; i$ < len$; ++i$) { | |
backlinks = ref$[i$]; | |
backlinks.insertAdjacentHTML('beforeend', Post[backlinks.parentNode.dataset.no].backlinks(true, backlinks.parentNode)); | |
} | |
Post.newBacklinks = {}; | |
document.dispatchEvent(new CustomEvent('html5chan-update', { | |
detail: { | |
thread: thread | |
} | |
})); | |
unread += thread['new'].length; | |
drawFavicon(); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
fadeWhenVisible(post); | |
} | |
if (window.scrollMaxY - window.scrollY < 50 && !document.hidden) { | |
last = window.scrollY; | |
repeat(50, function(){ | |
var remaining; | |
if (last > window.scrollY) { | |
this.stop(); | |
} else if ((remaining = window.scrollMaxY - window.scrollY) > 1) { | |
window.scrollBy(0, remaining / 4); | |
last = window.scrollY; | |
} | |
}); | |
} | |
$("t" + thread.no).querySelector(".thread-info").textContent = thread.replies.length + " replies and " + thread.imageReplies.length + " image replies."; | |
} | |
updater.countdown.restart(); | |
}).on('timeout', function(){ | |
updater.status.textContent = "request timed out..."; | |
updater.countdown(); | |
}).on('error', function(){ | |
updater.status.textContent = "Couldn't fetch thread page!"; | |
}).on('loadend', function(){ | |
updater.button.disabled = false; | |
}); | |
x$.send(); | |
}, | |
countdown: repeat(1000, { | |
start: false | |
}, function(t){ | |
this.t = t || this.t || 30; | |
updater.status.textContent = "Updating in " + this.t + " seconds..."; | |
if (--this.t === 0) { | |
this.stop(); | |
updater.update(); | |
} | |
}) | |
}; | |
fade = function(post){ | |
defer(100, function(){ | |
post.classList.remove('new'); | |
--unread; | |
drawFavicon(); | |
}); | |
}; | |
fadeWhenVisible = function(it){ | |
var post, y; | |
post = $("p" + it.no); | |
y = post.offsetTop; | |
if (window.innerHeight + window.scrollY > y) { | |
if (document.hidden) { | |
listen(window).once('focus', function(){ | |
fade(post); | |
}); | |
} else { | |
fade(post); | |
} | |
} else { | |
listen(window).scroll((function(){ | |
function reset(){ | |
if (window.innerHeight + window.scrollY > post.offsetTop) { | |
fade(post); | |
return listen(window).off('scroll', reset); | |
} | |
} | |
return reset; | |
}())); | |
} | |
}; | |
onready(function(){ | |
updater.status = $('update-status'); | |
updater.button = $('update-now'); | |
if (board.isThread) { | |
updater.countdown.start(); | |
listen($('update-now')).click(function(){ | |
var x$; | |
x$ = updater.countdown; | |
x$.stop(); | |
x$.t = 30; | |
updater.update(); | |
}); | |
} else { | |
$('updater').hidden = true; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var postStatus; | |
postStatus = function(it){ | |
return $('post-status').textContent = it; | |
}; | |
onready(function(){ | |
var checkValidity, cooldown, ref$; | |
checkValidity = function(e){ | |
var form, captcha, file, comment, email, ref$, x$, data, y$; | |
e.preventDefault(); | |
form = $('postform'); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
email = $('email'); | |
if (/^noko$/i.test(email.value)) { | |
email.value = ''; | |
} | |
captcha.setCustomValidity(!captcha.value ? "You forgot the captcha!" : ''); | |
file.setCustomValidity(!file.value && board.isBoard ? "You forgot your image!" : ''); | |
comment.setCustomValidity(!file.value && !comment.value ? "You didn't enter a comment or select a file!" : ''); | |
if (form.checkValidity()) { | |
$('post').disabled = true; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = true; | |
} | |
postStatus("Posting..."); | |
x$ = $('progress'); | |
x$.hidden = false; | |
x$.value = 0; | |
data = new FormData(form); | |
if (this === $('sage')) { | |
data.append('email', 'sage'); | |
} | |
y$ = new XMLHttpRequest; | |
y$.open('POST', form.action); | |
listen(y$).on('load', function(){ | |
var x$, html, captcha, file, comment, ref$, y$; | |
x$ = html = L('div'); | |
x$.innerHTML = this.response; | |
console.log(html); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
$('post').disabled = false; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = false; | |
} | |
if (/Post successful!|uploaded!/.test(html.textContent)) { | |
postStatus('Post successful!'); | |
cooldown(); | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
$('recaptcha_image').click(); | |
updater.countdown.restart(3); | |
return parser.lastParse = 0; | |
} else if (/mistyped the verification/.test(html.textContent)) { | |
postStatus('You mistyped the verification!'); | |
$('recaptcha_image').click(); | |
y$ = captcha; | |
y$.value = ''; | |
y$.focus(); | |
return y$; | |
} else if (/duplicate file entry detected/) { | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
return $('recaptcha_image').click(); | |
} | |
}).on('loadend', function(){ | |
return $('progress').hidden = true; | |
}); | |
listen(y$.upload).on('progress', function(e){ | |
return $('progress').value = 100 * e.loaded / e.total; | |
}); | |
y$.send(data); | |
} | |
return false; | |
}; | |
listen($('post')).click(checkValidity); | |
listen($('sage')).click(checkValidity); | |
cooldown = function(){ | |
var post, sage, message, tminus; | |
post = $('post'); | |
sage = $('sage'); | |
post.disabled = true; | |
if (sage != null) { | |
sage.disabled = true; | |
} | |
message = post.textContent; | |
tminus = 30; | |
post.textContent = tminus; | |
return setTimeout((function(){ | |
function tick(){ | |
if (tminus-- === 0) { | |
post.textContent = message; | |
post.disabled = false; | |
return sage != null ? sage.disabled = false : void 8; | |
} else { | |
post.textContent = tminus; | |
return setTimeout(tick, 1000); | |
} | |
} | |
return tick; | |
}()), 1000); | |
}; | |
listen($('name')).on('input', function(){ | |
return set({ | |
name: this.value | |
}); | |
}); | |
if ((ref$ = $('name')) != null) { | |
ref$.value = get('name') || ''; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var x$, html, y$, head, z$, z1$, body, d; | |
x$ = html = L('html'); | |
x$.appendChild((y$ = head = L('head'), y$.appendChild(L('title')), y$.appendChild((z$ = L('style'), z$.id = 'html5chan-style', z$.textContent = ' html {\n min-height: 100%;\n font-family: Droid Serif, serif;\n font-size: 10pt;\n}\n::selection {\n background: #29df75;\n color: #000;\n}\n::-moz-selection {\n background: #29df75;\n color: #000;\n}\n[hidden] {\n display: none !important;\n}\nbutton:enabled {\n cursor: pointer;\n}\n.bold {\n font-weight: bold;\n}\n.smaller {\n font-size: smaller;\n}\n#toplinks {\n float: right;\n width: 300px;\n}\n#header {\n margin: 1em 0;\n color: #af0a0f;\n}\n#board-name {\n font-size: 24pt;\n margin: 0;\n}\n#board-name a {\n color: #af0a0f !important;\n text-decoration: none;\n}\n#board-name a:hover {\n text-decoration: underline;\n}\n#board-subtitle {\n font-size: 10px;\n font-weight: normal;\n}\n#banner {\n margin-right: 1em;\n float: left;\n}\n#motd {\n margin: 1em 0;\n}\n#hide-motd {\n text-align: right;\n font-size: 10pt;\n}\n#message {\n clear: both;\n}\n.boardlinks {\n font-size: 9pt;\n text-align: center;\n}\n.boardlinks a {\n text-decoration: none;\n}\n#threads {\n clear: both;\n}\n#pages {\n text-align: center;\n margin: 0pt;\n padding: 0pt;\n}\n#pages li {\n display: inline;\n}\n#pages a {\n border-color: #aaa;\n border-style: solid;\n border-width: 1px 0;\n color: #000;\n display: inline-block;\n margin: 0.25em;\n padding: 0.5em 1em;\n text-decoration: none;\n}\n#pages a#current,\n#pages a:hover {\n background-color: rgba(200,200,200,0.7);\n}\n#updater {\n float: right;\n}\n.post {\n margin: 0.2em;\n padding: 1em;\n padding-right: 0;\n border-radius: 0.3em;\n}\n.reply {\n margin-left: 2em;\n transition-property: background-color;\n transition-duration: 3s;\n}\n.reply.new {\n background: #feffbf noise !important;\n}\n.sage > .post-header > .name:after {\n content: " (sage)";\n}\n.reply:before,\n.inlined-idx {\n content: attr(data-idx);\n position: absolute;\n text-align: right;\n display: inline-block;\n margin-left: -3em;\n width: 2em;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.inlined-idx {\n cursor: pointer;\n}\n.inlined-idx:hover {\n text-decoration: underline;\n}\n.post-header {\n margin: 0;\n padding: 0;\n font-size: 8pt;\n font-family: sans-serif;\n color: sfw-border -10%;\n font-weight: normal;\n float: right;\n}\n.post .subject {\n color: #0f0c5d;\n font-weight: 800;\n text-decoration: none;\n}\n.post .subject:hover {\n text-decoration: underline;\n}\n.name {\n color: sfw-border -10%;\n}\n.name:link {\n text-decoration: underline;\n}\n.tripcode,\n.fileinfo {\n display: table;\n color: sfw-border -20%;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.tripcode:not(:hover) > .saucelink,\n.fileinfo:not(:hover) > .saucelink,\n.tripcode:not(:hover) > .dimensions,\n.fileinfo:not(:hover) > .dimensions,\n.tripcode:not(:hover) > .size,\n.fileinfo:not(:hover) > .size {\n transition-delay: 0.5s;\n opacity: 0;\n}\n.saucelink,\n.dimensions,\n.size {\n transition-duration: 0.5s;\n}\n.file {\n display: block;\n float: left;\n margin: 0.3em 1em 0.3em 0;\n position: relative;\n}\n.full {\n display: block;\n}\n.capcode {\n font-weight: 800;\n}\n.mod .capcode:hover,\n.admin .capcode:hover {\n cursor: pointer;\n}\n.admin .name,\n.admin .capcode,\n.admin .tripcode {\n color: #f00;\n}\n.admin .capcode:after {\n content: url("https://static.4chan.org/image/adminicon.gif");\n}\n.mod .name,\n.mod .capcode {\n color: #800080;\n}\n.mod .capcode:after {\n content: url("https://static.4chan.org/image/modicon.gif");\n}\n.hide,\n.report {\n float: right;\n padding: 0 1px;\n background: transparent;\n border: 0;\n}\n.post.hidden {\n opacity: 0.6;\n}\n.post.hidden .file,\n.post.hidden .comment,\n.post.hidden .backlinks,\n.post.hidden .fileinfo {\n display: none;\n}\n.post.inlined {\n display: none;\n}\n.post.inlined:target {\n display: block;\n}\n.post.highlighted {\n background-color: #d6bad0 !important;\n}\n.quotelink {\n text-decoration: none;\n}\n.hiddenlink {\n text-decoration: line-through;\n}\n.replylink {\n text-decoration: none;\n}\n.deadlink {\n color: #808080;\n}\n.permalink {\n text-decoration: none;\n color: inherit;\n}\n.permalink .no:hover {\n text-decoration: underline;\n}\n.recursivelink {\n font-weight: bold;\n color: #000 !important;\n}\n.comment {\n margin: 0;\n word-wrap: break-word;\n line-height: 1.8em;\n width: 40em;\n}\n.op .comment {\n width: 50em;\n}\n.quote {\n font-weight: normal;\n color: #789922;\n}\n.prettyprint {\n background-color: #fff;\n padding: 0.5em;\n display: inline-block;\n max-width: 40em;\n overflow: auto;\n}\ns {\n text-decoration: none;\n transition-duration: 1s;\n}\ns:not(:hover) > *,\ns:not(:hover) {\n color: transparent !important;\n text-shadow: 0 0 7px #000;\n}\n.backlinks {\n clear: both;\n}\n.backlink {\n margin-right: 1em;\n}\na.quotelink.inlinedlink,\nstrong.quotelink.inlinedlink {\n font-weight: bold;\n color: #000;\n}\n#postpreview {\n outline: none;\n padding: 0.5em;\n box-shadow: 5px 5px 10px rgba(0,0,0,0.5);\n margin: 0;\n}\n.inline {\n margin-right: 0;\n padding-right: 0;\n}\n.comment .inline {\n display: table;\n}\n.backlink + .inline {\n margin-left: 2em;\n}\n.inline .backlinks > .recursivelink {\n display: none;\n}\n.forcedimage {\n text-decoration: none;\n}\n.backlink.inlinedlink {\n display: table;\n}\n.hovered {\n outline: 3px dashed #00f;\n}\n#postform {\n display: table;\n margin: 1em auto;\n}\n#postform #comment,\n#postform #recaptcha_response_field {\n width: 100%;\n}\n#name,\n#email,\n#subject {\n width: 31.3%;\n}\n#recaptcha_image {\n display: block;\n background: #fff;\n width: 100% !important;\n}\n#recaptcha_image img {\n display: block;\n margin: auto;\n}\n.thread {\n padding-bottom: 5px;\n clear: both;\n}\n.thread-info {\n clear: left;\n text-align: right;\n}\n.thread.hidden {\n opacity: 0.6;\n}\n.thread.hidden .replies,\n.thread.hidden .thread-info {\n display: none;\n}\n.thread.hidden .op .file,\n.thread.hidden .op .comment,\n.thread.hidden .op .backlinks,\n.thread.hidden .op .fileinfo {\n display: none;\n}\nbody.sfw {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw > header a,\nbody.sfw > footer a,\nbody.sfw .boardlinks a {\n color: #34345c;\n}\nbody.sfw .boardlinks {\n color: #89a;\n}\nbody.sfw .post:target {\n background: #d6bad0 url("data:image/png;base64,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") !important;\n}\nbody.sfw .reply {\n background: linear-gradient(180deg, rgba(0,0,0,0.01), transparent 2em, rgba(255,255,255,0) calc(98%), rgba(255,255,255,0.03));\n}\nbody.sfw #postpreview {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw .reply:before,\nbody.sfw .inlined-idx {\n color: #9db0cb;\n}\nbody.sfw #postpreview.op {\n background-color: #eef2ff;\n}\nbody.sfw .quotelink {\n color: #d00;\n}\nbody.nsfw {\n background: #ffe url("//static.4chan.org/image/fade.png") repeat-x;\n color: #800000;\n}\nbody.nsfw > header a,\nbody.nsfw > footer a,\nbody.nsfw .boardlinks a {\n color: #800;\n}\nbody.nsfw .boardlinks {\n color: #b86;\n}\nbody.nsfw .thread {\n border-color: #808080;\n}\nbody.nsfw .post:target {\n background-color: #f0c0b0 !important;\n}\nbody.nsfw .reply,\nbody.nsfw #postpreview {\n background-color: #d9bfb7;\n}\nbody.nsfw .reply {\n border-color: #d9bfb7;\n}\nbody.nsfw .reply:before {\n color: #d9bfb7;\n}\nbody.nsfw .inlined-idx {\n color: #bd9083;\n}\nbody.nsfw #postpreview.op {\n background-color: #ffe;\n}\nbody.nsfw .quotelink {\n color: #000080;\n}\n.youtube {\n position: relative;\n text-decoration: none;\n border: 3px solid;\n border-color: #c6312b;\n border-radius: 10px;\n transition: 0.5s;\n overflow: hidden;\n display: inline-block;\n vertical-align: top;\n margin: 0.25em;\n width: 120px;\n height: 90px;\n}\n.youtube:hover {\n border-color: #ffa200;\n}\n.youtube:after {\n position: absolute;\n top: 0;\n left: 0;\n width: 115px;\n font-size: smaller;\n font-family: sans-serif;\n color: #fff;\n background: rgba(0,0,0,0.5);\n padding: 0 0.5em;\n content: attr(data-title);\n}\n.youtube:not(:hover):after {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n', z$)), y$.appendChild((z1$ = L('script'), z1$.src = '//www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', z1$.addEventListener('load', function(){ | |
var x$; | |
head.appendChild((x$ = L('script'), x$.src = '//www.google.com/recaptcha/api/js/recaptcha.js', x$.addEventListener('load', function(){ | |
var x$; | |
x$ = L('script'); | |
x$.textContent = "(function() {var c;if (c = document.getElementById('captcha')) {Recaptcha._init_options({theme: 'custom',custom_theme_widget: c});Recaptcha.theme = 'custom';Recaptcha.widget = c;Recaptcha._finish_widget();}}())"; | |
if (board.ready) { | |
head.appendChild(x$); | |
} else { | |
onready(function(){ | |
head.appendChild(x$); | |
}); | |
} | |
}), x$)); | |
}), z1$)), y$)); | |
x$.appendChild(body = L('body')); | |
d = document.replaceChild(html, document.documentElement); | |
document.addEventListener('DOMContentLoaded', function(){ | |
var x$, ref$, thread, threads, y$, z$, i$, len$, post; | |
console.time("initial render"); | |
console.time("parse page"); | |
x$ = board; | |
x$.title = d.querySelector('.boardTitle').textContent; | |
x$.subtitle = ((ref$ = d.querySelector('.boardSubtitle')) != null ? ref$.innerHTML : void 8) || ''; | |
x$.nav = d.querySelector('#boardNavDesktop').innerHTML; | |
x$.banner = d.querySelector('.title').src; | |
x$.motd = (ref$ = d.querySelector('.globalMessage')) != null ? ref$.innerHTML : void 8; | |
x$.sfw = d.querySelector('link[rel="shortcut icon"]').href.slice(-6) === 'ws.ico'; | |
x$.type = x$.sfw ? 'sfw' : 'nsfw'; | |
x$.password = get('password') || Math.random().toString().substr(-8); | |
console.timeEnd("parse page"); | |
console.log(board); | |
if (board.isThread) { | |
board.thread = thread = parser.thread(d); | |
board.threads = threads = [thread]; | |
} else { | |
board.threads = threads = parser.board(d); | |
} | |
console.log(threads); | |
Post.newBacklinks = {}; | |
y$ = body; | |
y$.id = board.name; | |
y$.className = board.type + " " + (board.isThread ? 'threadpage' : 'boardpage'); | |
console.time("generate and render new body"); | |
console.profile(); | |
var bodyHtml = templates.board(board); | |
console.profileEnd(); | |
console.timeEnd("generate and render new body"); | |
console.time( "parse and render new body"); | |
body.innerHTML = bodyHtml; | |
console.timeEnd( "parse and render new body"); | |
if (board.isBoard) { | |
console.time("highlight current page"); | |
body.querySelector("#pages a[href=\"" + (board.page || board.url) + "\"]").id = 'current'; | |
console.timeEnd("highlight current page"); | |
} | |
console.time("set new page title"); | |
document.title = board.isThread | |
? (z$ = board.thread.op, truncate(z$.title || z$.text || ((ref$ = z$.image) != null ? ref$.filename : void 8) || z$.time.relativeTime()) + "\ - /" + board.name + "/") | |
: board.title; | |
console.timeEnd("set new page title"); | |
console.timeEnd("initial render"); | |
if (window.location.hash && !sget(document.URL)) { | |
window.location.hash = window.location.hash; | |
window.addEventListener('scroll', (function(){ | |
function registerPage(){ | |
var ref$; | |
sset((ref$ = {}, ref$[document.URL] = true, ref$)); | |
return window.removeEventListener('scroll', registerPage); | |
} | |
return registerPage; | |
}())); | |
} | |
console.time("initial post insertion handlers"); | |
for (i$ = 0, len$ = (ref$ = $$('.post')).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: post | |
} | |
})); | |
} | |
console.timeEnd("initial post insertion handlers"); | |
board.ready = true; | |
document.dispatchEvent(new CustomEvent('html5chan-ready', { | |
detail: { | |
post: post | |
} | |
})); | |
}); | |
}.call(this)); | |
(function(){ | |
var setUpdate; | |
setUpdate = function(el){ | |
var time, diff; | |
time = new Date(el.getAttribute('datetime')); | |
if ((diff = Date.now() - time.getTime()) < 8640000) { | |
return setTimeout(function(){ | |
el.textContent = time.relativeTime(); | |
return setUpdate(el); | |
}, diff > 3600000 | |
? diff % 3600000 | |
: diff > 60000 ? 300000 : 60000); | |
} | |
}; | |
onready(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = document.getElementsByTagName('time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
onupdate(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = $$('.new time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.file': { | |
click: function(e){ | |
var a, x$, ref$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
a = this; | |
this.hidden = true; | |
this.before((x$ = L('img'), x$.src = this.href, x$.className = 'full', ref$ = x$.style, ref$.display = 'block', ref$.maxWidth = '100%', x$.onclick = function(){ | |
var ref$, top; | |
if (this.width !== this.naturalWidth) { | |
this.style.removeProperty('max-width'); | |
} else { | |
a.hidden = false; | |
if ((ref$ = a.previousSibling) != null) { | |
ref$.remove(); | |
} | |
if (scroll && (top = a.getBoundingClientRect().top) < 0) { | |
window.scrollBy(0, top); | |
} | |
} | |
}, x$)); | |
} | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var objectFit, handlePreview; | |
objectFit = function(container, width, height){ | |
var ratio; | |
ratio = Math.min(1, container.height / height, container.width / width); | |
return { | |
width: ratio * width, | |
height: ratio * height | |
}; | |
}; | |
out$.handlePreview = handlePreview = tooltip({ | |
show: function(){ | |
var a, viewport, ref$, x$; | |
this.style.cursor = 'none'; | |
a = this.parentElement; | |
viewport = { | |
width: (ref$ = document.documentElement).clientWidth, | |
height: ref$.clientHeight | |
}; | |
document.body.append((x$ = L('img'), x$.id = 'imgpreview', x$.alt = "Loading...", x$.src = a.href, ref$ = objectFit(viewport, a.dataset.width, a.dataset.height), x$.width = ref$.width, x$.height = ref$.height, x$.addEventListener('load', function(){ | |
return this.removeAttribute('alt'); | |
}), x$.addEventListener('error', function(){ | |
return this.alt = "Unable to load image."; | |
}), ref$ = x$.style, ref$.position = 'fixed', ref$.left = 0, ref$.top = 0, ref$.pointerEvents = 'none', ref$.backgroundColor = 'rgba(0,0,0,.5)', ref$.padding = (viewport.height - x$.height) / 2 + "px " + (viewport.width - x$.width) / 2 + "px", ref$.transitionDuration = '.5s', ref$.opacity = 0, x$.addEventListener('transitionend', function(e){ | |
var propertyName; | |
propertyName = e.propertyName; | |
if (propertyName === 'opacity' && this.style.opacity === '0') { | |
return this.remove(); | |
} | |
}), defer(100, function(){ | |
x$.style.opacity = 1; | |
}), x$)); | |
}, | |
hide: function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.style.opacity = 0; | |
} | |
defer(100, function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.remove(); | |
} | |
}); | |
this.style.removeProperty('cursor'); | |
} | |
}); | |
onPosts({ | |
'.thumb': { | |
mouseover: handlePreview | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onready(function(){ | |
var hash, msg, btn; | |
hash = function(it){ | |
return it.innerHTML.length; | |
}; | |
if ($('motd')) { | |
msg = $('message'); | |
btn = $('hide-motd'); | |
if (get('motd-hash') === hash(msg)) { | |
msg.hidden = get('motd-hidden'); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
} else { | |
set('motd-hash', hash(msg)); | |
} | |
listen(btn).click(function(){ | |
msg.hidden = !msg.hidden; | |
set('motd-hidden', msg.hidden); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
}); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var threshold, hidden, e, persist, toggle; | |
threshold = 604800000; | |
hidden = { | |
threads: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-t-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()), | |
replies: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-r-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()) | |
}; | |
console.log(hidden); | |
(function(now){ | |
var type, ref$, hash, key, expiry; | |
for (type in ref$ = hidden) { | |
hash = ref$[type]; | |
for (key in hash) { | |
expiry = hash[key]; | |
if (expiry === true) { | |
hash[key] = Date.now(); | |
} else { | |
if (now - expiry > threshold) { | |
delete hash[key]; | |
} | |
} | |
} | |
} | |
}.call(this, Date.now())); | |
persist = function(){ | |
localStorage["4chan-hide-t-" + board.name] = JSON.stringify(hidden.threads); | |
localStorage["4chan-hide-r-" + board.name] = JSON.stringify(hidden.replies); | |
}; | |
toggle = function(prefix, no){ | |
var ref$; | |
classify($$(".quotelink[href$=\"#" + no + "\"]")).toggle('hiddenlink'); | |
return (ref$ = $(prefix + "" + no)) != null ? ref$.classList.toggle('hidden') : void 8; | |
}; | |
onready(function(){ | |
var i$, ref$, len$, btn, x$, no, y$; | |
for (i$ = 0, len$ = (ref$ = $$('.reply button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn$); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.op button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn1$); | |
} | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('reply')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
no = x$.dataset.no; | |
if (hidden.replies[no]) { | |
toggle('p', no); | |
} | |
} | |
if (board.isBoard) { | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('thread')).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
no = y$.dataset.no; | |
if (hidden.threads[no]) { | |
toggle('t', no); | |
} | |
} | |
} | |
function fn$(){ | |
toggle('p', this.value); | |
if (this.value in hidden.replies) { | |
delete hidden.replies[this.value]; | |
} else { | |
hidden.replies[this.value] = Date.now(); | |
} | |
persist(); | |
} | |
function fn1$(){ | |
var ref$; | |
toggle('t', this.value); | |
if (this.value in hidden.threads) { | |
delete hidden.threads[this.value]; | |
} else { | |
hidden.threads[this.value] = (ref$ = Thread[this.value]) != null && ref$.sticky | |
? Number.MAX_VALUE | |
: Date.now(); | |
} | |
persist(); | |
} | |
}); | |
onupdate(function(){ | |
var i$, ref$, len$, a; | |
for (i$ = 0, len$ = (ref$ = $$(".new .quotelink")).length; i$ < len$; ++i$) { | |
a = ref$[i$]; | |
if (a.hash.substring(1) in hidden.replies) { | |
a.classList.toggle('hiddenlink'); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var fetchNewPost, handlePreview, createPreview; | |
fetchNewPost = function(no){ | |
var ref$, board, thread, link, x$, xhr, stillHovered; | |
ref$ = this.pathname.split('/'), board = ref$[1], thread = ref$[3]; | |
link = this; | |
this.style.cursor = 'progress'; | |
x$ = xhr = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board + "/res/" + thread + ".json"); | |
x$.onload = function(){ | |
var thread; | |
if (this.status === 200) { | |
thread = parser.api(JSON.parse(this.response)); | |
if (stillHovered) { | |
link.style.removeProperty('cursor'); | |
createPreview.call(link, no, Post[no]); | |
} | |
} | |
}; | |
x$.send(); | |
stillHovered = true; | |
this.addEventListener('mouseout', (function(){ | |
function out(){ | |
stillHovered = false; | |
this.style.removeProperty('cursor'); | |
return this.removeEventListener('mouseout', out); | |
} | |
return out; | |
}())); | |
}; | |
handlePreview = function(){ | |
var no, post; | |
if (this.classList.contains('inlinedlink') || this.classList.contains('recursivelink')) { | |
return; | |
} | |
no = this.hash.substring(2); | |
if (!(post = Post[no])) { | |
fetchNewPost.call(this, no); | |
} else { | |
createPreview.call(this, no, post); | |
} | |
}; | |
createPreview = function(no, post){ | |
var ref$, host, hostid, width, height, left, top, x$, preview, i$, y$, len$, z$, z1$, ref1$, z2$, docWidth; | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
host = closest('.post', this); | |
hostid = (split$.call(host.id, '-')).pop(); | |
ref$ = this.getBoundingClientRect(), width = ref$.width, height = ref$.height, left = ref$.left, top = ref$.top; | |
x$ = preview = post.element('article', void 8, 'postpreview'); | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: preview | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = x$.querySelectorAll(".quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = x$.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
z2$ = x$.style; | |
z2$.position = 'fixed'; | |
if (left > (docWidth = document.documentElement.clientWidth) / 2) { | |
z2$.right = (docWidth - left - width) + "px"; | |
} else { | |
z2$.left = left + "px"; | |
} | |
if (this.classList.contains('backlink')) { | |
z2$.top = (top + height + 5) + "px"; | |
} else { | |
z2$.bottom = (window.innerHeight - top + 5) + "px"; | |
} | |
document.body.appendChild(x$); | |
classify($$(".post[data-no=\"" + no + "\"]")).add('hovered'); | |
listen(this).once('mouseout', function(){ | |
preview.remove(); | |
classify($$(".post[data-no=\"" + no + "\"]")).remove('hovered'); | |
}); | |
}; | |
onPosts({ | |
'.quotelink': { | |
mouseover: handlePreview | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('mouseover', handlePreview); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.no': { | |
click: function(e){ | |
var selection, x$; | |
e.preventDefault(); | |
selection = window.getSelection().toString().trim(); | |
if (selection) { | |
selection = ">" + selection + "\n"; | |
} | |
x$ = $('comment'); | |
x$.value += ">>" + this.textContent + "\n" + selection; | |
x$.focus(); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var munge; | |
munge = function(ctx){ | |
var i$, ref$, len$, quote, no, post, x$, j$, y$, ref1$, len1$, z$, text, z1$, z2$, z3$; | |
for (i$ = 0, len$ = (ref$ = ctx.querySelectorAll('.quotelink:not(.backlink):not(.forcequoted)')).length; i$ < len$; ++i$) { | |
quote = ref$[i$]; | |
if (quote.parentNode.className === 'smaller') { | |
continue; | |
} | |
no = quote.hash.substring(2); | |
if (post = Post[no]) { | |
if (post.comment.length > 0) { | |
x$ = L('div'); | |
x$.innerHTML = post.comment.replace(/<br>/g, ' '); | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('.quotelink')).length; j$ < len1$; ++j$) { | |
y$ = ref1$[j$]; | |
y$.remove(); | |
} | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('s')).length; j$ < len1$; ++j$) { | |
z$ = ref1$[j$]; | |
z$.remove(); | |
} | |
text = x$.textContent; | |
quote.after((z1$ = L('span'), z1$.textContent = ' ' + truncate(text, 70).replace(/^\s+/, ''), z1$.className = 'quote forcedquote', z1$)); | |
} | |
if (post.image) { | |
quote.after((z2$ = L('a'), z2$.className = 'forcedimage', z2$.textContent = ' ', z2$.setAttribute('data-width', post.image.width), z2$.setAttribute('data-height', post.image.height), z2$.href = post.image.url, z2$.appendChild((z3$ = L('img'), z3$.className = 'thumb', ref1$ = z3$.style, ref1$.maxHeight = '15px', ref1$.display = 'inline-block', ref1$.verticalAlign = 'middle', z3$.src = post.image.thumb.url, z3$.addEventListener('mouseover', handlePreview), z3$)), z2$)); | |
} | |
quote.textContent = "»" + post.idx; | |
quote.classList.add('forcequoted'); | |
} | |
} | |
}; | |
if (board.isThread) { | |
onpostinsert(function(it){ | |
munge(it.detail.post); | |
}); | |
} | |
}.call(this)); | |
(function(){ | |
var apiKey, batchSize, rate, requestQueue, ready, queue, cache, setTitle, pendingVideos, loadInfo, onclick; | |
apiKey = "AIzaSyCe5gXUv-EFyNMoESO8ONZnottbsd-2ayA"; | |
batchSize = 30; | |
rate = 5000; | |
requestQueue = []; | |
ready = true; | |
queue = function(req){ | |
requestQueue.push(req); | |
req.addEventListener('loadend', function(){ | |
requestQueue.shift(); | |
defer(rate, function(){ | |
var that; | |
if (that = requestQueue[0]) { | |
that.send(); | |
} else { | |
ready = true; | |
} | |
}); | |
}); | |
if (ready) { | |
ready = false; | |
req.send(); | |
} | |
}; | |
cache = {}; | |
setTitle = function(vid, data){ | |
vid.title = data.statistics.viewCount + " views.\n\n" + truncate(data.snippet.description, 200); | |
vid.dataset.title = data.snippet.title; | |
}; | |
pendingVideos = []; | |
loadInfo = debounce(2000, function(){ | |
var toFetch, i$, ref$, len$, vid, that, batches, batch, id, b; | |
toFetch = {}; | |
for (i$ = 0, len$ = (ref$ = pendingVideos).length; i$ < len$; ++i$) { | |
vid = ref$[i$]; | |
vid.addEventListener('click', onclick); | |
if (that = cache[vid.dataset.id]) { | |
setTitle(vid, that); | |
} else { | |
toFetch[vid.dataset.id] = true; | |
} | |
} | |
pendingVideos = []; | |
batches = []; | |
batch = []; | |
for (id in toFetch) { | |
batch.push(id); | |
if (batch.length === batchSize) { | |
batches.push(batch); | |
batch = []; | |
} | |
} | |
batches.push(batch); | |
if (batch.length > 0) { | |
for (i$ = 0, len$ = batches.length; i$ < len$; ++i$) { | |
b = batches[i$]; | |
(fn$.call(this, new XMLHttpRequest, b)); | |
} | |
} | |
function fn$(req, b){ | |
req.open('GET', "https://www.googleapis.com/youtube/v3/videos?id=" + encodeURIComponent(b) + "&part=snippet%2C+statistics&fields=items(id%2Csnippet%2Cstatistics)&key=" + apiKey); | |
req.addEventListener('load', function(){ | |
var ref$, data, i$, len$, v, j$, ref1$, len1$, vid; | |
if (200 <= (ref$ = this.status) && ref$ < 400) { | |
data = JSON.parse(this.response); | |
for (i$ = 0, len$ = (ref$ = data.items).length; i$ < len$; ++i$) { | |
v = ref$[i$]; | |
cache[v.id] = v; | |
for (j$ = 0, len1$ = (ref1$ = $$(".youtube[data-id=\"" + v.id + "\"]")).length; j$ < len1$; ++j$) { | |
vid = ref1$[j$]; | |
setTitle(vid, v); | |
} | |
} | |
} else { | |
console.error("error fetching youtube info!", this); | |
} | |
}); | |
req.addEventListener('error', function(){ | |
console.error("what happen", this); | |
}); | |
queue(req); | |
} | |
}); | |
onclick = function(e){ | |
var x$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
this.replace((x$ = L('iframe'), x$.width = 560, x$.height = 315, x$.src = "//www.youtube.com/embed/" + this.dataset.id + "?" + (this.dataset.params || '') + "&autoplay=1&wmode=transparent", x$.frameborder = 0, x$.allowfullscreen = '', x$)); | |
} | |
}; | |
onpostinsert(function(it){ | |
pendingVideos.push.apply(pendingVideos, it.detail.post.querySelectorAll('.youtube')); | |
loadInfo(); | |
}); | |
}.call(this)); | |
(function(){ | |
var highlighting, highlight, toggleHighlight; | |
highlighting = sget('highlighting') || { | |
admin: false, | |
mod: false | |
}; | |
highlight = function(it){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$(it)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
post.classList.add('highlighted'); | |
} | |
}; | |
toggleHighlight = function(klass){ | |
return function(){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$("." + klass)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
highlighting[klass] = !highlighting[klass]; | |
sset('highlighting', highlighting); | |
post.classList.toggle('highlighted'); | |
} | |
}; | |
}; | |
onPosts({ | |
'.admin .capcode': { | |
click: toggleHighlight('admin') | |
}, | |
'.mod .capcode': { | |
click: toggleHighlight('mod') | |
} | |
}); | |
onready(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight(klass); | |
} | |
} | |
}); | |
onupdate(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight("new." + klass); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var ref$, markScroll, scroll, toggleOff, onclick, follow; | |
ref$ = (function(){ | |
var last, el; | |
return { | |
markScroll: function(it){ | |
el = it; | |
return last = el.getBoundingClientRect().top; | |
}, | |
scroll: function(){ | |
return window.scrollBy(0, el.getBoundingClientRect().top - last); | |
} | |
}; | |
}.call(this)), markScroll = ref$.markScroll, scroll = ref$.scroll; | |
toggleOff = function(link, inlined){ | |
var no, ref$, i$, x$, len$, pid, ref1$, that; | |
no = link.hash.substring(2); | |
link.hidden = false; | |
markScroll(link); | |
link.classList.remove('inlinedlink'); | |
link.parentNode.classList.remove('inlinedquote'); | |
if ($$(".inline[data-no=\"" + no + "\"]").length === 1) { | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.remove('inlined'); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll('.post.inline')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
pid = (split$.call(x$.no, '-')).pop(); | |
if ($$(".inline[data-no=\"" + pid + "\"]").length === 1) { | |
if ((ref1$ = $("p" + pid)) != null) { | |
ref1$.classList.remove('inlined'); | |
} | |
} | |
} | |
inlined.remove(); | |
if (that = link.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
link.nextElementSibling.hidden = false; | |
} | |
if (that = link.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
link.nextElementSibling.nextElementSibling.hidden = false; | |
} | |
} | |
} | |
scroll(); | |
}; | |
onclick = function(e){ | |
var post, no, host, hostid, inlinedId, stubId, inlined, isBacklink, wrapper, x$, i$, y$, ref$, len$, z$, z1$, ref1$, that, this$ = this; | |
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { | |
return; | |
} | |
if (!(post = Post[no = this.hash.substring(2)])) { | |
return; | |
} | |
e.preventDefault(); | |
host = closest('.post', this).id; | |
hostid = (split$.call(host, '-')).pop(); | |
inlinedId = host + "-p" + no; | |
stubId = no + "-inlined-stub"; | |
if (inlined = $(inlinedId)) { | |
toggleOff(this, inlined); | |
} else { | |
isBacklink = this.classList.contains('backlink'); | |
inlined = post.element('article', "inline hovered", inlinedId); | |
wrapper = this; | |
while (wrapper.parentElement.matchesSelector('a,span')) { | |
wrapper = wrapper.parentElement; | |
} | |
markScroll(this); | |
wrapper[isBacklink ? 'after' : 'before'](inlined); | |
if (isBacklink) { | |
inlined.prepend((x$ = L('a'), x$.textContent = post.idx, x$.className = 'inlined-idx', x$.addEventListener('click', function(){ | |
toggleOff(this$, inlined); | |
}), x$)); | |
this.hidden = true; | |
} | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: inlined | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll("a.quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = inlined.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
this.classList.add('inlinedlink'); | |
this.parentNode.classList.add('inlinedquote'); | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.add('inlined'); | |
} | |
if (!isBacklink) { | |
if (that = this.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
this.nextElementSibling.hidden = true; | |
} | |
if (that = this.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
this.nextElementSibling.nextElementSibling.hidden = true; | |
} | |
} | |
} | |
} | |
if (!isBacklink) { | |
scroll(); | |
} | |
} | |
}; | |
follow = function(){ | |
var that; | |
if (that = this.hash) { | |
window.location.hash = that; | |
} | |
}; | |
onPosts({ | |
'.quotelink:not(.hiddenlink)': { | |
click: onclick, | |
dblclick: follow | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('click', onclick); | |
x$.addEventListener('dblclick', follow); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
console.timeEnd("init"); | |
onready(function(){ | |
console.timeEnd("onready handlers"); | |
console.timeEnd("interactive"); | |
console.timeStamp("html5chan-loaded"); | |
console.groupEnd(); | |
}); | |
}.call(this)); | |
}).call(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name html5chan-jade-nowith-noescape | |
// @namespace https://github.com/nami-doc/html5chan | |
// @description The Red Pill of 4chan userscripts | |
// | |
// @match *://boards.4chan.org/* | |
// @exclude *://boards.4chan.org/f/* | |
// @exclude *://boards.4chan.org/*/catalog | |
// @exclude *://boards.4chan.org/*/catalog/* | |
// @exclude *://boards.4chan.org/robots.txt | |
// | |
// @run-at document-start | |
// | |
// @grant none | |
// ==/UserScript== | |
(function(){ | |
"use strict"; | |
var | |
jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i<len;++i){var key=keys[i],val=obj[key];"boolean"==typeof val||null==val?val&&(terse?buf.push(key):buf.push(key+'="'+key+'"')):0==key.indexOf("data")&&"string"!=typeof val?buf.push(key+"='"+JSON.stringify(val)+"'"):"class"==key&&Array.isArray(val)?buf.push(key+'="'+(val.join(" "))+'"'):escaped&&escaped[key]?buf.push(key+'="'+(val)+'"'):buf.push(key+'="'+val+'"')}}return buf.join(" ")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); | |
var templates = {}; | |
templates.thread = function threadTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'id':(locals.id || "t" + (locals.thread.no) + ""), 'data-no':(locals.thread.no), "class": ('#classes ' + (locals.thread.className()) + '') }, {"id":true,"data-no":true,"class":true})); | |
buf.push('>'); | |
var __val__ = locals.thread.op.render('div', 'op') | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('<div class="thread-info">' + ((interp = (locals.thread.omitted && locals.thread.omitted.replies || 0) + locals.thread.replies.length) == null ? '' : interp) + ' replies and\n' + ((interp = (locals.thread.omitted && locals.thread.omitted.imageReplies || 0) + locals.thread.imageReplies.length) == null ? '' : interp) + ' images.'); | |
if ( locals.thread.preview) | |
{ | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.thread.url), "class": ('expand-link') }, {"href":true})); | |
buf.push('>Expand</a>'); | |
} | |
buf.push('</div><div class="replies">'); | |
// iterate thread.replies | |
;(function(){ | |
if ('number' == typeof locals.thread.replies.length) { | |
for (var $index = 0, $$l = locals.thread.replies.length; $index < $$l; $index++) { | |
var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.thread.replies) { | |
$$l++; var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
templates.board = function boardTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<nav id="toplinks" class="boardlinks">'); | |
var __val__ = board.nav | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</nav><header id="header"><a'); | |
buf.push(attrs({ 'id':('banner'), 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(board.banner), 'alt':('4chan::') }, {"src":true,"alt":true})); | |
buf.push('/></a><hgroup><h1 id="board-name"><a'); | |
buf.push(attrs({ 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = board.title | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a></h1><h2 id="board-subtitle">'); | |
var __val__ = board.subtitle | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</h2></hgroup></header>'); | |
if ( board.motd) | |
{ | |
buf.push('<div id="motd"><button id="hide-motd" type="button">Hide News</button><div id="message">'); | |
var __val__ = board.motd | |
buf.push(null == __val__ ? "" : __val__); | |
buf.push('</div></div>'); | |
} | |
buf.push('<div id="threads">'); | |
// iterate threads | |
;(function(){ | |
if ('number' == typeof locals.threads.length) { | |
for (var $index = 0, $$l = locals.threads.length; $index < $$l; $index++) { | |
var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.threads) { | |
$$l++; var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push((null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div>'); | |
if ( board.isBoard) | |
{ | |
buf.push('<ul id="pages">'); | |
if ( board.page > 0) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page - 1) }, {"href":true})); | |
buf.push('>previous</a></li>'); | |
} | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.url) }, {"href":true})); | |
buf.push('>0</a></li><li><a href="1">1</a></li><li><a href="2">2</a></li><li><a href="3">3</a></li><li><a href="4">4</a></li><li><a href="5">5</a></li><li><a href="6">6</a></li><li><a href="7">7</a></li><li><a href="8">8</a></li><li><a href="9">9</a></li><li><a href="10">10</a></li>'); | |
if ( board.page < 10) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page + 1) }, {"href":true})); | |
buf.push('>next</a></li>'); | |
} | |
buf.push('<li><a href="catalog">Catalog</a></li></ul>'); | |
} | |
if (!( board.locked)) | |
{ | |
buf.push('<div id="postform-wrapper"><form'); | |
buf.push(attrs({ 'id':('postform'), 'enctype':('multipart/form-data'), 'method':('POST'), 'action':('https://sys.4chan.org/' + (board.name) + '/post') }, {"enctype":true,"method":true,"action":true})); | |
buf.push('><input type="hidden" value="3145728" name="MAX_FILE_SIZE"/>'); | |
if ( board.threadId) | |
{ | |
buf.push('<input type="\" value="\" name="\"/>'); | |
} | |
buf.push('<input type="hidden" value="regist" name="mode"/><input'); | |
buf.push(attrs({ 'id':('password'), 'type':('hidden'), 'name':('pwd'), 'value':(board.password) }, {"type":true,"name":true,"value":true})); | |
buf.push('/><div id="fields"><input id="name" type="text" name="name" tabindex="10" placeholder="name#tripcode"/><input id="email" type="text" name="email" tabindex="10" placeholder="email"/><input id="subject" type="text" name="sub" tabindex="10" placeholder="subject"/><div id="comment-field"><textarea id="comment" name="com" rows="4" tabindex="10" placeholder="comment"></textarea></div><div id="captcha" style="display: none;"><a id="recaptcha_image" href="javascript:Recaptcha.reload()" title="Click for new captcha"></a><input id="recaptcha_response_field" type="text" name="recaptcha_response_field" tabindex="10" placeholder="captcha"/></div><div id="file-field"><input id="file" type="file" name="upfile" tabindex="10"/><label id="spoiler-field"><input type="checkbox" value="on" name="spoiler" tabindex="10"/>Spoiler?</label></div><div id="buttons"><button id="post" type="submit" tabindex="10" value="Submit">Post ' + ((interp = board.isThread ? 'Reply' : 'New Thread') == null ? '' : interp) + '</button>'); | |
if ( board.isThread) | |
{ | |
buf.push('<button id="sage" type="submit" name="email" value="sage" tabindex="10">Sage Reply</button>'); | |
} | |
buf.push('<span id="post-status"></span><progress id="progress" max="100" value="0" hidden=""></progress></div></div></form></div>'); | |
} | |
buf.push('<span id="updater"><span id="update-status"></span><button id="update-now">Update now</button></span>'); | |
return buf.join(""); | |
} | |
templates.post = function PostTemplate(locals, attrs, rethrow, merge) { | |
attrs = attrs || jade.attrs; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'data-no':(locals.post.no), 'data-idx':(locals.post.idx), 'id':(locals.id || "p" + (locals.post.no) + ""), "class": ('' + (locals.classes) + ' ' + (locals.post.className()) + '') }, {"data-no":true,"class":true,"data-idx":true,"id":true})); | |
buf.push('><h1 class="post-header ptdr"><button'); | |
buf.push(attrs({ 'type':('button'), 'value':(locals.post.no), "class": ('hide') }, {"type":true,"value":true})); | |
buf.push('>×</button><button'); | |
buf.push(attrs({ 'type':('submit'), 'form':('reportform'), 'name':('no'), 'value':(locals.post.no), "class": ('report') }, {"type":true,"form":true,"name":true,"value":true})); | |
buf.push('>!</button><a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('subject') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.subject | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a><a'); | |
buf.push(attrs({ 'href':(locals.post.email ? "href='mailto:' + (email) + ''" : ''), "class": ('name') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.name | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</a><span class="tripcode">'); | |
var __val__ = locals.post.tripcode | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="capcode">'); | |
var __val__ = locals.post.capcode | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="posteruid">'); | |
var __val__ = locals.post.uid && "(ID: #{post.uid})" | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><time'); | |
buf.push(attrs({ 'pubdate':(true), 'datetime':(locals.post.time.toISOString()), 'title':(locals.post.time) }, {"datetime":true,"title":true})); | |
buf.push('>'); | |
var __val__ = locals.post.time.relativeTime() | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</time>'); | |
if ( locals.post.op && locals.post.thread.sticky) | |
{ | |
buf.push('<img alt="sticky" src="//static.4chan.org/image/sticky.gif"/>'); | |
} | |
if ( locals.post.op && locals.post.thread.closed) | |
{ | |
buf.push('<img alt="closed" src="//static.4chan.org/image/closed.gif"/>'); | |
} | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('permalink') }, {"href":true})); | |
buf.push('>No.<span class="no">'); | |
var __val__ = locals.post.no | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span></a></h1>'); | |
if ( locals.post.image) | |
{ | |
buf.push('<div class="fileinfo"><span class="filename">'); | |
var __val__ = locals.post.image.filename || '' | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="dimensions">'); | |
var __val__ = locals.post.image.width + "x" + locals.post.image.height | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="size">'); | |
var __val__ = locals.post.image.size | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</span><a'); | |
buf.push(attrs({ 'href':('http://iqdb.org/?url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>iqdb</a><a'); | |
buf.push(attrs({ 'href':('http://google.com/searchbyimage?image_url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>google</a><a'); | |
buf.push(attrs({ 'href':('http://regex.info/exif.cgi/exif.cgi?imgurl=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>exif</a><a'); | |
buf.push(attrs({ 'href':('http://archive.foolz.us/' + (board.name) + '/search/image/' + (encodeURIComponent(locals.post.image.md5)) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>foolz</a></div><a'); | |
buf.push(attrs({ 'target':('_blank'), 'href':(locals.post.image.url), 'data-width':(locals.post.image.width), 'data-height':(locals.post.image.height), "class": ('file') }, {"target":true,"href":true,"data-width":true,"data-height":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(locals.post.image.thumb.url), 'width':(locals.post.image.thumb.width), 'height':(locals.post.image.thumb.height), "class": ('thumb') }, {"src":true,"width":true,"height":true})); | |
buf.push('/></a>'); | |
} | |
if ( locals.post.deletedImage) | |
{ | |
buf.push('<img alt="File deleted." src="//static.4chan.org/image/filedeleted.gif" class="deleted-image"/>'); | |
} | |
buf.push('<div class="comment">'); | |
var __val__ = locals.post.comment | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</div><footer class="backlinks">'); | |
var __val__ = locals.post.backlinks() | |
buf.push((null == __val__ ? "" : __val__)); | |
buf.push('</footer></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
var out$ = typeof exports != 'undefined' && exports || this, split$ = ''.split, slice$ = [].slice; | |
(function(){ | |
var board, x$, ref$, page, that, onready, onupdate, onpostinsert, onbacklink; | |
console.group("html5chan"); | |
console.timeStamp("html5chan-init"); | |
console.time("init"); | |
console.time("interactive"); | |
out$.board = board = {}; | |
x$ = board; | |
ref$ = split$.call(window.location.pathname, '/'), x$.name = ref$[1], page = ref$[2], x$.threadNo = ref$[3]; | |
x$.isThread = !!x$.threadNo; | |
x$.isBoard = !x$.isThread; | |
x$.page = parseInt(page, 10) || 0; | |
x$.url = "//boards.4chan.org/" + x$.name + "/"; | |
x$.threadurl = x$.url + 'res/'; | |
x$.threadPath = "/" + x$.name + "/res/" + x$.threadNo; | |
x$.archive = (function(){ | |
switch (that = x$.name) { | |
case 'a': | |
case 'jp': | |
case 'm': | |
case 'tg': | |
case 'u': | |
case 'tv': | |
case 'v': | |
case 'vg': | |
return "http://archive.foolz.us/" + that + "/thread"; | |
case 'lit': | |
return "http://fuuka.warosu.org/" + that + "/thread"; | |
case 'diy': | |
case 'g': | |
case 'sci': | |
return "http://archive.installgentoo.net/" + that + "/thread"; | |
} | |
}()); | |
x$.ready = false; | |
if (/404/.test(document.title) && board.archive) { | |
if (that = /\d+/.exec(window.location.pathname)) { | |
window.location = board.archive + "/" + that[0]; | |
return; | |
} | |
} | |
out$.onready = onready = function(it){ | |
document.addEventListener('html5chan-ready', it); | |
}; | |
out$.onupdate = onupdate = function(it){ | |
document.addEventListener('html5chan-update', it); | |
}; | |
out$.onpostinsert = onpostinsert = function(it){ | |
document.addEventListener('html5chan-postinsert', it); | |
}; | |
out$.onbacklink = onbacklink = function(it){ | |
document.addEventListener('html5chan-backlink', it); | |
}; | |
}.call(this)); | |
(function(){ | |
var truncate; | |
out$.truncate = truncate = function(it, length){ | |
length == null && (length = 20); | |
if (it.length > length) { | |
return it.substring(0, length) + "..."; | |
} else { | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var delay, deadZone, tooltip; | |
delay = 200; | |
deadZone = 10; | |
out$.tooltip = tooltip = function(arg$){ | |
var show, hide; | |
show = arg$.show, hide = arg$.hide; | |
return function(e){ | |
var x, y, timeout, lastEvent, createTooltip, resetTimeout, removeTooltip, this$ = this; | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
createTooltip = function(){ | |
show.call(this$, lastEvent); | |
listen(this$).off('mousemove', resetTimeout).on('mousemove', removeTooltip); | |
}; | |
resetTimeout = function(e){ | |
clearTimeout(timeout); | |
timeout = setTimeout(createTooltip, delay); | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
}; | |
removeTooltip = function(arg$){ | |
var cx, cy; | |
cx = arg$.clientX, cy = arg$.clientY; | |
if (Math.abs(x - cx) > deadZone || Math.abs(y - cy) > deadZone) { | |
hide.apply(this, arguments); | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
} | |
}; | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).once('mouseout', function(){ | |
hide.apply(this, arguments); | |
clearTimeout(timeout); | |
listen(this).off('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
}); | |
}; | |
}; | |
}.call(this)); | |
(function(){ | |
var $, $$, L, ref$, mutationMacro, x$, classify, closest; | |
out$.$ = $ = function(it){ | |
return document.getElementById(it); | |
}; | |
out$.$$ = $$ = function(it){ | |
return document.querySelectorAll(it); | |
}; | |
out$.L = L = function(it){ | |
return document.createElement(it); | |
}; | |
(ref$ = Element.prototype).matchesSelector == null && (ref$.matchesSelector = Element.prototype.mozMatchesSelector); | |
mutationMacro = function(nodes){ | |
var node, i$, len$, n; | |
if (nodes.length === 1) { | |
return typeof nodes[0] === 'string' | |
? document.createTextNode(nodes[0]) | |
: nodes[0]; | |
} | |
node = document.createDocumentFragment(); | |
for (i$ = 0, len$ = nodes.length; i$ < len$; ++i$) { | |
n = nodes[i$]; | |
if (typeof n === 'string') { | |
n = document.createTextNode(n); | |
} | |
node.appendChild(n); | |
} | |
return node; | |
}; | |
x$ = Node.prototype; | |
x$.prepend == null && (x$.prepend = function(){ | |
this.insertBefore(mutationMacro(arguments), this.firstChild); | |
}); | |
x$.append == null && (x$.append = function(){ | |
this.appendChild(mutationMacro(arguments)); | |
}); | |
x$.before == null && (x$.before = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this); | |
}); | |
x$.after == null && (x$.after = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this.nextSibling); | |
}); | |
x$.replace == null && (x$.replace = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.replaceChild(mutationMacro(arguments), this); | |
}); | |
x$.remove == null && (x$.remove = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.removeChild(this); | |
}); | |
out$.classify = classify = (function(){ | |
classify.displayName = 'classify'; | |
var prototype = classify.prototype, constructor = classify; | |
function classify(els){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.els = els; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.add = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.add(it); | |
} | |
}; | |
prototype.remove = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.remove(it); | |
} | |
}; | |
prototype.toggle = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.toggle(it); | |
} | |
}; | |
return classify; | |
}()); | |
out$.closest = closest = function(selector, el){ | |
for (; el; el = el.parentElement) { | |
if (el.matchesSelector(selector)) { | |
return el; | |
} | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var onPosts; | |
out$.onPosts = onPosts = function(listenerSpec){ | |
onpostinsert(function(it){ | |
var selector, ref$, listeners, i$, x$, ref1$, len$, event, listener; | |
for (selector in ref$ = listenerSpec) { | |
listeners = ref$[selector]; | |
for (i$ = 0, len$ = (ref1$ = it.detail.post.querySelectorAll(selector)).length; i$ < len$; ++i$) { | |
x$ = ref1$[i$]; | |
for (event in listeners) { | |
listener = listeners[event]; | |
x$.addEventListener(event, listener); | |
} | |
} | |
} | |
}); | |
}; | |
}.call(this)); | |
(function(){ | |
var pluralize; | |
pluralize = function(number, unit){ | |
return Math.round(number) + " " + unit + (number >= 1.5 ? 's' : '') + " ago"; | |
}; | |
Date.prototype.relativeTime = function(){ | |
var days, diff, hours, minutes, seconds; | |
if ((days = (diff = Date.now() - this.getTime()) / 86400000) > 1) { | |
return pluralize(days, 'day'); | |
} else if ((hours = days * 24) > 1) { | |
return pluralize(hours, 'hour'); | |
} else if ((minutes = hours * 60) > 1) { | |
return pluralize(minutes, 'minute'); | |
} else if ((seconds = minutes * 60) >= 1) { | |
return pluralize(seconds, 'second'); | |
} else { | |
return 'from the future!'; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var listen; | |
out$.listen = listen = (function(){ | |
listen.displayName = 'listen'; | |
var prototype = listen.prototype, constructor = listen; | |
function listen(element){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.element = element; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.on = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, handler); | |
} | |
return this; | |
}; | |
prototype.once = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, (function(){ | |
function once(e){ | |
var target; | |
target = e.target; | |
this.removeEventListener(event, once); | |
return handler.apply(this, arguments); | |
} | |
return once; | |
}())); | |
} | |
return this; | |
}; | |
prototype.off = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.removeEventListener(event, handler); | |
} | |
return this; | |
}; | |
['on', 'once', 'off'].forEach(function(method){ | |
var original; | |
original = prototype[method]; | |
prototype[method] = function(event, handler){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = split$.call(event, ' ')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
original.call(this, x$, handler); | |
} | |
return this; | |
}; | |
}); | |
['click', 'mouseover', 'scroll'].forEach(function(e){ | |
prototype[e] = function(selector, handler){ | |
return this.on(e, selector, handler); | |
}; | |
}); | |
return listen; | |
}()); | |
}.call(this)); | |
(function(){ | |
var debounce, defer, repeat; | |
out$.debounce = debounce = function(delay, fn){ | |
var timeout; | |
return function(){ | |
var ctx, args; | |
ctx = this; | |
args = arguments; | |
clearTimeout(timeout); | |
timeout = setTimeout(function(){ | |
fn.apply(ctx, args); | |
}, delay); | |
}; | |
}; | |
out$.defer = defer = function(delay, fn){ | |
var args; | |
if (typeof delay === 'function') { | |
fn = delay; | |
delay = 4; | |
args = Array.prototype.slice.call(arguments, 2); | |
} else { | |
args = Array.prototype.slice.call(arguments, 1); | |
} | |
return setTimeout.apply(null, [fn, delay].concat(args)); | |
}; | |
out$.repeat = repeat = (function(){ | |
repeat.displayName = 'repeat'; | |
var prototype = repeat.prototype, constructor = repeat; | |
function repeat(delay, options, fn){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.delay = delay; | |
if (typeof options === 'function') { | |
fn = options; | |
options = {}; | |
} | |
this$.fn = fn; | |
this$.timeoutee = function(){ | |
this$.fn.apply(this$, arguments); | |
if (this$.auto) { | |
this$.timeout = this$.repeat(); | |
} | |
}; | |
this$.auto = options.auto != null ? options.auto : true; | |
if (options.start !== false) { | |
this$.start(); | |
} | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.stop = function(){ | |
clearTimeout(this.timeout); | |
}; | |
prototype.start = function(){ | |
var args; | |
args = slice$.call(arguments); | |
this.stop(); | |
this.timeout = setTimeout.apply(null, [this.timeoutee, this.delay].concat(args)); | |
}; | |
prototype.restart = prototype.start; | |
prototype.repeat = prototype.start; | |
return repeat; | |
}()); | |
}.call(this)); | |
(function(){ | |
var setter, getter, ref$; | |
setter = function(storage){ | |
return function(key, val){ | |
var obj, ref$; | |
if (val != null) { | |
obj = (ref$ = {}, ref$[key] = val, ref$); | |
} | |
for (key in ref$ = obj || key) { | |
val = ref$[key]; | |
storage.setItem("html5chan-" + key, JSON.stringify(val)); | |
} | |
}; | |
}; | |
getter = function(storage){ | |
return function(it){ | |
try { | |
return JSON.parse(storage.getItem("html5chan-" + it)); | |
} catch (e$) {} | |
}; | |
}; | |
ref$ = out$; | |
ref$.set = setter(localStorage); | |
ref$.get = getter(localStorage); | |
ref$.sset = setter(sessionStorage); | |
ref$.sget = getter(sessionStorage); | |
}.call(this)); | |
(function(){ | |
var Post; | |
out$.Post = Post = (function(){ | |
Post.displayName = 'Post'; | |
var prototype = Post.prototype, constructor = Post; | |
prototype.postprocess = function(){ | |
var that, i$, len$, link, quoted, backlinks, ref$; | |
if (that = this.comment.match(/>>\d+/g)) { | |
for (i$ = 0, len$ = that.length; i$ < len$; ++i$) { | |
link = that[i$]; | |
quoted = link.substring(8); | |
backlinks = (ref$ = Post.backlinks)[quoted] || (ref$[quoted] = {}); | |
if (!backlinks[this.no]) { | |
((ref$ = Post.newBacklinks)[quoted] || (ref$[quoted] = {}))[this.no] = true; | |
backlinks[this.no] = true; | |
} | |
} | |
} | |
return constructor[this.no] = this; | |
}; | |
prototype.backlinks = function(onlyNew, postEl){ | |
var html, backlinks, post, idx; | |
html = ""; | |
backlinks = onlyNew | |
? Post.newBacklinks | |
: Post.backlinks; | |
if (backlinks[this.no]) { | |
for (post in backlinks[this.no]) { | |
if (board.isThread) { | |
idx = Post[post].idx; | |
} else { | |
idx = post; | |
} | |
html += "<a href=\"#p" + post + "\" class=\"backlink quotelink\">«" + idx + "</a> "; | |
if (onlyNew) { | |
document.dispatchEvent(new CustomEvent('html5chan-backlink', { | |
detail: { | |
no: post, | |
post: postEl | |
} | |
})); | |
} | |
} | |
} | |
return html; | |
}; | |
prototype.className = function(){ | |
var c, that; | |
c = "post "; | |
if (this.image) { | |
c += 'imagepost '; | |
} | |
if (this.sage) { | |
c += 'sage '; | |
} | |
if (that = this.tripcode) { | |
c += "tripcoded " + that + " "; | |
} | |
if (this.capcode) { | |
c += this.capcode === "## Admin" ? 'admin ' : 'mod '; | |
} | |
if (that = this.uid) { | |
c += "uid " + that; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
classes == null && (classes = ''); | |
return templates.post({ | |
container: container, | |
classes: classes, | |
id: id, | |
post: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, wrapper; | |
classes == null && (classes = ''); | |
x$ = wrapper = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return wrapper.firstElementChild; | |
}; | |
Object.defineProperty(prototype, 'text', { | |
get: function(){ | |
var x$; | |
x$ = L('div'); | |
return x$.innerHTML = this.comment, x$.textContent; | |
}, | |
configurable: true, | |
enumerable: true | |
}); | |
constructor.backlinks = {}; | |
constructor.newBacklinks = {}; | |
constructor.tripcodes = {}; | |
constructor.uids = {}; | |
function Post(){} | |
return Post; | |
}()); | |
}.call(this)); | |
(function(){ | |
var Thread; | |
out$.Thread = Thread = (function(){ | |
Thread.displayName = 'Thread'; | |
var prototype = Thread.prototype, constructor = Thread; | |
prototype.postprocess = function(){ | |
var i$, ref$, len$, reply; | |
this.posts = [this.op].concat(this.replies); | |
this.imageReplies = []; | |
this.reply = {}; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (reply.image) { | |
this.imageReplies.push(reply); | |
} | |
this.reply[reply.no] = reply; | |
} | |
if (Thread[this.no]) { | |
this['new'] = []; | |
this.deleted = []; | |
for (i$ = 0, len$ = (ref$ = Thread[this.no].replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!this.reply[reply.no]) { | |
this.deleted.push(reply); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!Thread[this.no].reply[reply.no]) { | |
this['new'].push(reply); | |
} | |
} | |
} | |
return Thread[this.no] = this; | |
}; | |
prototype.className = function(){ | |
var c; | |
c = 'thread '; | |
if (this.sticky) { | |
c += 'sticky '; | |
} | |
if (this.locked) { | |
c += ' locked'; | |
} | |
if (this.preview) { | |
c += ' preview'; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
container == null && (container = 'article'); | |
classes == null && (classes = ''); | |
return templates.thread({ | |
container: container, | |
classes: classes, | |
id: id, | |
thread: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, d; | |
x$ = d = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return d.firstElementChild; | |
}; | |
function Thread(){} | |
return Thread; | |
}()); | |
}.call(this)); | |
(function(){ | |
var dimensionRegex, sizeRegex, filenameRegex, spoilerRegex, sageRegex, parseThread, parsePost, parser, thumbsBase, imagesBase, humanized, parseApiPost; | |
dimensionRegex = /(\d+)x(\d+)/; | |
sizeRegex = /[\d\.]+ [KM]?B/; | |
filenameRegex = /title="([^"]+)"/; | |
spoilerRegex = /^Spoiler Image/; | |
sageRegex = /^sage$/i; | |
parseThread = function(el){ | |
var x$, omitted; | |
x$ = new Thread; | |
x$.no = el.id.substring(1); | |
x$.url = board.threadurl + x$.no; | |
x$.preview = true; | |
if (omitted = el.querySelector('.summary')) { | |
x$.omitted = { | |
replies: parseInt(omitted.textContent.match(/\d+(?= posts?)/), 10) || 0, | |
imageReplies: parseInt(omitted.textContent.match(/\d+(?= image (?:replies|reply))/), 10) || 0 | |
}; | |
} | |
x$.sticky = el.querySelector('.stickyIcon') != null; | |
x$.closed = el.querySelector('.closedIcon') != null; | |
x$.op = parsePost.call(x$, el.querySelector('.op')); | |
x$.op.idx = 0; | |
x$.replies = Array.prototype.map.call(el.getElementsByClassName('reply'), parsePost, x$); | |
x$.postprocess(); | |
return x$; | |
}; | |
parsePost = function(el, idx){ | |
var thread, x$, ref$, that, img, thumb, info, dimensions; | |
thread = this; | |
x$ = new Post; | |
x$.idx = 1 + idx + (((ref$ = thread.omitted) != null ? ref$.replies : void 8) || 0); | |
x$.thread = thread; | |
x$.no = el.id.substring(1); | |
x$.url = (x$.op = el.classList.contains('op')) | |
? thread.url | |
: thread.url + "#p" + x$.no; | |
x$.time = new Date(parseInt(el.querySelector('.dateTime').dataset.utc, 10) * 1000); | |
x$.subject = el.querySelector('.postInfo.desktop .subject').innerHTML; | |
x$.name = el.querySelector('.name').innerHTML; | |
x$.tripcode = (ref$ = el.querySelector('.postertrip')) != null ? ref$.innerHTML : void 8; | |
x$.capcode = (ref$ = el.querySelector('.capcode')) != null ? ref$.innerHTML : void 8; | |
x$.email = (ref$ = el.querySelector('.useremail')) != null ? ref$.href.substring(7) : void 8; | |
if (that = x$.email) { | |
x$.sage = sageRegex.test(that); | |
} | |
x$.comment = parser.enhance(el.querySelector('.postMessage').innerHTML); | |
x$.uid = (ref$ = el.querySelector('.hand')) != null ? ref$.textContent : void 8; | |
if (img = el.querySelector('.fileThumb')) { | |
if (img.firstElementChild.alt === "File deleted.") { | |
x$.deletedImage = true; | |
} else { | |
thumb = img.firstElementChild; | |
info = el.querySelector('.fileInfo').innerHTML; | |
dimensions = dimensionRegex.exec(info); | |
x$.image = { | |
thumb: { | |
url: thumb.src, | |
width: parseInt(thumb.style.width, 10), | |
height: parseInt(thumb.style.height, 10) | |
}, | |
url: thumb.parentNode.href, | |
width: parseInt(dimensions[1], 10), | |
height: parseInt(dimensions[2], 10), | |
size: sizeRegex.exec(thumb.alt)[0], | |
filename: (ref$ = filenameRegex.exec(info)) != null ? ref$[1] : void 8, | |
md5: thumb.dataset.md5, | |
spoiler: spoilerRegex.test(thumb.alt) | |
}; | |
} | |
} | |
x$.postprocess(); | |
return x$; | |
}; | |
out$.parser = parser = { | |
board: function(document){ | |
var threads; | |
console.time("parse board"); | |
threads = Array.prototype.map.call(document.querySelectorAll('.thread'), parseThread); | |
console.timeEnd("parse board"); | |
return threads; | |
}, | |
thread: function(document){ | |
var thread; | |
console.time("parse thread"); | |
thread = parseThread(document.querySelector('.thread')); | |
console.timeEnd("parse thread"); | |
return thread; | |
}, | |
api: function(data){ | |
var op, x$, ref$; | |
op = data.posts[0]; | |
x$ = new Thread; | |
x$.no = op.no; | |
x$.url = board.threadurl + op.no; | |
x$.preview = !!op.omitted_posts; | |
x$.sticky = !!op.sticky; | |
x$.closed = !!op.closed; | |
ref$ = data.posts.map(parseApiPost, x$), x$['op'] = ref$[0], x$['replies'] = slice$.call(ref$, 1); | |
x$.postprocess(); | |
return x$; | |
} | |
}; | |
thumbsBase = "//thumbs.4chan.org/" + board.name + "/thumb/"; | |
imagesBase = "//images.4chan.org/" + board.name + "/src/"; | |
humanized = function(bytes){ | |
var kbytes; | |
if (bytes < 1024) { | |
return bytes + " B"; | |
} else if ((kbytes = Math.round(bytes / 1024)) < 1024) { | |
return kbytes + " KB"; | |
} else { | |
return (kbytes / 1024).toString().substring(0, 3) + " MB"; | |
} | |
}; | |
parseApiPost = function(data, i){ | |
var x$, that; | |
x$ = new Post; | |
x$.idx = i; | |
x$.thread = this; | |
x$.url = this.url; | |
x$.time = new Date(data.time * 1000); | |
x$.no = data.no; | |
x$.subject = data.sub; | |
x$.name = data.name; | |
x$.tripcode = data.trip; | |
x$.uid = data.id; | |
x$.capcode = data.capcode; | |
x$.email = data.email; | |
x$.sage = x$.email === 'sage'; | |
x$.comment = (that = data.com) ? parser.enhance(that) : ''; | |
x$.image = data.fsize ? { | |
thumb: { | |
url: thumbsBase + data.tim + 's.jpg', | |
width: data.tn_w, | |
height: data.tn_h | |
}, | |
url: imagesBase + "" + data.tim + data.ext, | |
width: data.w, | |
height: data.h, | |
size: humanized(data.fsize), | |
filename: data.filename + "" + data.ext, | |
md5: data.md5, | |
spoiler: !!data.spoiler | |
} : void 8; | |
x$.deletedImage = !!data.filedeleted; | |
x$.postprocess(); | |
return x$; | |
}; | |
}.call(this)); | |
(function(){ | |
parser.enhance = function(it){ | |
if (it.length === 0) { | |
return it; | |
} | |
return it.replace(/<wbr>/g, '').replace(/(?:https?:\/\/)?(?:www\.)?(youtu\.be\/([\w\-_]+)(\?[&=\w\-_;\#]*)?|youtube\.com\/watch\?([&=\w\-_;\.\?\#\%]*)v=([\w\-_]+)([&=\w\-\._;\?\#\%]*))/g, '<a href="https://$1" class="youtube" data-id="$2$5" data-params="$3$4$6" target="_blank"><img src="//img.youtube.com/vi/$2$5/2.jpg"></a>').replace(/\((https?:\/\/)([^<\s\)]+)\)/g, '(<a class="external" rel="noreferrer" href="$1$2" title="$1$2" target="_blank">$2</a>)').replace(/([^"']|^)(https?:\/\/)([^<\s]+)/g, '$1<a class="external" rel="noreferrer" href="$2$3" title="$2$3" target="_blank">$3</a>').replace(/(^|>|;|\s)([\w\.\-]+\.(?:com|net|org|eu|jp|us|co\.uk)(\/[^<\s]*)?(?=[\s<]|$))/g, '$1<a class="external" rel="noreferrer" href="http://$2" title="$2" target="_blank">$2</a>').replace(/<span\x20class="deadlink">>>(\d+)<\/span>/g, board.archivelink); | |
}; | |
board.archivelink = board.archive ? "<a href=\"" + board.archive + "/$1\" class=\"deadlink\">>>$1</a>" : '$&'; | |
}.call(this)); | |
(function(){ | |
var lastUpdate, unread, favicons, x$, y$, drawFavicon, updater, fade, fadeWhenVisible; | |
lastUpdate = new Date; | |
unread = 0; | |
favicons = { | |
sfw: (x$ = L('img'), x$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv8AAAD/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/AAAA/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfj/////////////w==', x$), | |
nsfw: (y$ = L('img'), y$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8AAAD/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/AAAA/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfjAAD//////////w==', y$) | |
}; | |
drawFavicon = debounce(200, function(){ | |
var ref$, x$, link, y$, z$; | |
if ((ref$ = $('favicon')) != null) { | |
ref$.remove(); | |
} | |
x$ = link = L('link'); | |
x$.id = 'favicon'; | |
x$.rel = 'icon'; | |
x$.type = 'image/x-icon'; | |
y$ = L('canvas'); | |
y$.width = 16; | |
y$.height = 16; | |
z$ = y$.getContext('2d'); | |
z$.drawImage(favicons[board.type], 0, 0); | |
if (unread > 0) { | |
z$.font = '8px monospace'; | |
z$.fillStyle = '#000'; | |
z$.strokeStyle = '#fff'; | |
z$.lineWidth = 4; | |
z$.textBaseline = 'bottom'; | |
z$.textAlign = 'right'; | |
z$.strokeText(unread, 16, 16); | |
z$.fillText(unread, 16, 16); | |
} | |
link.href = y$.toDataURL('image/png'); | |
document.head.appendChild(link); | |
}); | |
out$.updater = updater = { | |
update: function(){ | |
var x$; | |
updater.status.textContent = "Updating thread..."; | |
updater.button.disabled = true; | |
x$ = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board.name + "/res/" + board.thread.no + ".json"); | |
x$.setRequestHeader('If-Modified-Since', lastUpdate.toUTCString()); | |
listen(x$).on('load', function(){ | |
var lastModified, thread, i$, ref$, len$, post, backlinks, last; | |
if (this.status === 404) { | |
document.title += '(dead)'; | |
updater.status.textContent = "thread 404'd"; | |
return; | |
} | |
if (this.status === 304) { | |
updater.countdown.restart(); | |
return; | |
} | |
lastModified = new Date(this.getResponseHeader('Last-Modified')); | |
if (!(lastModified > lastUpdate)) { | |
updater.countdown.restart(); | |
return; | |
} | |
updater.status.textContent = "update detected, parsing"; | |
lastUpdate = lastModified; | |
thread = parser.api(JSON.parse(this.response)); | |
if (thread['new'].length > 0) { | |
$("t" + thread.no).lastElementChild.insertAdjacentHTML('beforeend', (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'new reply')); | |
} | |
return results$; | |
}()).join('')); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: $("p" + post.no) | |
} | |
})); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.thread .backlinks')).length; i$ < len$; ++i$) { | |
backlinks = ref$[i$]; | |
backlinks.insertAdjacentHTML('beforeend', Post[backlinks.parentNode.dataset.no].backlinks(true, backlinks.parentNode)); | |
} | |
Post.newBacklinks = {}; | |
document.dispatchEvent(new CustomEvent('html5chan-update', { | |
detail: { | |
thread: thread | |
} | |
})); | |
unread += thread['new'].length; | |
drawFavicon(); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
fadeWhenVisible(post); | |
} | |
if (window.scrollMaxY - window.scrollY < 50 && !document.hidden) { | |
last = window.scrollY; | |
repeat(50, function(){ | |
var remaining; | |
if (last > window.scrollY) { | |
this.stop(); | |
} else if ((remaining = window.scrollMaxY - window.scrollY) > 1) { | |
window.scrollBy(0, remaining / 4); | |
last = window.scrollY; | |
} | |
}); | |
} | |
$("t" + thread.no).querySelector(".thread-info").textContent = thread.replies.length + " replies and " + thread.imageReplies.length + " image replies."; | |
} | |
updater.countdown.restart(); | |
}).on('timeout', function(){ | |
updater.status.textContent = "request timed out..."; | |
updater.countdown(); | |
}).on('error', function(){ | |
updater.status.textContent = "Couldn't fetch thread page!"; | |
}).on('loadend', function(){ | |
updater.button.disabled = false; | |
}); | |
x$.send(); | |
}, | |
countdown: repeat(1000, { | |
start: false | |
}, function(t){ | |
this.t = t || this.t || 30; | |
updater.status.textContent = "Updating in " + this.t + " seconds..."; | |
if (--this.t === 0) { | |
this.stop(); | |
updater.update(); | |
} | |
}) | |
}; | |
fade = function(post){ | |
defer(100, function(){ | |
post.classList.remove('new'); | |
--unread; | |
drawFavicon(); | |
}); | |
}; | |
fadeWhenVisible = function(it){ | |
var post, y; | |
post = $("p" + it.no); | |
y = post.offsetTop; | |
if (window.innerHeight + window.scrollY > y) { | |
if (document.hidden) { | |
listen(window).once('focus', function(){ | |
fade(post); | |
}); | |
} else { | |
fade(post); | |
} | |
} else { | |
listen(window).scroll((function(){ | |
function reset(){ | |
if (window.innerHeight + window.scrollY > post.offsetTop) { | |
fade(post); | |
return listen(window).off('scroll', reset); | |
} | |
} | |
return reset; | |
}())); | |
} | |
}; | |
onready(function(){ | |
updater.status = $('update-status'); | |
updater.button = $('update-now'); | |
if (board.isThread) { | |
updater.countdown.start(); | |
listen($('update-now')).click(function(){ | |
var x$; | |
x$ = updater.countdown; | |
x$.stop(); | |
x$.t = 30; | |
updater.update(); | |
}); | |
} else { | |
$('updater').hidden = true; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var postStatus; | |
postStatus = function(it){ | |
return $('post-status').textContent = it; | |
}; | |
onready(function(){ | |
var checkValidity, cooldown, ref$; | |
checkValidity = function(e){ | |
var form, captcha, file, comment, email, ref$, x$, data, y$; | |
e.preventDefault(); | |
form = $('postform'); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
email = $('email'); | |
if (/^noko$/i.test(email.value)) { | |
email.value = ''; | |
} | |
captcha.setCustomValidity(!captcha.value ? "You forgot the captcha!" : ''); | |
file.setCustomValidity(!file.value && board.isBoard ? "You forgot your image!" : ''); | |
comment.setCustomValidity(!file.value && !comment.value ? "You didn't enter a comment or select a file!" : ''); | |
if (form.checkValidity()) { | |
$('post').disabled = true; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = true; | |
} | |
postStatus("Posting..."); | |
x$ = $('progress'); | |
x$.hidden = false; | |
x$.value = 0; | |
data = new FormData(form); | |
if (this === $('sage')) { | |
data.append('email', 'sage'); | |
} | |
y$ = new XMLHttpRequest; | |
y$.open('POST', form.action); | |
listen(y$).on('load', function(){ | |
var x$, html, captcha, file, comment, ref$, y$; | |
x$ = html = L('div'); | |
x$.innerHTML = this.response; | |
console.log(html); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
$('post').disabled = false; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = false; | |
} | |
if (/Post successful!|uploaded!/.test(html.textContent)) { | |
postStatus('Post successful!'); | |
cooldown(); | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
$('recaptcha_image').click(); | |
updater.countdown.restart(3); | |
return parser.lastParse = 0; | |
} else if (/mistyped the verification/.test(html.textContent)) { | |
postStatus('You mistyped the verification!'); | |
$('recaptcha_image').click(); | |
y$ = captcha; | |
y$.value = ''; | |
y$.focus(); | |
return y$; | |
} else if (/duplicate file entry detected/) { | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
return $('recaptcha_image').click(); | |
} | |
}).on('loadend', function(){ | |
return $('progress').hidden = true; | |
}); | |
listen(y$.upload).on('progress', function(e){ | |
return $('progress').value = 100 * e.loaded / e.total; | |
}); | |
y$.send(data); | |
} | |
return false; | |
}; | |
listen($('post')).click(checkValidity); | |
listen($('sage')).click(checkValidity); | |
cooldown = function(){ | |
var post, sage, message, tminus; | |
post = $('post'); | |
sage = $('sage'); | |
post.disabled = true; | |
if (sage != null) { | |
sage.disabled = true; | |
} | |
message = post.textContent; | |
tminus = 30; | |
post.textContent = tminus; | |
return setTimeout((function(){ | |
function tick(){ | |
if (tminus-- === 0) { | |
post.textContent = message; | |
post.disabled = false; | |
return sage != null ? sage.disabled = false : void 8; | |
} else { | |
post.textContent = tminus; | |
return setTimeout(tick, 1000); | |
} | |
} | |
return tick; | |
}()), 1000); | |
}; | |
listen($('name')).on('input', function(){ | |
return set({ | |
name: this.value | |
}); | |
}); | |
if ((ref$ = $('name')) != null) { | |
ref$.value = get('name') || ''; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var x$, html, y$, head, z$, z1$, body, d; | |
x$ = html = L('html'); | |
x$.appendChild((y$ = head = L('head'), y$.appendChild(L('title')), y$.appendChild((z$ = L('style'), z$.id = 'html5chan-style', z$.textContent = ' html {\n min-height: 100%;\n font-family: Droid Serif, serif;\n font-size: 10pt;\n}\n::selection {\n background: #29df75;\n color: #000;\n}\n::-moz-selection {\n background: #29df75;\n color: #000;\n}\n[hidden] {\n display: none !important;\n}\nbutton:enabled {\n cursor: pointer;\n}\n.bold {\n font-weight: bold;\n}\n.smaller {\n font-size: smaller;\n}\n#toplinks {\n float: right;\n width: 300px;\n}\n#header {\n margin: 1em 0;\n color: #af0a0f;\n}\n#board-name {\n font-size: 24pt;\n margin: 0;\n}\n#board-name a {\n color: #af0a0f !important;\n text-decoration: none;\n}\n#board-name a:hover {\n text-decoration: underline;\n}\n#board-subtitle {\n font-size: 10px;\n font-weight: normal;\n}\n#banner {\n margin-right: 1em;\n float: left;\n}\n#motd {\n margin: 1em 0;\n}\n#hide-motd {\n text-align: right;\n font-size: 10pt;\n}\n#message {\n clear: both;\n}\n.boardlinks {\n font-size: 9pt;\n text-align: center;\n}\n.boardlinks a {\n text-decoration: none;\n}\n#threads {\n clear: both;\n}\n#pages {\n text-align: center;\n margin: 0pt;\n padding: 0pt;\n}\n#pages li {\n display: inline;\n}\n#pages a {\n border-color: #aaa;\n border-style: solid;\n border-width: 1px 0;\n color: #000;\n display: inline-block;\n margin: 0.25em;\n padding: 0.5em 1em;\n text-decoration: none;\n}\n#pages a#current,\n#pages a:hover {\n background-color: rgba(200,200,200,0.7);\n}\n#updater {\n float: right;\n}\n.post {\n margin: 0.2em;\n padding: 1em;\n padding-right: 0;\n border-radius: 0.3em;\n}\n.reply {\n margin-left: 2em;\n transition-property: background-color;\n transition-duration: 3s;\n}\n.reply.new {\n background: #feffbf noise !important;\n}\n.sage > .post-header > .name:after {\n content: " (sage)";\n}\n.reply:before,\n.inlined-idx {\n content: attr(data-idx);\n position: absolute;\n text-align: right;\n display: inline-block;\n margin-left: -3em;\n width: 2em;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.inlined-idx {\n cursor: pointer;\n}\n.inlined-idx:hover {\n text-decoration: underline;\n}\n.post-header {\n margin: 0;\n padding: 0;\n font-size: 8pt;\n font-family: sans-serif;\n color: sfw-border -10%;\n font-weight: normal;\n float: right;\n}\n.post .subject {\n color: #0f0c5d;\n font-weight: 800;\n text-decoration: none;\n}\n.post .subject:hover {\n text-decoration: underline;\n}\n.name {\n color: sfw-border -10%;\n}\n.name:link {\n text-decoration: underline;\n}\n.tripcode,\n.fileinfo {\n display: table;\n color: sfw-border -20%;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.tripcode:not(:hover) > .saucelink,\n.fileinfo:not(:hover) > .saucelink,\n.tripcode:not(:hover) > .dimensions,\n.fileinfo:not(:hover) > .dimensions,\n.tripcode:not(:hover) > .size,\n.fileinfo:not(:hover) > .size {\n transition-delay: 0.5s;\n opacity: 0;\n}\n.saucelink,\n.dimensions,\n.size {\n transition-duration: 0.5s;\n}\n.file {\n display: block;\n float: left;\n margin: 0.3em 1em 0.3em 0;\n position: relative;\n}\n.full {\n display: block;\n}\n.capcode {\n font-weight: 800;\n}\n.mod .capcode:hover,\n.admin .capcode:hover {\n cursor: pointer;\n}\n.admin .name,\n.admin .capcode,\n.admin .tripcode {\n color: #f00;\n}\n.admin .capcode:after {\n content: url("https://static.4chan.org/image/adminicon.gif");\n}\n.mod .name,\n.mod .capcode {\n color: #800080;\n}\n.mod .capcode:after {\n content: url("https://static.4chan.org/image/modicon.gif");\n}\n.hide,\n.report {\n float: right;\n padding: 0 1px;\n background: transparent;\n border: 0;\n}\n.post.hidden {\n opacity: 0.6;\n}\n.post.hidden .file,\n.post.hidden .comment,\n.post.hidden .backlinks,\n.post.hidden .fileinfo {\n display: none;\n}\n.post.inlined {\n display: none;\n}\n.post.inlined:target {\n display: block;\n}\n.post.highlighted {\n background-color: #d6bad0 !important;\n}\n.quotelink {\n text-decoration: none;\n}\n.hiddenlink {\n text-decoration: line-through;\n}\n.replylink {\n text-decoration: none;\n}\n.deadlink {\n color: #808080;\n}\n.permalink {\n text-decoration: none;\n color: inherit;\n}\n.permalink .no:hover {\n text-decoration: underline;\n}\n.recursivelink {\n font-weight: bold;\n color: #000 !important;\n}\n.comment {\n margin: 0;\n word-wrap: break-word;\n line-height: 1.8em;\n width: 40em;\n}\n.op .comment {\n width: 50em;\n}\n.quote {\n font-weight: normal;\n color: #789922;\n}\n.prettyprint {\n background-color: #fff;\n padding: 0.5em;\n display: inline-block;\n max-width: 40em;\n overflow: auto;\n}\ns {\n text-decoration: none;\n transition-duration: 1s;\n}\ns:not(:hover) > *,\ns:not(:hover) {\n color: transparent !important;\n text-shadow: 0 0 7px #000;\n}\n.backlinks {\n clear: both;\n}\n.backlink {\n margin-right: 1em;\n}\na.quotelink.inlinedlink,\nstrong.quotelink.inlinedlink {\n font-weight: bold;\n color: #000;\n}\n#postpreview {\n outline: none;\n padding: 0.5em;\n box-shadow: 5px 5px 10px rgba(0,0,0,0.5);\n margin: 0;\n}\n.inline {\n margin-right: 0;\n padding-right: 0;\n}\n.comment .inline {\n display: table;\n}\n.backlink + .inline {\n margin-left: 2em;\n}\n.inline .backlinks > .recursivelink {\n display: none;\n}\n.forcedimage {\n text-decoration: none;\n}\n.backlink.inlinedlink {\n display: table;\n}\n.hovered {\n outline: 3px dashed #00f;\n}\n#postform {\n display: table;\n margin: 1em auto;\n}\n#postform #comment,\n#postform #recaptcha_response_field {\n width: 100%;\n}\n#name,\n#email,\n#subject {\n width: 31.3%;\n}\n#recaptcha_image {\n display: block;\n background: #fff;\n width: 100% !important;\n}\n#recaptcha_image img {\n display: block;\n margin: auto;\n}\n.thread {\n padding-bottom: 5px;\n clear: both;\n}\n.thread-info {\n clear: left;\n text-align: right;\n}\n.thread.hidden {\n opacity: 0.6;\n}\n.thread.hidden .replies,\n.thread.hidden .thread-info {\n display: none;\n}\n.thread.hidden .op .file,\n.thread.hidden .op .comment,\n.thread.hidden .op .backlinks,\n.thread.hidden .op .fileinfo {\n display: none;\n}\nbody.sfw {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw > header a,\nbody.sfw > footer a,\nbody.sfw .boardlinks a {\n color: #34345c;\n}\nbody.sfw .boardlinks {\n color: #89a;\n}\nbody.sfw .post:target {\n background: #d6bad0 url("data:image/png;base64,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") !important;\n}\nbody.sfw .reply {\n background: linear-gradient(180deg, rgba(0,0,0,0.01), transparent 2em, rgba(255,255,255,0) calc(98%), rgba(255,255,255,0.03));\n}\nbody.sfw #postpreview {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw .reply:before,\nbody.sfw .inlined-idx {\n color: #9db0cb;\n}\nbody.sfw #postpreview.op {\n background-color: #eef2ff;\n}\nbody.sfw .quotelink {\n color: #d00;\n}\nbody.nsfw {\n background: #ffe url("//static.4chan.org/image/fade.png") repeat-x;\n color: #800000;\n}\nbody.nsfw > header a,\nbody.nsfw > footer a,\nbody.nsfw .boardlinks a {\n color: #800;\n}\nbody.nsfw .boardlinks {\n color: #b86;\n}\nbody.nsfw .thread {\n border-color: #808080;\n}\nbody.nsfw .post:target {\n background-color: #f0c0b0 !important;\n}\nbody.nsfw .reply,\nbody.nsfw #postpreview {\n background-color: #d9bfb7;\n}\nbody.nsfw .reply {\n border-color: #d9bfb7;\n}\nbody.nsfw .reply:before {\n color: #d9bfb7;\n}\nbody.nsfw .inlined-idx {\n color: #bd9083;\n}\nbody.nsfw #postpreview.op {\n background-color: #ffe;\n}\nbody.nsfw .quotelink {\n color: #000080;\n}\n.youtube {\n position: relative;\n text-decoration: none;\n border: 3px solid;\n border-color: #c6312b;\n border-radius: 10px;\n transition: 0.5s;\n overflow: hidden;\n display: inline-block;\n vertical-align: top;\n margin: 0.25em;\n width: 120px;\n height: 90px;\n}\n.youtube:hover {\n border-color: #ffa200;\n}\n.youtube:after {\n position: absolute;\n top: 0;\n left: 0;\n width: 115px;\n font-size: smaller;\n font-family: sans-serif;\n color: #fff;\n background: rgba(0,0,0,0.5);\n padding: 0 0.5em;\n content: attr(data-title);\n}\n.youtube:not(:hover):after {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n', z$)), y$.appendChild((z1$ = L('script'), z1$.src = '//www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', z1$.addEventListener('load', function(){ | |
var x$; | |
head.appendChild((x$ = L('script'), x$.src = '//www.google.com/recaptcha/api/js/recaptcha.js', x$.addEventListener('load', function(){ | |
var x$; | |
x$ = L('script'); | |
x$.textContent = "(function() {var c;if (c = document.getElementById('captcha')) {Recaptcha._init_options({theme: 'custom',custom_theme_widget: c});Recaptcha.theme = 'custom';Recaptcha.widget = c;Recaptcha._finish_widget();}}())"; | |
if (board.ready) { | |
head.appendChild(x$); | |
} else { | |
onready(function(){ | |
head.appendChild(x$); | |
}); | |
} | |
}), x$)); | |
}), z1$)), y$)); | |
x$.appendChild(body = L('body')); | |
d = document.replaceChild(html, document.documentElement); | |
document.addEventListener('DOMContentLoaded', function(){ | |
var x$, ref$, thread, threads, y$, z$, i$, len$, post; | |
console.time("initial render"); | |
console.time("parse page"); | |
x$ = board; | |
x$.title = d.querySelector('.boardTitle').textContent; | |
x$.subtitle = ((ref$ = d.querySelector('.boardSubtitle')) != null ? ref$.innerHTML : void 8) || ''; | |
x$.nav = d.querySelector('#boardNavDesktop').innerHTML; | |
x$.banner = d.querySelector('.title').src; | |
x$.motd = (ref$ = d.querySelector('.globalMessage')) != null ? ref$.innerHTML : void 8; | |
x$.sfw = d.querySelector('link[rel="shortcut icon"]').href.slice(-6) === 'ws.ico'; | |
x$.type = x$.sfw ? 'sfw' : 'nsfw'; | |
x$.password = get('password') || Math.random().toString().substr(-8); | |
console.timeEnd("parse page"); | |
console.log(board); | |
if (board.isThread) { | |
board.thread = thread = parser.thread(d); | |
board.threads = threads = [thread]; | |
} else { | |
board.threads = threads = parser.board(d); | |
} | |
console.log(threads); | |
Post.newBacklinks = {}; | |
y$ = body; | |
y$.id = board.name; | |
y$.className = board.type + " " + (board.isThread ? 'threadpage' : 'boardpage'); | |
console.time("generate and render new body"); | |
var bodyHtml = templates.board(board); | |
console.timeEnd("generate and render new body"); | |
console.time( "parse and render new body"); | |
body.innerHTML = bodyHtml; | |
console.timeEnd( "parse and render new body"); | |
if (board.isBoard) { | |
console.time("highlight current page"); | |
body.querySelector("#pages a[href=\"" + (board.page || board.url) + "\"]").id = 'current'; | |
console.timeEnd("highlight current page"); | |
} | |
console.time("set new page title"); | |
document.title = board.isThread | |
? (z$ = board.thread.op, truncate(z$.title || z$.text || ((ref$ = z$.image) != null ? ref$.filename : void 8) || z$.time.relativeTime()) + "\ - /" + board.name + "/") | |
: board.title; | |
console.timeEnd("set new page title"); | |
console.timeEnd("initial render"); | |
if (window.location.hash && !sget(document.URL)) { | |
window.location.hash = window.location.hash; | |
window.addEventListener('scroll', (function(){ | |
function registerPage(){ | |
var ref$; | |
sset((ref$ = {}, ref$[document.URL] = true, ref$)); | |
return window.removeEventListener('scroll', registerPage); | |
} | |
return registerPage; | |
}())); | |
} | |
console.time("initial post insertion handlers"); | |
for (i$ = 0, len$ = (ref$ = $$('.post')).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: post | |
} | |
})); | |
} | |
console.timeEnd("initial post insertion handlers"); | |
board.ready = true; | |
document.dispatchEvent(new CustomEvent('html5chan-ready', { | |
detail: { | |
post: post | |
} | |
})); | |
}); | |
}.call(this)); | |
(function(){ | |
var setUpdate; | |
setUpdate = function(el){ | |
var time, diff; | |
time = new Date(el.getAttribute('datetime')); | |
if ((diff = Date.now() - time.getTime()) < 8640000) { | |
return setTimeout(function(){ | |
el.textContent = time.relativeTime(); | |
return setUpdate(el); | |
}, diff > 3600000 | |
? diff % 3600000 | |
: diff > 60000 ? 300000 : 60000); | |
} | |
}; | |
onready(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = document.getElementsByTagName('time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
onupdate(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = $$('.new time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.file': { | |
click: function(e){ | |
var a, x$, ref$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
a = this; | |
this.hidden = true; | |
this.before((x$ = L('img'), x$.src = this.href, x$.className = 'full', ref$ = x$.style, ref$.display = 'block', ref$.maxWidth = '100%', x$.onclick = function(){ | |
var ref$, top; | |
if (this.width !== this.naturalWidth) { | |
this.style.removeProperty('max-width'); | |
} else { | |
a.hidden = false; | |
if ((ref$ = a.previousSibling) != null) { | |
ref$.remove(); | |
} | |
if (scroll && (top = a.getBoundingClientRect().top) < 0) { | |
window.scrollBy(0, top); | |
} | |
} | |
}, x$)); | |
} | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var objectFit, handlePreview; | |
objectFit = function(container, width, height){ | |
var ratio; | |
ratio = Math.min(1, container.height / height, container.width / width); | |
return { | |
width: ratio * width, | |
height: ratio * height | |
}; | |
}; | |
out$.handlePreview = handlePreview = tooltip({ | |
show: function(){ | |
var a, viewport, ref$, x$; | |
this.style.cursor = 'none'; | |
a = this.parentElement; | |
viewport = { | |
width: (ref$ = document.documentElement).clientWidth, | |
height: ref$.clientHeight | |
}; | |
document.body.append((x$ = L('img'), x$.id = 'imgpreview', x$.alt = "Loading...", x$.src = a.href, ref$ = objectFit(viewport, a.dataset.width, a.dataset.height), x$.width = ref$.width, x$.height = ref$.height, x$.addEventListener('load', function(){ | |
return this.removeAttribute('alt'); | |
}), x$.addEventListener('error', function(){ | |
return this.alt = "Unable to load image."; | |
}), ref$ = x$.style, ref$.position = 'fixed', ref$.left = 0, ref$.top = 0, ref$.pointerEvents = 'none', ref$.backgroundColor = 'rgba(0,0,0,.5)', ref$.padding = (viewport.height - x$.height) / 2 + "px " + (viewport.width - x$.width) / 2 + "px", ref$.transitionDuration = '.5s', ref$.opacity = 0, x$.addEventListener('transitionend', function(e){ | |
var propertyName; | |
propertyName = e.propertyName; | |
if (propertyName === 'opacity' && this.style.opacity === '0') { | |
return this.remove(); | |
} | |
}), defer(100, function(){ | |
x$.style.opacity = 1; | |
}), x$)); | |
}, | |
hide: function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.style.opacity = 0; | |
} | |
defer(100, function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.remove(); | |
} | |
}); | |
this.style.removeProperty('cursor'); | |
} | |
}); | |
onPosts({ | |
'.thumb': { | |
mouseover: handlePreview | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onready(function(){ | |
var hash, msg, btn; | |
hash = function(it){ | |
return it.innerHTML.length; | |
}; | |
if ($('motd')) { | |
msg = $('message'); | |
btn = $('hide-motd'); | |
if (get('motd-hash') === hash(msg)) { | |
msg.hidden = get('motd-hidden'); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
} else { | |
set('motd-hash', hash(msg)); | |
} | |
listen(btn).click(function(){ | |
msg.hidden = !msg.hidden; | |
set('motd-hidden', msg.hidden); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
}); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var threshold, hidden, e, persist, toggle; | |
threshold = 604800000; | |
hidden = { | |
threads: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-t-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()), | |
replies: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-r-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()) | |
}; | |
console.log(hidden); | |
(function(now){ | |
var type, ref$, hash, key, expiry; | |
for (type in ref$ = hidden) { | |
hash = ref$[type]; | |
for (key in hash) { | |
expiry = hash[key]; | |
if (expiry === true) { | |
hash[key] = Date.now(); | |
} else { | |
if (now - expiry > threshold) { | |
delete hash[key]; | |
} | |
} | |
} | |
} | |
}.call(this, Date.now())); | |
persist = function(){ | |
localStorage["4chan-hide-t-" + board.name] = JSON.stringify(hidden.threads); | |
localStorage["4chan-hide-r-" + board.name] = JSON.stringify(hidden.replies); | |
}; | |
toggle = function(prefix, no){ | |
var ref$; | |
classify($$(".quotelink[href$=\"#" + no + "\"]")).toggle('hiddenlink'); | |
return (ref$ = $(prefix + "" + no)) != null ? ref$.classList.toggle('hidden') : void 8; | |
}; | |
onready(function(){ | |
var i$, ref$, len$, btn, x$, no, y$; | |
for (i$ = 0, len$ = (ref$ = $$('.reply button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn$); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.op button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn1$); | |
} | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('reply')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
no = x$.dataset.no; | |
if (hidden.replies[no]) { | |
toggle('p', no); | |
} | |
} | |
if (board.isBoard) { | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('thread')).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
no = y$.dataset.no; | |
if (hidden.threads[no]) { | |
toggle('t', no); | |
} | |
} | |
} | |
function fn$(){ | |
toggle('p', this.value); | |
if (this.value in hidden.replies) { | |
delete hidden.replies[this.value]; | |
} else { | |
hidden.replies[this.value] = Date.now(); | |
} | |
persist(); | |
} | |
function fn1$(){ | |
var ref$; | |
toggle('t', this.value); | |
if (this.value in hidden.threads) { | |
delete hidden.threads[this.value]; | |
} else { | |
hidden.threads[this.value] = (ref$ = Thread[this.value]) != null && ref$.sticky | |
? Number.MAX_VALUE | |
: Date.now(); | |
} | |
persist(); | |
} | |
}); | |
onupdate(function(){ | |
var i$, ref$, len$, a; | |
for (i$ = 0, len$ = (ref$ = $$(".new .quotelink")).length; i$ < len$; ++i$) { | |
a = ref$[i$]; | |
if (a.hash.substring(1) in hidden.replies) { | |
a.classList.toggle('hiddenlink'); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var fetchNewPost, handlePreview, createPreview; | |
fetchNewPost = function(no){ | |
var ref$, board, thread, link, x$, xhr, stillHovered; | |
ref$ = this.pathname.split('/'), board = ref$[1], thread = ref$[3]; | |
link = this; | |
this.style.cursor = 'progress'; | |
x$ = xhr = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board + "/res/" + thread + ".json"); | |
x$.onload = function(){ | |
var thread; | |
if (this.status === 200) { | |
thread = parser.api(JSON.parse(this.response)); | |
if (stillHovered) { | |
link.style.removeProperty('cursor'); | |
createPreview.call(link, no, Post[no]); | |
} | |
} | |
}; | |
x$.send(); | |
stillHovered = true; | |
this.addEventListener('mouseout', (function(){ | |
function out(){ | |
stillHovered = false; | |
this.style.removeProperty('cursor'); | |
return this.removeEventListener('mouseout', out); | |
} | |
return out; | |
}())); | |
}; | |
handlePreview = function(){ | |
var no, post; | |
if (this.classList.contains('inlinedlink') || this.classList.contains('recursivelink')) { | |
return; | |
} | |
no = this.hash.substring(2); | |
if (!(post = Post[no])) { | |
fetchNewPost.call(this, no); | |
} else { | |
createPreview.call(this, no, post); | |
} | |
}; | |
createPreview = function(no, post){ | |
var ref$, host, hostid, width, height, left, top, x$, preview, i$, y$, len$, z$, z1$, ref1$, z2$, docWidth; | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
host = closest('.post', this); | |
hostid = (split$.call(host.id, '-')).pop(); | |
ref$ = this.getBoundingClientRect(), width = ref$.width, height = ref$.height, left = ref$.left, top = ref$.top; | |
x$ = preview = post.element('article', void 8, 'postpreview'); | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: preview | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = x$.querySelectorAll(".quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = x$.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
z2$ = x$.style; | |
z2$.position = 'fixed'; | |
if (left > (docWidth = document.documentElement.clientWidth) / 2) { | |
z2$.right = (docWidth - left - width) + "px"; | |
} else { | |
z2$.left = left + "px"; | |
} | |
if (this.classList.contains('backlink')) { | |
z2$.top = (top + height + 5) + "px"; | |
} else { | |
z2$.bottom = (window.innerHeight - top + 5) + "px"; | |
} | |
document.body.appendChild(x$); | |
classify($$(".post[data-no=\"" + no + "\"]")).add('hovered'); | |
listen(this).once('mouseout', function(){ | |
preview.remove(); | |
classify($$(".post[data-no=\"" + no + "\"]")).remove('hovered'); | |
}); | |
}; | |
onPosts({ | |
'.quotelink': { | |
mouseover: handlePreview | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('mouseover', handlePreview); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.no': { | |
click: function(e){ | |
var selection, x$; | |
e.preventDefault(); | |
selection = window.getSelection().toString().trim(); | |
if (selection) { | |
selection = ">" + selection + "\n"; | |
} | |
x$ = $('comment'); | |
x$.value += ">>" + this.textContent + "\n" + selection; | |
x$.focus(); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var munge; | |
munge = function(ctx){ | |
var i$, ref$, len$, quote, no, post, x$, j$, y$, ref1$, len1$, z$, text, z1$, z2$, z3$; | |
for (i$ = 0, len$ = (ref$ = ctx.querySelectorAll('.quotelink:not(.backlink):not(.forcequoted)')).length; i$ < len$; ++i$) { | |
quote = ref$[i$]; | |
if (quote.parentNode.className === 'smaller') { | |
continue; | |
} | |
no = quote.hash.substring(2); | |
if (post = Post[no]) { | |
if (post.comment.length > 0) { | |
x$ = L('div'); | |
x$.innerHTML = post.comment.replace(/<br>/g, ' '); | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('.quotelink')).length; j$ < len1$; ++j$) { | |
y$ = ref1$[j$]; | |
y$.remove(); | |
} | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('s')).length; j$ < len1$; ++j$) { | |
z$ = ref1$[j$]; | |
z$.remove(); | |
} | |
text = x$.textContent; | |
quote.after((z1$ = L('span'), z1$.textContent = ' ' + truncate(text, 70).replace(/^\s+/, ''), z1$.className = 'quote forcedquote', z1$)); | |
} | |
if (post.image) { | |
quote.after((z2$ = L('a'), z2$.className = 'forcedimage', z2$.textContent = ' ', z2$.setAttribute('data-width', post.image.width), z2$.setAttribute('data-height', post.image.height), z2$.href = post.image.url, z2$.appendChild((z3$ = L('img'), z3$.className = 'thumb', ref1$ = z3$.style, ref1$.maxHeight = '15px', ref1$.display = 'inline-block', ref1$.verticalAlign = 'middle', z3$.src = post.image.thumb.url, z3$.addEventListener('mouseover', handlePreview), z3$)), z2$)); | |
} | |
quote.textContent = "»" + post.idx; | |
quote.classList.add('forcequoted'); | |
} | |
} | |
}; | |
if (board.isThread) { | |
onpostinsert(function(it){ | |
munge(it.detail.post); | |
}); | |
} | |
}.call(this)); | |
(function(){ | |
var apiKey, batchSize, rate, requestQueue, ready, queue, cache, setTitle, pendingVideos, loadInfo, onclick; | |
apiKey = "AIzaSyCe5gXUv-EFyNMoESO8ONZnottbsd-2ayA"; | |
batchSize = 30; | |
rate = 5000; | |
requestQueue = []; | |
ready = true; | |
queue = function(req){ | |
requestQueue.push(req); | |
req.addEventListener('loadend', function(){ | |
requestQueue.shift(); | |
defer(rate, function(){ | |
var that; | |
if (that = requestQueue[0]) { | |
that.send(); | |
} else { | |
ready = true; | |
} | |
}); | |
}); | |
if (ready) { | |
ready = false; | |
req.send(); | |
} | |
}; | |
cache = {}; | |
setTitle = function(vid, data){ | |
vid.title = data.statistics.viewCount + " views.\n\n" + truncate(data.snippet.description, 200); | |
vid.dataset.title = data.snippet.title; | |
}; | |
pendingVideos = []; | |
loadInfo = debounce(2000, function(){ | |
var toFetch, i$, ref$, len$, vid, that, batches, batch, id, b; | |
toFetch = {}; | |
for (i$ = 0, len$ = (ref$ = pendingVideos).length; i$ < len$; ++i$) { | |
vid = ref$[i$]; | |
vid.addEventListener('click', onclick); | |
if (that = cache[vid.dataset.id]) { | |
setTitle(vid, that); | |
} else { | |
toFetch[vid.dataset.id] = true; | |
} | |
} | |
pendingVideos = []; | |
batches = []; | |
batch = []; | |
for (id in toFetch) { | |
batch.push(id); | |
if (batch.length === batchSize) { | |
batches.push(batch); | |
batch = []; | |
} | |
} | |
batches.push(batch); | |
if (batch.length > 0) { | |
for (i$ = 0, len$ = batches.length; i$ < len$; ++i$) { | |
b = batches[i$]; | |
(fn$.call(this, new XMLHttpRequest, b)); | |
} | |
} | |
function fn$(req, b){ | |
req.open('GET', "https://www.googleapis.com/youtube/v3/videos?id=" + encodeURIComponent(b) + "&part=snippet%2C+statistics&fields=items(id%2Csnippet%2Cstatistics)&key=" + apiKey); | |
req.addEventListener('load', function(){ | |
var ref$, data, i$, len$, v, j$, ref1$, len1$, vid; | |
if (200 <= (ref$ = this.status) && ref$ < 400) { | |
data = JSON.parse(this.response); | |
for (i$ = 0, len$ = (ref$ = data.items).length; i$ < len$; ++i$) { | |
v = ref$[i$]; | |
cache[v.id] = v; | |
for (j$ = 0, len1$ = (ref1$ = $$(".youtube[data-id=\"" + v.id + "\"]")).length; j$ < len1$; ++j$) { | |
vid = ref1$[j$]; | |
setTitle(vid, v); | |
} | |
} | |
} else { | |
console.error("error fetching youtube info!", this); | |
} | |
}); | |
req.addEventListener('error', function(){ | |
console.error("what happen", this); | |
}); | |
queue(req); | |
} | |
}); | |
onclick = function(e){ | |
var x$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
this.replace((x$ = L('iframe'), x$.width = 560, x$.height = 315, x$.src = "//www.youtube.com/embed/" + this.dataset.id + "?" + (this.dataset.params || '') + "&autoplay=1&wmode=transparent", x$.frameborder = 0, x$.allowfullscreen = '', x$)); | |
} | |
}; | |
onpostinsert(function(it){ | |
pendingVideos.push.apply(pendingVideos, it.detail.post.querySelectorAll('.youtube')); | |
loadInfo(); | |
}); | |
}.call(this)); | |
(function(){ | |
var highlighting, highlight, toggleHighlight; | |
highlighting = sget('highlighting') || { | |
admin: false, | |
mod: false | |
}; | |
highlight = function(it){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$(it)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
post.classList.add('highlighted'); | |
} | |
}; | |
toggleHighlight = function(klass){ | |
return function(){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$("." + klass)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
highlighting[klass] = !highlighting[klass]; | |
sset('highlighting', highlighting); | |
post.classList.toggle('highlighted'); | |
} | |
}; | |
}; | |
onPosts({ | |
'.admin .capcode': { | |
click: toggleHighlight('admin') | |
}, | |
'.mod .capcode': { | |
click: toggleHighlight('mod') | |
} | |
}); | |
onready(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight(klass); | |
} | |
} | |
}); | |
onupdate(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight("new." + klass); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var ref$, markScroll, scroll, toggleOff, onclick, follow; | |
ref$ = (function(){ | |
var last, el; | |
return { | |
markScroll: function(it){ | |
el = it; | |
return last = el.getBoundingClientRect().top; | |
}, | |
scroll: function(){ | |
return window.scrollBy(0, el.getBoundingClientRect().top - last); | |
} | |
}; | |
}.call(this)), markScroll = ref$.markScroll, scroll = ref$.scroll; | |
toggleOff = function(link, inlined){ | |
var no, ref$, i$, x$, len$, pid, ref1$, that; | |
no = link.hash.substring(2); | |
link.hidden = false; | |
markScroll(link); | |
link.classList.remove('inlinedlink'); | |
link.parentNode.classList.remove('inlinedquote'); | |
if ($$(".inline[data-no=\"" + no + "\"]").length === 1) { | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.remove('inlined'); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll('.post.inline')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
pid = (split$.call(x$.no, '-')).pop(); | |
if ($$(".inline[data-no=\"" + pid + "\"]").length === 1) { | |
if ((ref1$ = $("p" + pid)) != null) { | |
ref1$.classList.remove('inlined'); | |
} | |
} | |
} | |
inlined.remove(); | |
if (that = link.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
link.nextElementSibling.hidden = false; | |
} | |
if (that = link.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
link.nextElementSibling.nextElementSibling.hidden = false; | |
} | |
} | |
} | |
scroll(); | |
}; | |
onclick = function(e){ | |
var post, no, host, hostid, inlinedId, stubId, inlined, isBacklink, wrapper, x$, i$, y$, ref$, len$, z$, z1$, ref1$, that, this$ = this; | |
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { | |
return; | |
} | |
if (!(post = Post[no = this.hash.substring(2)])) { | |
return; | |
} | |
e.preventDefault(); | |
host = closest('.post', this).id; | |
hostid = (split$.call(host, '-')).pop(); | |
inlinedId = host + "-p" + no; | |
stubId = no + "-inlined-stub"; | |
if (inlined = $(inlinedId)) { | |
toggleOff(this, inlined); | |
} else { | |
isBacklink = this.classList.contains('backlink'); | |
inlined = post.element('article', "inline hovered", inlinedId); | |
wrapper = this; | |
while (wrapper.parentElement.matchesSelector('a,span')) { | |
wrapper = wrapper.parentElement; | |
} | |
markScroll(this); | |
wrapper[isBacklink ? 'after' : 'before'](inlined); | |
if (isBacklink) { | |
inlined.prepend((x$ = L('a'), x$.textContent = post.idx, x$.className = 'inlined-idx', x$.addEventListener('click', function(){ | |
toggleOff(this$, inlined); | |
}), x$)); | |
this.hidden = true; | |
} | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: inlined | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll("a.quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = inlined.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
this.classList.add('inlinedlink'); | |
this.parentNode.classList.add('inlinedquote'); | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.add('inlined'); | |
} | |
if (!isBacklink) { | |
if (that = this.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
this.nextElementSibling.hidden = true; | |
} | |
if (that = this.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
this.nextElementSibling.nextElementSibling.hidden = true; | |
} | |
} | |
} | |
} | |
if (!isBacklink) { | |
scroll(); | |
} | |
} | |
}; | |
follow = function(){ | |
var that; | |
if (that = this.hash) { | |
window.location.hash = that; | |
} | |
}; | |
onPosts({ | |
'.quotelink:not(.hiddenlink)': { | |
click: onclick, | |
dblclick: follow | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('click', onclick); | |
x$.addEventListener('dblclick', follow); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
console.timeEnd("init"); | |
onready(function(){ | |
console.timeEnd("onready handlers"); | |
console.timeEnd("interactive"); | |
console.timeStamp("html5chan-loaded"); | |
console.groupEnd(); | |
}); | |
}.call(this)); | |
}).call(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name html5chan-jade-nowith | |
// @namespace https://github.com/nami-doc/html5chan | |
// @description The Beggin' Strips of 4chan userscripts | |
// | |
// @match *://boards.4chan.org/* | |
// @exclude *://boards.4chan.org/f/* | |
// @exclude *://boards.4chan.org/*/catalog | |
// @exclude *://boards.4chan.org/*/catalog/* | |
// @exclude *://boards.4chan.org/robots.txt | |
// | |
// @run-at document-start | |
// | |
// @grant none | |
// ==/UserScript== | |
(function(){ | |
"use strict"; | |
var | |
jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i<len;++i){var key=keys[i],val=obj[key];"boolean"==typeof val||null==val?val&&(terse?buf.push(key):buf.push(key+'="'+key+'"')):0==key.indexOf("data")&&"string"!=typeof val?buf.push(key+"='"+JSON.stringify(val)+"'"):"class"==key&&Array.isArray(val)?buf.push(key+'="'+exports.escape(val.join(" "))+'"'):escaped&&escaped[key]?buf.push(key+'="'+exports.escape(val)+'"'):buf.push(key+'="'+val+'"')}}return buf.join(" ")},exports.escape=function escape(html){return String(html).replace(/&(?!(\w+|\#\d+);)/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); | |
jade.escape = function (it) { return it; }; | |
var templates = {}; | |
templates.thread = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'id':(locals.id || "t" + (locals.thread.no) + ""), 'data-no':(locals.thread.no), "class": ('#classes ' + (locals.thread.className()) + '') }, {"id":true,"data-no":true,"class":true})); | |
buf.push('>'); | |
var __val__ = locals.thread.op.render('div', 'op') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('<div class="thread-info">' + escape((interp = (locals.thread.omitted && locals.thread.omitted.replies || 0) + locals.thread.replies.length) == null ? '' : interp) + ' replies and\n' + escape((interp = (locals.thread.omitted && locals.thread.omitted.imageReplies || 0) + locals.thread.imageReplies.length) == null ? '' : interp) + ' images.'); | |
if ( locals.thread.preview) | |
{ | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.thread.url), "class": ('expand-link') }, {"href":true})); | |
buf.push('>Expand</a>'); | |
} | |
buf.push('</div><div class="replies">'); | |
// iterate thread.replies | |
;(function(){ | |
if ('number' == typeof locals.thread.replies.length) { | |
for (var $index = 0, $$l = locals.thread.replies.length; $index < $$l; $index++) { | |
var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.thread.replies) { | |
$$l++; var reply = locals.thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
templates.board = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<nav id="toplinks" class="boardlinks">'); | |
var __val__ = board.nav | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</nav><header id="header"><a'); | |
buf.push(attrs({ 'id':('banner'), 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(board.banner), 'alt':('4chan::') }, {"src":true,"alt":true})); | |
buf.push('/></a><hgroup><h1 id="board-name"><a'); | |
buf.push(attrs({ 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = board.title | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a></h1><h2 id="board-subtitle">'); | |
var __val__ = board.subtitle | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</h2></hgroup></header>'); | |
if ( board.motd) | |
{ | |
buf.push('<div id="motd"><button id="hide-motd" type="button">Hide News</button><div id="message">'); | |
var __val__ = board.motd | |
buf.push(null == __val__ ? "" : __val__); | |
buf.push('</div></div>'); | |
} | |
buf.push('<div id="threads">'); | |
// iterate threads | |
;(function(){ | |
if ('number' == typeof locals.threads.length) { | |
for (var $index = 0, $$l = locals.threads.length; $index < $$l; $index++) { | |
var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in locals.threads) { | |
$$l++; var thread = locals.threads[$index]; | |
var __val__ = thread.render() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div>'); | |
if ( board.isBoard) | |
{ | |
buf.push('<ul id="pages">'); | |
if ( board.page > 0) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page - 1) }, {"href":true})); | |
buf.push('>previous</a></li>'); | |
} | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.url) }, {"href":true})); | |
buf.push('>0</a></li><li><a href="1">1</a></li><li><a href="2">2</a></li><li><a href="3">3</a></li><li><a href="4">4</a></li><li><a href="5">5</a></li><li><a href="6">6</a></li><li><a href="7">7</a></li><li><a href="8">8</a></li><li><a href="9">9</a></li><li><a href="10">10</a></li>'); | |
if ( board.page < 10) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page + 1) }, {"href":true})); | |
buf.push('>next</a></li>'); | |
} | |
buf.push('<li><a href="catalog">Catalog</a></li></ul>'); | |
} | |
if (!( board.locked)) | |
{ | |
buf.push('<div id="postform-wrapper"><form'); | |
buf.push(attrs({ 'id':('postform'), 'enctype':('multipart/form-data'), 'method':('POST'), 'action':('https://sys.4chan.org/' + (board.name) + '/post') }, {"enctype":true,"method":true,"action":true})); | |
buf.push('><input type="hidden" value="3145728" name="MAX_FILE_SIZE"/>'); | |
if ( board.threadId) | |
{ | |
buf.push('<input type="\" value="\" name="\"/>'); | |
} | |
buf.push('<input type="hidden" value="regist" name="mode"/><input'); | |
buf.push(attrs({ 'id':('password'), 'type':('hidden'), 'name':('pwd'), 'value':(board.password) }, {"type":true,"name":true,"value":true})); | |
buf.push('/><div id="fields"><input id="name" type="text" name="name" tabindex="10" placeholder="name#tripcode"/><input id="email" type="text" name="email" tabindex="10" placeholder="email"/><input id="subject" type="text" name="sub" tabindex="10" placeholder="subject"/><div id="comment-field"><textarea id="comment" name="com" rows="4" tabindex="10" placeholder="comment"></textarea></div><div id="captcha" style="display: none;"><a id="recaptcha_image" href="javascript:Recaptcha.reload()" title="Click for new captcha"></a><input id="recaptcha_response_field" type="text" name="recaptcha_response_field" tabindex="10" placeholder="captcha"/></div><div id="file-field"><input id="file" type="file" name="upfile" tabindex="10"/><label id="spoiler-field"><input type="checkbox" value="on" name="spoiler" tabindex="10"/>Spoiler?</label></div><div id="buttons"><button id="post" type="submit" tabindex="10" value="Submit">Post ' + escape((interp = board.isThread ? 'Reply' : 'New Thread') == null ? '' : interp) + '</button>'); | |
if ( board.isThread) | |
{ | |
buf.push('<button id="sage" type="submit" name="email" value="sage" tabindex="10">Sage Reply</button>'); | |
} | |
buf.push('<span id="post-status"></span><progress id="progress" max="100" value="0" hidden=""></progress></div></div></form></div>'); | |
} | |
buf.push('<span id="updater"><span id="update-status"></span><button id="update-now">Update now</button></span>'); | |
return buf.join(""); | |
} | |
templates.post = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
var interp; | |
buf.push('<' + (locals.container) + ''); | |
buf.push(attrs({ 'data-no':(locals.post.no), 'data-idx':(locals.post.idx), 'id':(locals.id || "p" + (locals.post.no) + ""), "class": ('' + (locals.classes) + ' ' + (locals.post.className()) + '') }, {"data-no":true,"class":true,"data-idx":true,"id":true})); | |
buf.push('><h1 class="post-header ptdr"><button'); | |
buf.push(attrs({ 'type':('button'), 'value':(locals.post.no), "class": ('hide') }, {"type":true,"value":true})); | |
buf.push('>×</button><button'); | |
buf.push(attrs({ 'type':('submit'), 'form':('reportform'), 'name':('no'), 'value':(locals.post.no), "class": ('report') }, {"type":true,"form":true,"name":true,"value":true})); | |
buf.push('>!</button><a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('subject') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.subject | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a><a'); | |
buf.push(attrs({ 'href':(locals.post.email ? "href='mailto:' + (email) + ''" : ''), "class": ('name') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = locals.post.name | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a><span class="tripcode">'); | |
var __val__ = locals.post.tripcode | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="capcode">'); | |
var __val__ = locals.post.capcode | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="posteruid">'); | |
var __val__ = locals.post.uid && "(ID: #{post.uid})" | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><time'); | |
buf.push(attrs({ 'pubdate':(true), 'datetime':(locals.post.time.toISOString()), 'title':(locals.post.time) }, {"datetime":true,"title":true})); | |
buf.push('>'); | |
var __val__ = locals.post.time.relativeTime() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</time>'); | |
if ( locals.post.op && locals.post.thread.sticky) | |
{ | |
buf.push('<img alt="sticky" src="//static.4chan.org/image/sticky.gif"/>'); | |
} | |
if ( locals.post.op && locals.post.thread.closed) | |
{ | |
buf.push('<img alt="closed" src="//static.4chan.org/image/closed.gif"/>'); | |
} | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(locals.post.url), "class": ('permalink') }, {"href":true})); | |
buf.push('>No.<span class="no">'); | |
var __val__ = locals.post.no | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span></a></h1>'); | |
if ( locals.post.image) | |
{ | |
buf.push('<div class="fileinfo"><span class="filename">'); | |
var __val__ = locals.post.image.filename || '' | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="dimensions">'); | |
var __val__ = locals.post.image.width + "x" + locals.post.image.height | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="size">'); | |
var __val__ = locals.post.image.size | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><a'); | |
buf.push(attrs({ 'href':('http://iqdb.org/?url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>iqdb</a><a'); | |
buf.push(attrs({ 'href':('http://google.com/searchbyimage?image_url=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>google</a><a'); | |
buf.push(attrs({ 'href':('http://regex.info/exif.cgi/exif.cgi?imgurl=' + (locals.post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>exif</a><a'); | |
buf.push(attrs({ 'href':('http://archive.foolz.us/' + (board.name) + '/search/image/' + (encodeURIComponent(locals.post.image.md5)) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>foolz</a></div><a'); | |
buf.push(attrs({ 'target':('_blank'), 'href':(locals.post.image.url), 'data-width':(locals.post.image.width), 'data-height':(locals.post.image.height), "class": ('file') }, {"target":true,"href":true,"data-width":true,"data-height":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(locals.post.image.thumb.url), 'width':(locals.post.image.thumb.width), 'height':(locals.post.image.thumb.height), "class": ('thumb') }, {"src":true,"width":true,"height":true})); | |
buf.push('/></a>'); | |
} | |
if ( locals.post.deletedImage) | |
{ | |
buf.push('<img alt="File deleted." src="//static.4chan.org/image/filedeleted.gif" class="deleted-image"/>'); | |
} | |
buf.push('<div class="comment">'); | |
var __val__ = locals.post.comment | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</div><footer class="backlinks">'); | |
var __val__ = locals.post.backlinks() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</footer></' + (locals.container) + '>'); | |
return buf.join(""); | |
} | |
var out$ = typeof exports != 'undefined' && exports || this, split$ = ''.split, slice$ = [].slice; | |
(function(){ | |
var board, x$, ref$, page, that, onready, onupdate, onpostinsert, onbacklink; | |
console.group("html5chan"); | |
console.timeStamp("html5chan-init"); | |
console.time("init"); | |
console.time("interactive"); | |
out$.board = board = {}; | |
x$ = board; | |
ref$ = split$.call(window.location.pathname, '/'), x$.name = ref$[1], page = ref$[2], x$.threadNo = ref$[3]; | |
x$.isThread = !!x$.threadNo; | |
x$.isBoard = !x$.isThread; | |
x$.page = parseInt(page, 10) || 0; | |
x$.url = "//boards.4chan.org/" + x$.name + "/"; | |
x$.threadurl = x$.url + 'res/'; | |
x$.threadPath = "/" + x$.name + "/res/" + x$.threadNo; | |
x$.archive = (function(){ | |
switch (that = x$.name) { | |
case 'a': | |
case 'jp': | |
case 'm': | |
case 'tg': | |
case 'u': | |
case 'tv': | |
case 'v': | |
case 'vg': | |
return "http://archive.foolz.us/" + that + "/thread"; | |
case 'lit': | |
return "http://fuuka.warosu.org/" + that + "/thread"; | |
case 'diy': | |
case 'g': | |
case 'sci': | |
return "http://archive.installgentoo.net/" + that + "/thread"; | |
} | |
}()); | |
x$.ready = false; | |
if (/404/.test(document.title) && board.archive) { | |
if (that = /\d+/.exec(window.location.pathname)) { | |
window.location = board.archive + "/" + that[0]; | |
return; | |
} | |
} | |
out$.onready = onready = function(it){ | |
document.addEventListener('html5chan-ready', it); | |
}; | |
out$.onupdate = onupdate = function(it){ | |
document.addEventListener('html5chan-update', it); | |
}; | |
out$.onpostinsert = onpostinsert = function(it){ | |
document.addEventListener('html5chan-postinsert', it); | |
}; | |
out$.onbacklink = onbacklink = function(it){ | |
document.addEventListener('html5chan-backlink', it); | |
}; | |
}.call(this)); | |
(function(){ | |
var truncate; | |
out$.truncate = truncate = function(it, length){ | |
length == null && (length = 20); | |
if (it.length > length) { | |
return it.substring(0, length) + "..."; | |
} else { | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var delay, deadZone, tooltip; | |
delay = 200; | |
deadZone = 10; | |
out$.tooltip = tooltip = function(arg$){ | |
var show, hide; | |
show = arg$.show, hide = arg$.hide; | |
return function(e){ | |
var x, y, timeout, lastEvent, createTooltip, resetTimeout, removeTooltip, this$ = this; | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
createTooltip = function(){ | |
show.call(this$, lastEvent); | |
listen(this$).off('mousemove', resetTimeout).on('mousemove', removeTooltip); | |
}; | |
resetTimeout = function(e){ | |
clearTimeout(timeout); | |
timeout = setTimeout(createTooltip, delay); | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
}; | |
removeTooltip = function(arg$){ | |
var cx, cy; | |
cx = arg$.clientX, cy = arg$.clientY; | |
if (Math.abs(x - cx) > deadZone || Math.abs(y - cy) > deadZone) { | |
hide.apply(this, arguments); | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
} | |
}; | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).once('mouseout', function(){ | |
hide.apply(this, arguments); | |
clearTimeout(timeout); | |
listen(this).off('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
}); | |
}; | |
}; | |
}.call(this)); | |
(function(){ | |
var $, $$, L, ref$, mutationMacro, x$, classify, closest; | |
out$.$ = $ = function(it){ | |
return document.getElementById(it); | |
}; | |
out$.$$ = $$ = function(it){ | |
return document.querySelectorAll(it); | |
}; | |
out$.L = L = function(it){ | |
return document.createElement(it); | |
}; | |
(ref$ = Element.prototype).matchesSelector == null && (ref$.matchesSelector = Element.prototype.mozMatchesSelector); | |
mutationMacro = function(nodes){ | |
var node, i$, len$, n; | |
if (nodes.length === 1) { | |
return typeof nodes[0] === 'string' | |
? document.createTextNode(nodes[0]) | |
: nodes[0]; | |
} | |
node = document.createDocumentFragment(); | |
for (i$ = 0, len$ = nodes.length; i$ < len$; ++i$) { | |
n = nodes[i$]; | |
if (typeof n === 'string') { | |
n = document.createTextNode(n); | |
} | |
node.appendChild(n); | |
} | |
return node; | |
}; | |
x$ = Node.prototype; | |
x$.prepend == null && (x$.prepend = function(){ | |
this.insertBefore(mutationMacro(arguments), this.firstChild); | |
}); | |
x$.append == null && (x$.append = function(){ | |
this.appendChild(mutationMacro(arguments)); | |
}); | |
x$.before == null && (x$.before = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this); | |
}); | |
x$.after == null && (x$.after = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this.nextSibling); | |
}); | |
x$.replace == null && (x$.replace = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.replaceChild(mutationMacro(arguments), this); | |
}); | |
x$.remove == null && (x$.remove = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.removeChild(this); | |
}); | |
out$.classify = classify = (function(){ | |
classify.displayName = 'classify'; | |
var prototype = classify.prototype, constructor = classify; | |
function classify(els){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.els = els; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.add = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.add(it); | |
} | |
}; | |
prototype.remove = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.remove(it); | |
} | |
}; | |
prototype.toggle = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.toggle(it); | |
} | |
}; | |
return classify; | |
}()); | |
out$.closest = closest = function(selector, el){ | |
for (; el; el = el.parentElement) { | |
if (el.matchesSelector(selector)) { | |
return el; | |
} | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var onPosts; | |
out$.onPosts = onPosts = function(listenerSpec){ | |
onpostinsert(function(it){ | |
var selector, ref$, listeners, i$, x$, ref1$, len$, event, listener; | |
for (selector in ref$ = listenerSpec) { | |
listeners = ref$[selector]; | |
for (i$ = 0, len$ = (ref1$ = it.detail.post.querySelectorAll(selector)).length; i$ < len$; ++i$) { | |
x$ = ref1$[i$]; | |
for (event in listeners) { | |
listener = listeners[event]; | |
x$.addEventListener(event, listener); | |
} | |
} | |
} | |
}); | |
}; | |
}.call(this)); | |
(function(){ | |
var pluralize; | |
pluralize = function(number, unit){ | |
return Math.round(number) + " " + unit + (number >= 1.5 ? 's' : '') + " ago"; | |
}; | |
Date.prototype.relativeTime = function(){ | |
var days, diff, hours, minutes, seconds; | |
if ((days = (diff = Date.now() - this.getTime()) / 86400000) > 1) { | |
return pluralize(days, 'day'); | |
} else if ((hours = days * 24) > 1) { | |
return pluralize(hours, 'hour'); | |
} else if ((minutes = hours * 60) > 1) { | |
return pluralize(minutes, 'minute'); | |
} else if ((seconds = minutes * 60) >= 1) { | |
return pluralize(seconds, 'second'); | |
} else { | |
return 'from the future!'; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var listen; | |
out$.listen = listen = (function(){ | |
listen.displayName = 'listen'; | |
var prototype = listen.prototype, constructor = listen; | |
function listen(element){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.element = element; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.on = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, handler); | |
} | |
return this; | |
}; | |
prototype.once = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, (function(){ | |
function once(e){ | |
var target; | |
target = e.target; | |
this.removeEventListener(event, once); | |
return handler.apply(this, arguments); | |
} | |
return once; | |
}())); | |
} | |
return this; | |
}; | |
prototype.off = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.removeEventListener(event, handler); | |
} | |
return this; | |
}; | |
['on', 'once', 'off'].forEach(function(method){ | |
var original; | |
original = prototype[method]; | |
prototype[method] = function(event, handler){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = split$.call(event, ' ')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
original.call(this, x$, handler); | |
} | |
return this; | |
}; | |
}); | |
['click', 'mouseover', 'scroll'].forEach(function(e){ | |
prototype[e] = function(selector, handler){ | |
return this.on(e, selector, handler); | |
}; | |
}); | |
return listen; | |
}()); | |
}.call(this)); | |
(function(){ | |
var debounce, defer, repeat; | |
out$.debounce = debounce = function(delay, fn){ | |
var timeout; | |
return function(){ | |
var ctx, args; | |
ctx = this; | |
args = arguments; | |
clearTimeout(timeout); | |
timeout = setTimeout(function(){ | |
fn.apply(ctx, args); | |
}, delay); | |
}; | |
}; | |
out$.defer = defer = function(delay, fn){ | |
var args; | |
if (typeof delay === 'function') { | |
fn = delay; | |
delay = 4; | |
args = Array.prototype.slice.call(arguments, 2); | |
} else { | |
args = Array.prototype.slice.call(arguments, 1); | |
} | |
return setTimeout.apply(null, [fn, delay].concat(args)); | |
}; | |
out$.repeat = repeat = (function(){ | |
repeat.displayName = 'repeat'; | |
var prototype = repeat.prototype, constructor = repeat; | |
function repeat(delay, options, fn){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.delay = delay; | |
if (typeof options === 'function') { | |
fn = options; | |
options = {}; | |
} | |
this$.fn = fn; | |
this$.timeoutee = function(){ | |
this$.fn.apply(this$, arguments); | |
if (this$.auto) { | |
this$.timeout = this$.repeat(); | |
} | |
}; | |
this$.auto = options.auto != null ? options.auto : true; | |
if (options.start !== false) { | |
this$.start(); | |
} | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.stop = function(){ | |
clearTimeout(this.timeout); | |
}; | |
prototype.start = function(){ | |
var args; | |
args = slice$.call(arguments); | |
this.stop(); | |
this.timeout = setTimeout.apply(null, [this.timeoutee, this.delay].concat(args)); | |
}; | |
prototype.restart = prototype.start; | |
prototype.repeat = prototype.start; | |
return repeat; | |
}()); | |
}.call(this)); | |
(function(){ | |
var setter, getter, ref$; | |
setter = function(storage){ | |
return function(key, val){ | |
var obj, ref$; | |
if (val != null) { | |
obj = (ref$ = {}, ref$[key] = val, ref$); | |
} | |
for (key in ref$ = obj || key) { | |
val = ref$[key]; | |
storage.setItem("html5chan-" + key, JSON.stringify(val)); | |
} | |
}; | |
}; | |
getter = function(storage){ | |
return function(it){ | |
try { | |
return JSON.parse(storage.getItem("html5chan-" + it)); | |
} catch (e$) {} | |
}; | |
}; | |
ref$ = out$; | |
ref$.set = setter(localStorage); | |
ref$.get = getter(localStorage); | |
ref$.sset = setter(sessionStorage); | |
ref$.sget = getter(sessionStorage); | |
}.call(this)); | |
(function(){ | |
var Post; | |
out$.Post = Post = (function(){ | |
Post.displayName = 'Post'; | |
var prototype = Post.prototype, constructor = Post; | |
prototype.postprocess = function(){ | |
var that, i$, len$, link, quoted, backlinks, ref$; | |
if (that = this.comment.match(/>>\d+/g)) { | |
for (i$ = 0, len$ = that.length; i$ < len$; ++i$) { | |
link = that[i$]; | |
quoted = link.substring(8); | |
backlinks = (ref$ = Post.backlinks)[quoted] || (ref$[quoted] = {}); | |
if (!backlinks[this.no]) { | |
((ref$ = Post.newBacklinks)[quoted] || (ref$[quoted] = {}))[this.no] = true; | |
backlinks[this.no] = true; | |
} | |
} | |
} | |
return constructor[this.no] = this; | |
}; | |
prototype.backlinks = function(onlyNew, postEl){ | |
var html, backlinks, post, idx; | |
html = ""; | |
backlinks = onlyNew | |
? Post.newBacklinks | |
: Post.backlinks; | |
if (backlinks[this.no]) { | |
for (post in backlinks[this.no]) { | |
if (board.isThread) { | |
idx = Post[post].idx; | |
} else { | |
idx = post; | |
} | |
html += "<a href=\"#p" + post + "\" class=\"backlink quotelink\">«" + idx + "</a> "; | |
if (onlyNew) { | |
document.dispatchEvent(new CustomEvent('html5chan-backlink', { | |
detail: { | |
no: post, | |
post: postEl | |
} | |
})); | |
} | |
} | |
} | |
return html; | |
}; | |
prototype.className = function(){ | |
var c, that; | |
c = "post "; | |
if (this.image) { | |
c += 'imagepost '; | |
} | |
if (this.sage) { | |
c += 'sage '; | |
} | |
if (that = this.tripcode) { | |
c += "tripcoded " + that + " "; | |
} | |
if (this.capcode) { | |
c += this.capcode === "## Admin" ? 'admin ' : 'mod '; | |
} | |
if (that = this.uid) { | |
c += "uid " + that; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
classes == null && (classes = ''); | |
return templates.post({ | |
container: container, | |
classes: classes, | |
id: id, | |
post: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, wrapper; | |
classes == null && (classes = ''); | |
x$ = wrapper = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return wrapper.firstElementChild; | |
}; | |
Object.defineProperty(prototype, 'text', { | |
get: function(){ | |
var x$; | |
x$ = L('div'); | |
return x$.innerHTML = this.comment, x$.textContent; | |
}, | |
configurable: true, | |
enumerable: true | |
}); | |
constructor.backlinks = {}; | |
constructor.newBacklinks = {}; | |
constructor.tripcodes = {}; | |
constructor.uids = {}; | |
function Post(){} | |
return Post; | |
}()); | |
}.call(this)); | |
(function(){ | |
var Thread; | |
out$.Thread = Thread = (function(){ | |
Thread.displayName = 'Thread'; | |
var prototype = Thread.prototype, constructor = Thread; | |
prototype.postprocess = function(){ | |
var i$, ref$, len$, reply; | |
this.posts = [this.op].concat(this.replies); | |
this.imageReplies = []; | |
this.reply = {}; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (reply.image) { | |
this.imageReplies.push(reply); | |
} | |
this.reply[reply.no] = reply; | |
} | |
if (Thread[this.no]) { | |
this['new'] = []; | |
this.deleted = []; | |
for (i$ = 0, len$ = (ref$ = Thread[this.no].replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!this.reply[reply.no]) { | |
this.deleted.push(reply); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!Thread[this.no].reply[reply.no]) { | |
this['new'].push(reply); | |
} | |
} | |
} | |
return Thread[this.no] = this; | |
}; | |
prototype.className = function(){ | |
var c; | |
c = 'thread '; | |
if (this.sticky) { | |
c += 'sticky '; | |
} | |
if (this.locked) { | |
c += ' locked'; | |
} | |
if (this.preview) { | |
c += ' preview'; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
container == null && (container = 'article'); | |
classes == null && (classes = ''); | |
return templates.thread({ | |
container: container, | |
classes: classes, | |
id: id, | |
thread: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, d; | |
x$ = d = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return d.firstElementChild; | |
}; | |
function Thread(){} | |
return Thread; | |
}()); | |
}.call(this)); | |
(function(){ | |
var dimensionRegex, sizeRegex, filenameRegex, spoilerRegex, sageRegex, parseThread, parsePost, parser, thumbsBase, imagesBase, humanized, parseApiPost; | |
dimensionRegex = /(\d+)x(\d+)/; | |
sizeRegex = /[\d\.]+ [KM]?B/; | |
filenameRegex = /title="([^"]+)"/; | |
spoilerRegex = /^Spoiler Image/; | |
sageRegex = /^sage$/i; | |
parseThread = function(el){ | |
var x$, omitted; | |
x$ = new Thread; | |
x$.no = el.id.substring(1); | |
x$.url = board.threadurl + x$.no; | |
x$.preview = true; | |
if (omitted = el.querySelector('.summary')) { | |
x$.omitted = { | |
replies: parseInt(omitted.textContent.match(/\d+(?= posts?)/), 10) || 0, | |
imageReplies: parseInt(omitted.textContent.match(/\d+(?= image (?:replies|reply))/), 10) || 0 | |
}; | |
} | |
x$.sticky = el.querySelector('.stickyIcon') != null; | |
x$.closed = el.querySelector('.closedIcon') != null; | |
x$.op = parsePost.call(x$, el.querySelector('.op')); | |
x$.op.idx = 0; | |
x$.replies = Array.prototype.map.call(el.getElementsByClassName('reply'), parsePost, x$); | |
x$.postprocess(); | |
return x$; | |
}; | |
parsePost = function(el, idx){ | |
var thread, x$, ref$, that, img, thumb, info, dimensions; | |
thread = this; | |
x$ = new Post; | |
x$.idx = 1 + idx + (((ref$ = thread.omitted) != null ? ref$.replies : void 8) || 0); | |
x$.thread = thread; | |
x$.no = el.id.substring(1); | |
x$.url = (x$.op = el.classList.contains('op')) | |
? thread.url | |
: thread.url + "#p" + x$.no; | |
x$.time = new Date(parseInt(el.querySelector('.dateTime').dataset.utc, 10) * 1000); | |
x$.subject = el.querySelector('.postInfo.desktop .subject').innerHTML; | |
x$.name = el.querySelector('.name').innerHTML; | |
x$.tripcode = (ref$ = el.querySelector('.postertrip')) != null ? ref$.innerHTML : void 8; | |
x$.capcode = (ref$ = el.querySelector('.capcode')) != null ? ref$.innerHTML : void 8; | |
x$.email = (ref$ = el.querySelector('.useremail')) != null ? ref$.href.substring(7) : void 8; | |
if (that = x$.email) { | |
x$.sage = sageRegex.test(that); | |
} | |
x$.comment = parser.enhance(el.querySelector('.postMessage').innerHTML); | |
x$.uid = (ref$ = el.querySelector('.hand')) != null ? ref$.textContent : void 8; | |
if (img = el.querySelector('.fileThumb')) { | |
if (img.firstElementChild.alt === "File deleted.") { | |
x$.deletedImage = true; | |
} else { | |
thumb = img.firstElementChild; | |
info = el.querySelector('.fileInfo').innerHTML; | |
dimensions = dimensionRegex.exec(info); | |
x$.image = { | |
thumb: { | |
url: thumb.src, | |
width: parseInt(thumb.style.width, 10), | |
height: parseInt(thumb.style.height, 10) | |
}, | |
url: thumb.parentNode.href, | |
width: parseInt(dimensions[1], 10), | |
height: parseInt(dimensions[2], 10), | |
size: sizeRegex.exec(thumb.alt)[0], | |
filename: (ref$ = filenameRegex.exec(info)) != null ? ref$[1] : void 8, | |
md5: thumb.dataset.md5, | |
spoiler: spoilerRegex.test(thumb.alt) | |
}; | |
} | |
} | |
x$.postprocess(); | |
return x$; | |
}; | |
out$.parser = parser = { | |
board: function(document){ | |
var threads; | |
console.time("parse board"); | |
threads = Array.prototype.map.call(document.querySelectorAll('.thread'), parseThread); | |
console.timeEnd("parse board"); | |
return threads; | |
}, | |
thread: function(document){ | |
var thread; | |
console.time("parse thread"); | |
thread = parseThread(document.querySelector('.thread')); | |
console.timeEnd("parse thread"); | |
return thread; | |
}, | |
api: function(data){ | |
var op, x$, ref$; | |
op = data.posts[0]; | |
x$ = new Thread; | |
x$.no = op.no; | |
x$.url = board.threadurl + op.no; | |
x$.preview = !!op.omitted_posts; | |
x$.sticky = !!op.sticky; | |
x$.closed = !!op.closed; | |
ref$ = data.posts.map(parseApiPost, x$), x$['op'] = ref$[0], x$['replies'] = slice$.call(ref$, 1); | |
x$.postprocess(); | |
return x$; | |
} | |
}; | |
thumbsBase = "//thumbs.4chan.org/" + board.name + "/thumb/"; | |
imagesBase = "//images.4chan.org/" + board.name + "/src/"; | |
humanized = function(bytes){ | |
var kbytes; | |
if (bytes < 1024) { | |
return bytes + " B"; | |
} else if ((kbytes = Math.round(bytes / 1024)) < 1024) { | |
return kbytes + " KB"; | |
} else { | |
return (kbytes / 1024).toString().substring(0, 3) + " MB"; | |
} | |
}; | |
parseApiPost = function(data, i){ | |
var x$, that; | |
x$ = new Post; | |
x$.idx = i; | |
x$.thread = this; | |
x$.url = this.url; | |
x$.time = new Date(data.time * 1000); | |
x$.no = data.no; | |
x$.subject = data.sub; | |
x$.name = data.name; | |
x$.tripcode = data.trip; | |
x$.uid = data.id; | |
x$.capcode = data.capcode; | |
x$.email = data.email; | |
x$.sage = x$.email === 'sage'; | |
x$.comment = (that = data.com) ? parser.enhance(that) : ''; | |
x$.image = data.fsize ? { | |
thumb: { | |
url: thumbsBase + data.tim + 's.jpg', | |
width: data.tn_w, | |
height: data.tn_h | |
}, | |
url: imagesBase + "" + data.tim + data.ext, | |
width: data.w, | |
height: data.h, | |
size: humanized(data.fsize), | |
filename: data.filename + "" + data.ext, | |
md5: data.md5, | |
spoiler: !!data.spoiler | |
} : void 8; | |
x$.deletedImage = !!data.filedeleted; | |
x$.postprocess(); | |
return x$; | |
}; | |
}.call(this)); | |
(function(){ | |
parser.enhance = function(it){ | |
if (it.length === 0) { | |
return it; | |
} | |
return it.replace(/<wbr>/g, '').replace(/(?:https?:\/\/)?(?:www\.)?(youtu\.be\/([\w\-_]+)(\?[&=\w\-_;\#]*)?|youtube\.com\/watch\?([&=\w\-_;\.\?\#\%]*)v=([\w\-_]+)([&=\w\-\._;\?\#\%]*))/g, '<a href="https://$1" class="youtube" data-id="$2$5" data-params="$3$4$6" target="_blank"><img src="//img.youtube.com/vi/$2$5/2.jpg"></a>').replace(/\((https?:\/\/)([^<\s\)]+)\)/g, '(<a class="external" rel="noreferrer" href="$1$2" title="$1$2" target="_blank">$2</a>)').replace(/([^"']|^)(https?:\/\/)([^<\s]+)/g, '$1<a class="external" rel="noreferrer" href="$2$3" title="$2$3" target="_blank">$3</a>').replace(/(^|>|;|\s)([\w\.\-]+\.(?:com|net|org|eu|jp|us|co\.uk)(\/[^<\s]*)?(?=[\s<]|$))/g, '$1<a class="external" rel="noreferrer" href="http://$2" title="$2" target="_blank">$2</a>').replace(/<span\x20class="deadlink">>>(\d+)<\/span>/g, board.archivelink); | |
}; | |
board.archivelink = board.archive ? "<a href=\"" + board.archive + "/$1\" class=\"deadlink\">>>$1</a>" : '$&'; | |
}.call(this)); | |
(function(){ | |
var lastUpdate, unread, favicons, x$, y$, drawFavicon, updater, fade, fadeWhenVisible; | |
lastUpdate = new Date; | |
unread = 0; | |
favicons = { | |
sfw: (x$ = L('img'), x$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv8AAAD/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/AAAA/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfj/////////////w==', x$), | |
nsfw: (y$ = L('img'), y$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8AAAD/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/AAAA/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfjAAD//////////w==', y$) | |
}; | |
drawFavicon = debounce(200, function(){ | |
var ref$, x$, link, y$, z$; | |
if ((ref$ = $('favicon')) != null) { | |
ref$.remove(); | |
} | |
x$ = link = L('link'); | |
x$.id = 'favicon'; | |
x$.rel = 'icon'; | |
x$.type = 'image/x-icon'; | |
y$ = L('canvas'); | |
y$.width = 16; | |
y$.height = 16; | |
z$ = y$.getContext('2d'); | |
z$.drawImage(favicons[board.type], 0, 0); | |
if (unread > 0) { | |
z$.font = '8px monospace'; | |
z$.fillStyle = '#000'; | |
z$.strokeStyle = '#fff'; | |
z$.lineWidth = 4; | |
z$.textBaseline = 'bottom'; | |
z$.textAlign = 'right'; | |
z$.strokeText(unread, 16, 16); | |
z$.fillText(unread, 16, 16); | |
} | |
link.href = y$.toDataURL('image/png'); | |
document.head.appendChild(link); | |
}); | |
out$.updater = updater = { | |
update: function(){ | |
var x$; | |
updater.status.textContent = "Updating thread..."; | |
updater.button.disabled = true; | |
x$ = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board.name + "/res/" + board.thread.no + ".json"); | |
x$.setRequestHeader('If-Modified-Since', lastUpdate.toUTCString()); | |
listen(x$).on('load', function(){ | |
var lastModified, thread, i$, ref$, len$, post, backlinks, last; | |
if (this.status === 404) { | |
document.title += '(dead)'; | |
updater.status.textContent = "thread 404'd"; | |
return; | |
} | |
if (this.status === 304) { | |
updater.countdown.restart(); | |
return; | |
} | |
lastModified = new Date(this.getResponseHeader('Last-Modified')); | |
if (!(lastModified > lastUpdate)) { | |
updater.countdown.restart(); | |
return; | |
} | |
updater.status.textContent = "update detected, parsing"; | |
lastUpdate = lastModified; | |
thread = parser.api(JSON.parse(this.response)); | |
if (thread['new'].length > 0) { | |
$("t" + thread.no).lastElementChild.insertAdjacentHTML('beforeend', (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'new reply')); | |
} | |
return results$; | |
}()).join('')); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: $("p" + post.no) | |
} | |
})); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.thread .backlinks')).length; i$ < len$; ++i$) { | |
backlinks = ref$[i$]; | |
backlinks.insertAdjacentHTML('beforeend', Post[backlinks.parentNode.dataset.no].backlinks(true, backlinks.parentNode)); | |
} | |
Post.newBacklinks = {}; | |
document.dispatchEvent(new CustomEvent('html5chan-update', { | |
detail: { | |
thread: thread | |
} | |
})); | |
unread += thread['new'].length; | |
drawFavicon(); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
fadeWhenVisible(post); | |
} | |
if (window.scrollMaxY - window.scrollY < 50 && !document.hidden) { | |
last = window.scrollY; | |
repeat(50, function(){ | |
var remaining; | |
if (last > window.scrollY) { | |
this.stop(); | |
} else if ((remaining = window.scrollMaxY - window.scrollY) > 1) { | |
window.scrollBy(0, remaining / 4); | |
last = window.scrollY; | |
} | |
}); | |
} | |
$("t" + thread.no).querySelector(".thread-info").textContent = thread.replies.length + " replies and " + thread.imageReplies.length + " image replies."; | |
} | |
updater.countdown.restart(); | |
}).on('timeout', function(){ | |
updater.status.textContent = "request timed out..."; | |
updater.countdown(); | |
}).on('error', function(){ | |
updater.status.textContent = "Couldn't fetch thread page!"; | |
}).on('loadend', function(){ | |
updater.button.disabled = false; | |
}); | |
x$.send(); | |
}, | |
countdown: repeat(1000, { | |
start: false | |
}, function(t){ | |
this.t = t || this.t || 30; | |
updater.status.textContent = "Updating in " + this.t + " seconds..."; | |
if (--this.t === 0) { | |
this.stop(); | |
updater.update(); | |
} | |
}) | |
}; | |
fade = function(post){ | |
defer(100, function(){ | |
post.classList.remove('new'); | |
--unread; | |
drawFavicon(); | |
}); | |
}; | |
fadeWhenVisible = function(it){ | |
var post, y; | |
post = $("p" + it.no); | |
y = post.offsetTop; | |
if (window.innerHeight + window.scrollY > y) { | |
if (document.hidden) { | |
listen(window).once('focus', function(){ | |
fade(post); | |
}); | |
} else { | |
fade(post); | |
} | |
} else { | |
listen(window).scroll((function(){ | |
function reset(){ | |
if (window.innerHeight + window.scrollY > post.offsetTop) { | |
fade(post); | |
return listen(window).off('scroll', reset); | |
} | |
} | |
return reset; | |
}())); | |
} | |
}; | |
onready(function(){ | |
updater.status = $('update-status'); | |
updater.button = $('update-now'); | |
if (board.isThread) { | |
updater.countdown.start(); | |
listen($('update-now')).click(function(){ | |
var x$; | |
x$ = updater.countdown; | |
x$.stop(); | |
x$.t = 30; | |
updater.update(); | |
}); | |
} else { | |
$('updater').hidden = true; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var postStatus; | |
postStatus = function(it){ | |
return $('post-status').textContent = it; | |
}; | |
onready(function(){ | |
var checkValidity, cooldown, ref$; | |
checkValidity = function(e){ | |
var form, captcha, file, comment, email, ref$, x$, data, y$; | |
e.preventDefault(); | |
form = $('postform'); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
email = $('email'); | |
if (/^noko$/i.test(email.value)) { | |
email.value = ''; | |
} | |
captcha.setCustomValidity(!captcha.value ? "You forgot the captcha!" : ''); | |
file.setCustomValidity(!file.value && board.isBoard ? "You forgot your image!" : ''); | |
comment.setCustomValidity(!file.value && !comment.value ? "You didn't enter a comment or select a file!" : ''); | |
if (form.checkValidity()) { | |
$('post').disabled = true; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = true; | |
} | |
postStatus("Posting..."); | |
x$ = $('progress'); | |
x$.hidden = false; | |
x$.value = 0; | |
data = new FormData(form); | |
if (this === $('sage')) { | |
data.append('email', 'sage'); | |
} | |
y$ = new XMLHttpRequest; | |
y$.open('POST', form.action); | |
listen(y$).on('load', function(){ | |
var x$, html, captcha, file, comment, ref$, y$; | |
x$ = html = L('div'); | |
x$.innerHTML = this.response; | |
console.log(html); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
$('post').disabled = false; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = false; | |
} | |
if (/Post successful!|uploaded!/.test(html.textContent)) { | |
postStatus('Post successful!'); | |
cooldown(); | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
$('recaptcha_image').click(); | |
updater.countdown.restart(3); | |
return parser.lastParse = 0; | |
} else if (/mistyped the verification/.test(html.textContent)) { | |
postStatus('You mistyped the verification!'); | |
$('recaptcha_image').click(); | |
y$ = captcha; | |
y$.value = ''; | |
y$.focus(); | |
return y$; | |
} else if (/duplicate file entry detected/) { | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
return $('recaptcha_image').click(); | |
} | |
}).on('loadend', function(){ | |
return $('progress').hidden = true; | |
}); | |
listen(y$.upload).on('progress', function(e){ | |
return $('progress').value = 100 * e.loaded / e.total; | |
}); | |
y$.send(data); | |
} | |
return false; | |
}; | |
listen($('post')).click(checkValidity); | |
listen($('sage')).click(checkValidity); | |
cooldown = function(){ | |
var post, sage, message, tminus; | |
post = $('post'); | |
sage = $('sage'); | |
post.disabled = true; | |
if (sage != null) { | |
sage.disabled = true; | |
} | |
message = post.textContent; | |
tminus = 30; | |
post.textContent = tminus; | |
return setTimeout((function(){ | |
function tick(){ | |
if (tminus-- === 0) { | |
post.textContent = message; | |
post.disabled = false; | |
return sage != null ? sage.disabled = false : void 8; | |
} else { | |
post.textContent = tminus; | |
return setTimeout(tick, 1000); | |
} | |
} | |
return tick; | |
}()), 1000); | |
}; | |
listen($('name')).on('input', function(){ | |
return set({ | |
name: this.value | |
}); | |
}); | |
if ((ref$ = $('name')) != null) { | |
ref$.value = get('name') || ''; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var x$, html, y$, head, z$, z1$, body, d; | |
x$ = html = L('html'); | |
x$.appendChild((y$ = head = L('head'), y$.appendChild(L('title')), y$.appendChild((z$ = L('style'), z$.id = 'html5chan-style', z$.textContent = ' html {\n min-height: 100%;\n font-family: Droid Serif, serif;\n font-size: 10pt;\n}\n::selection {\n background: #29df75;\n color: #000;\n}\n::-moz-selection {\n background: #29df75;\n color: #000;\n}\n[hidden] {\n display: none !important;\n}\nbutton:enabled {\n cursor: pointer;\n}\n.bold {\n font-weight: bold;\n}\n.smaller {\n font-size: smaller;\n}\n#toplinks {\n float: right;\n width: 300px;\n}\n#header {\n margin: 1em 0;\n color: #af0a0f;\n}\n#board-name {\n font-size: 24pt;\n margin: 0;\n}\n#board-name a {\n color: #af0a0f !important;\n text-decoration: none;\n}\n#board-name a:hover {\n text-decoration: underline;\n}\n#board-subtitle {\n font-size: 10px;\n font-weight: normal;\n}\n#banner {\n margin-right: 1em;\n float: left;\n}\n#motd {\n margin: 1em 0;\n}\n#hide-motd {\n text-align: right;\n font-size: 10pt;\n}\n#message {\n clear: both;\n}\n.boardlinks {\n font-size: 9pt;\n text-align: center;\n}\n.boardlinks a {\n text-decoration: none;\n}\n#threads {\n clear: both;\n}\n#pages {\n text-align: center;\n margin: 0pt;\n padding: 0pt;\n}\n#pages li {\n display: inline;\n}\n#pages a {\n border-color: #aaa;\n border-style: solid;\n border-width: 1px 0;\n color: #000;\n display: inline-block;\n margin: 0.25em;\n padding: 0.5em 1em;\n text-decoration: none;\n}\n#pages a#current,\n#pages a:hover {\n background-color: rgba(200,200,200,0.7);\n}\n#updater {\n float: right;\n}\n.post {\n margin: 0.2em;\n padding: 1em;\n padding-right: 0;\n border-radius: 0.3em;\n}\n.reply {\n margin-left: 2em;\n transition-property: background-color;\n transition-duration: 3s;\n}\n.reply.new {\n background: #feffbf noise !important;\n}\n.sage > .post-header > .name:after {\n content: " (sage)";\n}\n.reply:before,\n.inlined-idx {\n content: attr(data-idx);\n position: absolute;\n text-align: right;\n display: inline-block;\n margin-left: -3em;\n width: 2em;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.inlined-idx {\n cursor: pointer;\n}\n.inlined-idx:hover {\n text-decoration: underline;\n}\n.post-header {\n margin: 0;\n padding: 0;\n font-size: 8pt;\n font-family: sans-serif;\n color: sfw-border -10%;\n font-weight: normal;\n float: right;\n}\n.post .subject {\n color: #0f0c5d;\n font-weight: 800;\n text-decoration: none;\n}\n.post .subject:hover {\n text-decoration: underline;\n}\n.name {\n color: sfw-border -10%;\n}\n.name:link {\n text-decoration: underline;\n}\n.tripcode,\n.fileinfo {\n display: table;\n color: sfw-border -20%;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.tripcode:not(:hover) > .saucelink,\n.fileinfo:not(:hover) > .saucelink,\n.tripcode:not(:hover) > .dimensions,\n.fileinfo:not(:hover) > .dimensions,\n.tripcode:not(:hover) > .size,\n.fileinfo:not(:hover) > .size {\n transition-delay: 0.5s;\n opacity: 0;\n}\n.saucelink,\n.dimensions,\n.size {\n transition-duration: 0.5s;\n}\n.file {\n display: block;\n float: left;\n margin: 0.3em 1em 0.3em 0;\n position: relative;\n}\n.full {\n display: block;\n}\n.capcode {\n font-weight: 800;\n}\n.mod .capcode:hover,\n.admin .capcode:hover {\n cursor: pointer;\n}\n.admin .name,\n.admin .capcode,\n.admin .tripcode {\n color: #f00;\n}\n.admin .capcode:after {\n content: url("https://static.4chan.org/image/adminicon.gif");\n}\n.mod .name,\n.mod .capcode {\n color: #800080;\n}\n.mod .capcode:after {\n content: url("https://static.4chan.org/image/modicon.gif");\n}\n.hide,\n.report {\n float: right;\n padding: 0 1px;\n background: transparent;\n border: 0;\n}\n.post.hidden {\n opacity: 0.6;\n}\n.post.hidden .file,\n.post.hidden .comment,\n.post.hidden .backlinks,\n.post.hidden .fileinfo {\n display: none;\n}\n.post.inlined {\n display: none;\n}\n.post.inlined:target {\n display: block;\n}\n.post.highlighted {\n background-color: #d6bad0 !important;\n}\n.quotelink {\n text-decoration: none;\n}\n.hiddenlink {\n text-decoration: line-through;\n}\n.replylink {\n text-decoration: none;\n}\n.deadlink {\n color: #808080;\n}\n.permalink {\n text-decoration: none;\n color: inherit;\n}\n.permalink .no:hover {\n text-decoration: underline;\n}\n.recursivelink {\n font-weight: bold;\n color: #000 !important;\n}\n.comment {\n margin: 0;\n word-wrap: break-word;\n line-height: 1.8em;\n width: 40em;\n}\n.op .comment {\n width: 50em;\n}\n.quote {\n font-weight: normal;\n color: #789922;\n}\n.prettyprint {\n background-color: #fff;\n padding: 0.5em;\n display: inline-block;\n max-width: 40em;\n overflow: auto;\n}\ns {\n text-decoration: none;\n transition-duration: 1s;\n}\ns:not(:hover) > *,\ns:not(:hover) {\n color: transparent !important;\n text-shadow: 0 0 7px #000;\n}\n.backlinks {\n clear: both;\n}\n.backlink {\n margin-right: 1em;\n}\na.quotelink.inlinedlink,\nstrong.quotelink.inlinedlink {\n font-weight: bold;\n color: #000;\n}\n#postpreview {\n outline: none;\n padding: 0.5em;\n box-shadow: 5px 5px 10px rgba(0,0,0,0.5);\n margin: 0;\n}\n.inline {\n margin-right: 0;\n padding-right: 0;\n}\n.comment .inline {\n display: table;\n}\n.backlink + .inline {\n margin-left: 2em;\n}\n.inline .backlinks > .recursivelink {\n display: none;\n}\n.forcedimage {\n text-decoration: none;\n}\n.backlink.inlinedlink {\n display: table;\n}\n.hovered {\n outline: 3px dashed #00f;\n}\n#postform {\n display: table;\n margin: 1em auto;\n}\n#postform #comment,\n#postform #recaptcha_response_field {\n width: 100%;\n}\n#name,\n#email,\n#subject {\n width: 31.3%;\n}\n#recaptcha_image {\n display: block;\n background: #fff;\n width: 100% !important;\n}\n#recaptcha_image img {\n display: block;\n margin: auto;\n}\n.thread {\n padding-bottom: 5px;\n clear: both;\n}\n.thread-info {\n clear: left;\n text-align: right;\n}\n.thread.hidden {\n opacity: 0.6;\n}\n.thread.hidden .replies,\n.thread.hidden .thread-info {\n display: none;\n}\n.thread.hidden .op .file,\n.thread.hidden .op .comment,\n.thread.hidden .op .backlinks,\n.thread.hidden .op .fileinfo {\n display: none;\n}\nbody.sfw {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw > header a,\nbody.sfw > footer a,\nbody.sfw .boardlinks a {\n color: #34345c;\n}\nbody.sfw .boardlinks {\n color: #89a;\n}\nbody.sfw .post:target {\n background: #d6bad0 url("data:image/png;base64,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") !important;\n}\nbody.sfw .reply {\n background: linear-gradient(180deg, rgba(0,0,0,0.01), transparent 2em, rgba(255,255,255,0) calc(98%), rgba(255,255,255,0.03));\n}\nbody.sfw #postpreview {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw .reply:before,\nbody.sfw .inlined-idx {\n color: #9db0cb;\n}\nbody.sfw #postpreview.op {\n background-color: #eef2ff;\n}\nbody.sfw .quotelink {\n color: #d00;\n}\nbody.nsfw {\n background: #ffe url("//static.4chan.org/image/fade.png") repeat-x;\n color: #800000;\n}\nbody.nsfw > header a,\nbody.nsfw > footer a,\nbody.nsfw .boardlinks a {\n color: #800;\n}\nbody.nsfw .boardlinks {\n color: #b86;\n}\nbody.nsfw .thread {\n border-color: #808080;\n}\nbody.nsfw .post:target {\n background-color: #f0c0b0 !important;\n}\nbody.nsfw .reply,\nbody.nsfw #postpreview {\n background-color: #d9bfb7;\n}\nbody.nsfw .reply {\n border-color: #d9bfb7;\n}\nbody.nsfw .reply:before {\n color: #d9bfb7;\n}\nbody.nsfw .inlined-idx {\n color: #bd9083;\n}\nbody.nsfw #postpreview.op {\n background-color: #ffe;\n}\nbody.nsfw .quotelink {\n color: #000080;\n}\n.youtube {\n position: relative;\n text-decoration: none;\n border: 3px solid;\n border-color: #c6312b;\n border-radius: 10px;\n transition: 0.5s;\n overflow: hidden;\n display: inline-block;\n vertical-align: top;\n margin: 0.25em;\n width: 120px;\n height: 90px;\n}\n.youtube:hover {\n border-color: #ffa200;\n}\n.youtube:after {\n position: absolute;\n top: 0;\n left: 0;\n width: 115px;\n font-size: smaller;\n font-family: sans-serif;\n color: #fff;\n background: rgba(0,0,0,0.5);\n padding: 0 0.5em;\n content: attr(data-title);\n}\n.youtube:not(:hover):after {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n', z$)), y$.appendChild((z1$ = L('script'), z1$.src = '//www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', z1$.addEventListener('load', function(){ | |
var x$; | |
head.appendChild((x$ = L('script'), x$.src = '//www.google.com/recaptcha/api/js/recaptcha.js', x$.addEventListener('load', function(){ | |
var x$; | |
x$ = L('script'); | |
x$.textContent = "(function() {var c;if (c = document.getElementById('captcha')) {Recaptcha._init_options({theme: 'custom',custom_theme_widget: c});Recaptcha.theme = 'custom';Recaptcha.widget = c;Recaptcha._finish_widget();}}())"; | |
if (board.ready) { | |
head.appendChild(x$); | |
} else { | |
onready(function(){ | |
head.appendChild(x$); | |
}); | |
} | |
}), x$)); | |
}), z1$)), y$)); | |
x$.appendChild(body = L('body')); | |
d = document.replaceChild(html, document.documentElement); | |
document.addEventListener('DOMContentLoaded', function(){ | |
var x$, ref$, thread, threads, y$, z$, i$, len$, post; | |
console.time("initial render"); | |
console.time("parse page"); | |
x$ = board; | |
x$.title = d.querySelector('.boardTitle').textContent; | |
x$.subtitle = ((ref$ = d.querySelector('.boardSubtitle')) != null ? ref$.innerHTML : void 8) || ''; | |
x$.nav = d.querySelector('#boardNavDesktop').innerHTML; | |
x$.banner = d.querySelector('.title').src; | |
x$.motd = (ref$ = d.querySelector('.globalMessage')) != null ? ref$.innerHTML : void 8; | |
x$.sfw = d.querySelector('link[rel="shortcut icon"]').href.slice(-6) === 'ws.ico'; | |
x$.type = x$.sfw ? 'sfw' : 'nsfw'; | |
x$.password = get('password') || Math.random().toString().substr(-8); | |
console.timeEnd("parse page"); | |
console.log(board); | |
if (board.isThread) { | |
board.thread = thread = parser.thread(d); | |
board.threads = threads = [thread]; | |
} else { | |
board.threads = threads = parser.board(d); | |
} | |
console.log(threads); | |
Post.newBacklinks = {}; | |
y$ = body; | |
y$.id = board.name; | |
y$.className = board.type + " " + (board.isThread ? 'threadpage' : 'boardpage'); | |
console.time("generate and render new body"); | |
var bodyHtml = templates.board(board); | |
console.timeEnd("generate and render new body"); | |
console.time( "parse and render new body"); | |
body.innerHTML = bodyHtml; | |
console.timeEnd( "parse and render new body"); | |
if (board.isBoard) { | |
console.time("highlight current page"); | |
body.querySelector("#pages a[href=\"" + (board.page || board.url) + "\"]").id = 'current'; | |
console.timeEnd("highlight current page"); | |
} | |
console.time("set new page title"); | |
document.title = board.isThread | |
? (z$ = board.thread.op, truncate(z$.title || z$.text || ((ref$ = z$.image) != null ? ref$.filename : void 8) || z$.time.relativeTime()) + "\ - /" + board.name + "/") | |
: board.title; | |
console.timeEnd("set new page title"); | |
console.timeEnd("initial render"); | |
if (window.location.hash && !sget(document.URL)) { | |
window.location.hash = window.location.hash; | |
window.addEventListener('scroll', (function(){ | |
function registerPage(){ | |
var ref$; | |
sset((ref$ = {}, ref$[document.URL] = true, ref$)); | |
return window.removeEventListener('scroll', registerPage); | |
} | |
return registerPage; | |
}())); | |
} | |
console.time("initial post insertion handlers"); | |
for (i$ = 0, len$ = (ref$ = $$('.post')).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: post | |
} | |
})); | |
} | |
console.timeEnd("initial post insertion handlers"); | |
board.ready = true; | |
document.dispatchEvent(new CustomEvent('html5chan-ready', { | |
detail: { | |
post: post | |
} | |
})); | |
}); | |
}.call(this)); | |
(function(){ | |
var setUpdate; | |
setUpdate = function(el){ | |
var time, diff; | |
time = new Date(el.getAttribute('datetime')); | |
if ((diff = Date.now() - time.getTime()) < 8640000) { | |
return setTimeout(function(){ | |
el.textContent = time.relativeTime(); | |
return setUpdate(el); | |
}, diff > 3600000 | |
? diff % 3600000 | |
: diff > 60000 ? 300000 : 60000); | |
} | |
}; | |
onready(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = document.getElementsByTagName('time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
onupdate(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = $$('.new time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.file': { | |
click: function(e){ | |
var a, x$, ref$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
a = this; | |
this.hidden = true; | |
this.before((x$ = L('img'), x$.src = this.href, x$.className = 'full', ref$ = x$.style, ref$.display = 'block', ref$.maxWidth = '100%', x$.onclick = function(){ | |
var ref$, top; | |
if (this.width !== this.naturalWidth) { | |
this.style.removeProperty('max-width'); | |
} else { | |
a.hidden = false; | |
if ((ref$ = a.previousSibling) != null) { | |
ref$.remove(); | |
} | |
if (scroll && (top = a.getBoundingClientRect().top) < 0) { | |
window.scrollBy(0, top); | |
} | |
} | |
}, x$)); | |
} | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var objectFit, handlePreview; | |
objectFit = function(container, width, height){ | |
var ratio; | |
ratio = Math.min(1, container.height / height, container.width / width); | |
return { | |
width: ratio * width, | |
height: ratio * height | |
}; | |
}; | |
out$.handlePreview = handlePreview = tooltip({ | |
show: function(){ | |
var a, viewport, ref$, x$; | |
this.style.cursor = 'none'; | |
a = this.parentElement; | |
viewport = { | |
width: (ref$ = document.documentElement).clientWidth, | |
height: ref$.clientHeight | |
}; | |
document.body.append((x$ = L('img'), x$.id = 'imgpreview', x$.alt = "Loading...", x$.src = a.href, ref$ = objectFit(viewport, a.dataset.width, a.dataset.height), x$.width = ref$.width, x$.height = ref$.height, x$.addEventListener('load', function(){ | |
return this.removeAttribute('alt'); | |
}), x$.addEventListener('error', function(){ | |
return this.alt = "Unable to load image."; | |
}), ref$ = x$.style, ref$.position = 'fixed', ref$.left = 0, ref$.top = 0, ref$.pointerEvents = 'none', ref$.backgroundColor = 'rgba(0,0,0,.5)', ref$.padding = (viewport.height - x$.height) / 2 + "px " + (viewport.width - x$.width) / 2 + "px", ref$.transitionDuration = '.5s', ref$.opacity = 0, x$.addEventListener('transitionend', function(e){ | |
var propertyName; | |
propertyName = e.propertyName; | |
if (propertyName === 'opacity' && this.style.opacity === '0') { | |
return this.remove(); | |
} | |
}), defer(100, function(){ | |
x$.style.opacity = 1; | |
}), x$)); | |
}, | |
hide: function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.style.opacity = 0; | |
} | |
defer(100, function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.remove(); | |
} | |
}); | |
this.style.removeProperty('cursor'); | |
} | |
}); | |
onPosts({ | |
'.thumb': { | |
mouseover: handlePreview | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onready(function(){ | |
var hash, msg, btn; | |
hash = function(it){ | |
return it.innerHTML.length; | |
}; | |
if ($('motd')) { | |
msg = $('message'); | |
btn = $('hide-motd'); | |
if (get('motd-hash') === hash(msg)) { | |
msg.hidden = get('motd-hidden'); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
} else { | |
set('motd-hash', hash(msg)); | |
} | |
listen(btn).click(function(){ | |
msg.hidden = !msg.hidden; | |
set('motd-hidden', msg.hidden); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
}); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var threshold, hidden, e, persist, toggle; | |
threshold = 604800000; | |
hidden = { | |
threads: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-t-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()), | |
replies: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-r-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()) | |
}; | |
console.log(hidden); | |
(function(now){ | |
var type, ref$, hash, key, expiry; | |
for (type in ref$ = hidden) { | |
hash = ref$[type]; | |
for (key in hash) { | |
expiry = hash[key]; | |
if (expiry === true) { | |
hash[key] = Date.now(); | |
} else { | |
if (now - expiry > threshold) { | |
delete hash[key]; | |
} | |
} | |
} | |
} | |
}.call(this, Date.now())); | |
persist = function(){ | |
localStorage["4chan-hide-t-" + board.name] = JSON.stringify(hidden.threads); | |
localStorage["4chan-hide-r-" + board.name] = JSON.stringify(hidden.replies); | |
}; | |
toggle = function(prefix, no){ | |
var ref$; | |
classify($$(".quotelink[href$=\"#" + no + "\"]")).toggle('hiddenlink'); | |
return (ref$ = $(prefix + "" + no)) != null ? ref$.classList.toggle('hidden') : void 8; | |
}; | |
onready(function(){ | |
var i$, ref$, len$, btn, x$, no, y$; | |
for (i$ = 0, len$ = (ref$ = $$('.reply button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn$); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.op button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn1$); | |
} | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('reply')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
no = x$.dataset.no; | |
if (hidden.replies[no]) { | |
toggle('p', no); | |
} | |
} | |
if (board.isBoard) { | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('thread')).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
no = y$.dataset.no; | |
if (hidden.threads[no]) { | |
toggle('t', no); | |
} | |
} | |
} | |
function fn$(){ | |
toggle('p', this.value); | |
if (this.value in hidden.replies) { | |
delete hidden.replies[this.value]; | |
} else { | |
hidden.replies[this.value] = Date.now(); | |
} | |
persist(); | |
} | |
function fn1$(){ | |
var ref$; | |
toggle('t', this.value); | |
if (this.value in hidden.threads) { | |
delete hidden.threads[this.value]; | |
} else { | |
hidden.threads[this.value] = (ref$ = Thread[this.value]) != null && ref$.sticky | |
? Number.MAX_VALUE | |
: Date.now(); | |
} | |
persist(); | |
} | |
}); | |
onupdate(function(){ | |
var i$, ref$, len$, a; | |
for (i$ = 0, len$ = (ref$ = $$(".new .quotelink")).length; i$ < len$; ++i$) { | |
a = ref$[i$]; | |
if (a.hash.substring(1) in hidden.replies) { | |
a.classList.toggle('hiddenlink'); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var fetchNewPost, handlePreview, createPreview; | |
fetchNewPost = function(no){ | |
var ref$, board, thread, link, x$, xhr, stillHovered; | |
ref$ = this.pathname.split('/'), board = ref$[1], thread = ref$[3]; | |
link = this; | |
this.style.cursor = 'progress'; | |
x$ = xhr = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board + "/res/" + thread + ".json"); | |
x$.onload = function(){ | |
var thread; | |
if (this.status === 200) { | |
thread = parser.api(JSON.parse(this.response)); | |
if (stillHovered) { | |
link.style.removeProperty('cursor'); | |
createPreview.call(link, no, Post[no]); | |
} | |
} | |
}; | |
x$.send(); | |
stillHovered = true; | |
this.addEventListener('mouseout', (function(){ | |
function out(){ | |
stillHovered = false; | |
this.style.removeProperty('cursor'); | |
return this.removeEventListener('mouseout', out); | |
} | |
return out; | |
}())); | |
}; | |
handlePreview = function(){ | |
var no, post; | |
if (this.classList.contains('inlinedlink') || this.classList.contains('recursivelink')) { | |
return; | |
} | |
no = this.hash.substring(2); | |
if (!(post = Post[no])) { | |
fetchNewPost.call(this, no); | |
} else { | |
createPreview.call(this, no, post); | |
} | |
}; | |
createPreview = function(no, post){ | |
var ref$, host, hostid, width, height, left, top, x$, preview, i$, y$, len$, z$, z1$, ref1$, z2$, docWidth; | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
host = closest('.post', this); | |
hostid = (split$.call(host.id, '-')).pop(); | |
ref$ = this.getBoundingClientRect(), width = ref$.width, height = ref$.height, left = ref$.left, top = ref$.top; | |
x$ = preview = post.element('article', void 8, 'postpreview'); | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: preview | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = x$.querySelectorAll(".quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = x$.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
z2$ = x$.style; | |
z2$.position = 'fixed'; | |
if (left > (docWidth = document.documentElement.clientWidth) / 2) { | |
z2$.right = (docWidth - left - width) + "px"; | |
} else { | |
z2$.left = left + "px"; | |
} | |
if (this.classList.contains('backlink')) { | |
z2$.top = (top + height + 5) + "px"; | |
} else { | |
z2$.bottom = (window.innerHeight - top + 5) + "px"; | |
} | |
document.body.appendChild(x$); | |
classify($$(".post[data-no=\"" + no + "\"]")).add('hovered'); | |
listen(this).once('mouseout', function(){ | |
preview.remove(); | |
classify($$(".post[data-no=\"" + no + "\"]")).remove('hovered'); | |
}); | |
}; | |
onPosts({ | |
'.quotelink': { | |
mouseover: handlePreview | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('mouseover', handlePreview); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.no': { | |
click: function(e){ | |
var selection, x$; | |
e.preventDefault(); | |
selection = window.getSelection().toString().trim(); | |
if (selection) { | |
selection = ">" + selection + "\n"; | |
} | |
x$ = $('comment'); | |
x$.value += ">>" + this.textContent + "\n" + selection; | |
x$.focus(); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var munge; | |
munge = function(ctx){ | |
var i$, ref$, len$, quote, no, post, x$, j$, y$, ref1$, len1$, z$, text, z1$, z2$, z3$; | |
for (i$ = 0, len$ = (ref$ = ctx.querySelectorAll('.quotelink:not(.backlink):not(.forcequoted)')).length; i$ < len$; ++i$) { | |
quote = ref$[i$]; | |
if (quote.parentNode.className === 'smaller') { | |
continue; | |
} | |
no = quote.hash.substring(2); | |
if (post = Post[no]) { | |
if (post.comment.length > 0) { | |
x$ = L('div'); | |
x$.innerHTML = post.comment.replace(/<br>/g, ' '); | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('.quotelink')).length; j$ < len1$; ++j$) { | |
y$ = ref1$[j$]; | |
y$.remove(); | |
} | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('s')).length; j$ < len1$; ++j$) { | |
z$ = ref1$[j$]; | |
z$.remove(); | |
} | |
text = x$.textContent; | |
quote.after((z1$ = L('span'), z1$.textContent = ' ' + truncate(text, 70).replace(/^\s+/, ''), z1$.className = 'quote forcedquote', z1$)); | |
} | |
if (post.image) { | |
quote.after((z2$ = L('a'), z2$.className = 'forcedimage', z2$.textContent = ' ', z2$.setAttribute('data-width', post.image.width), z2$.setAttribute('data-height', post.image.height), z2$.href = post.image.url, z2$.appendChild((z3$ = L('img'), z3$.className = 'thumb', ref1$ = z3$.style, ref1$.maxHeight = '15px', ref1$.display = 'inline-block', ref1$.verticalAlign = 'middle', z3$.src = post.image.thumb.url, z3$.addEventListener('mouseover', handlePreview), z3$)), z2$)); | |
} | |
quote.textContent = "»" + post.idx; | |
quote.classList.add('forcequoted'); | |
} | |
} | |
}; | |
if (board.isThread) { | |
onpostinsert(function(it){ | |
munge(it.detail.post); | |
}); | |
} | |
}.call(this)); | |
(function(){ | |
var apiKey, batchSize, rate, requestQueue, ready, queue, cache, setTitle, pendingVideos, loadInfo, onclick; | |
apiKey = "AIzaSyCe5gXUv-EFyNMoESO8ONZnottbsd-2ayA"; | |
batchSize = 30; | |
rate = 5000; | |
requestQueue = []; | |
ready = true; | |
queue = function(req){ | |
requestQueue.push(req); | |
req.addEventListener('loadend', function(){ | |
requestQueue.shift(); | |
defer(rate, function(){ | |
var that; | |
if (that = requestQueue[0]) { | |
that.send(); | |
} else { | |
ready = true; | |
} | |
}); | |
}); | |
if (ready) { | |
ready = false; | |
req.send(); | |
} | |
}; | |
cache = {}; | |
setTitle = function(vid, data){ | |
vid.title = data.statistics.viewCount + " views.\n\n" + truncate(data.snippet.description, 200); | |
vid.dataset.title = data.snippet.title; | |
}; | |
pendingVideos = []; | |
loadInfo = debounce(2000, function(){ | |
var toFetch, i$, ref$, len$, vid, that, batches, batch, id, b; | |
toFetch = {}; | |
for (i$ = 0, len$ = (ref$ = pendingVideos).length; i$ < len$; ++i$) { | |
vid = ref$[i$]; | |
vid.addEventListener('click', onclick); | |
if (that = cache[vid.dataset.id]) { | |
setTitle(vid, that); | |
} else { | |
toFetch[vid.dataset.id] = true; | |
} | |
} | |
pendingVideos = []; | |
batches = []; | |
batch = []; | |
for (id in toFetch) { | |
batch.push(id); | |
if (batch.length === batchSize) { | |
batches.push(batch); | |
batch = []; | |
} | |
} | |
batches.push(batch); | |
if (batch.length > 0) { | |
for (i$ = 0, len$ = batches.length; i$ < len$; ++i$) { | |
b = batches[i$]; | |
(fn$.call(this, new XMLHttpRequest, b)); | |
} | |
} | |
function fn$(req, b){ | |
req.open('GET', "https://www.googleapis.com/youtube/v3/videos?id=" + encodeURIComponent(b) + "&part=snippet%2C+statistics&fields=items(id%2Csnippet%2Cstatistics)&key=" + apiKey); | |
req.addEventListener('load', function(){ | |
var ref$, data, i$, len$, v, j$, ref1$, len1$, vid; | |
if (200 <= (ref$ = this.status) && ref$ < 400) { | |
data = JSON.parse(this.response); | |
for (i$ = 0, len$ = (ref$ = data.items).length; i$ < len$; ++i$) { | |
v = ref$[i$]; | |
cache[v.id] = v; | |
for (j$ = 0, len1$ = (ref1$ = $$(".youtube[data-id=\"" + v.id + "\"]")).length; j$ < len1$; ++j$) { | |
vid = ref1$[j$]; | |
setTitle(vid, v); | |
} | |
} | |
} else { | |
console.error("error fetching youtube info!", this); | |
} | |
}); | |
req.addEventListener('error', function(){ | |
console.error("what happen", this); | |
}); | |
queue(req); | |
} | |
}); | |
onclick = function(e){ | |
var x$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
this.replace((x$ = L('iframe'), x$.width = 560, x$.height = 315, x$.src = "//www.youtube.com/embed/" + this.dataset.id + "?" + (this.dataset.params || '') + "&autoplay=1&wmode=transparent", x$.frameborder = 0, x$.allowfullscreen = '', x$)); | |
} | |
}; | |
onpostinsert(function(it){ | |
pendingVideos.push.apply(pendingVideos, it.detail.post.querySelectorAll('.youtube')); | |
loadInfo(); | |
}); | |
}.call(this)); | |
(function(){ | |
var highlighting, highlight, toggleHighlight; | |
highlighting = sget('highlighting') || { | |
admin: false, | |
mod: false | |
}; | |
highlight = function(it){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$(it)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
post.classList.add('highlighted'); | |
} | |
}; | |
toggleHighlight = function(klass){ | |
return function(){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$("." + klass)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
highlighting[klass] = !highlighting[klass]; | |
sset('highlighting', highlighting); | |
post.classList.toggle('highlighted'); | |
} | |
}; | |
}; | |
onPosts({ | |
'.admin .capcode': { | |
click: toggleHighlight('admin') | |
}, | |
'.mod .capcode': { | |
click: toggleHighlight('mod') | |
} | |
}); | |
onready(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight(klass); | |
} | |
} | |
}); | |
onupdate(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight("new." + klass); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var ref$, markScroll, scroll, toggleOff, onclick, follow; | |
ref$ = (function(){ | |
var last, el; | |
return { | |
markScroll: function(it){ | |
el = it; | |
return last = el.getBoundingClientRect().top; | |
}, | |
scroll: function(){ | |
return window.scrollBy(0, el.getBoundingClientRect().top - last); | |
} | |
}; | |
}.call(this)), markScroll = ref$.markScroll, scroll = ref$.scroll; | |
toggleOff = function(link, inlined){ | |
var no, ref$, i$, x$, len$, pid, ref1$, that; | |
no = link.hash.substring(2); | |
link.hidden = false; | |
markScroll(link); | |
link.classList.remove('inlinedlink'); | |
link.parentNode.classList.remove('inlinedquote'); | |
if ($$(".inline[data-no=\"" + no + "\"]").length === 1) { | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.remove('inlined'); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll('.post.inline')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
pid = (split$.call(x$.no, '-')).pop(); | |
if ($$(".inline[data-no=\"" + pid + "\"]").length === 1) { | |
if ((ref1$ = $("p" + pid)) != null) { | |
ref1$.classList.remove('inlined'); | |
} | |
} | |
} | |
inlined.remove(); | |
if (that = link.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
link.nextElementSibling.hidden = false; | |
} | |
if (that = link.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
link.nextElementSibling.nextElementSibling.hidden = false; | |
} | |
} | |
} | |
scroll(); | |
}; | |
onclick = function(e){ | |
var post, no, host, hostid, inlinedId, stubId, inlined, isBacklink, wrapper, x$, i$, y$, ref$, len$, z$, z1$, ref1$, that, this$ = this; | |
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { | |
return; | |
} | |
if (!(post = Post[no = this.hash.substring(2)])) { | |
return; | |
} | |
e.preventDefault(); | |
host = closest('.post', this).id; | |
hostid = (split$.call(host, '-')).pop(); | |
inlinedId = host + "-p" + no; | |
stubId = no + "-inlined-stub"; | |
if (inlined = $(inlinedId)) { | |
toggleOff(this, inlined); | |
} else { | |
isBacklink = this.classList.contains('backlink'); | |
inlined = post.element('article', "inline hovered", inlinedId); | |
wrapper = this; | |
while (wrapper.parentElement.matchesSelector('a,span')) { | |
wrapper = wrapper.parentElement; | |
} | |
markScroll(this); | |
wrapper[isBacklink ? 'after' : 'before'](inlined); | |
if (isBacklink) { | |
inlined.prepend((x$ = L('a'), x$.textContent = post.idx, x$.className = 'inlined-idx', x$.addEventListener('click', function(){ | |
toggleOff(this$, inlined); | |
}), x$)); | |
this.hidden = true; | |
} | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: inlined | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll("a.quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = inlined.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
this.classList.add('inlinedlink'); | |
this.parentNode.classList.add('inlinedquote'); | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.add('inlined'); | |
} | |
if (!isBacklink) { | |
if (that = this.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
this.nextElementSibling.hidden = true; | |
} | |
if (that = this.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
this.nextElementSibling.nextElementSibling.hidden = true; | |
} | |
} | |
} | |
} | |
if (!isBacklink) { | |
scroll(); | |
} | |
} | |
}; | |
follow = function(){ | |
var that; | |
if (that = this.hash) { | |
window.location.hash = that; | |
} | |
}; | |
onPosts({ | |
'.quotelink:not(.hiddenlink)': { | |
click: onclick, | |
dblclick: follow | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('click', onclick); | |
x$.addEventListener('dblclick', follow); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
console.timeEnd("init"); | |
onready(function(){ | |
console.timeEnd("onready handlers"); | |
console.timeEnd("interactive"); | |
console.timeStamp("html5chan-loaded"); | |
console.groupEnd(); | |
}); | |
}.call(this)); | |
}).call(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name html5chan-jade | |
// @namespace https://github.com/nami-doc/html5chan | |
// @description The Kevin Bacon of 4chan userscripts | |
// | |
// @match *://boards.4chan.org/* | |
// @exclude *://boards.4chan.org/f/* | |
// @exclude *://boards.4chan.org/*/catalog | |
// @exclude *://boards.4chan.org/*/catalog/* | |
// @exclude *://boards.4chan.org/robots.txt | |
// | |
// @run-at document-start | |
// | |
// @grant none | |
// ==/UserScript== | |
(function(){var | |
jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i<len;++i){var key=keys[i],val=obj[key];"boolean"==typeof val||null==val?val&&(terse?buf.push(key):buf.push(key+'="'+key+'"')):0==key.indexOf("data")&&"string"!=typeof val?buf.push(key+"='"+JSON.stringify(val)+"'"):"class"==key&&Array.isArray(val)?buf.push(key+'="'+exports.escape(val.join(" "))+'"'):escaped&&escaped[key]?buf.push(key+'="'+exports.escape(val)+'"'):buf.push(key+'="'+val+'"')}}return buf.join(" ")},exports.escape=function escape(html){return String(html).replace(/&(?!(\w+|\#\d+);)/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); | |
jade.escape = function (it) { return it; }; | |
var templates = {}; | |
templates.thread = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
with (locals || {}) { | |
var interp; | |
buf.push('<' + (container) + ''); | |
buf.push(attrs({ 'id':(id || "t" + (thread.no) + ""), 'data-no':(thread.no), "class": ('#classes ' + (thread.className()) + '') }, {"id":true,"data-no":true,"class":true})); | |
buf.push('>'); | |
var __val__ = thread.op.render('div', 'op') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('<div class="thread-info">' + escape((interp = (thread.omitted && thread.omitted.replies || 0) + thread.replies.length) == null ? '' : interp) + ' replies and\n' + escape((interp = (thread.omitted && thread.omitted.imageReplies || 0) + thread.imageReplies.length) == null ? '' : interp) + ' images.'); | |
if ( thread.preview) | |
{ | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(thread.url), "class": ('expand-link') }, {"href":true})); | |
buf.push('>Expand</a>'); | |
} | |
buf.push('</div><div class="replies">'); | |
// iterate thread.replies | |
;(function(){ | |
if ('number' == typeof thread.replies.length) { | |
for (var $index = 0, $$l = thread.replies.length; $index < $$l; $index++) { | |
var reply = thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in thread.replies) { | |
$$l++; var reply = thread.replies[$index]; | |
var __val__ = reply.render('article', 'reply') | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div></' + (container) + '>'); | |
} | |
return buf.join(""); | |
} | |
templates.board = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
with (locals || {}) { | |
var interp; | |
buf.push('<nav id="toplinks" class="boardlinks">'); | |
var __val__ = board.nav | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</nav><header id="header"><a'); | |
buf.push(attrs({ 'id':('banner'), 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(board.banner), 'alt':('4chan::') }, {"src":true,"alt":true})); | |
buf.push('/></a><hgroup><h1 id="board-name"><a'); | |
buf.push(attrs({ 'href':('//boards.4chan.org/' + (board.name) + '/') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = board.title | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a></h1><h2 id="board-subtitle">'); | |
var __val__ = board.subtitle | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</h2></hgroup></header>'); | |
if ( board.motd) | |
{ | |
buf.push('<div id="motd"><button id="hide-motd" type="button">Hide News</button><div id="message">'); | |
var __val__ = board.motd | |
buf.push(null == __val__ ? "" : __val__); | |
buf.push('</div></div>'); | |
} | |
buf.push('<div id="threads">'); | |
// iterate threads | |
;(function(){ | |
if ('number' == typeof threads.length) { | |
for (var $index = 0, $$l = threads.length; $index < $$l; $index++) { | |
var thread = threads[$index]; | |
var __val__ = thread.render() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} else { | |
var $$l = 0; | |
for (var $index in threads) { | |
$$l++; var thread = threads[$index]; | |
var __val__ = thread.render() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
} | |
} | |
}).call(this); | |
buf.push('</div>'); | |
if ( board.isBoard) | |
{ | |
buf.push('<ul id="pages">'); | |
if ( board.page > 0) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page - 1) }, {"href":true})); | |
buf.push('>previous</a></li>'); | |
} | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.url) }, {"href":true})); | |
buf.push('>0</a></li><li><a href="1">1</a></li><li><a href="2">2</a></li><li><a href="3">3</a></li><li><a href="4">4</a></li><li><a href="5">5</a></li><li><a href="6">6</a></li><li><a href="7">7</a></li><li><a href="8">8</a></li><li><a href="9">9</a></li><li><a href="10">10</a></li>'); | |
if ( board.page < 10) | |
{ | |
buf.push('<li><a'); | |
buf.push(attrs({ 'href':(board.page + 1) }, {"href":true})); | |
buf.push('>next</a></li>'); | |
} | |
buf.push('<li><a href="catalog">Catalog</a></li></ul>'); | |
} | |
if (!( board.locked)) | |
{ | |
buf.push('<div id="postform-wrapper"><form'); | |
buf.push(attrs({ 'id':('postform'), 'enctype':('multipart/form-data'), 'method':('POST'), 'action':('https://sys.4chan.org/' + (board.name) + '/post') }, {"enctype":true,"method":true,"action":true})); | |
buf.push('><input type="hidden" value="3145728" name="MAX_FILE_SIZE"/>'); | |
if ( board.threadId) | |
{ | |
buf.push('<input type="\" value="\" name="\"/>'); | |
} | |
buf.push('<input type="hidden" value="regist" name="mode"/><input'); | |
buf.push(attrs({ 'id':('password'), 'type':('hidden'), 'name':('pwd'), 'value':(board.password) }, {"type":true,"name":true,"value":true})); | |
buf.push('/><div id="fields"><input id="name" type="text" name="name" tabindex="10" placeholder="name#tripcode"/><input id="email" type="text" name="email" tabindex="10" placeholder="email"/><input id="subject" type="text" name="sub" tabindex="10" placeholder="subject"/><div id="comment-field"><textarea id="comment" name="com" rows="4" tabindex="10" placeholder="comment"></textarea></div><div id="captcha" style="display: none;"><a id="recaptcha_image" href="javascript:Recaptcha.reload()" title="Click for new captcha"></a><input id="recaptcha_response_field" type="text" name="recaptcha_response_field" tabindex="10" placeholder="captcha"/></div><div id="file-field"><input id="file" type="file" name="upfile" tabindex="10"/><label id="spoiler-field"><input type="checkbox" value="on" name="spoiler" tabindex="10"/>Spoiler?</label></div><div id="buttons"><button id="post" type="submit" tabindex="10" value="Submit">Post ' + escape((interp = board.isThread ? 'Reply' : 'New Thread') == null ? '' : interp) + '</button>'); | |
if ( board.isThread) | |
{ | |
buf.push('<button id="sage" type="submit" name="email" value="sage" tabindex="10">Sage Reply</button>'); | |
} | |
buf.push('<span id="post-status"></span><progress id="progress" max="100" value="0" hidden=""></progress></div></div></form></div>'); | |
} | |
buf.push('<span id="updater"><span id="update-status"></span><button id="update-now">Update now</button></span>'); | |
} | |
return buf.join(""); | |
} | |
templates.post = function anonymous(locals, attrs, escape, rethrow, merge) { | |
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; | |
var buf = []; | |
with (locals || {}) { | |
var interp; | |
buf.push('<' + (container) + ''); | |
buf.push(attrs({ 'data-no':(post.no), 'data-idx':(post.idx), 'id':(id || "p" + (post.no) + ""), "class": ('' + (classes) + ' ' + (post.className()) + '') }, {"data-no":true,"class":true,"data-idx":true,"id":true})); | |
buf.push('><h1 class="post-header ptdr"><button'); | |
buf.push(attrs({ 'type':('button'), 'value':(post.no), "class": ('hide') }, {"type":true,"value":true})); | |
buf.push('>×</button><button'); | |
buf.push(attrs({ 'type':('submit'), 'form':('reportform'), 'name':('no'), 'value':(post.no), "class": ('report') }, {"type":true,"form":true,"name":true,"value":true})); | |
buf.push('>!</button><a'); | |
buf.push(attrs({ 'href':(post.url), "class": ('subject') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = post.subject | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a><a'); | |
buf.push(attrs({ 'href':(post.email ? "href='mailto:' + (email) + ''" : ''), "class": ('name') }, {"href":true})); | |
buf.push('>'); | |
var __val__ = post.name | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</a><span class="tripcode">'); | |
var __val__ = post.tripcode | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="capcode">'); | |
var __val__ = post.capcode | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="posteruid">'); | |
var __val__ = post.uid && "(ID: #{post.uid})" | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><time'); | |
buf.push(attrs({ 'pubdate':(true), 'datetime':(post.time.toISOString()), 'title':(post.time) }, {"datetime":true,"title":true})); | |
buf.push('>'); | |
var __val__ = post.time.relativeTime() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</time>'); | |
if ( post.op && post.thread.sticky) | |
{ | |
buf.push('<img alt="sticky" src="//static.4chan.org/image/sticky.gif"/>'); | |
} | |
if ( post.op && post.thread.closed) | |
{ | |
buf.push('<img alt="closed" src="//static.4chan.org/image/closed.gif"/>'); | |
} | |
buf.push('<a'); | |
buf.push(attrs({ 'href':(post.url), "class": ('permalink') }, {"href":true})); | |
buf.push('>No.<span class="no">'); | |
var __val__ = post.no | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span></a></h1>'); | |
if ( post.image) | |
{ | |
buf.push('<div class="fileinfo"><span class="filename">'); | |
var __val__ = post.image.filename || '' | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="dimensions">'); | |
var __val__ = post.image.width + "x" + post.image.height | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><span class="size">'); | |
var __val__ = post.image.size | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</span><a'); | |
buf.push(attrs({ 'href':('http://iqdb.org/?url=' + (post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>iqdb</a><a'); | |
buf.push(attrs({ 'href':('http://google.com/searchbyimage?image_url=' + (post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>google</a><a'); | |
buf.push(attrs({ 'href':('http://regex.info/exif.cgi/exif.cgi?imgurl=' + (post.image.url) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>exif</a><a'); | |
buf.push(attrs({ 'href':('http://archive.foolz.us/' + (board.name) + '/search/image/' + (encodeURIComponent(post.image.md5)) + ''), 'target':('_blank'), "class": ('saucelink') }, {"href":true,"target":true})); | |
buf.push('>foolz</a></div><a'); | |
buf.push(attrs({ 'target':('_blank'), 'href':(post.image.url), 'data-width':(post.image.width), 'data-height':(post.image.height), "class": ('file') }, {"target":true,"href":true,"data-width":true,"data-height":true})); | |
buf.push('><img'); | |
buf.push(attrs({ 'src':(post.image.thumb.url), 'width':(post.image.thumb.width), 'height':(post.image.thumb.height), "class": ('thumb') }, {"src":true,"width":true,"height":true})); | |
buf.push('/></a>'); | |
} | |
if ( post.deletedImage) | |
{ | |
buf.push('<img alt="File deleted." src="//static.4chan.org/image/filedeleted.gif" class="deleted-image"/>'); | |
} | |
buf.push('<div class="comment">'); | |
var __val__ = post.comment | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</div><footer class="backlinks">'); | |
var __val__ = post.backlinks() | |
buf.push(escape(null == __val__ ? "" : __val__)); | |
buf.push('</footer></' + (container) + '>'); | |
} | |
return buf.join(""); | |
} | |
var out$ = typeof exports != 'undefined' && exports || this, split$ = ''.split, slice$ = [].slice; | |
(function(){ | |
var board, x$, ref$, page, that, onready, onupdate, onpostinsert, onbacklink; | |
console.group("html5chan"); | |
console.timeStamp("html5chan-init"); | |
console.time("init"); | |
console.time("interactive"); | |
out$.board = board = {}; | |
x$ = board; | |
ref$ = split$.call(window.location.pathname, '/'), x$.name = ref$[1], page = ref$[2], x$.threadNo = ref$[3]; | |
x$.isThread = !!x$.threadNo; | |
x$.isBoard = !x$.isThread; | |
x$.page = parseInt(page, 10) || 0; | |
x$.url = "//boards.4chan.org/" + x$.name + "/"; | |
x$.threadurl = x$.url + 'res/'; | |
x$.threadPath = "/" + x$.name + "/res/" + x$.threadNo; | |
x$.archive = (function(){ | |
switch (that = x$.name) { | |
case 'a': | |
case 'jp': | |
case 'm': | |
case 'tg': | |
case 'u': | |
case 'tv': | |
case 'v': | |
case 'vg': | |
return "http://archive.foolz.us/" + that + "/thread"; | |
case 'lit': | |
return "http://fuuka.warosu.org/" + that + "/thread"; | |
case 'diy': | |
case 'g': | |
case 'sci': | |
return "http://archive.installgentoo.net/" + that + "/thread"; | |
} | |
}()); | |
x$.ready = false; | |
if (/404/.test(document.title) && board.archive) { | |
if (that = /\d+/.exec(window.location.pathname)) { | |
window.location = board.archive + "/" + that[0]; | |
return; | |
} | |
} | |
out$.onready = onready = function(it){ | |
document.addEventListener('html5chan-ready', it); | |
}; | |
out$.onupdate = onupdate = function(it){ | |
document.addEventListener('html5chan-update', it); | |
}; | |
out$.onpostinsert = onpostinsert = function(it){ | |
document.addEventListener('html5chan-postinsert', it); | |
}; | |
out$.onbacklink = onbacklink = function(it){ | |
document.addEventListener('html5chan-backlink', it); | |
}; | |
}.call(this)); | |
(function(){ | |
var truncate; | |
out$.truncate = truncate = function(it, length){ | |
length == null && (length = 20); | |
if (it.length > length) { | |
return it.substring(0, length) + "..."; | |
} else { | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var delay, deadZone, tooltip; | |
delay = 200; | |
deadZone = 10; | |
out$.tooltip = tooltip = function(arg$){ | |
var show, hide; | |
show = arg$.show, hide = arg$.hide; | |
return function(e){ | |
var x, y, timeout, lastEvent, createTooltip, resetTimeout, removeTooltip, this$ = this; | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
createTooltip = function(){ | |
show.call(this$, lastEvent); | |
listen(this$).off('mousemove', resetTimeout).on('mousemove', removeTooltip); | |
}; | |
resetTimeout = function(e){ | |
clearTimeout(timeout); | |
timeout = setTimeout(createTooltip, delay); | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
}; | |
removeTooltip = function(arg$){ | |
var cx, cy; | |
cx = arg$.clientX, cy = arg$.clientY; | |
if (Math.abs(x - cx) > deadZone || Math.abs(y - cy) > deadZone) { | |
hide.apply(this, arguments); | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
} | |
}; | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).once('mouseout', function(){ | |
hide.apply(this, arguments); | |
clearTimeout(timeout); | |
listen(this).off('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
}); | |
}; | |
}; | |
}.call(this)); | |
(function(){ | |
var $, $$, L, ref$, mutationMacro, x$, classify, closest; | |
out$.$ = $ = function(it){ | |
return document.getElementById(it); | |
}; | |
out$.$$ = $$ = function(it){ | |
return document.querySelectorAll(it); | |
}; | |
out$.L = L = function(it){ | |
return document.createElement(it); | |
}; | |
(ref$ = Element.prototype).matchesSelector == null && (ref$.matchesSelector = Element.prototype.mozMatchesSelector); | |
mutationMacro = function(nodes){ | |
var node, i$, len$, n; | |
if (nodes.length === 1) { | |
return typeof nodes[0] === 'string' | |
? document.createTextNode(nodes[0]) | |
: nodes[0]; | |
} | |
node = document.createDocumentFragment(); | |
for (i$ = 0, len$ = nodes.length; i$ < len$; ++i$) { | |
n = nodes[i$]; | |
if (typeof n === 'string') { | |
n = document.createTextNode(n); | |
} | |
node.appendChild(n); | |
} | |
return node; | |
}; | |
x$ = Node.prototype; | |
x$.prepend == null && (x$.prepend = function(){ | |
this.insertBefore(mutationMacro(arguments), this.firstChild); | |
}); | |
x$.append == null && (x$.append = function(){ | |
this.appendChild(mutationMacro(arguments)); | |
}); | |
x$.before == null && (x$.before = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this); | |
}); | |
x$.after == null && (x$.after = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this.nextSibling); | |
}); | |
x$.replace == null && (x$.replace = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.replaceChild(mutationMacro(arguments), this); | |
}); | |
x$.remove == null && (x$.remove = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.removeChild(this); | |
}); | |
out$.classify = classify = (function(){ | |
classify.displayName = 'classify'; | |
var prototype = classify.prototype, constructor = classify; | |
function classify(els){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.els = els; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.add = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.add(it); | |
} | |
}; | |
prototype.remove = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.remove(it); | |
} | |
}; | |
prototype.toggle = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.toggle(it); | |
} | |
}; | |
return classify; | |
}()); | |
out$.closest = closest = function(selector, el){ | |
for (; el; el = el.parentElement) { | |
if (el.matchesSelector(selector)) { | |
return el; | |
} | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var onPosts; | |
out$.onPosts = onPosts = function(listenerSpec){ | |
onpostinsert(function(it){ | |
var selector, ref$, listeners, i$, x$, ref1$, len$, event, listener; | |
for (selector in ref$ = listenerSpec) { | |
listeners = ref$[selector]; | |
for (i$ = 0, len$ = (ref1$ = it.detail.post.querySelectorAll(selector)).length; i$ < len$; ++i$) { | |
x$ = ref1$[i$]; | |
for (event in listeners) { | |
listener = listeners[event]; | |
x$.addEventListener(event, listener); | |
} | |
} | |
} | |
}); | |
}; | |
}.call(this)); | |
(function(){ | |
var pluralize; | |
pluralize = function(number, unit){ | |
return Math.round(number) + " " + unit + (number >= 1.5 ? 's' : '') + " ago"; | |
}; | |
Date.prototype.relativeTime = function(){ | |
var days, diff, hours, minutes, seconds; | |
if ((days = (diff = Date.now() - this.getTime()) / 86400000) > 1) { | |
return pluralize(days, 'day'); | |
} else if ((hours = days * 24) > 1) { | |
return pluralize(hours, 'hour'); | |
} else if ((minutes = hours * 60) > 1) { | |
return pluralize(minutes, 'minute'); | |
} else if ((seconds = minutes * 60) >= 1) { | |
return pluralize(seconds, 'second'); | |
} else { | |
return 'from the future!'; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var listen; | |
out$.listen = listen = (function(){ | |
listen.displayName = 'listen'; | |
var prototype = listen.prototype, constructor = listen; | |
function listen(element){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.element = element; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.on = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, handler); | |
} | |
return this; | |
}; | |
prototype.once = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, (function(){ | |
function once(e){ | |
var target; | |
target = e.target; | |
this.removeEventListener(event, once); | |
return handler.apply(this, arguments); | |
} | |
return once; | |
}())); | |
} | |
return this; | |
}; | |
prototype.off = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.removeEventListener(event, handler); | |
} | |
return this; | |
}; | |
['on', 'once', 'off'].forEach(function(method){ | |
var original; | |
original = prototype[method]; | |
prototype[method] = function(event, handler){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = split$.call(event, ' ')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
original.call(this, x$, handler); | |
} | |
return this; | |
}; | |
}); | |
['click', 'mouseover', 'scroll'].forEach(function(e){ | |
prototype[e] = function(selector, handler){ | |
return this.on(e, selector, handler); | |
}; | |
}); | |
return listen; | |
}()); | |
}.call(this)); | |
(function(){ | |
var debounce, defer, repeat; | |
out$.debounce = debounce = function(delay, fn){ | |
var timeout; | |
return function(){ | |
var ctx, args; | |
ctx = this; | |
args = arguments; | |
clearTimeout(timeout); | |
timeout = setTimeout(function(){ | |
fn.apply(ctx, args); | |
}, delay); | |
}; | |
}; | |
out$.defer = defer = function(delay, fn){ | |
var args; | |
if (typeof delay === 'function') { | |
fn = delay; | |
delay = 4; | |
args = Array.prototype.slice.call(arguments, 2); | |
} else { | |
args = Array.prototype.slice.call(arguments, 1); | |
} | |
return setTimeout.apply(null, [fn, delay].concat(args)); | |
}; | |
out$.repeat = repeat = (function(){ | |
repeat.displayName = 'repeat'; | |
var prototype = repeat.prototype, constructor = repeat; | |
function repeat(delay, options, fn){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.delay = delay; | |
if (typeof options === 'function') { | |
fn = options; | |
options = {}; | |
} | |
this$.fn = fn; | |
this$.timeoutee = function(){ | |
this$.fn.apply(this$, arguments); | |
if (this$.auto) { | |
this$.timeout = this$.repeat(); | |
} | |
}; | |
this$.auto = options.auto != null ? options.auto : true; | |
if (options.start !== false) { | |
this$.start(); | |
} | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.stop = function(){ | |
clearTimeout(this.timeout); | |
}; | |
prototype.start = function(){ | |
var args; | |
args = slice$.call(arguments); | |
this.stop(); | |
this.timeout = setTimeout.apply(null, [this.timeoutee, this.delay].concat(args)); | |
}; | |
prototype.restart = prototype.start; | |
prototype.repeat = prototype.start; | |
return repeat; | |
}()); | |
}.call(this)); | |
(function(){ | |
var setter, getter, ref$; | |
setter = function(storage){ | |
return function(key, val){ | |
var obj, ref$; | |
if (val != null) { | |
obj = (ref$ = {}, ref$[key] = val, ref$); | |
} | |
for (key in ref$ = obj || key) { | |
val = ref$[key]; | |
storage.setItem("html5chan-" + key, JSON.stringify(val)); | |
} | |
}; | |
}; | |
getter = function(storage){ | |
return function(it){ | |
try { | |
return JSON.parse(storage.getItem("html5chan-" + it)); | |
} catch (e$) {} | |
}; | |
}; | |
ref$ = out$; | |
ref$.set = setter(localStorage); | |
ref$.get = getter(localStorage); | |
ref$.sset = setter(sessionStorage); | |
ref$.sget = getter(sessionStorage); | |
}.call(this)); | |
(function(){ | |
var Post; | |
out$.Post = Post = (function(){ | |
Post.displayName = 'Post'; | |
var prototype = Post.prototype, constructor = Post; | |
prototype.postprocess = function(){ | |
var that, i$, len$, link, quoted, backlinks, ref$; | |
if (that = this.comment.match(/>>\d+/g)) { | |
for (i$ = 0, len$ = that.length; i$ < len$; ++i$) { | |
link = that[i$]; | |
quoted = link.substring(8); | |
backlinks = (ref$ = Post.backlinks)[quoted] || (ref$[quoted] = {}); | |
if (!backlinks[this.no]) { | |
((ref$ = Post.newBacklinks)[quoted] || (ref$[quoted] = {}))[this.no] = true; | |
backlinks[this.no] = true; | |
} | |
} | |
} | |
return constructor[this.no] = this; | |
}; | |
prototype.backlinks = function(onlyNew, postEl){ | |
var html, backlinks, post, idx; | |
html = ""; | |
backlinks = onlyNew | |
? Post.newBacklinks | |
: Post.backlinks; | |
if (backlinks[this.no]) { | |
for (post in backlinks[this.no]) { | |
if (board.isThread) { | |
idx = Post[post].idx; | |
} else { | |
idx = post; | |
} | |
html += "<a href=\"#p" + post + "\" class=\"backlink quotelink\">«" + idx + "</a> "; | |
if (onlyNew) { | |
document.dispatchEvent(new CustomEvent('html5chan-backlink', { | |
detail: { | |
no: post, | |
post: postEl | |
} | |
})); | |
} | |
} | |
} | |
return html; | |
}; | |
prototype.className = function(){ | |
var c, that; | |
c = "post "; | |
if (this.image) { | |
c += 'imagepost '; | |
} | |
if (this.sage) { | |
c += 'sage '; | |
} | |
if (that = this.tripcode) { | |
c += "tripcoded " + that + " "; | |
} | |
if (this.capcode) { | |
c += this.capcode === "## Admin" ? 'admin ' : 'mod '; | |
} | |
if (that = this.uid) { | |
c += "uid " + that; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
classes == null && (classes = ''); | |
return templates.post({ | |
container: container, | |
classes: classes, | |
id: id, | |
post: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, wrapper; | |
classes == null && (classes = ''); | |
x$ = wrapper = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return wrapper.firstElementChild; | |
}; | |
Object.defineProperty(prototype, 'text', { | |
get: function(){ | |
var x$; | |
x$ = L('div'); | |
return x$.innerHTML = this.comment, x$.textContent; | |
}, | |
configurable: true, | |
enumerable: true | |
}); | |
constructor.backlinks = {}; | |
constructor.newBacklinks = {}; | |
constructor.tripcodes = {}; | |
constructor.uids = {}; | |
function Post(){} | |
return Post; | |
}()); | |
}.call(this)); | |
(function(){ | |
var Thread; | |
out$.Thread = Thread = (function(){ | |
Thread.displayName = 'Thread'; | |
var prototype = Thread.prototype, constructor = Thread; | |
prototype.postprocess = function(){ | |
var i$, ref$, len$, reply; | |
this.posts = [this.op].concat(this.replies); | |
this.imageReplies = []; | |
this.reply = {}; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (reply.image) { | |
this.imageReplies.push(reply); | |
} | |
this.reply[reply.no] = reply; | |
} | |
if (Thread[this.no]) { | |
this['new'] = []; | |
this.deleted = []; | |
for (i$ = 0, len$ = (ref$ = Thread[this.no].replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!this.reply[reply.no]) { | |
this.deleted.push(reply); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!Thread[this.no].reply[reply.no]) { | |
this['new'].push(reply); | |
} | |
} | |
} | |
return Thread[this.no] = this; | |
}; | |
prototype.className = function(){ | |
var c; | |
c = 'thread '; | |
if (this.sticky) { | |
c += 'sticky '; | |
} | |
if (this.locked) { | |
c += ' locked'; | |
} | |
if (this.preview) { | |
c += ' preview'; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
container == null && (container = 'article'); | |
classes == null && (classes = ''); | |
return templates.thread({ | |
container: container, | |
classes: classes, | |
id: id, | |
thread: this | |
}); | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, d; | |
x$ = d = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return d.firstElementChild; | |
}; | |
function Thread(){} | |
return Thread; | |
}()); | |
}.call(this)); | |
(function(){ | |
var dimensionRegex, sizeRegex, filenameRegex, spoilerRegex, sageRegex, parseThread, parsePost, parser, thumbsBase, imagesBase, humanized, parseApiPost; | |
dimensionRegex = /(\d+)x(\d+)/; | |
sizeRegex = /[\d\.]+ [KM]?B/; | |
filenameRegex = /title="([^"]+)"/; | |
spoilerRegex = /^Spoiler Image/; | |
sageRegex = /^sage$/i; | |
parseThread = function(el){ | |
var x$, omitted; | |
x$ = new Thread; | |
x$.no = el.id.substring(1); | |
x$.url = board.threadurl + x$.no; | |
x$.preview = true; | |
if (omitted = el.querySelector('.summary')) { | |
x$.omitted = { | |
replies: parseInt(omitted.textContent.match(/\d+(?= posts?)/), 10) || 0, | |
imageReplies: parseInt(omitted.textContent.match(/\d+(?= image (?:replies|reply))/), 10) || 0 | |
}; | |
} | |
x$.sticky = el.querySelector('.stickyIcon') != null; | |
x$.closed = el.querySelector('.closedIcon') != null; | |
x$.op = parsePost.call(x$, el.querySelector('.op')); | |
x$.op.idx = 0; | |
x$.replies = Array.prototype.map.call(el.getElementsByClassName('reply'), parsePost, x$); | |
x$.postprocess(); | |
return x$; | |
}; | |
parsePost = function(el, idx){ | |
var thread, x$, ref$, that, img, thumb, info, dimensions; | |
thread = this; | |
x$ = new Post; | |
x$.idx = 1 + idx + (((ref$ = thread.omitted) != null ? ref$.replies : void 8) || 0); | |
x$.thread = thread; | |
x$.no = el.id.substring(1); | |
x$.url = (x$.op = el.classList.contains('op')) | |
? thread.url | |
: thread.url + "#p" + x$.no; | |
x$.time = new Date(parseInt(el.querySelector('.dateTime').dataset.utc, 10) * 1000); | |
x$.subject = el.querySelector('.postInfo.desktop .subject').innerHTML; | |
x$.name = el.querySelector('.name').innerHTML; | |
x$.tripcode = (ref$ = el.querySelector('.postertrip')) != null ? ref$.innerHTML : void 8; | |
x$.capcode = (ref$ = el.querySelector('.capcode')) != null ? ref$.innerHTML : void 8; | |
x$.email = (ref$ = el.querySelector('.useremail')) != null ? ref$.href.substring(7) : void 8; | |
if (that = x$.email) { | |
x$.sage = sageRegex.test(that); | |
} | |
x$.comment = parser.enhance(el.querySelector('.postMessage').innerHTML); | |
x$.uid = (ref$ = el.querySelector('.hand')) != null ? ref$.textContent : void 8; | |
if (img = el.querySelector('.fileThumb')) { | |
if (img.firstElementChild.alt === "File deleted.") { | |
x$.deletedImage = true; | |
} else { | |
thumb = img.firstElementChild; | |
info = el.querySelector('.fileInfo').innerHTML; | |
dimensions = dimensionRegex.exec(info); | |
x$.image = { | |
thumb: { | |
url: thumb.src, | |
width: parseInt(thumb.style.width, 10), | |
height: parseInt(thumb.style.height, 10) | |
}, | |
url: thumb.parentNode.href, | |
width: parseInt(dimensions[1], 10), | |
height: parseInt(dimensions[2], 10), | |
size: sizeRegex.exec(thumb.alt)[0], | |
filename: (ref$ = filenameRegex.exec(info)) != null ? ref$[1] : void 8, | |
md5: thumb.dataset.md5, | |
spoiler: spoilerRegex.test(thumb.alt) | |
}; | |
} | |
} | |
x$.postprocess(); | |
return x$; | |
}; | |
out$.parser = parser = { | |
board: function(document){ | |
var threads; | |
console.time("parse board"); | |
threads = Array.prototype.map.call(document.querySelectorAll('.thread'), parseThread); | |
console.timeEnd("parse board"); | |
return threads; | |
}, | |
thread: function(document){ | |
var thread; | |
console.time("parse thread"); | |
thread = parseThread(document.querySelector('.thread')); | |
console.timeEnd("parse thread"); | |
return thread; | |
}, | |
api: function(data){ | |
var op, x$, ref$; | |
op = data.posts[0]; | |
x$ = new Thread; | |
x$.no = op.no; | |
x$.url = board.threadurl + op.no; | |
x$.preview = !!op.omitted_posts; | |
x$.sticky = !!op.sticky; | |
x$.closed = !!op.closed; | |
ref$ = data.posts.map(parseApiPost, x$), x$['op'] = ref$[0], x$['replies'] = slice$.call(ref$, 1); | |
x$.postprocess(); | |
return x$; | |
} | |
}; | |
thumbsBase = "//thumbs.4chan.org/" + board.name + "/thumb/"; | |
imagesBase = "//images.4chan.org/" + board.name + "/src/"; | |
humanized = function(bytes){ | |
var kbytes; | |
if (bytes < 1024) { | |
return bytes + " B"; | |
} else if ((kbytes = Math.round(bytes / 1024)) < 1024) { | |
return kbytes + " KB"; | |
} else { | |
return (kbytes / 1024).toString().substring(0, 3) + " MB"; | |
} | |
}; | |
parseApiPost = function(data, i){ | |
var x$, that; | |
x$ = new Post; | |
x$.idx = i; | |
x$.thread = this; | |
x$.url = this.url; | |
x$.time = new Date(data.time * 1000); | |
x$.no = data.no; | |
x$.subject = data.sub; | |
x$.name = data.name; | |
x$.tripcode = data.trip; | |
x$.uid = data.id; | |
x$.capcode = data.capcode; | |
x$.email = data.email; | |
x$.sage = x$.email === 'sage'; | |
x$.comment = (that = data.com) ? parser.enhance(that) : ''; | |
x$.image = data.fsize ? { | |
thumb: { | |
url: thumbsBase + data.tim + 's.jpg', | |
width: data.tn_w, | |
height: data.tn_h | |
}, | |
url: imagesBase + "" + data.tim + data.ext, | |
width: data.w, | |
height: data.h, | |
size: humanized(data.fsize), | |
filename: data.filename + "" + data.ext, | |
md5: data.md5, | |
spoiler: !!data.spoiler | |
} : void 8; | |
x$.deletedImage = !!data.filedeleted; | |
x$.postprocess(); | |
return x$; | |
}; | |
}.call(this)); | |
(function(){ | |
parser.enhance = function(it){ | |
if (it.length === 0) { | |
return it; | |
} | |
return it.replace(/<wbr>/g, '').replace(/(?:https?:\/\/)?(?:www\.)?(youtu\.be\/([\w\-_]+)(\?[&=\w\-_;\#]*)?|youtube\.com\/watch\?([&=\w\-_;\.\?\#\%]*)v=([\w\-_]+)([&=\w\-\._;\?\#\%]*))/g, '<a href="https://$1" class="youtube" data-id="$2$5" data-params="$3$4$6" target="_blank"><img src="//img.youtube.com/vi/$2$5/2.jpg"></a>').replace(/\((https?:\/\/)([^<\s\)]+)\)/g, '(<a class="external" rel="noreferrer" href="$1$2" title="$1$2" target="_blank">$2</a>)').replace(/([^"']|^)(https?:\/\/)([^<\s]+)/g, '$1<a class="external" rel="noreferrer" href="$2$3" title="$2$3" target="_blank">$3</a>').replace(/(^|>|;|\s)([\w\.\-]+\.(?:com|net|org|eu|jp|us|co\.uk)(\/[^<\s]*)?(?=[\s<]|$))/g, '$1<a class="external" rel="noreferrer" href="http://$2" title="$2" target="_blank">$2</a>').replace(/<span\x20class="deadlink">>>(\d+)<\/span>/g, board.archivelink); | |
}; | |
board.archivelink = board.archive ? "<a href=\"" + board.archive + "/$1\" class=\"deadlink\">>>$1</a>" : '$&'; | |
}.call(this)); | |
(function(){ | |
var lastUpdate, unread, favicons, x$, y$, drawFavicon, updater, fade, fadeWhenVisible; | |
lastUpdate = new Date; | |
unread = 0; | |
favicons = { | |
sfw: (x$ = L('img'), x$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv8AAAD/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/AAAA/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfj/////////////w==', x$), | |
nsfw: (y$ = L('img'), y$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8AAAD/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/AAAA/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfjAAD//////////w==', y$) | |
}; | |
drawFavicon = debounce(200, function(){ | |
var ref$, x$, link, y$, z$; | |
if ((ref$ = $('favicon')) != null) { | |
ref$.remove(); | |
} | |
x$ = link = L('link'); | |
x$.id = 'favicon'; | |
x$.rel = 'icon'; | |
x$.type = 'image/x-icon'; | |
y$ = L('canvas'); | |
y$.width = 16; | |
y$.height = 16; | |
z$ = y$.getContext('2d'); | |
z$.drawImage(favicons[board.type], 0, 0); | |
if (unread > 0) { | |
z$.font = '8px monospace'; | |
z$.fillStyle = '#000'; | |
z$.strokeStyle = '#fff'; | |
z$.lineWidth = 4; | |
z$.textBaseline = 'bottom'; | |
z$.textAlign = 'right'; | |
z$.strokeText(unread, 16, 16); | |
z$.fillText(unread, 16, 16); | |
} | |
link.href = y$.toDataURL('image/png'); | |
document.head.appendChild(link); | |
}); | |
out$.updater = updater = { | |
update: function(){ | |
var x$; | |
updater.status.textContent = "Updating thread..."; | |
updater.button.disabled = true; | |
x$ = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board.name + "/res/" + board.thread.no + ".json"); | |
x$.setRequestHeader('If-Modified-Since', lastUpdate.toUTCString()); | |
listen(x$).on('load', function(){ | |
var lastModified, thread, i$, ref$, len$, post, backlinks, last; | |
if (this.status === 404) { | |
document.title += '(dead)'; | |
updater.status.textContent = "thread 404'd"; | |
return; | |
} | |
if (this.status === 304) { | |
updater.countdown.restart(); | |
return; | |
} | |
lastModified = new Date(this.getResponseHeader('Last-Modified')); | |
if (!(lastModified > lastUpdate)) { | |
updater.countdown.restart(); | |
return; | |
} | |
updater.status.textContent = "update detected, parsing"; | |
lastUpdate = lastModified; | |
thread = parser.api(JSON.parse(this.response)); | |
if (thread['new'].length > 0) { | |
$("t" + thread.no).lastElementChild.insertAdjacentHTML('beforeend', (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'new reply')); | |
} | |
return results$; | |
}()).join('')); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: $("p" + post.no) | |
} | |
})); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.thread .backlinks')).length; i$ < len$; ++i$) { | |
backlinks = ref$[i$]; | |
backlinks.insertAdjacentHTML('beforeend', Post[backlinks.parentNode.dataset.no].backlinks(true, backlinks.parentNode)); | |
} | |
Post.newBacklinks = {}; | |
document.dispatchEvent(new CustomEvent('html5chan-update', { | |
detail: { | |
thread: thread | |
} | |
})); | |
unread += thread['new'].length; | |
drawFavicon(); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
fadeWhenVisible(post); | |
} | |
if (window.scrollMaxY - window.scrollY < 50 && !document.hidden) { | |
last = window.scrollY; | |
repeat(50, function(){ | |
var remaining; | |
if (last > window.scrollY) { | |
this.stop(); | |
} else if ((remaining = window.scrollMaxY - window.scrollY) > 1) { | |
window.scrollBy(0, remaining / 4); | |
last = window.scrollY; | |
} | |
}); | |
} | |
$("t" + thread.no).querySelector(".thread-info").textContent = thread.replies.length + " replies and " + thread.imageReplies.length + " image replies."; | |
} | |
updater.countdown.restart(); | |
}).on('timeout', function(){ | |
updater.status.textContent = "request timed out..."; | |
updater.countdown(); | |
}).on('error', function(){ | |
updater.status.textContent = "Couldn't fetch thread page!"; | |
}).on('loadend', function(){ | |
updater.button.disabled = false; | |
}); | |
x$.send(); | |
}, | |
countdown: repeat(1000, { | |
start: false | |
}, function(t){ | |
this.t = t || this.t || 30; | |
updater.status.textContent = "Updating in " + this.t + " seconds..."; | |
if (--this.t === 0) { | |
this.stop(); | |
updater.update(); | |
} | |
}) | |
}; | |
fade = function(post){ | |
defer(100, function(){ | |
post.classList.remove('new'); | |
--unread; | |
drawFavicon(); | |
}); | |
}; | |
fadeWhenVisible = function(it){ | |
var post, y; | |
post = $("p" + it.no); | |
y = post.offsetTop; | |
if (window.innerHeight + window.scrollY > y) { | |
if (document.hidden) { | |
listen(window).once('focus', function(){ | |
fade(post); | |
}); | |
} else { | |
fade(post); | |
} | |
} else { | |
listen(window).scroll((function(){ | |
function reset(){ | |
if (window.innerHeight + window.scrollY > post.offsetTop) { | |
fade(post); | |
return listen(window).off('scroll', reset); | |
} | |
} | |
return reset; | |
}())); | |
} | |
}; | |
onready(function(){ | |
updater.status = $('update-status'); | |
updater.button = $('update-now'); | |
if (board.isThread) { | |
updater.countdown.start(); | |
listen($('update-now')).click(function(){ | |
var x$; | |
x$ = updater.countdown; | |
x$.stop(); | |
x$.t = 30; | |
updater.update(); | |
}); | |
} else { | |
$('updater').hidden = true; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var postStatus; | |
postStatus = function(it){ | |
return $('post-status').textContent = it; | |
}; | |
onready(function(){ | |
var checkValidity, cooldown, ref$; | |
checkValidity = function(e){ | |
var form, captcha, file, comment, email, ref$, x$, data, y$; | |
e.preventDefault(); | |
form = $('postform'); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
email = $('email'); | |
if (/^noko$/i.test(email.value)) { | |
email.value = ''; | |
} | |
captcha.setCustomValidity(!captcha.value ? "You forgot the captcha!" : ''); | |
file.setCustomValidity(!file.value && board.isBoard ? "You forgot your image!" : ''); | |
comment.setCustomValidity(!file.value && !comment.value ? "You didn't enter a comment or select a file!" : ''); | |
if (form.checkValidity()) { | |
$('post').disabled = true; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = true; | |
} | |
postStatus("Posting..."); | |
x$ = $('progress'); | |
x$.hidden = false; | |
x$.value = 0; | |
data = new FormData(form); | |
if (this === $('sage')) { | |
data.append('email', 'sage'); | |
} | |
y$ = new XMLHttpRequest; | |
y$.open('POST', form.action); | |
listen(y$).on('load', function(){ | |
var x$, html, captcha, file, comment, ref$, y$; | |
x$ = html = L('div'); | |
x$.innerHTML = this.response; | |
console.log(html); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
$('post').disabled = false; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = false; | |
} | |
if (/Post successful!|uploaded!/.test(html.textContent)) { | |
postStatus('Post successful!'); | |
cooldown(); | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
$('recaptcha_image').click(); | |
updater.countdown.restart(3); | |
return parser.lastParse = 0; | |
} else if (/mistyped the verification/.test(html.textContent)) { | |
postStatus('You mistyped the verification!'); | |
$('recaptcha_image').click(); | |
y$ = captcha; | |
y$.value = ''; | |
y$.focus(); | |
return y$; | |
} else if (/duplicate file entry detected/) { | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
return $('recaptcha_image').click(); | |
} | |
}).on('loadend', function(){ | |
return $('progress').hidden = true; | |
}); | |
listen(y$.upload).on('progress', function(e){ | |
return $('progress').value = 100 * e.loaded / e.total; | |
}); | |
y$.send(data); | |
} | |
return false; | |
}; | |
listen($('post')).click(checkValidity); | |
listen($('sage')).click(checkValidity); | |
cooldown = function(){ | |
var post, sage, message, tminus; | |
post = $('post'); | |
sage = $('sage'); | |
post.disabled = true; | |
if (sage != null) { | |
sage.disabled = true; | |
} | |
message = post.textContent; | |
tminus = 30; | |
post.textContent = tminus; | |
return setTimeout((function(){ | |
function tick(){ | |
if (tminus-- === 0) { | |
post.textContent = message; | |
post.disabled = false; | |
return sage != null ? sage.disabled = false : void 8; | |
} else { | |
post.textContent = tminus; | |
return setTimeout(tick, 1000); | |
} | |
} | |
return tick; | |
}()), 1000); | |
}; | |
listen($('name')).on('input', function(){ | |
return set({ | |
name: this.value | |
}); | |
}); | |
if ((ref$ = $('name')) != null) { | |
ref$.value = get('name') || ''; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var x$, html, y$, head, z$, z1$, body, d; | |
x$ = html = L('html'); | |
x$.appendChild((y$ = head = L('head'), y$.appendChild(L('title')), y$.appendChild((z$ = L('style'), z$.id = 'html5chan-style', z$.textContent = ' html {\n min-height: 100%;\n font-family: Droid Serif, serif;\n font-size: 10pt;\n}\n::selection {\n background: #29df75;\n color: #000;\n}\n::-moz-selection {\n background: #29df75;\n color: #000;\n}\n[hidden] {\n display: none !important;\n}\nbutton:enabled {\n cursor: pointer;\n}\n.bold {\n font-weight: bold;\n}\n.smaller {\n font-size: smaller;\n}\n#toplinks {\n float: right;\n width: 300px;\n}\n#header {\n margin: 1em 0;\n color: #af0a0f;\n}\n#board-name {\n font-size: 24pt;\n margin: 0;\n}\n#board-name a {\n color: #af0a0f !important;\n text-decoration: none;\n}\n#board-name a:hover {\n text-decoration: underline;\n}\n#board-subtitle {\n font-size: 10px;\n font-weight: normal;\n}\n#banner {\n margin-right: 1em;\n float: left;\n}\n#motd {\n margin: 1em 0;\n}\n#hide-motd {\n text-align: right;\n font-size: 10pt;\n}\n#message {\n clear: both;\n}\n.boardlinks {\n font-size: 9pt;\n text-align: center;\n}\n.boardlinks a {\n text-decoration: none;\n}\n#threads {\n clear: both;\n}\n#pages {\n text-align: center;\n margin: 0pt;\n padding: 0pt;\n}\n#pages li {\n display: inline;\n}\n#pages a {\n border-color: #aaa;\n border-style: solid;\n border-width: 1px 0;\n color: #000;\n display: inline-block;\n margin: 0.25em;\n padding: 0.5em 1em;\n text-decoration: none;\n}\n#pages a#current,\n#pages a:hover {\n background-color: rgba(200,200,200,0.7);\n}\n#updater {\n float: right;\n}\n.post {\n margin: 0.2em;\n padding: 1em;\n padding-right: 0;\n border-radius: 0.3em;\n}\n.reply {\n margin-left: 2em;\n transition-property: background-color;\n transition-duration: 3s;\n}\n.reply.new {\n background: #feffbf noise !important;\n}\n.sage > .post-header > .name:after {\n content: " (sage)";\n}\n.reply:before,\n.inlined-idx {\n content: attr(data-idx);\n position: absolute;\n text-align: right;\n display: inline-block;\n margin-left: -3em;\n width: 2em;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.inlined-idx {\n cursor: pointer;\n}\n.inlined-idx:hover {\n text-decoration: underline;\n}\n.post-header {\n margin: 0;\n padding: 0;\n font-size: 8pt;\n font-family: sans-serif;\n color: sfw-border -10%;\n font-weight: normal;\n float: right;\n}\n.post .subject {\n color: #0f0c5d;\n font-weight: 800;\n text-decoration: none;\n}\n.post .subject:hover {\n text-decoration: underline;\n}\n.name {\n color: sfw-border -10%;\n}\n.name:link {\n text-decoration: underline;\n}\n.tripcode,\n.fileinfo {\n display: table;\n color: sfw-border -20%;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.tripcode:not(:hover) > .saucelink,\n.fileinfo:not(:hover) > .saucelink,\n.tripcode:not(:hover) > .dimensions,\n.fileinfo:not(:hover) > .dimensions,\n.tripcode:not(:hover) > .size,\n.fileinfo:not(:hover) > .size {\n transition-delay: 0.5s;\n opacity: 0;\n}\n.saucelink,\n.dimensions,\n.size {\n transition-duration: 0.5s;\n}\n.file {\n display: block;\n float: left;\n margin: 0.3em 1em 0.3em 0;\n position: relative;\n}\n.full {\n display: block;\n}\n.capcode {\n font-weight: 800;\n}\n.mod .capcode:hover,\n.admin .capcode:hover {\n cursor: pointer;\n}\n.admin .name,\n.admin .capcode,\n.admin .tripcode {\n color: #f00;\n}\n.admin .capcode:after {\n content: url("https://static.4chan.org/image/adminicon.gif");\n}\n.mod .name,\n.mod .capcode {\n color: #800080;\n}\n.mod .capcode:after {\n content: url("https://static.4chan.org/image/modicon.gif");\n}\n.hide,\n.report {\n float: right;\n padding: 0 1px;\n background: transparent;\n border: 0;\n}\n.post.hidden {\n opacity: 0.6;\n}\n.post.hidden .file,\n.post.hidden .comment,\n.post.hidden .backlinks,\n.post.hidden .fileinfo {\n display: none;\n}\n.post.inlined {\n display: none;\n}\n.post.inlined:target {\n display: block;\n}\n.post.highlighted {\n background-color: #d6bad0 !important;\n}\n.quotelink {\n text-decoration: none;\n}\n.hiddenlink {\n text-decoration: line-through;\n}\n.replylink {\n text-decoration: none;\n}\n.deadlink {\n color: #808080;\n}\n.permalink {\n text-decoration: none;\n color: inherit;\n}\n.permalink .no:hover {\n text-decoration: underline;\n}\n.recursivelink {\n font-weight: bold;\n color: #000 !important;\n}\n.comment {\n margin: 0;\n word-wrap: break-word;\n line-height: 1.8em;\n width: 40em;\n}\n.op .comment {\n width: 50em;\n}\n.quote {\n font-weight: normal;\n color: #789922;\n}\n.prettyprint {\n background-color: #fff;\n padding: 0.5em;\n display: inline-block;\n max-width: 40em;\n overflow: auto;\n}\ns {\n text-decoration: none;\n transition-duration: 1s;\n}\ns:not(:hover) > *,\ns:not(:hover) {\n color: transparent !important;\n text-shadow: 0 0 7px #000;\n}\n.backlinks {\n clear: both;\n}\n.backlink {\n margin-right: 1em;\n}\na.quotelink.inlinedlink,\nstrong.quotelink.inlinedlink {\n font-weight: bold;\n color: #000;\n}\n#postpreview {\n outline: none;\n padding: 0.5em;\n box-shadow: 5px 5px 10px rgba(0,0,0,0.5);\n margin: 0;\n}\n.inline {\n margin-right: 0;\n padding-right: 0;\n}\n.comment .inline {\n display: table;\n}\n.backlink + .inline {\n margin-left: 2em;\n}\n.inline .backlinks > .recursivelink {\n display: none;\n}\n.forcedimage {\n text-decoration: none;\n}\n.backlink.inlinedlink {\n display: table;\n}\n.hovered {\n outline: 3px dashed #00f;\n}\n#postform {\n display: table;\n margin: 1em auto;\n}\n#postform #comment,\n#postform #recaptcha_response_field {\n width: 100%;\n}\n#name,\n#email,\n#subject {\n width: 31.3%;\n}\n#recaptcha_image {\n display: block;\n background: #fff;\n width: 100% !important;\n}\n#recaptcha_image img {\n display: block;\n margin: auto;\n}\n.thread {\n padding-bottom: 5px;\n clear: both;\n}\n.thread-info {\n clear: left;\n text-align: right;\n}\n.thread.hidden {\n opacity: 0.6;\n}\n.thread.hidden .replies,\n.thread.hidden .thread-info {\n display: none;\n}\n.thread.hidden .op .file,\n.thread.hidden .op .comment,\n.thread.hidden .op .backlinks,\n.thread.hidden .op .fileinfo {\n display: none;\n}\nbody.sfw {\n background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wDAgEOI3mn8mQAAAgMSURBVGjedZrZbgO5DkSprRff+/+fGtta52FE46DQEyBw0ovEtVikHMzsZWbJzPr+fJtZNLNpZqf9+xPNLJhZNbPDzP72e3/7ft7P9P0ZzayZ2dr3y34m7Otjr//az7T9bjCz28w++521r0W80/He1/dN+4W4L46tjL/sP66gb3pjYVci73sT/6+tOA0QsUfC83M/H7dMfX+6XAf2pHzdBYxQYO3PBgu7VwIEnyJ4wCa+IQWd2IcG8v8r9u/w7Nj7BxigbI91yFHS3jhDmQlruqc8rApCzoX2MEpQcohR8hbcrX7ue74mLVyg4LGvNfwWeMiVmwlhsrD5gmAmcRngSV/cvdD3My/EfIJng8S9bUOt/b7hM8Pyh1wr2wieg0eClQaSukmCXftaQNh4OAw8a1vggbjPUDJvoT4QekHBG6Dg691b2XN/5q1IAxB93bIX0GZC44gc6dj4BMpF/C7Etxug4zNIbpwIPTdohTIT4ePK5A08mR5M0O6EZV3rJInn8Tnk+QakSvCMIdfCvu8GOJATjoSG3JkITXp/4dcNdCcsUPeLAfDYIMyJhQgOLgSt6Sj3gRf8fzdOQ8wn1ATf80BCLxggwIP+d00Cja59A7S6En/wUoVVGP8TGzpKpf372SCgYZXE07eg4VdAxsGF+0d3MbWMQJQD7u5AtIA6YkApt7yjVIWBjr1+g5UjqjpjnmtPPG+IkGMreZlZI8TSygX4XaRC0/oGqyx4MCOnPPErjOY1yC38gsIFVk9I/CiMwYHm4wXxQpwXcdkBq7b9rMnf/lwBHBbEfYDCAYp24U8TBdjvVwDRBGUxhGsxs0wk6BLfEQsaLJTE+hGoNBGOb9QNVzwhZBIguaDmJHj0xDMOuze87F7/1ZEDGi8k/5K6MbdFEoTxZ6vAcZa6QYZL+D6w9kK1n7g3hJ91wPHhofU/COxJuoReBIn/BLjOqPqsuhnx3LclAzxEVCxCTC8oFPHMQIkwsOTKenGI5kz8LiE1ABK+xgm0a7BcQGwf0ipMrB/A1RJ6GO55C1NwT/4WDIBINj4TVltAnYgQKSh8X4THLfnVca/D6x2MOiI/BgoskXAKQ789tF7A84C4m6DYE6TthNVP3A9I1PUQnhHQPZCPNwyzQEeisAu2BGQAxeD2hV6ELHPCogsKOK2viP+J0GT3dsHq3iXeG4EMOXDjnSkEdUC2j6CemUCf/mRYaWyB3rhnyJslPXbD+hUA4d52GKVV/7A3+dkpUM6i7PvnBCRhPxHgPsZs3gp94akPNvMKTq8sUXrik630EJKYJIzIiqlwMyQ4WW2TotjFU1U6QpLDiHUWKMoB5Qmf7EYZUmQQC7DeZSrj3WON0DwL9EZBlyB9+QVrZ9CUhHXYBjgdUoE6coTklR1gA0C8IJ97907C7U0Y8BQoXTLZ4IBhYVxk0oM3SeAsfccb+7PZegHxFtZLws9+hSfggSFF75KhwQTrNJkzNdSMFwQtgHSOiE4InmSKwr494H0TUts82RdCg/yGc6gllIPtaXsgmQneaRJK9HR7GPl0CigFmFTqBcP+EjQDcabgOj3gFP0NRBnyHOdkAd6osGRCbfrCYJewhiDgQ480GGYlM/s/EpsMuKLB/wIMvujR3aKs9hM5M8W7E6GzhOOxHr1RQ9rDCCkqKERJzggr3tI5Hg9Ic4JfaS99gn5U5MsXeVWk8EYU0PhQOLOAgQNAJAtNWNAeXMpJhsJ1RTGNAI2KZM1CSbqEmsFzAYJ/UVcCukXOmnuS5PZYPgXuTngrYuMhdeQNFpCwTnpguLYFrHLkcKGZOuX4YGH/DK+OJEXtIwhxSAvMfp4Vvsn0nFP2KO1ARSP1EcJ5SD0LQDFOXSZQrZpZjECVIJuyyJnkx4QAHe42YcxFmEICRE94oUDRN/YM0lhVmRv4uz83V17EIUuTyh6kexyIY9J9HXKnhw4yPvTkAWGacapVZMjdBLUKD11OESbKsGxuqp2hMMkiafwlQwLPnzdypaFR0yMFhvt4OGOJkOH0AZ1O9obQEdLsW44gOOqMMt3IcgRxSpwXOeYLMskf0gJ0GNTAxaa3ureEUpBJiDJNHn5+kQtVQisjhC5h0wlkk0WTbUQQysQzyYlZwe98ROdZUyaCnNQXxHkQqs42NCIM/HqTXtyktpjMCw4YgrWuouA6EISIytnRY5sMEniKxPrC+e4fwpCE8A0jkSdleNQkHB2a2RqwBbhhtGVmF8eTt+Azw6zKYI4d34AllyRsQB6xx75g2cLeG2FnAIWCdTkI/4VcEkhUtBjS4ZkM8YK0uGyf9UsEAWDwkWM+E6I6QR71SwMstB4xTWe4JxRJiOVDYr6i1eW3HHiWODH74mkYe/Ai3IpnlxXH2AHNVZIG7feFAeZCECEWkr/CUwu8aIAfGcajPCfkeQYH1l+ZVh7Y94UaEmSoYYJ+wYfYH/IW0I4uCRhlckg0aQi5iETXb000CKzzNR7PVQg8YTzywN+5ZcRmhENX6pajLhW2AmE41tRJiYHD3TzoR9gMNFBR4D7L1D5JDk9W1g7rFunkOIxQFw+J2yQzL519VWlZ0380dvHhYEh/fk0eC9chp6l/sFaQoUF6+JLNS6iOF60DAob/OKYL0iV+cerFEhFlMPE79/QEfAEB+sOc60DTleABfh3pg2tNqjb7mYj3D5lemoxiMwzBIwWeJf5mv1n4/ynWZke3oFzB4hHwXWU4od/nYv35SHE7pZb4gWpDuGdJg2Rm6R9tvEGBOM7EjAAAAABJRU5ErkJggg==") #dce0f4;\n}\nbody.sfw > header a,\nbody.sfw > footer a,\nbody.sfw .boardlinks a {\n color: #34345c;\n}\nbody.sfw .boardlinks {\n color: #89a;\n}\nbody.sfw .post:target {\n background: #d6bad0 url("data:image/png;base64,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") !important;\n}\nbody.sfw .reply {\n background: linear-gradient(180deg, rgba(0,0,0,0.01), transparent 2em, rgba(255,255,255,0) calc(98%), rgba(255,255,255,0.03));\n}\nbody.sfw #postpreview {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw .reply:before,\nbody.sfw .inlined-idx {\n color: #9db0cb;\n}\nbody.sfw #postpreview.op {\n background-color: #eef2ff;\n}\nbody.sfw .quotelink {\n color: #d00;\n}\nbody.nsfw {\n background: #ffe url("//static.4chan.org/image/fade.png") repeat-x;\n color: #800000;\n}\nbody.nsfw > header a,\nbody.nsfw > footer a,\nbody.nsfw .boardlinks a {\n color: #800;\n}\nbody.nsfw .boardlinks {\n color: #b86;\n}\nbody.nsfw .thread {\n border-color: #808080;\n}\nbody.nsfw .post:target {\n background-color: #f0c0b0 !important;\n}\nbody.nsfw .reply,\nbody.nsfw #postpreview {\n background-color: #d9bfb7;\n}\nbody.nsfw .reply {\n border-color: #d9bfb7;\n}\nbody.nsfw .reply:before {\n color: #d9bfb7;\n}\nbody.nsfw .inlined-idx {\n color: #bd9083;\n}\nbody.nsfw #postpreview.op {\n background-color: #ffe;\n}\nbody.nsfw .quotelink {\n color: #000080;\n}\n.youtube {\n position: relative;\n text-decoration: none;\n border: 3px solid;\n border-color: #c6312b;\n border-radius: 10px;\n transition: 0.5s;\n overflow: hidden;\n display: inline-block;\n vertical-align: top;\n margin: 0.25em;\n width: 120px;\n height: 90px;\n}\n.youtube:hover {\n border-color: #ffa200;\n}\n.youtube:after {\n position: absolute;\n top: 0;\n left: 0;\n width: 115px;\n font-size: smaller;\n font-family: sans-serif;\n color: #fff;\n background: rgba(0,0,0,0.5);\n padding: 0 0.5em;\n content: attr(data-title);\n}\n.youtube:not(:hover):after {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n', z$)), y$.appendChild((z1$ = L('script'), z1$.src = '//www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', z1$.addEventListener('load', function(){ | |
var x$; | |
head.appendChild((x$ = L('script'), x$.src = '//www.google.com/recaptcha/api/js/recaptcha.js', x$.addEventListener('load', function(){ | |
var x$; | |
x$ = L('script'); | |
x$.textContent = "(function() {var c;if (c = document.getElementById('captcha')) {Recaptcha._init_options({theme: 'custom',custom_theme_widget: c});Recaptcha.theme = 'custom';Recaptcha.widget = c;Recaptcha._finish_widget();}}())"; | |
if (board.ready) { | |
head.appendChild(x$); | |
} else { | |
onready(function(){ | |
head.appendChild(x$); | |
}); | |
} | |
}), x$)); | |
}), z1$)), y$)); | |
x$.appendChild(body = L('body')); | |
d = document.replaceChild(html, document.documentElement); | |
document.addEventListener('DOMContentLoaded', function(){ | |
var x$, ref$, thread, threads, y$, z$, i$, len$, post; | |
console.time("initial render"); | |
console.time("parse page"); | |
x$ = board; | |
x$.title = d.querySelector('.boardTitle').textContent; | |
x$.subtitle = ((ref$ = d.querySelector('.boardSubtitle')) != null ? ref$.innerHTML : void 8) || ''; | |
x$.nav = d.querySelector('#boardNavDesktop').innerHTML; | |
x$.banner = d.querySelector('.title').src; | |
x$.motd = (ref$ = d.querySelector('.globalMessage')) != null ? ref$.innerHTML : void 8; | |
x$.sfw = d.querySelector('link[rel="shortcut icon"]').href.slice(-6) === 'ws.ico'; | |
x$.type = x$.sfw ? 'sfw' : 'nsfw'; | |
x$.password = get('password') || Math.random().toString().substr(-8); | |
console.timeEnd("parse page"); | |
console.log(board); | |
if (board.isThread) { | |
board.thread = thread = parser.thread(d); | |
board.threads = threads = [thread]; | |
} else { | |
board.threads = threads = parser.board(d); | |
} | |
console.log(threads); | |
Post.newBacklinks = {}; | |
y$ = body; | |
y$.id = board.name; | |
y$.className = board.type + " " + (board.isThread ? 'threadpage' : 'boardpage'); | |
console.time("generate and render new body"); | |
bodyHtml = templates.board(board); | |
console.timeEnd("generate and render new body"); | |
console.time( "parse and render new body"); | |
body.innerHTML = bodyHtml; | |
console.timeEnd( "parse and render new body"); | |
if (board.isBoard) { | |
console.time("highlight current page"); | |
body.querySelector("#pages a[href=\"" + (board.page || board.url) + "\"]").id = 'current'; | |
console.timeEnd("highlight current page"); | |
} | |
console.time("set new page title"); | |
document.title = board.isThread | |
? (z$ = board.thread.op, truncate(z$.title || z$.text || ((ref$ = z$.image) != null ? ref$.filename : void 8) || z$.time.relativeTime()) + "\ - /" + board.name + "/") | |
: board.title; | |
console.timeEnd("set new page title"); | |
console.timeEnd("initial render"); | |
if (window.location.hash && !sget(document.URL)) { | |
window.location.hash = window.location.hash; | |
window.addEventListener('scroll', (function(){ | |
function registerPage(){ | |
var ref$; | |
sset((ref$ = {}, ref$[document.URL] = true, ref$)); | |
return window.removeEventListener('scroll', registerPage); | |
} | |
return registerPage; | |
}())); | |
} | |
console.time("initial post insertion handlers"); | |
for (i$ = 0, len$ = (ref$ = $$('.post')).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: post | |
} | |
})); | |
} | |
console.timeEnd("initial post insertion handlers"); | |
board.ready = true; | |
document.dispatchEvent(new CustomEvent('html5chan-ready', { | |
detail: { | |
post: post | |
} | |
})); | |
}); | |
}.call(this)); | |
(function(){ | |
var setUpdate; | |
setUpdate = function(el){ | |
var time, diff; | |
time = new Date(el.getAttribute('datetime')); | |
if ((diff = Date.now() - time.getTime()) < 8640000) { | |
return setTimeout(function(){ | |
el.textContent = time.relativeTime(); | |
return setUpdate(el); | |
}, diff > 3600000 | |
? diff % 3600000 | |
: diff > 60000 ? 300000 : 60000); | |
} | |
}; | |
onready(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = document.getElementsByTagName('time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
onupdate(function(){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = $$('.new time')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
setUpdate(x$); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.file': { | |
click: function(e){ | |
var a, x$, ref$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
a = this; | |
this.hidden = true; | |
this.before((x$ = L('img'), x$.src = this.href, x$.className = 'full', ref$ = x$.style, ref$.display = 'block', ref$.maxWidth = '100%', x$.onclick = function(){ | |
var ref$, top; | |
if (this.width !== this.naturalWidth) { | |
this.style.removeProperty('max-width'); | |
} else { | |
a.hidden = false; | |
if ((ref$ = a.previousSibling) != null) { | |
ref$.remove(); | |
} | |
if (scroll && (top = a.getBoundingClientRect().top) < 0) { | |
window.scrollBy(0, top); | |
} | |
} | |
}, x$)); | |
} | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var objectFit, handlePreview; | |
objectFit = function(container, width, height){ | |
var ratio; | |
ratio = Math.min(1, container.height / height, container.width / width); | |
return { | |
width: ratio * width, | |
height: ratio * height | |
}; | |
}; | |
out$.handlePreview = handlePreview = tooltip({ | |
show: function(){ | |
var a, viewport, ref$, x$; | |
this.style.cursor = 'none'; | |
a = this.parentElement; | |
viewport = { | |
width: (ref$ = document.documentElement).clientWidth, | |
height: ref$.clientHeight | |
}; | |
document.body.append((x$ = L('img'), x$.id = 'imgpreview', x$.alt = "Loading...", x$.src = a.href, ref$ = objectFit(viewport, a.dataset.width, a.dataset.height), x$.width = ref$.width, x$.height = ref$.height, x$.addEventListener('load', function(){ | |
return this.removeAttribute('alt'); | |
}), x$.addEventListener('error', function(){ | |
return this.alt = "Unable to load image."; | |
}), ref$ = x$.style, ref$.position = 'fixed', ref$.left = 0, ref$.top = 0, ref$.pointerEvents = 'none', ref$.backgroundColor = 'rgba(0,0,0,.5)', ref$.padding = (viewport.height - x$.height) / 2 + "px " + (viewport.width - x$.width) / 2 + "px", ref$.transitionDuration = '.5s', ref$.opacity = 0, x$.addEventListener('transitionend', function(e){ | |
var propertyName; | |
propertyName = e.propertyName; | |
if (propertyName === 'opacity' && this.style.opacity === '0') { | |
return this.remove(); | |
} | |
}), defer(100, function(){ | |
x$.style.opacity = 1; | |
}), x$)); | |
}, | |
hide: function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.style.opacity = 0; | |
} | |
defer(100, function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.remove(); | |
} | |
}); | |
this.style.removeProperty('cursor'); | |
} | |
}); | |
onPosts({ | |
'.thumb': { | |
mouseover: handlePreview | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onready(function(){ | |
var hash, msg, btn; | |
hash = function(it){ | |
return it.innerHTML.length; | |
}; | |
if ($('motd')) { | |
msg = $('message'); | |
btn = $('hide-motd'); | |
if (get('motd-hash') === hash(msg)) { | |
msg.hidden = get('motd-hidden'); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
} else { | |
set('motd-hash', hash(msg)); | |
} | |
listen(btn).click(function(){ | |
msg.hidden = !msg.hidden; | |
set('motd-hidden', msg.hidden); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
}); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var threshold, hidden, e, persist, toggle; | |
threshold = 604800000; | |
hidden = { | |
threads: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-t-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()), | |
replies: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-r-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()) | |
}; | |
console.log(hidden); | |
(function(now){ | |
var type, ref$, hash, key, expiry; | |
for (type in ref$ = hidden) { | |
hash = ref$[type]; | |
for (key in hash) { | |
expiry = hash[key]; | |
if (expiry === true) { | |
hash[key] = Date.now(); | |
} else { | |
if (now - expiry > threshold) { | |
delete hash[key]; | |
} | |
} | |
} | |
} | |
}.call(this, Date.now())); | |
persist = function(){ | |
localStorage["4chan-hide-t-" + board.name] = JSON.stringify(hidden.threads); | |
localStorage["4chan-hide-r-" + board.name] = JSON.stringify(hidden.replies); | |
}; | |
toggle = function(prefix, no){ | |
var ref$; | |
classify($$(".quotelink[href$=\"#" + no + "\"]")).toggle('hiddenlink'); | |
return (ref$ = $(prefix + "" + no)) != null ? ref$.classList.toggle('hidden') : void 8; | |
}; | |
onready(function(){ | |
var i$, ref$, len$, btn, x$, no, y$; | |
for (i$ = 0, len$ = (ref$ = $$('.reply button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn$); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.op button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn1$); | |
} | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('reply')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
no = x$.dataset.no; | |
if (hidden.replies[no]) { | |
toggle('p', no); | |
} | |
} | |
if (board.isBoard) { | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('thread')).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
no = y$.dataset.no; | |
if (hidden.threads[no]) { | |
toggle('t', no); | |
} | |
} | |
} | |
function fn$(){ | |
toggle('p', this.value); | |
if (this.value in hidden.replies) { | |
delete hidden.replies[this.value]; | |
} else { | |
hidden.replies[this.value] = Date.now(); | |
} | |
persist(); | |
} | |
function fn1$(){ | |
var ref$; | |
toggle('t', this.value); | |
if (this.value in hidden.threads) { | |
delete hidden.threads[this.value]; | |
} else { | |
hidden.threads[this.value] = (ref$ = Thread[this.value]) != null && ref$.sticky | |
? Number.MAX_VALUE | |
: Date.now(); | |
} | |
persist(); | |
} | |
}); | |
onupdate(function(){ | |
var i$, ref$, len$, a; | |
for (i$ = 0, len$ = (ref$ = $$(".new .quotelink")).length; i$ < len$; ++i$) { | |
a = ref$[i$]; | |
if (a.hash.substring(1) in hidden.replies) { | |
a.classList.toggle('hiddenlink'); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var fetchNewPost, handlePreview, createPreview; | |
fetchNewPost = function(no){ | |
var ref$, board, thread, link, x$, xhr, stillHovered; | |
ref$ = this.pathname.split('/'), board = ref$[1], thread = ref$[3]; | |
link = this; | |
this.style.cursor = 'progress'; | |
x$ = xhr = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board + "/res/" + thread + ".json"); | |
x$.onload = function(){ | |
var thread; | |
if (this.status === 200) { | |
thread = parser.api(JSON.parse(this.response)); | |
if (stillHovered) { | |
link.style.removeProperty('cursor'); | |
createPreview.call(link, no, Post[no]); | |
} | |
} | |
}; | |
x$.send(); | |
stillHovered = true; | |
this.addEventListener('mouseout', (function(){ | |
function out(){ | |
stillHovered = false; | |
this.style.removeProperty('cursor'); | |
return this.removeEventListener('mouseout', out); | |
} | |
return out; | |
}())); | |
}; | |
handlePreview = function(){ | |
var no, post; | |
if (this.classList.contains('inlinedlink') || this.classList.contains('recursivelink')) { | |
return; | |
} | |
no = this.hash.substring(2); | |
if (!(post = Post[no])) { | |
fetchNewPost.call(this, no); | |
} else { | |
createPreview.call(this, no, post); | |
} | |
}; | |
createPreview = function(no, post){ | |
var ref$, host, hostid, width, height, left, top, x$, preview, i$, y$, len$, z$, z1$, ref1$, z2$, docWidth; | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
host = closest('.post', this); | |
hostid = (split$.call(host.id, '-')).pop(); | |
ref$ = this.getBoundingClientRect(), width = ref$.width, height = ref$.height, left = ref$.left, top = ref$.top; | |
x$ = preview = post.element('article', void 8, 'postpreview'); | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: preview | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = x$.querySelectorAll(".quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = x$.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
z2$ = x$.style; | |
z2$.position = 'fixed'; | |
if (left > (docWidth = document.documentElement.clientWidth) / 2) { | |
z2$.right = (docWidth - left - width) + "px"; | |
} else { | |
z2$.left = left + "px"; | |
} | |
if (this.classList.contains('backlink')) { | |
z2$.top = (top + height + 5) + "px"; | |
} else { | |
z2$.bottom = (window.innerHeight - top + 5) + "px"; | |
} | |
document.body.appendChild(x$); | |
classify($$(".post[data-no=\"" + no + "\"]")).add('hovered'); | |
listen(this).once('mouseout', function(){ | |
preview.remove(); | |
classify($$(".post[data-no=\"" + no + "\"]")).remove('hovered'); | |
}); | |
}; | |
onPosts({ | |
'.quotelink': { | |
mouseover: handlePreview | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('mouseover', handlePreview); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.no': { | |
click: function(e){ | |
var selection, x$; | |
e.preventDefault(); | |
selection = window.getSelection().toString().trim(); | |
if (selection) { | |
selection = ">" + selection + "\n"; | |
} | |
x$ = $('comment'); | |
x$.value += ">>" + this.textContent + "\n" + selection; | |
x$.focus(); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var munge; | |
munge = function(ctx){ | |
var i$, ref$, len$, quote, no, post, x$, j$, y$, ref1$, len1$, z$, text, z1$, z2$, z3$; | |
for (i$ = 0, len$ = (ref$ = ctx.querySelectorAll('.quotelink:not(.backlink):not(.forcequoted)')).length; i$ < len$; ++i$) { | |
quote = ref$[i$]; | |
if (quote.parentNode.className === 'smaller') { | |
continue; | |
} | |
no = quote.hash.substring(2); | |
if (post = Post[no]) { | |
if (post.comment.length > 0) { | |
x$ = L('div'); | |
x$.innerHTML = post.comment.replace(/<br>/g, ' '); | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('.quotelink')).length; j$ < len1$; ++j$) { | |
y$ = ref1$[j$]; | |
y$.remove(); | |
} | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('s')).length; j$ < len1$; ++j$) { | |
z$ = ref1$[j$]; | |
z$.remove(); | |
} | |
text = x$.textContent; | |
quote.after((z1$ = L('span'), z1$.textContent = ' ' + truncate(text, 70).replace(/^\s+/, ''), z1$.className = 'quote forcedquote', z1$)); | |
} | |
if (post.image) { | |
quote.after((z2$ = L('a'), z2$.className = 'forcedimage', z2$.textContent = ' ', z2$.setAttribute('data-width', post.image.width), z2$.setAttribute('data-height', post.image.height), z2$.href = post.image.url, z2$.appendChild((z3$ = L('img'), z3$.className = 'thumb', ref1$ = z3$.style, ref1$.maxHeight = '15px', ref1$.display = 'inline-block', ref1$.verticalAlign = 'middle', z3$.src = post.image.thumb.url, z3$.addEventListener('mouseover', handlePreview), z3$)), z2$)); | |
} | |
quote.textContent = "»" + post.idx; | |
quote.classList.add('forcequoted'); | |
} | |
} | |
}; | |
if (board.isThread) { | |
onpostinsert(function(it){ | |
munge(it.detail.post); | |
}); | |
} | |
}.call(this)); | |
(function(){ | |
var apiKey, batchSize, rate, requestQueue, ready, queue, cache, setTitle, pendingVideos, loadInfo, onclick; | |
apiKey = "AIzaSyCe5gXUv-EFyNMoESO8ONZnottbsd-2ayA"; | |
batchSize = 30; | |
rate = 5000; | |
requestQueue = []; | |
ready = true; | |
queue = function(req){ | |
requestQueue.push(req); | |
req.addEventListener('loadend', function(){ | |
requestQueue.shift(); | |
defer(rate, function(){ | |
var that; | |
if (that = requestQueue[0]) { | |
that.send(); | |
} else { | |
ready = true; | |
} | |
}); | |
}); | |
if (ready) { | |
ready = false; | |
req.send(); | |
} | |
}; | |
cache = {}; | |
setTitle = function(vid, data){ | |
vid.title = data.statistics.viewCount + " views.\n\n" + truncate(data.snippet.description, 200); | |
vid.dataset.title = data.snippet.title; | |
}; | |
pendingVideos = []; | |
loadInfo = debounce(2000, function(){ | |
var toFetch, i$, ref$, len$, vid, that, batches, batch, id, b; | |
toFetch = {}; | |
for (i$ = 0, len$ = (ref$ = pendingVideos).length; i$ < len$; ++i$) { | |
vid = ref$[i$]; | |
vid.addEventListener('click', onclick); | |
if (that = cache[vid.dataset.id]) { | |
setTitle(vid, that); | |
} else { | |
toFetch[vid.dataset.id] = true; | |
} | |
} | |
pendingVideos = []; | |
batches = []; | |
batch = []; | |
for (id in toFetch) { | |
batch.push(id); | |
if (batch.length === batchSize) { | |
batches.push(batch); | |
batch = []; | |
} | |
} | |
batches.push(batch); | |
if (batch.length > 0) { | |
for (i$ = 0, len$ = batches.length; i$ < len$; ++i$) { | |
b = batches[i$]; | |
(fn$.call(this, new XMLHttpRequest, b)); | |
} | |
} | |
function fn$(req, b){ | |
req.open('GET', "https://www.googleapis.com/youtube/v3/videos?id=" + encodeURIComponent(b) + "&part=snippet%2C+statistics&fields=items(id%2Csnippet%2Cstatistics)&key=" + apiKey); | |
req.addEventListener('load', function(){ | |
var ref$, data, i$, len$, v, j$, ref1$, len1$, vid; | |
if (200 <= (ref$ = this.status) && ref$ < 400) { | |
data = JSON.parse(this.response); | |
for (i$ = 0, len$ = (ref$ = data.items).length; i$ < len$; ++i$) { | |
v = ref$[i$]; | |
cache[v.id] = v; | |
for (j$ = 0, len1$ = (ref1$ = $$(".youtube[data-id=\"" + v.id + "\"]")).length; j$ < len1$; ++j$) { | |
vid = ref1$[j$]; | |
setTitle(vid, v); | |
} | |
} | |
} else { | |
console.error("error fetching youtube info!", this); | |
} | |
}); | |
req.addEventListener('error', function(){ | |
console.error("what happen", this); | |
}); | |
queue(req); | |
} | |
}); | |
onclick = function(e){ | |
var x$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
this.replace((x$ = L('iframe'), x$.width = 560, x$.height = 315, x$.src = "//www.youtube.com/embed/" + this.dataset.id + "?" + (this.dataset.params || '') + "&autoplay=1&wmode=transparent", x$.frameborder = 0, x$.allowfullscreen = '', x$)); | |
} | |
}; | |
onpostinsert(function(it){ | |
pendingVideos.push.apply(pendingVideos, it.detail.post.querySelectorAll('.youtube')); | |
loadInfo(); | |
}); | |
}.call(this)); | |
(function(){ | |
var highlighting, highlight, toggleHighlight; | |
highlighting = sget('highlighting') || { | |
admin: false, | |
mod: false | |
}; | |
highlight = function(it){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$(it)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
post.classList.add('highlighted'); | |
} | |
}; | |
toggleHighlight = function(klass){ | |
return function(){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$("." + klass)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
highlighting[klass] = !highlighting[klass]; | |
sset('highlighting', highlighting); | |
post.classList.toggle('highlighted'); | |
} | |
}; | |
}; | |
onPosts({ | |
'.admin .capcode': { | |
click: toggleHighlight('admin') | |
}, | |
'.mod .capcode': { | |
click: toggleHighlight('mod') | |
} | |
}); | |
onready(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight(klass); | |
} | |
} | |
}); | |
onupdate(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight("new." + klass); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var ref$, markScroll, scroll, toggleOff, onclick, follow; | |
ref$ = (function(){ | |
var last, el; | |
return { | |
markScroll: function(it){ | |
el = it; | |
return last = el.getBoundingClientRect().top; | |
}, | |
scroll: function(){ | |
return window.scrollBy(0, el.getBoundingClientRect().top - last); | |
} | |
}; | |
}.call(this)), markScroll = ref$.markScroll, scroll = ref$.scroll; | |
toggleOff = function(link, inlined){ | |
var no, ref$, i$, x$, len$, pid, ref1$, that; | |
no = link.hash.substring(2); | |
link.hidden = false; | |
markScroll(link); | |
link.classList.remove('inlinedlink'); | |
link.parentNode.classList.remove('inlinedquote'); | |
if ($$(".inline[data-no=\"" + no + "\"]").length === 1) { | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.remove('inlined'); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll('.post.inline')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
pid = (split$.call(x$.no, '-')).pop(); | |
if ($$(".inline[data-no=\"" + pid + "\"]").length === 1) { | |
if ((ref1$ = $("p" + pid)) != null) { | |
ref1$.classList.remove('inlined'); | |
} | |
} | |
} | |
inlined.remove(); | |
if (that = link.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
link.nextElementSibling.hidden = false; | |
} | |
if (that = link.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
link.nextElementSibling.nextElementSibling.hidden = false; | |
} | |
} | |
} | |
scroll(); | |
}; | |
onclick = function(e){ | |
var post, no, host, hostid, inlinedId, stubId, inlined, isBacklink, wrapper, x$, i$, y$, ref$, len$, z$, z1$, ref1$, that, this$ = this; | |
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { | |
return; | |
} | |
if (!(post = Post[no = this.hash.substring(2)])) { | |
return; | |
} | |
e.preventDefault(); | |
host = closest('.post', this).id; | |
hostid = (split$.call(host, '-')).pop(); | |
inlinedId = host + "-p" + no; | |
stubId = no + "-inlined-stub"; | |
if (inlined = $(inlinedId)) { | |
toggleOff(this, inlined); | |
} else { | |
isBacklink = this.classList.contains('backlink'); | |
inlined = post.element('article', "inline hovered", inlinedId); | |
wrapper = this; | |
while (wrapper.parentElement.matchesSelector('a,span')) { | |
wrapper = wrapper.parentElement; | |
} | |
markScroll(this); | |
wrapper[isBacklink ? 'after' : 'before'](inlined); | |
if (isBacklink) { | |
inlined.prepend((x$ = L('a'), x$.textContent = post.idx, x$.className = 'inlined-idx', x$.addEventListener('click', function(){ | |
toggleOff(this$, inlined); | |
}), x$)); | |
this.hidden = true; | |
} | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: inlined | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll("a.quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = inlined.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
this.classList.add('inlinedlink'); | |
this.parentNode.classList.add('inlinedquote'); | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.add('inlined'); | |
} | |
if (!isBacklink) { | |
if (that = this.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
this.nextElementSibling.hidden = true; | |
} | |
if (that = this.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
this.nextElementSibling.nextElementSibling.hidden = true; | |
} | |
} | |
} | |
} | |
if (!isBacklink) { | |
scroll(); | |
} | |
} | |
}; | |
follow = function(){ | |
var that; | |
if (that = this.hash) { | |
window.location.hash = that; | |
} | |
}; | |
onPosts({ | |
'.quotelink:not(.hiddenlink)': { | |
click: onclick, | |
dblclick: follow | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('click', onclick); | |
x$.addEventListener('dblclick', follow); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
console.timeEnd("init"); | |
onready(function(){ | |
console.timeEnd("onready handlers"); | |
console.timeEnd("interactive"); | |
console.timeStamp("html5chan-loaded"); | |
console.groupEnd(); | |
}); | |
}.call(this)); | |
}).call(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name html5chan | |
// @namespace https://github.com/qqueue/html5chan | |
// @description The Roger Bacon of 4chan userscripts | |
// | |
// @match *://boards.4chan.org/* | |
// @exclude *://boards.4chan.org/f/* | |
// @exclude *://boards.4chan.org/*/catalog | |
// @exclude *://boards.4chan.org/*/catalog/* | |
// @exclude *://boards.4chan.org/robots.txt | |
// | |
// @run-at document-start | |
// | |
// @grant none | |
// ==/UserScript== | |
(function(){ | |
"use strict"; | |
var out$ = typeof exports != 'undefined' && exports || this, split$ = ''.split, slice$ = [].slice; | |
(function(){ | |
var board, x$, ref$, page, onready, onupdate, onpostinsert, onbacklink; | |
console.group("html5chan"); | |
console.timeStamp("html5chan-init"); | |
console.time("init"); | |
console.time("interactive"); | |
out$.board = board = {}; | |
x$ = board; | |
ref$ = split$.call(window.location.pathname, '/'), x$.name = ref$[1], page = ref$[2], x$.threadNo = ref$[3]; | |
x$.isThread = !!x$.threadNo; | |
x$.isBoard = !x$.isThread; | |
x$.page = parseInt(page, 10) || 0; | |
x$.url = "//boards.4chan.org/" + x$.name + "/"; | |
x$.threadurl = x$.url + 'res/'; | |
x$.threadPath = "/" + x$.name + "/res/" + x$.threadNo; | |
x$.ready = false; | |
out$.onready = onready = function(it){ | |
document.addEventListener('html5chan-ready', it); | |
}; | |
out$.onupdate = onupdate = function(it){ | |
document.addEventListener('html5chan-update', it); | |
}; | |
out$.onpostinsert = onpostinsert = function(it){ | |
document.addEventListener('html5chan-postinsert', it); | |
}; | |
out$.onbacklink = onbacklink = function(it){ | |
document.addEventListener('html5chan-backlink', it); | |
}; | |
}.call(this)); | |
(function(){ | |
var truncate; | |
out$.truncate = truncate = function(it, length){ | |
length == null && (length = 20); | |
if (it.length > length) { | |
return it.substring(0, length) + "..."; | |
} else { | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var delay, deadZone, tooltip; | |
delay = 200; | |
deadZone = 10; | |
out$.tooltip = tooltip = function(arg$){ | |
var show, hide; | |
show = arg$.show, hide = arg$.hide; | |
return function(e){ | |
var x, y, timeout, lastEvent, createTooltip, resetTimeout, removeTooltip, this$ = this; | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
createTooltip = function(){ | |
show.call(this$, lastEvent); | |
listen(this$).off('mousemove', resetTimeout).on('mousemove', removeTooltip); | |
}; | |
resetTimeout = function(e){ | |
clearTimeout(timeout); | |
timeout = setTimeout(createTooltip, delay); | |
x = e.clientX, y = e.clientY; | |
lastEvent = e; | |
}; | |
removeTooltip = function(arg$){ | |
var cx, cy; | |
cx = arg$.clientX, cy = arg$.clientY; | |
if (Math.abs(x - cx) > deadZone || Math.abs(y - cy) > deadZone) { | |
hide.apply(this, arguments); | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
} | |
}; | |
timeout = setTimeout(createTooltip, delay); | |
listen(this).on('mousemove', resetTimeout).once('mouseout', function(){ | |
hide.apply(this, arguments); | |
clearTimeout(timeout); | |
listen(this).off('mousemove', resetTimeout).off('mousemove', removeTooltip); | |
}); | |
}; | |
}; | |
}.call(this)); | |
(function(){ | |
var $, $$, L, ref$, mutationMacro, x$, classify, closest; | |
out$.$ = $ = function(it){ | |
return document.getElementById(it); | |
}; | |
out$.$$ = $$ = function(it){ | |
return document.querySelectorAll(it); | |
}; | |
out$.L = L = function(it){ | |
return document.createElement(it); | |
}; | |
(ref$ = Element.prototype).matchesSelector == null && (ref$.matchesSelector = Element.prototype.mozMatchesSelector); | |
mutationMacro = function(nodes){ | |
var node, i$, len$, n; | |
if (nodes.length === 1) { | |
return typeof nodes[0] === 'string' | |
? document.createTextNode(nodes[0]) | |
: nodes[0]; | |
} | |
node = document.createDocumentFragment(); | |
for (i$ = 0, len$ = nodes.length; i$ < len$; ++i$) { | |
n = nodes[i$]; | |
if (typeof n === 'string') { | |
n = document.createTextNode(n); | |
} | |
node.appendChild(n); | |
} | |
return node; | |
}; | |
x$ = Node.prototype; | |
x$.prepend == null && (x$.prepend = function(){ | |
this.insertBefore(mutationMacro(arguments), this.firstChild); | |
}); | |
x$.append == null && (x$.append = function(){ | |
this.appendChild(mutationMacro(arguments)); | |
}); | |
x$.before == null && (x$.before = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this); | |
}); | |
x$.after == null && (x$.after = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.insertBefore(mutationMacro(arguments), this.nextSibling); | |
}); | |
x$.replace == null && (x$.replace = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.replaceChild(mutationMacro(arguments), this); | |
}); | |
x$.remove == null && (x$.remove = function(){ | |
if (!this.parentNode) { | |
return; | |
} | |
this.parentNode.removeChild(this); | |
}); | |
out$.classify = classify = (function(){ | |
classify.displayName = 'classify'; | |
var prototype = classify.prototype, constructor = classify; | |
function classify(els){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.els = els; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.add = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.add(it); | |
} | |
}; | |
prototype.remove = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.remove(it); | |
} | |
}; | |
prototype.toggle = function(it){ | |
var i$, ref$, len$, el; | |
for (i$ = 0, len$ = (ref$ = this.els).length; i$ < len$; ++i$) { | |
el = ref$[i$]; | |
el.classList.toggle(it); | |
} | |
}; | |
return classify; | |
}()); | |
out$.closest = closest = function(selector, el){ | |
for (; el; el = el.parentElement) { | |
if (el.matchesSelector(selector)) { | |
return el; | |
} | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var onPosts; | |
out$.onPosts = onPosts = function(listenerSpec){ | |
onpostinsert(function(it){ | |
var selector, ref$, listeners, i$, x$, ref1$, len$, event, listener; | |
for (selector in ref$ = listenerSpec) { | |
listeners = ref$[selector]; | |
for (i$ = 0, len$ = (ref1$ = it.detail.post.querySelectorAll(selector)).length; i$ < len$; ++i$) { | |
x$ = ref1$[i$]; | |
for (event in listeners) { | |
listener = listeners[event]; | |
x$.addEventListener(event, listener); | |
} | |
} | |
} | |
}); | |
}; | |
}.call(this)); | |
(function(){ | |
var pluralize; | |
pluralize = function(number, unit){ | |
return Math.round(number) + " " + unit + (number >= 1.5 ? 's' : '') + " ago"; | |
}; | |
Date.prototype.relativeTime = function(){ | |
var days, diff, hours, minutes, seconds; | |
if ((days = (diff = Date.now() - this.getTime()) / 86400000) > 1) { | |
return pluralize(days, 'day'); | |
} else if ((hours = days * 24) > 1) { | |
return pluralize(hours, 'hour'); | |
} else if ((minutes = hours * 60) > 1) { | |
return pluralize(minutes, 'minute'); | |
} else if ((seconds = minutes * 60) >= 1) { | |
return pluralize(seconds, 'second'); | |
} else { | |
return 'from the future!'; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var listen; | |
out$.listen = listen = (function(){ | |
listen.displayName = 'listen'; | |
var prototype = listen.prototype, constructor = listen; | |
function listen(element){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.element = element; | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.on = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, handler); | |
} | |
return this; | |
}; | |
prototype.once = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.addEventListener(event, (function(){ | |
function once(e){ | |
var target; | |
target = e.target; | |
this.removeEventListener(event, once); | |
return handler.apply(this, arguments); | |
} | |
return once; | |
}())); | |
} | |
return this; | |
}; | |
prototype.off = function(event, handler){ | |
var ref$; | |
if ((ref$ = this.element) != null) { | |
ref$.removeEventListener(event, handler); | |
} | |
return this; | |
}; | |
['on', 'once', 'off'].forEach(function(method){ | |
var original; | |
original = prototype[method]; | |
prototype[method] = function(event, handler){ | |
var i$, x$, ref$, len$; | |
for (i$ = 0, len$ = (ref$ = split$.call(event, ' ')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
original.call(this, x$, handler); | |
} | |
return this; | |
}; | |
}); | |
['click', 'mouseover', 'scroll'].forEach(function(e){ | |
prototype[e] = function(selector, handler){ | |
return this.on(e, selector, handler); | |
}; | |
}); | |
return listen; | |
}()); | |
}.call(this)); | |
(function(){ | |
var debounce, defer, repeat; | |
out$.debounce = debounce = function(delay, fn){ | |
var timeout; | |
return function(){ | |
var ctx, args; | |
ctx = this; | |
args = arguments; | |
clearTimeout(timeout); | |
timeout = setTimeout(function(){ | |
fn.apply(ctx, args); | |
}, delay); | |
}; | |
}; | |
out$.defer = defer = function(delay, fn){ | |
var args; | |
if (typeof delay === 'function') { | |
fn = delay; | |
delay = 4; | |
args = Array.prototype.slice.call(arguments, 2); | |
} else { | |
args = Array.prototype.slice.call(arguments, 1); | |
} | |
return setTimeout.apply(null, [fn, delay].concat(args)); | |
}; | |
out$.repeat = repeat = (function(){ | |
repeat.displayName = 'repeat'; | |
var prototype = repeat.prototype, constructor = repeat; | |
function repeat(delay, options, fn){ | |
var this$ = this instanceof ctor$ ? this : new ctor$; | |
this$.delay = delay; | |
if (typeof options === 'function') { | |
fn = options; | |
options = {}; | |
} | |
this$.fn = fn; | |
this$.timeoutee = function(){ | |
this$.fn.apply(this$, arguments); | |
if (this$.auto) { | |
this$.timeout = this$.repeat(); | |
} | |
}; | |
this$.auto = options.auto != null ? options.auto : true; | |
if (options.start !== false) { | |
this$.start(); | |
} | |
return this$; | |
} function ctor$(){} ctor$.prototype = prototype; | |
prototype.stop = function(){ | |
clearTimeout(this.timeout); | |
}; | |
prototype.start = function(){ | |
var args; | |
args = slice$.call(arguments); | |
this.stop(); | |
this.timeout = setTimeout.apply(null, [this.timeoutee, this.delay].concat(args)); | |
}; | |
prototype.restart = prototype.start; | |
prototype.repeat = prototype.start; | |
return repeat; | |
}()); | |
}.call(this)); | |
(function(){ | |
var setter, getter, ref$; | |
setter = function(storage){ | |
return function(key, val){ | |
var obj, ref$; | |
if (val != null) { | |
obj = (ref$ = {}, ref$[key] = val, ref$); | |
} | |
for (key in ref$ = obj || key) { | |
val = ref$[key]; | |
storage.setItem("html5chan-" + key, JSON.stringify(val)); | |
} | |
}; | |
}; | |
getter = function(storage){ | |
return function(it){ | |
try { | |
return JSON.parse(storage.getItem("html5chan-" + it)); | |
} catch (e$) {} | |
}; | |
}; | |
ref$ = out$; | |
ref$.set = setter(localStorage); | |
ref$.get = getter(localStorage); | |
ref$.sset = setter(sessionStorage); | |
ref$.sget = getter(sessionStorage); | |
}.call(this)); | |
(function(){ | |
var Post; | |
out$.Post = Post = (function(){ | |
Post.displayName = 'Post'; | |
var prototype = Post.prototype, constructor = Post; | |
prototype.postprocess = function(){ | |
var that, i$, len$, link, quoted, backlinks, ref$; | |
if (that = this.comment.match(/>>\d+/g)) { | |
for (i$ = 0, len$ = that.length; i$ < len$; ++i$) { | |
link = that[i$]; | |
quoted = link.substring(8); | |
backlinks = (ref$ = Post.backlinks)[quoted] || (ref$[quoted] = {}); | |
if (!backlinks[this.no]) { | |
((ref$ = Post.newBacklinks)[quoted] || (ref$[quoted] = {}))[this.no] = true; | |
backlinks[this.no] = true; | |
} | |
} | |
} | |
return constructor[this.no] = this; | |
}; | |
prototype.backlinks = function(onlyNew, postEl){ | |
var html, backlinks, post, idx; | |
html = ""; | |
backlinks = onlyNew | |
? Post.newBacklinks | |
: Post.backlinks; | |
if (backlinks[this.no]) { | |
for (post in backlinks[this.no]) { | |
if (board.isThread) { | |
idx = Post[post].idx; | |
} else { | |
idx = post; | |
} | |
html += "<a href=\"#p" + post + "\" class=\"backlink quotelink\">«" + idx + "</a> "; | |
if (onlyNew) { | |
document.dispatchEvent(new CustomEvent('html5chan-backlink', { | |
detail: { | |
no: post, | |
post: postEl | |
} | |
})); | |
} | |
} | |
} | |
return html; | |
}; | |
prototype.className = function(){ | |
var c, that; | |
c = "post "; | |
if (this.image) { | |
c += 'imagepost '; | |
} | |
if (this.sage) { | |
c += 'sage '; | |
} | |
if (that = this.tripcode) { | |
c += "tripcoded " + that + " "; | |
} | |
if (this.capcode) { | |
c += this.capcode === "## Admin" ? 'admin ' : 'mod '; | |
} | |
if (that = this.uid) { | |
c += "uid " + that; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
var that; | |
classes == null && (classes = ''); | |
return "<" + container + " data-no=" + this.no + " class='" + classes + " " + this.className() + "' data-idx=" + this.idx + " id='" + (id || "p" + this.no) + "'><h1 class=post-header><button type=button class=hide value=" + this.no + ">×</button><button type=submit form=reportform class=report name=no value=" + this.no + ">!</button><a href='" + this.url + "' class=subject>" + (this.subject || '') + "</a> <a class=name " + (this.email ? "href=\"mailto:" + this.email + "\"" : '') + ">" + this.name + "</a> <span class=tripcode>" + (this.tripcode || '') + "</span> <span class=capcode>" + (this.capcode || '') + "</span> <span class=posteruid>" + ((that = this.uid) ? "(ID: " + that + ")" : '') + "</span> <time pubdate datetime='" + this.time.toISOString() + "' title='" + this.time + "'>" + this.time.relativeTime() + "</time> " + (this.op && this.thread.sticky ? '<img alt=sticky src=//static.4chan.org/image/sticky.gif> ' : '') + "" + (this.op && this.thread.closed ? '<img alt=closed src=//static.4chan.org/image/closed.gif> ' : '') + "<a href='" + this.url + "' class=permalink>No.<span class=no>" + this.no + "</span></a></h1>" + (this.image ? "<div class=fileinfo><span class=filename>" + (this.image.filename || '') + "</span> <span class=dimensions>" + this.image.width + "x" + this.image.height + "</span> <span class=size>" + this.image.size + "</span> <a class=saucelink href='http://iqdb.org/?url=" + this.image.url + "' target=_blank>iqdb</a> <a class=saucelink href='http://google.com/searchbyimage?image_url=" + this.image.url + "' target=_blank>google</a> <a class=saucelink href='http://regex.info/exif.cgi/exif.cgi?imgurl=" + this.image.url + "' target=_blank>exif</a> <a class=saucelink href='http://archive.foolz.us/" + board.name + "/search/image/" + encodeURIComponent(this.image.md5) + "' target=_blank>foolz</a></div><a class=file target=_blank href='" + this.image.url + "' data-width=" + this.image.width + " data-height=" + this.image.height + "><img class=thumb src='" + this.image.thumb.url + "' width=" + this.image.thumb.width + " height=" + this.image.thumb.height + "></a>" : '') + "" + (this.deletedImage ? '<img class=deleted-imagealt="File deleted."src=//static.4chan.org/image/filedeleted.gif>' : '') + "<div class=comment>" + this.comment + "</div><footer class=backlinks>" + this.backlinks() + "</footer></" + container + ">"; | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, wrapper; | |
classes == null && (classes = ''); | |
x$ = wrapper = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return wrapper.firstElementChild; | |
}; | |
Object.defineProperty(prototype, 'text', { | |
get: function(){ | |
var x$; | |
x$ = L('div'); | |
return x$.innerHTML = this.comment, x$.textContent; | |
}, | |
configurable: true, | |
enumerable: true | |
}); | |
constructor.backlinks = {}; | |
constructor.newBacklinks = {}; | |
constructor.tripcodes = {}; | |
constructor.uids = {}; | |
function Post(){} | |
return Post; | |
}()); | |
}.call(this)); | |
(function(){ | |
var Thread; | |
out$.Thread = Thread = (function(){ | |
Thread.displayName = 'Thread'; | |
var prototype = Thread.prototype, constructor = Thread; | |
prototype.postprocess = function(){ | |
var i$, ref$, len$, reply; | |
this.posts = [this.op].concat(this.replies); | |
this.imageReplies = []; | |
this.reply = {}; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (reply.image) { | |
this.imageReplies.push(reply); | |
} | |
this.reply[reply.no] = reply; | |
} | |
if (Thread[this.no]) { | |
this['new'] = []; | |
this.deleted = []; | |
for (i$ = 0, len$ = (ref$ = Thread[this.no].replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!this.reply[reply.no]) { | |
this.deleted.push(reply); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
reply = ref$[i$]; | |
if (!Thread[this.no].reply[reply.no]) { | |
this['new'].push(reply); | |
} | |
} | |
} | |
return Thread[this.no] = this; | |
}; | |
prototype.className = function(){ | |
var c; | |
c = 'thread '; | |
if (this.sticky) { | |
c += 'sticky '; | |
} | |
if (this.locked) { | |
c += ' locked'; | |
} | |
if (this.preview) { | |
c += ' preview'; | |
} | |
return c; | |
}; | |
prototype.render = function(container, classes, id){ | |
var ref$; | |
container == null && (container = 'article'); | |
classes == null && (classes = ''); | |
return "<" + container + " id='" + (id || "t" + this.no) + "' data-no=" + this.no + " class='" + classes + " " + this.className() + "'>" + this.op.render('div', 'op') + "<div class=thread-info>" + ((((ref$ = this.omitted) != null ? ref$.replies : void 8) || 0) + this.replies.length) + " replies and " + ((((ref$ = this.omitted) != null ? ref$.imageReplies : void 8) || 0) + this.imageReplies.length) + " images." + (this.preview ? "<a class=expand-link href='" + this.url + "'>Expand</a>" : '') + "</div><div class=replies>" + (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = this.replies).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'reply')); | |
} | |
return results$; | |
}.call(this)).join('') + "</div></" + container + ">"; | |
}; | |
prototype.element = function(container, classes, id){ | |
var x$, d; | |
x$ = d = L('div'); | |
x$.innerHTML = this.render(container, classes, id); | |
return d.firstElementChild; | |
}; | |
function Thread(){} | |
return Thread; | |
}()); | |
}.call(this)); | |
(function(){ | |
var dimensionRegex, sizeRegex, filenameRegex, spoilerRegex, sageRegex, parseThread, parsePost, parser, thumbsBase, imagesBase, humanized, parseApiPost; | |
dimensionRegex = /(\d+)x(\d+)/; | |
sizeRegex = /[\d\.]+ [KM]?B/; | |
filenameRegex = /title="([^"]+)"/; | |
spoilerRegex = /^Spoiler Image/; | |
sageRegex = /^sage$/i; | |
parseThread = function(el){ | |
var x$, omitted; | |
x$ = new Thread; | |
x$.no = el.id.substring(1); | |
x$.url = board.threadurl + x$.no; | |
x$.preview = true; | |
if (omitted = el.querySelector('.summary')) { | |
x$.omitted = { | |
replies: parseInt(omitted.textContent.match(/\d+(?= posts?)/), 10) || 0, | |
imageReplies: parseInt(omitted.textContent.match(/\d+(?= image (?:replies|reply))/), 10) || 0 | |
}; | |
} | |
x$.sticky = el.querySelector('.stickyIcon') != null; | |
x$.closed = el.querySelector('.closedIcon') != null; | |
x$.op = parsePost.call(x$, el.querySelector('.op')); | |
x$.op.idx = 0; | |
x$.replies = Array.prototype.map.call(el.getElementsByClassName('reply'), parsePost, x$); | |
x$.postprocess(); | |
return x$; | |
}; | |
parsePost = function(el, idx){ | |
var thread, x$, ref$, that, img, thumb, info, dimensions; | |
thread = this; | |
x$ = new Post; | |
x$.idx = 1 + idx + (((ref$ = thread.omitted) != null ? ref$.replies : void 8) || 0); | |
x$.thread = thread; | |
x$.no = el.id.substring(1); | |
x$.url = (x$.op = el.classList.contains('op')) | |
? thread.url | |
: thread.url + "#p" + x$.no; | |
x$.time = new Date(parseInt(el.querySelector('.dateTime').dataset.utc, 10) * 1000); | |
x$.subject = el.querySelector('.postInfo.desktop .subject').innerHTML; | |
x$.name = el.querySelector('.name').innerHTML; | |
x$.tripcode = (ref$ = el.querySelector('.postertrip')) != null ? ref$.innerHTML : void 8; | |
x$.capcode = (ref$ = el.querySelector('.capcode')) != null ? ref$.innerHTML : void 8; | |
x$.email = (ref$ = el.querySelector('.useremail')) != null ? ref$.href.substring(7) : void 8; | |
if (that = x$.email) { | |
x$.sage = sageRegex.test(that); | |
} | |
x$.comment = enhancer.enhance(el.querySelector('.postMessage').innerHTML); | |
x$.uid = (ref$ = el.querySelector('.hand')) != null ? ref$.textContent : void 8; | |
if (img = el.querySelector('.fileThumb')) { | |
if (img.firstElementChild.alt === "File deleted.") { | |
x$.deletedImage = true; | |
} else { | |
thumb = img.firstElementChild; | |
info = el.querySelector('.fileInfo').innerHTML; | |
dimensions = dimensionRegex.exec(info); | |
x$.image = { | |
thumb: { | |
url: thumb.src, | |
width: parseInt(thumb.style.width, 10), | |
height: parseInt(thumb.style.height, 10) | |
}, | |
url: thumb.parentNode.href, | |
width: parseInt(dimensions[1], 10), | |
height: parseInt(dimensions[2], 10), | |
size: sizeRegex.exec(thumb.alt)[0], | |
filename: (ref$ = filenameRegex.exec(info)) != null ? ref$[1] : void 8, | |
md5: thumb.dataset.md5, | |
spoiler: spoilerRegex.test(thumb.alt) | |
}; | |
} | |
} | |
x$.postprocess(); | |
return x$; | |
}; | |
out$.parser = parser = { | |
board: function(document){ | |
var threads; | |
console.time("parse board"); | |
threads = Array.prototype.map.call(document.querySelectorAll('.thread'), parseThread); | |
console.timeEnd("parse board"); | |
return threads; | |
}, | |
thread: function(document){ | |
var thread; | |
console.time("parse thread"); | |
thread = parseThread(document.querySelector('.thread')); | |
console.timeEnd("parse thread"); | |
return thread; | |
}, | |
api: function(data){ | |
var op, x$, ref$; | |
op = data.posts[0]; | |
x$ = new Thread; | |
x$.no = op.no; | |
x$.url = board.threadurl + op.no; | |
x$.preview = !!op.omitted_posts; | |
x$.sticky = !!op.sticky; | |
x$.closed = !!op.closed; | |
ref$ = data.posts.map(parseApiPost, x$), x$['op'] = ref$[0], x$['replies'] = slice$.call(ref$, 1); | |
x$.postprocess(); | |
return x$; | |
} | |
}; | |
thumbsBase = "//thumbs.4chan.org/" + board.name + "/thumb/"; | |
imagesBase = "//images.4chan.org/" + board.name + "/src/"; | |
humanized = function(bytes){ | |
var kbytes; | |
if (bytes < 1024) { | |
return bytes + " B"; | |
} else if ((kbytes = Math.round(bytes / 1024)) < 1024) { | |
return kbytes + " KB"; | |
} else { | |
return (kbytes / 1024).toString().substring(0, 3) + " MB"; | |
} | |
}; | |
parseApiPost = function(data, i){ | |
var x$, that; | |
x$ = new Post; | |
x$.idx = i; | |
x$.thread = this; | |
x$.url = this.url; | |
x$.time = new Date(data.time * 1000); | |
x$.no = data.no; | |
x$.subject = data.sub; | |
x$.name = data.name; | |
x$.tripcode = data.trip; | |
x$.uid = data.id; | |
x$.capcode = data.capcode; | |
x$.email = data.email; | |
x$.sage = x$.email === 'sage'; | |
x$.comment = (that = data.com) ? enhancer.enhance(that) : ''; | |
x$.image = data.fsize ? { | |
thumb: { | |
url: thumbsBase + data.tim + 's.jpg', | |
width: data.tn_w, | |
height: data.tn_h | |
}, | |
url: imagesBase + "" + data.tim + data.ext, | |
width: data.w, | |
height: data.h, | |
size: humanized(data.fsize), | |
filename: data.filename + "" + data.ext, | |
md5: data.md5, | |
spoiler: !!data.spoiler | |
} : void 8; | |
x$.deletedImage = !!data.filedeleted; | |
x$.postprocess(); | |
return x$; | |
}; | |
}.call(this)); | |
(function(){ | |
var enhancer; | |
out$.enhancer = enhancer = { | |
replacements: [[/<wbr>/g, ''], [/(?:https?:\/\/)?(?:www\.)?(youtu\.be\/([\w\-_]+)(\?[&=\w\-_;\#]*)?|youtube\.com\/watch\?([&=\w\-_;\.\?\#\%]*)v=([\w\-_]+)([&=\w\-\._;\?\#\%]*))/g, '<a href="https://$1" class="youtube" data-id="$2$5" data-params="$3$4$6" target="_blank"><img src="//img.youtube.com/vi/$2$5/2.jpg"></a>'], [/\((https?:\/\/)([^<\s\)]+)\)/g, '(<a class="external" rel="noreferrer" href="$1$2" title="$1$2" target="_blank">$2</a>)'], [/([^"']|^)(https?:\/\/)([^<\s]+)/g, '$1<a class="external" rel="noreferrer" href="$2$3" title="$2$3" target="_blank">$3</a>'], [/(^|>|;|\s)([\w\.\-]+\.(?:com|net|org|eu|jp|us|co\.uk)(\/[^<\s]*)?(?=[\s<]|$))/g, '$1<a class="external" rel="noreferrer" href="http://$2" title="$2" target="_blank">$2</a>']], | |
addReplacement: function(pattern, replacement){ | |
this.replacements.push([pattern, replacement]); | |
}, | |
enhance: function(it){ | |
var i$, ref$, len$, ref1$, pattern, replacement; | |
for (i$ = 0, len$ = (ref$ = enhancer.replacements).length; i$ < len$; ++i$) { | |
ref1$ = ref$[i$], pattern = ref1$[0], replacement = ref1$[1]; | |
it = it.replace(pattern, replacement); | |
} | |
return it; | |
} | |
}; | |
}.call(this)); | |
(function(){ | |
var archiveOf, that; | |
archiveOf = function(name){ | |
var that; | |
switch (that = name) { | |
case 'a': | |
case 'co': | |
case 'jp': | |
case 'm': | |
case 'q': | |
case 'sp': | |
case 'tg': | |
case 'tv': | |
case 'v': | |
case 'vg': | |
case 'wsg': | |
return "http://archive.foolz.us/" + that + "/thread"; | |
case 'lit': | |
return "http://fuuka.warosu.org/" + that + "/thread"; | |
case 'diy': | |
case 'g': | |
case 'sci': | |
return "http://archive.installgentoo.net/" + that + "/thread"; | |
} | |
}; | |
board.archive = archiveOf(board.name); | |
if (/404/.test(document.title) && board.archive) { | |
if (that = /\d+/.exec(window.location.pathname)) { | |
window.location = board.archive + "/" + that[0]; | |
} | |
} | |
if (board.archive) { | |
enhancer.addReplacement(/<span class="deadlink">(>>(\d+))<\/span>/g, "<a href=\"" + board.archive + "/$2\" class=\"deadlink\">$1</a>"); | |
} | |
enhancer.addReplacement(/<span class="deadlink">(>>>\/([a-z]+)\/(\d+))<\/span>/g, function(original, text, name, no){ | |
var that; | |
if (that = archiveOf(name)) { | |
return "<a href=\"" + that + "/" + no + "\" class=\"deadlink\">" + text + "</a>"; | |
} else { | |
return original; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var lastUpdate, unread, favicons, x$, y$, drawFavicon, delay, currentDelay, appendUpdatesTo, updater, fade, fadeWhenVisible; | |
lastUpdate = new Date; | |
unread = 0; | |
favicons = { | |
sfw: (x$ = L('img'), x$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv8AAAD/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/AAAA/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv/Dly7/AAAA/wAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAP/Dly7/w5cu/8OXLv8AAAD/AAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/8OXLv/Dly7/w5cu/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfj/////////////w==', x$), | |
nsfw: (y$ = L('img'), y$.src = 'data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8AAAD/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/AAAA/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8zzGb/AAAA/wAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAP8zzGb/M8xm/zPMZv8AAAD/AAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/zPMZv8zzGb/M8xm/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAOvXAADBgwAAwYMAAMGDAADhhwAA888AAP//AAD//wAAw8MAAIGBAADBgwAAg8EAAMfjAAD//////////w==', y$) | |
}; | |
drawFavicon = debounce(200, function(){ | |
var ref$, x$, link, y$, z$; | |
if ((ref$ = $('favicon')) != null) { | |
ref$.remove(); | |
} | |
x$ = link = L('link'); | |
x$.id = 'favicon'; | |
x$.rel = 'icon'; | |
x$.type = 'image/x-icon'; | |
y$ = L('canvas'); | |
y$.width = 16; | |
y$.height = 16; | |
z$ = y$.getContext('2d'); | |
z$.drawImage(favicons[board.type], 0, 0); | |
if (unread > 0) { | |
z$.font = '8px monospace'; | |
z$.fillStyle = '#000'; | |
z$.strokeStyle = '#fff'; | |
z$.lineWidth = 4; | |
z$.textBaseline = 'bottom'; | |
z$.textAlign = 'right'; | |
z$.strokeText(unread, 16, 16); | |
z$.fillText(unread, 16, 16); | |
} | |
link.href = y$.toDataURL('image/png'); | |
document.head.appendChild(link); | |
}); | |
delay = [10, 15, 20, 30, 60, 90, 120, 180, 240, 300]; | |
currentDelay = 0; | |
appendUpdatesTo = function(thread){ | |
var i$, ref$, len$, post, backlinks, last; | |
$("t" + thread.no).lastElementChild.insertAdjacentHTML('beforeend', (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article', 'new reply')); | |
} | |
return results$; | |
}()).join('')); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: $("p" + post.no) | |
} | |
})); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.thread .backlinks')).length; i$ < len$; ++i$) { | |
backlinks = ref$[i$]; | |
backlinks.insertAdjacentHTML('beforeend', Post[backlinks.parentNode.dataset.no].backlinks(true, backlinks.parentNode)); | |
} | |
Post.newBacklinks = {}; | |
document.dispatchEvent(new CustomEvent('html5chan-update', { | |
detail: { | |
thread: thread | |
} | |
})); | |
unread += thread['new'].length; | |
drawFavicon(); | |
for (i$ = 0, len$ = (ref$ = thread['new']).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
fadeWhenVisible(post); | |
} | |
if (window.scrollMaxY - window.scrollY < 50 && !document.hidden) { | |
last = window.scrollY; | |
repeat(50, function(){ | |
var remaining; | |
if (last > window.scrollY) { | |
this.stop(); | |
} else if ((remaining = window.scrollMaxY - window.scrollY) > 1) { | |
window.scrollBy(0, remaining / 4); | |
last = window.scrollY; | |
} | |
}); | |
} | |
$("t" + thread.no).querySelector(".thread-info").textContent = thread.replies.length + " replies and " + thread.imageReplies.length + " image replies."; | |
}; | |
out$.updater = updater = { | |
update: function(){ | |
var x$; | |
updater.status.textContent = "Updating thread..."; | |
updater.button.disabled = true; | |
x$ = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board.name + "/res/" + board.thread.no + ".json"); | |
x$.setRequestHeader('If-Modified-Since', lastUpdate.toUTCString()); | |
x$.onload = function(){ | |
var lastModified, thread, ref$, ref1$; | |
if (this.status === 404) { | |
document.title += '(dead)'; | |
updater.status.textContent = "thread 404'd"; | |
} else { | |
lastModified = new Date(this.getResponseHeader('Last-Modified')); | |
if (lastModified > lastUpdate) { | |
updater.status.textContent = "update detected, parsing"; | |
lastUpdate = lastModified; | |
thread = parser.api(JSON.parse(this.response)); | |
appendUpdatesTo(thread); | |
currentDelay = 0; | |
} else { | |
currentDelay = (ref$ = currentDelay + 1) < (ref1$ = delay.length - 1) ? ref$ : ref1$; | |
} | |
updater.tminus = delay[currentDelay]; | |
updater.countdown.restart(); | |
} | |
}; | |
x$.ontimeout = function(){ | |
updater.status.textContent = "request timed out..."; | |
updater.tminus = delay[currentDelay]; | |
updater.countdown.restart(); | |
}; | |
x$.onerror = function(){ | |
updater.status.textContent = "Couldn't fetch thread page!"; | |
}; | |
x$.onloadend = function(){ | |
updater.button.disabled = false; | |
}; | |
x$.send(); | |
}, | |
tminus: delay[currentDelay], | |
countdown: repeat(1000, { | |
start: false | |
}, function(){ | |
updater.status.textContent = "Updating in " + updater.tminus + " seconds..."; | |
if (--updater.tminus === 0) { | |
this.stop(); | |
updater.update(); | |
} | |
}) | |
}; | |
fade = function(post){ | |
defer(100, function(){ | |
post.classList.remove('new'); | |
--unread; | |
drawFavicon(); | |
}); | |
}; | |
fadeWhenVisible = function(it){ | |
var post, y; | |
post = $("p" + it.no); | |
y = post.offsetTop; | |
if (window.innerHeight + window.scrollY > y) { | |
if (document.hidden) { | |
listen(window).once('focus', function(){ | |
fade(post); | |
}); | |
} else { | |
fade(post); | |
} | |
} else { | |
window.addEventListener('scroll', (function(){ | |
function reset(){ | |
if (window.innerHeight + window.scrollY > post.offsetTop) { | |
fade(post); | |
return window.removeEventListener('scroll', reset); | |
} | |
} | |
return reset; | |
}())); | |
} | |
}; | |
onready(function(){ | |
updater.status = $('update-status'); | |
updater.button = $('update-now'); | |
if (board.isThread) { | |
updater.countdown.start(); | |
listen($('update-now')).click(function(){ | |
var x$; | |
x$ = updater; | |
x$.countdown.stop(); | |
x$.tminus = delay[currentDelay]; | |
x$.update(); | |
}); | |
} else { | |
$('updater').hidden = true; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var postStatus; | |
postStatus = function(it){ | |
return $('post-status').textContent = it; | |
}; | |
onready(function(){ | |
var checkValidity, cooldown, ref$; | |
checkValidity = function(e){ | |
var form, captcha, file, comment, email, ref$, x$, data, y$; | |
e.preventDefault(); | |
form = $('postform'); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
email = $('email'); | |
if (/^noko$/i.test(email.value)) { | |
email.value = ''; | |
} | |
captcha.setCustomValidity(!captcha.value ? "You forgot the captcha!" : ''); | |
file.setCustomValidity(!file.value && board.isBoard ? "You forgot your image!" : ''); | |
comment.setCustomValidity(!file.value && !comment.value ? "You didn't enter a comment or select a file!" : ''); | |
if (form.checkValidity()) { | |
$('post').disabled = true; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = true; | |
} | |
postStatus("Posting..."); | |
x$ = $('progress'); | |
x$.hidden = false; | |
x$.value = 0; | |
data = new FormData(form); | |
if (this === $('sage')) { | |
data.append('email', 'sage'); | |
} | |
y$ = new XMLHttpRequest; | |
y$.open('POST', form.action); | |
listen(y$).on('load', function(){ | |
var x$, html, captcha, file, comment, ref$, y$; | |
x$ = html = L('div'); | |
x$.innerHTML = this.response; | |
console.log(html); | |
captcha = $('recaptcha_response_field'); | |
file = $('file'); | |
comment = $('comment'); | |
$('post').disabled = false; | |
if ((ref$ = $('sage')) != null) { | |
ref$.disabled = false; | |
} | |
if (/Post successful!|uploaded!/.test(html.textContent)) { | |
postStatus('Post successful!'); | |
cooldown(); | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
$('recaptcha_image').click(); | |
updater.countdown.restart(3); | |
return parser.lastParse = 0; | |
} else if (/mistyped the verification/.test(html.textContent)) { | |
postStatus('You mistyped the verification!'); | |
$('recaptcha_image').click(); | |
y$ = captcha; | |
y$.value = ''; | |
y$.focus(); | |
return y$; | |
} else if (/duplicate file entry detected/) { | |
$('postform').reset(); | |
$('name').value = get('name') || ''; | |
return $('recaptcha_image').click(); | |
} | |
}).on('loadend', function(){ | |
return $('progress').hidden = true; | |
}); | |
listen(y$.upload).on('progress', function(e){ | |
return $('progress').value = 100 * e.loaded / e.total; | |
}); | |
y$.send(data); | |
} | |
return false; | |
}; | |
listen($('post')).click(checkValidity); | |
listen($('sage')).click(checkValidity); | |
cooldown = function(){ | |
var post, sage, message, tminus; | |
post = $('post'); | |
sage = $('sage'); | |
post.disabled = true; | |
if (sage != null) { | |
sage.disabled = true; | |
} | |
message = post.textContent; | |
tminus = 30; | |
post.textContent = tminus; | |
return setTimeout((function(){ | |
function tick(){ | |
if (tminus-- === 0) { | |
post.textContent = message; | |
post.disabled = false; | |
return sage != null ? sage.disabled = false : void 8; | |
} else { | |
post.textContent = tminus; | |
return setTimeout(tick, 1000); | |
} | |
} | |
return tick; | |
}()), 1000); | |
}; | |
listen($('name')).on('input', function(){ | |
return set({ | |
name: this.value | |
}); | |
}); | |
if ((ref$ = $('name')) != null) { | |
ref$.value = get('name') || ''; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var x$, html, y$, head, z$, z1$, body, d; | |
x$ = html = L('html'); | |
x$.appendChild((y$ = head = L('head'), y$.appendChild(L('title')), y$.appendChild((z$ = L('style'), z$.id = 'html5chan-style', z$.textContent = ' html {\n min-height: 100%;\n font-family: Droid Serif, serif;\n font-size: 10pt;\n}\n::selection {\n background: #29df75;\n color: #000;\n}\n::-moz-selection {\n background: #29df75;\n color: #000;\n}\n[hidden] {\n display: none !important;\n}\nbutton:enabled {\n cursor: pointer;\n}\n.bold {\n font-weight: bold;\n}\n.smaller {\n font-size: smaller;\n}\nbody.sfw {\n background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wDAgEOI3mn8mQAAAgMSURBVGjedZrZbgO5DkSprRff+/+fGtta52FE46DQEyBw0ovEtVikHMzsZWbJzPr+fJtZNLNpZqf9+xPNLJhZNbPDzP72e3/7ft7P9P0ZzayZ2dr3y34m7Otjr//az7T9bjCz28w++521r0W80/He1/dN+4W4L46tjL/sP66gb3pjYVci73sT/6+tOA0QsUfC83M/H7dMfX+6XAf2pHzdBYxQYO3PBgu7VwIEnyJ4wCa+IQWd2IcG8v8r9u/w7Nj7BxigbI91yFHS3jhDmQlruqc8rApCzoX2MEpQcohR8hbcrX7ue74mLVyg4LGvNfwWeMiVmwlhsrD5gmAmcRngSV/cvdD3My/EfIJng8S9bUOt/b7hM8Pyh1wr2wieg0eClQaSukmCXftaQNh4OAw8a1vggbjPUDJvoT4QekHBG6Dg691b2XN/5q1IAxB93bIX0GZC44gc6dj4BMpF/C7Etxug4zNIbpwIPTdohTIT4ePK5A08mR5M0O6EZV3rJInn8Tnk+QakSvCMIdfCvu8GOJATjoSG3JkITXp/4dcNdCcsUPeLAfDYIMyJhQgOLgSt6Sj3gRf8fzdOQ8wn1ATf80BCLxggwIP+d00Cja59A7S6En/wUoVVGP8TGzpKpf372SCgYZXE07eg4VdAxsGF+0d3MbWMQJQD7u5AtIA6YkApt7yjVIWBjr1+g5UjqjpjnmtPPG+IkGMreZlZI8TSygX4XaRC0/oGqyx4MCOnPPErjOY1yC38gsIFVk9I/CiMwYHm4wXxQpwXcdkBq7b9rMnf/lwBHBbEfYDCAYp24U8TBdjvVwDRBGUxhGsxs0wk6BLfEQsaLJTE+hGoNBGOb9QNVzwhZBIguaDmJHj0xDMOuze87F7/1ZEDGi8k/5K6MbdFEoTxZ6vAcZa6QYZL+D6w9kK1n7g3hJ91wPHhofU/COxJuoReBIn/BLjOqPqsuhnx3LclAzxEVCxCTC8oFPHMQIkwsOTKenGI5kz8LiE1ABK+xgm0a7BcQGwf0ipMrB/A1RJ6GO55C1NwT/4WDIBINj4TVltAnYgQKSh8X4THLfnVca/D6x2MOiI/BgoskXAKQ789tF7A84C4m6DYE6TthNVP3A9I1PUQnhHQPZCPNwyzQEeisAu2BGQAxeD2hV6ELHPCogsKOK2viP+J0GT3dsHq3iXeG4EMOXDjnSkEdUC2j6CemUCf/mRYaWyB3rhnyJslPXbD+hUA4d52GKVV/7A3+dkpUM6i7PvnBCRhPxHgPsZs3gp94akPNvMKTq8sUXrik630EJKYJIzIiqlwMyQ4WW2TotjFU1U6QpLDiHUWKMoB5Qmf7EYZUmQQC7DeZSrj3WON0DwL9EZBlyB9+QVrZ9CUhHXYBjgdUoE6coTklR1gA0C8IJ97907C7U0Y8BQoXTLZ4IBhYVxk0oM3SeAsfccb+7PZegHxFtZLws9+hSfggSFF75KhwQTrNJkzNdSMFwQtgHSOiE4InmSKwr494H0TUts82RdCg/yGc6gllIPtaXsgmQneaRJK9HR7GPl0CigFmFTqBcP+EjQDcabgOj3gFP0NRBnyHOdkAd6osGRCbfrCYJewhiDgQ480GGYlM/s/EpsMuKLB/wIMvujR3aKs9hM5M8W7E6GzhOOxHr1RQ9rDCCkqKERJzggr3tI5Hg9Ic4JfaS99gn5U5MsXeVWk8EYU0PhQOLOAgQNAJAtNWNAeXMpJhsJ1RTGNAI2KZM1CSbqEmsFzAYJ/UVcCukXOmnuS5PZYPgXuTngrYuMhdeQNFpCwTnpguLYFrHLkcKGZOuX4YGH/DK+OJEXtIwhxSAvMfp4Vvsn0nFP2KO1ARSP1EcJ5SD0LQDFOXSZQrZpZjECVIJuyyJnkx4QAHe42YcxFmEICRE94oUDRN/YM0lhVmRv4uz83V17EIUuTyh6kexyIY9J9HXKnhw4yPvTkAWGacapVZMjdBLUKD11OESbKsGxuqp2hMMkiafwlQwLPnzdypaFR0yMFhvt4OGOJkOH0AZ1O9obQEdLsW44gOOqMMt3IcgRxSpwXOeYLMskf0gJ0GNTAxaa3ureEUpBJiDJNHn5+kQtVQisjhC5h0wlkk0WTbUQQysQzyYlZwe98ROdZUyaCnNQXxHkQqs42NCIM/HqTXtyktpjMCw4YgrWuouA6EISIytnRY5sMEniKxPrC+e4fwpCE8A0jkSdleNQkHB2a2RqwBbhhtGVmF8eTt+Azw6zKYI4d34AllyRsQB6xx75g2cLeG2FnAIWCdTkI/4VcEkhUtBjS4ZkM8YK0uGyf9UsEAWDwkWM+E6I6QR71SwMstB4xTWe4JxRJiOVDYr6i1eW3HHiWODH74mkYe/Ai3IpnlxXH2AHNVZIG7feFAeZCECEWkr/CUwu8aIAfGcajPCfkeQYH1l+ZVh7Y94UaEmSoYYJ+wYfYH/IW0I4uCRhlckg0aQi5iETXb000CKzzNR7PVQg8YTzywN+5ZcRmhENX6pajLhW2AmE41tRJiYHD3TzoR9gMNFBR4D7L1D5JDk9W1g7rFunkOIxQFw+J2yQzL519VWlZ0380dvHhYEh/fk0eC9chp6l/sFaQoUF6+JLNS6iOF60DAob/OKYL0iV+cerFEhFlMPE79/QEfAEB+sOc60DTleABfh3pg2tNqjb7mYj3D5lemoxiMwzBIwWeJf5mv1n4/ynWZke3oFzB4hHwXWU4od/nYv35SHE7pZb4gWpDuGdJg2Rm6R9tvEGBOM7EjAAAAABJRU5ErkJggg==") #dce0f4;\n}\nbody.sfw > header a,\nbody.sfw > footer a,\nbody.sfw .boardlinks a {\n color: #34345c;\n}\nbody.sfw .boardlinks {\n color: #89a;\n}\nbody.sfw .post:target {\n background: #d6bad0 url("data:image/png;base64,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") !important;\n}\nbody.sfw .reply {\n background: linear-gradient(180deg, rgba(0,0,0,0.01), transparent 2em, rgba(255,255,255,0) calc(98%), rgba(255,255,255,0.03));\n}\nbody.sfw #postpreview {\n background: url("data:image/png;base64,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") #dce0f4;\n}\nbody.sfw .reply:before,\nbody.sfw .inlined-idx {\n color: #9db0cb;\n}\nbody.sfw #postpreview.op {\n background-color: #eef2ff;\n}\nbody.sfw .quotelink {\n color: #d00;\n}\nbody.nsfw {\n background: #ffe url("//static.4chan.org/image/fade.png") repeat-x;\n color: #800000;\n}\nbody.nsfw > header a,\nbody.nsfw > footer a,\nbody.nsfw .boardlinks a {\n color: #800;\n}\nbody.nsfw .boardlinks {\n color: #b86;\n}\nbody.nsfw .thread {\n border-color: #808080;\n}\nbody.nsfw .post:target {\n background-color: #f0c0b0 !important;\n}\nbody.nsfw .reply,\nbody.nsfw #postpreview {\n background-color: #d9bfb7;\n}\nbody.nsfw .reply {\n border-color: #d9bfb7;\n}\nbody.nsfw .reply:before {\n color: #d9bfb7;\n}\nbody.nsfw .inlined-idx {\n color: #bd9083;\n}\nbody.nsfw #postpreview.op {\n background-color: #ffe;\n}\nbody.nsfw .quotelink {\n color: #000080;\n}\n#toplinks {\n float: right;\n width: 300px;\n}\n#header {\n margin: 1em 0;\n color: #af0a0f;\n}\n#board-name {\n font-size: 24pt;\n margin: 0;\n}\n#board-name a {\n color: #af0a0f !important;\n text-decoration: none;\n}\n#board-name a:hover {\n text-decoration: underline;\n}\n#board-subtitle {\n font-size: 10px;\n font-weight: normal;\n}\n#banner {\n margin-right: 1em;\n float: left;\n}\n#motd {\n margin: 1em 0;\n}\n#hide-motd {\n text-align: right;\n font-size: 10pt;\n}\n#message {\n clear: both;\n}\n.boardlinks {\n font-size: 9pt;\n text-align: center;\n}\n.boardlinks a {\n text-decoration: none;\n}\n#threads {\n clear: both;\n}\n#pages {\n text-align: center;\n margin: 0pt;\n padding: 0pt;\n}\n#pages li {\n display: inline;\n}\n#pages a {\n border-color: #aaa;\n border-style: solid;\n border-width: 1px 0;\n color: #000;\n display: inline-block;\n margin: 0.25em;\n padding: 0.5em 1em;\n text-decoration: none;\n}\n#pages a#current,\n#pages a:hover {\n background-color: rgba(200,200,200,0.7);\n}\n#updater {\n float: right;\n}\n.post {\n margin: 0.2em;\n padding: 1em;\n padding-right: 0;\n border-radius: 0.3em;\n}\n.reply {\n margin-left: 2em;\n transition-property: background-color;\n transition-duration: 3s;\n}\n.reply.new {\n background: #feffbf url("data:image/png;base64,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") !important;\n}\n.sage > .post-header > .name:after {\n content: " (sage)";\n}\n.reply:before,\n.inlined-idx {\n content: attr(data-idx);\n position: absolute;\n text-align: right;\n display: inline-block;\n margin-left: -3em;\n width: 2em;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.inlined-idx {\n cursor: pointer;\n}\n.inlined-idx:hover {\n text-decoration: underline;\n}\n.post-header {\n margin: 0;\n padding: 0;\n font-size: 8pt;\n font-family: sans-serif;\n color: #9db0cb;\n font-weight: normal;\n float: right;\n}\n.post .subject {\n color: #0f0c5d;\n font-weight: 800;\n text-decoration: none;\n}\n.post .subject:hover {\n text-decoration: underline;\n}\n.name {\n color: #9db0cb;\n}\n.name:link {\n text-decoration: underline;\n}\n.tripcode,\n.fileinfo {\n display: table;\n color: #839bbd;\n font-size: 8pt;\n font-family: sans-serif;\n}\n.tripcode:not(:hover) > .saucelink,\n.fileinfo:not(:hover) > .saucelink,\n.tripcode:not(:hover) > .dimensions,\n.fileinfo:not(:hover) > .dimensions,\n.tripcode:not(:hover) > .size,\n.fileinfo:not(:hover) > .size {\n transition-delay: 0.5s;\n opacity: 0;\n}\n.saucelink,\n.dimensions,\n.size {\n transition-duration: 0.5s;\n}\n.file {\n display: block;\n float: left;\n margin: 0.3em 1em 0.3em 0;\n position: relative;\n}\n.full {\n display: block;\n}\n.capcode {\n font-weight: 800;\n}\n.mod .capcode:hover,\n.admin .capcode:hover {\n cursor: pointer;\n}\n.admin .name,\n.admin .capcode,\n.admin .tripcode {\n color: #f00;\n}\n.admin .capcode:after {\n content: url("https://static.4chan.org/image/adminicon.gif");\n}\n.mod .name,\n.mod .capcode {\n color: #800080;\n}\n.mod .capcode:after {\n content: url("https://static.4chan.org/image/modicon.gif");\n}\n.hide,\n.report {\n float: right;\n padding: 0 1px;\n background: transparent;\n border: 0;\n}\n.post.hidden {\n opacity: 0.6;\n}\n.post.hidden .file,\n.post.hidden .comment,\n.post.hidden .backlinks,\n.post.hidden .fileinfo {\n display: none;\n}\n.post.inlined {\n display: none;\n}\n.post.inlined:target {\n display: block;\n}\n.post.highlighted {\n background-color: #d6bad0 !important;\n}\n.quotelink {\n text-decoration: none;\n}\n.hiddenlink {\n text-decoration: line-through;\n}\n.replylink {\n text-decoration: none;\n}\n.deadlink {\n color: #808080;\n}\n.permalink {\n text-decoration: none;\n color: inherit;\n}\n.permalink .no:hover {\n text-decoration: underline;\n}\n.recursivelink {\n font-weight: bold;\n color: #000 !important;\n}\n.comment {\n margin: 0;\n word-wrap: break-word;\n line-height: 1.8em;\n width: 40em;\n}\n.op .comment {\n width: 50em;\n}\n.quote {\n font-weight: normal;\n color: #789922;\n}\n.prettyprint {\n background-color: #fff;\n padding: 0.5em;\n display: inline-block;\n max-width: 40em;\n overflow: auto;\n}\ns {\n text-decoration: none;\n transition-duration: 1s;\n}\ns:not(:hover) > *,\ns:not(:hover) {\n color: transparent !important;\n text-shadow: 0 0 7px #000;\n}\n.backlinks {\n clear: both;\n}\n.backlink {\n margin-right: 1em;\n}\na.quotelink.inlinedlink,\nstrong.quotelink.inlinedlink {\n font-weight: bold;\n color: #000;\n}\n#postpreview {\n outline: none;\n padding: 0.5em;\n box-shadow: 5px 5px 10px rgba(0,0,0,0.5);\n margin: 0;\n}\n.inline {\n margin-right: 0;\n padding-right: 0;\n}\n.comment .inline {\n display: table;\n}\n.backlink + .inline {\n margin-left: 2em;\n}\n.inline .backlinks > .recursivelink {\n display: none;\n}\n.forcedimage {\n text-decoration: none;\n}\n.backlink.inlinedlink {\n display: table;\n}\n.hovered {\n outline: 3px dashed #00f;\n}\n#postform {\n display: table;\n margin: 1em auto;\n}\n#postform #comment,\n#postform #recaptcha_response_field {\n width: 100%;\n}\n#name,\n#email,\n#subject {\n width: 31.3%;\n}\n#recaptcha_image {\n display: block;\n background: #fff;\n width: 100% !important;\n}\n#recaptcha_image img {\n display: block;\n margin: auto;\n}\n.thread {\n padding-bottom: 5px;\n clear: both;\n}\n.thread-info {\n clear: left;\n text-align: right;\n}\n.thread.hidden {\n opacity: 0.6;\n}\n.thread.hidden .replies,\n.thread.hidden .thread-info {\n display: none;\n}\n.thread.hidden .op .file,\n.thread.hidden .op .comment,\n.thread.hidden .op .backlinks,\n.thread.hidden .op .fileinfo {\n display: none;\n}\n.youtube {\n position: relative;\n text-decoration: none;\n border: 3px solid;\n border-color: #c6312b;\n border-radius: 10px;\n transition: 0.5s;\n overflow: hidden;\n display: inline-block;\n vertical-align: top;\n margin: 0.25em;\n width: 120px;\n height: 90px;\n}\n.youtube:hover {\n border-color: #ffa200;\n}\n.youtube:after {\n position: absolute;\n top: 0;\n left: 0;\n width: 115px;\n font-size: smaller;\n font-family: sans-serif;\n color: #fff;\n background: rgba(0,0,0,0.5);\n padding: 0 0.5em;\n content: attr(data-title);\n}\n.youtube:not(:hover):after {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n', z$)), y$.appendChild((z1$ = L('script'), z1$.src = '//www.google.com/recaptcha/api/challenge?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', z1$.addEventListener('load', function(){ | |
var x$; | |
head.appendChild((x$ = L('script'), x$.src = '//www.google.com/recaptcha/api/js/recaptcha.js', x$.addEventListener('load', function(){ | |
var x$; | |
x$ = L('script'); | |
x$.textContent = "(function() {var c;if (c = document.getElementById('captcha')) {Recaptcha._init_options({theme: 'custom',custom_theme_widget: c});Recaptcha.theme = 'custom';Recaptcha.widget = c;Recaptcha._finish_widget();}}())"; | |
if (board.ready) { | |
head.appendChild(x$); | |
} else { | |
onready(function(){ | |
head.appendChild(x$); | |
}); | |
} | |
}), x$)); | |
}), z1$)), y$)); | |
x$.appendChild(body = L('body')); | |
d = document.replaceChild(html, document.documentElement); | |
document.addEventListener('DOMContentLoaded', function(){ | |
var x$, ref$, thread, threads, y$, bodyHtml, that, z$, i$, len$, post; | |
console.time("initial render"); | |
console.time("parse page"); | |
x$ = board; | |
x$.title = d.querySelector('.boardTitle').textContent; | |
x$.subtitle = ((ref$ = d.querySelector('.boardSubtitle')) != null ? ref$.innerHTML : void 8) || ''; | |
x$.nav = d.querySelector('#boardNavDesktop').innerHTML; | |
x$.banner = d.querySelector('.title').src; | |
x$.motd = (ref$ = d.querySelector('.globalMessage')) != null ? ref$.innerHTML : void 8; | |
x$.sfw = d.querySelector('link[rel="shortcut icon"]').href.slice(-6) === 'ws.ico'; | |
x$.type = x$.sfw ? 'sfw' : 'nsfw'; | |
x$.password = get('password') || Math.random().toString().substr(-8); | |
console.timeEnd("parse page"); | |
console.log(board); | |
if (board.isThread) { | |
board.thread = thread = parser.thread(d); | |
board.threads = threads = [thread]; | |
} else { | |
board.threads = threads = parser.board(d); | |
} | |
console.log(threads); | |
Post.newBacklinks = {}; | |
y$ = body; | |
y$.id = board.name; | |
y$.className = board.type + " " + (board.isThread ? 'threadpage' : 'boardpage'); | |
console.time("generate new HTML body"); | |
bodyHtml = " <nav class=\"boardlinks\" id=\"toplinks\">" + board.nav + "</nav>\n<header id=header>\n <a href=\"//boards.4chan.org/" + board.name + "/\" id=\"banner\">\n <img src=\"" + board.banner + "\" alt=\"4chan::\"/>\n </a>\n <hgroup>\n <h1 id=board-name><a href=\"//boards.4chan.org/" + board.name + "/\">" + board.title + "</a></h1>\n <h2 id=board-subtitle>" + board.subtitle + "</h2>\n </hgroup>\n</header>\n" + ((that = board.motd) ? "<div id=\"motd\">\n <button type=\"button\" id=\"hide-motd\">Hide News</button>\n <div id=\"message\">" + that + "</div>\n</div>" : '') + "\n\n<div id=\"threads\">" + (function(){ | |
var i$, x$, ref$, len$, results$ = []; | |
for (i$ = 0, len$ = (ref$ = threads).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
results$.push(x$.render('article')); | |
} | |
return results$; | |
}()).join('') + "</div>\n\n" + (board.isBoard ? "<ul id=\"pages\">\n " + (board.page > 0 ? "<li><a href=\"" + (board.page - 1) + "\">previous</a></li>" : '') + "\n <li><a href=\"" + board.url + "\">0</a></li>\n <li><a href=\"1\">1</a></li>\n <li><a href=\"2\">2</a></li>\n <li><a href=\"3\">3</a></li>\n <li><a href=\"4\">4</a></li>\n <li><a href=\"5\">5</a></li>\n <li><a href=\"6\">6</a></li>\n <li><a href=\"7\">7</a></li>\n <li><a href=\"8\">8</a></li>\n <li><a href=\"9\">9</a></li>\n <li><a href=\"10\">10</a></li>\n " + (board.page < 10 ? "<li><a href=\"" + (board.page + 1) + "\">next</a></li>" : '') + "\n <li><a href=\"catalog\">Catalog</a></li>\n</ul>" : '') + "\n\n" + (!board.locked ? "<div id=\"postform-wrapper\">\n <form id=\"postform\" enctype=\"multipart/form-data\" method=\"POST\" action=\"https://sys.4chan.org/" + board.name + "/post\">\n <input type=\"hidden\" value=\"3145728\" name=\"MAX_FILE_SIZE\">\n " + ((that = board.threadId) ? "<input type=\"hidden\" value=\"" + that + "\" name=\"resto\">" : '') + "\n <input type=\"hidden\" value=\"regist\" name=\"mode\">\n <input id=\"password\" type=\"hidden\" name=\"pwd\" value=\"" + board.password + "\">\n <div id=\"fields\">\n <input type=\"text\" name=\"name\" id=\"name\" tabindex=\"10\" placeholder=\"name#tripcode\" />\n <input type=\"text\" id=\"email\" name=\"email\" tabindex=\"10\" placeholder=\"email\" />\n <input type=\"text\" id=\"subject\" name=\"sub\" tabindex=\"10\" placeholder=\"subject\" />\n <div id=\"comment-field\"><textarea name=\"com\" id=\"comment\" rows=\"4\" tabindex=\"10\" placeholder=\"comment\"></textarea></div>\n <div id=\"captcha\" style=\"display:none\">\n <a id=\"recaptcha_image\" href=\"javascript:Recaptcha.reload()\" title=\"Click for new captcha\" ></a>\n <input type=\"text\" id=\"recaptcha_response_field\" name=\"recaptcha_response_field\" tabindex=\"10\" placeholder=\"captcha\" />\n </div>\n <div id=\"file-field\">\n <input type=\"file\" id=\"file\" name=\"upfile\" tabindex=\"10\" />\n <label id=\"spoiler-field\"><input type=\"checkbox\" value=\"on\" name=\"spoiler\" tabindex=\"10\" /> Spoiler?</label>\n </div>\n <div id=\"buttons\">\n <button type=\"submit\" tabindex=\"10\" id=\"post\" value=\"Submit\">Post " + (board.isThread ? 'Reply' : 'New Thread') + "</button>\n " + (board.isThread ? "<button type=\"submit\" name=\"email\" value=\"sage\" tabindex=\"10\" id=\"sage\">Sage Reply</button>" : '') + "\n <span id=\"post-status\"></span><progress max=\"100\" value=\"0\" hidden=\"\" id=\"progress\"></progress>\n </div>\n </div>\n </form>\n</div>" : '') + "\n\n<span id=\"updater\">\n <span id=\"update-status\"></span>\n <button id=\"update-now\">Update now</button>\n</span>\n\n"; | |
console.timeEnd("generate new HTML body"); | |
console.time("parse and render new body"); | |
body.innerHTML = bodyHtml; | |
console.timeEnd("parse and render new body"); | |
if (board.isBoard) { | |
console.time("highlight current page"); | |
body.querySelector("#pages a[href=\"" + (board.page || board.url) + "\"]").id = 'current'; | |
console.timeEnd("highlight current page"); | |
} | |
console.time("set new page title"); | |
document.title = board.isThread | |
? (z$ = board.thread.op, truncate(z$.title || z$.text || ((ref$ = z$.image) != null ? ref$.filename : void 8) || z$.time.relativeTime()) + "\ - /" + board.name + "/") | |
: board.title; | |
console.timeEnd("set new page title"); | |
console.timeEnd("initial render"); | |
if (window.location.hash && !sget(document.URL)) { | |
window.location.hash = window.location.hash; | |
window.addEventListener('scroll', (function(){ | |
function registerPage(){ | |
var ref$; | |
sset((ref$ = {}, ref$[document.URL] = true, ref$)); | |
return window.removeEventListener('scroll', registerPage); | |
} | |
return registerPage; | |
}())); | |
} | |
console.time("initial post insertion handlers"); | |
for (i$ = 0, len$ = (ref$ = $$('.post')).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: post | |
} | |
})); | |
} | |
console.timeEnd("initial post insertion handlers"); | |
board.ready = true; | |
document.dispatchEvent(new CustomEvent('html5chan-ready', { | |
detail: { | |
post: post | |
} | |
})); | |
}); | |
}.call(this)); | |
(function(){ | |
var stale, DAY, HOUR, MINUTE, SECOND, debounceLeading, flush, setUpdate; | |
stale = []; | |
DAY = 8640000; | |
HOUR = 3600000; | |
MINUTE = 60000; | |
SECOND = 1000; | |
debounceLeading = function(delay, fn){ | |
var timeout; | |
return function(){ | |
if (!timeout) { | |
fn.apply(this, arguments); | |
return timeout = defer(delay, function(){ | |
timeout = null; | |
}); | |
} | |
}; | |
}; | |
flush = debounceLeading(SECOND, function(){ | |
var i$, x$, ref$, len$, time; | |
for (i$ = 0, len$ = (ref$ = stale).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
time = new Date(x$.getAttribute('datetime')); | |
x$.textContent = time.relativeTime(); | |
setUpdate(x$); | |
} | |
}); | |
setUpdate = function(el){ | |
var time, diff, delay; | |
time = new Date(el.getAttribute('datetime')); | |
diff = Date.now() - time.getTime(); | |
delay = diff > DAY | |
? diff % DAY | |
: diff > HOUR | |
? diff % HOUR | |
: diff > MINUTE | |
? diff % MINUTE | |
: diff % SECOND; | |
defer(delay, function(){ | |
stale.push(el); | |
}); | |
}; | |
onpostinsert(function(it){ | |
setUpdate(it.detail.post.querySelector('time')); | |
flush(); | |
}); | |
window.addEventListener('visibilitystatechange', function(){ | |
if (!document.hidden) { | |
return flush; | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.file': { | |
click: function(e){ | |
var a, x$, ref$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
a = this; | |
this.hidden = true; | |
this.before((x$ = L('img'), x$.src = this.href, x$.className = 'full', ref$ = x$.style, ref$.display = 'block', ref$.maxWidth = '100%', x$.onclick = function(){ | |
var ref$, top; | |
if (this.width !== this.naturalWidth) { | |
this.style.removeProperty('max-width'); | |
} else { | |
a.hidden = false; | |
if ((ref$ = a.previousSibling) != null) { | |
ref$.remove(); | |
} | |
if (scroll && (top = a.getBoundingClientRect().top) < 0) { | |
window.scrollBy(0, top); | |
} | |
} | |
}, x$)); | |
} | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var objectFit, handlePreview; | |
objectFit = function(container, width, height){ | |
var ratio; | |
ratio = Math.min(1, container.height / height, container.width / width); | |
return { | |
width: ratio * width, | |
height: ratio * height | |
}; | |
}; | |
out$.handlePreview = handlePreview = tooltip({ | |
show: function(){ | |
var a, viewport, ref$, x$; | |
this.style.cursor = 'none'; | |
a = this.parentElement; | |
viewport = { | |
width: (ref$ = document.documentElement).clientWidth, | |
height: ref$.clientHeight | |
}; | |
document.body.append((x$ = L('img'), x$.id = 'imgpreview', x$.alt = "Loading...", x$.src = a.href, ref$ = objectFit(viewport, a.dataset.width, a.dataset.height), x$.width = ref$.width, x$.height = ref$.height, x$.addEventListener('load', function(){ | |
return this.removeAttribute('alt'); | |
}), x$.addEventListener('error', function(){ | |
return this.alt = "Unable to load image."; | |
}), ref$ = x$.style, ref$.position = 'fixed', ref$.left = 0, ref$.top = 0, ref$.pointerEvents = 'none', ref$.backgroundColor = 'rgba(0,0,0,.5)', ref$.padding = (viewport.height - x$.height) / 2 + "px " + (viewport.width - x$.width) / 2 + "px", ref$.transitionDuration = '.5s', ref$.opacity = 0, x$.addEventListener('transitionend', function(e){ | |
var propertyName; | |
propertyName = e.propertyName; | |
if (propertyName === 'opacity' && this.style.opacity === '0') { | |
return this.remove(); | |
} | |
}), defer(100, function(){ | |
x$.style.opacity = 1; | |
}), x$)); | |
}, | |
hide: function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.style.opacity = 0; | |
} | |
defer(100, function(){ | |
var ref$; | |
if ((ref$ = $('imgpreview')) != null) { | |
ref$.remove(); | |
} | |
}); | |
this.style.removeProperty('cursor'); | |
} | |
}); | |
onPosts({ | |
'.thumb': { | |
mouseover: handlePreview | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
onready(function(){ | |
var hash, msg, btn; | |
hash = function(it){ | |
return it.innerHTML.length; | |
}; | |
if ($('motd')) { | |
msg = $('message'); | |
btn = $('hide-motd'); | |
if (get('motd-hash') === hash(msg)) { | |
msg.hidden = get('motd-hidden'); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
} else { | |
set('motd-hash', hash(msg)); | |
} | |
listen(btn).click(function(){ | |
msg.hidden = !msg.hidden; | |
set('motd-hidden', msg.hidden); | |
btn.textContent = (msg.hidden ? "Show" : "Hide") + " News"; | |
}); | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var threshold, hidden, e, persist, toggle; | |
threshold = 604800000; | |
hidden = { | |
threads: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-t-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()), | |
replies: (function(){ | |
try { | |
return JSON.parse(localStorage["4chan-hide-r-" + board.name]) || {}; | |
} catch (e$) { | |
e = e$; | |
return {}; | |
} | |
}()) | |
}; | |
console.log(hidden); | |
(function(now){ | |
var type, ref$, hash, key, expiry; | |
for (type in ref$ = hidden) { | |
hash = ref$[type]; | |
for (key in hash) { | |
expiry = hash[key]; | |
if (expiry === true) { | |
hash[key] = Date.now(); | |
} else { | |
if (now - expiry > threshold) { | |
delete hash[key]; | |
} | |
} | |
} | |
} | |
}.call(this, Date.now())); | |
persist = function(){ | |
localStorage["4chan-hide-t-" + board.name] = JSON.stringify(hidden.threads); | |
localStorage["4chan-hide-r-" + board.name] = JSON.stringify(hidden.replies); | |
}; | |
toggle = function(prefix, no){ | |
var ref$; | |
classify($$(".quotelink[href$=\"#" + no + "\"]")).toggle('hiddenlink'); | |
return (ref$ = $(prefix + "" + no)) != null ? ref$.classList.toggle('hidden') : void 8; | |
}; | |
onready(function(){ | |
var i$, ref$, len$, btn, x$, no, y$; | |
for (i$ = 0, len$ = (ref$ = $$('.reply button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn$); | |
} | |
for (i$ = 0, len$ = (ref$ = $$('.op button.hide')).length; i$ < len$; ++i$) { | |
btn = ref$[i$]; | |
btn.addEventListener('click', fn1$); | |
} | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('reply')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
no = x$.dataset.no; | |
if (hidden.replies[no]) { | |
toggle('p', no); | |
} | |
} | |
if (board.isBoard) { | |
for (i$ = 0, len$ = (ref$ = document.getElementsByClassName('thread')).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
no = y$.dataset.no; | |
if (hidden.threads[no]) { | |
toggle('t', no); | |
} | |
} | |
} | |
function fn$(){ | |
toggle('p', this.value); | |
if (this.value in hidden.replies) { | |
delete hidden.replies[this.value]; | |
} else { | |
hidden.replies[this.value] = Date.now(); | |
} | |
persist(); | |
} | |
function fn1$(){ | |
var ref$; | |
toggle('t', this.value); | |
if (this.value in hidden.threads) { | |
delete hidden.threads[this.value]; | |
} else { | |
hidden.threads[this.value] = (ref$ = Thread[this.value]) != null && ref$.sticky | |
? Number.MAX_VALUE | |
: Date.now(); | |
} | |
persist(); | |
} | |
}); | |
onupdate(function(){ | |
var i$, ref$, len$, a; | |
for (i$ = 0, len$ = (ref$ = $$(".new .quotelink")).length; i$ < len$; ++i$) { | |
a = ref$[i$]; | |
if (a.hash.substring(1) in hidden.replies) { | |
a.classList.toggle('hiddenlink'); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var fetchNewPost, handlePreview, createPreview; | |
fetchNewPost = function(no){ | |
var ref$, board, thread, link, x$, xhr, stillHovered; | |
ref$ = this.pathname.split('/'), board = ref$[1], thread = ref$[3]; | |
link = this; | |
this.style.cursor = 'progress'; | |
x$ = xhr = new XMLHttpRequest; | |
x$.open('GET', "//api.4chan.org/" + board + "/res/" + thread + ".json"); | |
x$.onload = function(){ | |
var thread; | |
if (this.status === 200) { | |
thread = parser.api(JSON.parse(this.response)); | |
if (stillHovered) { | |
link.style.removeProperty('cursor'); | |
createPreview.call(link, no, Post[no]); | |
} | |
} | |
}; | |
x$.send(); | |
stillHovered = true; | |
this.addEventListener('mouseout', (function(){ | |
function out(){ | |
stillHovered = false; | |
this.style.removeProperty('cursor'); | |
return this.removeEventListener('mouseout', out); | |
} | |
return out; | |
}())); | |
}; | |
handlePreview = function(){ | |
var no, post; | |
if (this.classList.contains('inlinedlink') || this.classList.contains('recursivelink')) { | |
return; | |
} | |
no = this.hash.substring(2); | |
if (!(post = Post[no])) { | |
fetchNewPost.call(this, no); | |
} else { | |
createPreview.call(this, no, post); | |
} | |
}; | |
createPreview = function(no, post){ | |
var ref$, host, hostid, width, height, left, top, x$, preview, i$, y$, len$, z$, z1$, ref1$, z2$, docWidth; | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
host = closest('.post', this); | |
hostid = (split$.call(host.id, '-')).pop(); | |
ref$ = this.getBoundingClientRect(), width = ref$.width, height = ref$.height, left = ref$.left, top = ref$.top; | |
x$ = preview = post.element('article', void 8, 'postpreview'); | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: preview | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = x$.querySelectorAll(".quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = x$.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
z2$ = x$.style; | |
z2$.position = 'fixed'; | |
if (left > (docWidth = document.documentElement.clientWidth) / 2) { | |
z2$.right = (docWidth - left - width) + "px"; | |
} else { | |
z2$.left = left + "px"; | |
} | |
if (this.classList.contains('backlink')) { | |
z2$.top = (top + height + 5) + "px"; | |
} else { | |
z2$.bottom = (window.innerHeight - top + 5) + "px"; | |
} | |
document.body.appendChild(x$); | |
classify($$(".post[data-no=\"" + no + "\"]")).add('hovered'); | |
listen(this).once('mouseout', function(){ | |
preview.remove(); | |
classify($$(".post[data-no=\"" + no + "\"]")).remove('hovered'); | |
}); | |
}; | |
onPosts({ | |
'.quotelink': { | |
mouseover: handlePreview | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('mouseover', handlePreview); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
onPosts({ | |
'.no': { | |
click: function(e){ | |
var selection, x$; | |
e.preventDefault(); | |
selection = window.getSelection().toString().trim(); | |
if (selection) { | |
selection = ">" + selection + "\n"; | |
} | |
x$ = $('comment'); | |
x$.value += ">>" + this.textContent + "\n" + selection; | |
x$.focus(); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var munge; | |
munge = function(ctx){ | |
var i$, ref$, len$, quote, no, post, x$, j$, y$, ref1$, len1$, z$, text, z1$, z2$, z3$; | |
for (i$ = 0, len$ = (ref$ = ctx.querySelectorAll('.quotelink:not(.backlink):not(.forcequoted)')).length; i$ < len$; ++i$) { | |
quote = ref$[i$]; | |
if (quote.parentNode.className === 'smaller') { | |
continue; | |
} | |
no = quote.hash.substring(2); | |
if (post = Post[no]) { | |
if (post.comment.length > 0) { | |
x$ = L('div'); | |
x$.innerHTML = post.comment.replace(/<br>/g, ' '); | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('.quotelink')).length; j$ < len1$; ++j$) { | |
y$ = ref1$[j$]; | |
y$.remove(); | |
} | |
for (j$ = 0, len1$ = (ref1$ = x$.querySelectorAll('s')).length; j$ < len1$; ++j$) { | |
z$ = ref1$[j$]; | |
z$.remove(); | |
} | |
text = x$.textContent; | |
quote.after((z1$ = L('span'), z1$.textContent = ' ' + truncate(text, 70).replace(/^\s+/, ''), z1$.className = 'quote forcedquote', z1$)); | |
} | |
if (post.image) { | |
quote.after((z2$ = L('a'), z2$.className = 'forcedimage', z2$.textContent = ' ', z2$.setAttribute('data-width', post.image.width), z2$.setAttribute('data-height', post.image.height), z2$.href = post.image.url, z2$.appendChild((z3$ = L('img'), z3$.className = 'thumb', ref1$ = z3$.style, ref1$.maxHeight = '15px', ref1$.display = 'inline-block', ref1$.verticalAlign = 'middle', z3$.src = post.image.thumb.url, z3$.addEventListener('mouseover', handlePreview), z3$)), z2$)); | |
} | |
quote.textContent = "»" + post.idx; | |
quote.classList.add('forcequoted'); | |
} | |
} | |
}; | |
if (board.isThread) { | |
onpostinsert(function(it){ | |
munge(it.detail.post); | |
}); | |
} | |
}.call(this)); | |
(function(){ | |
var apiKey, batchSize, rate, requestQueue, ready, queue, cache, setTitle, pendingVideos, loadInfo, onclick; | |
apiKey = "AIzaSyCe5gXUv-EFyNMoESO8ONZnottbsd-2ayA"; | |
batchSize = 30; | |
rate = 5000; | |
requestQueue = []; | |
ready = true; | |
queue = function(req){ | |
requestQueue.push(req); | |
req.addEventListener('loadend', function(){ | |
requestQueue.shift(); | |
defer(rate, function(){ | |
var that; | |
if (that = requestQueue[0]) { | |
that.send(); | |
} else { | |
ready = true; | |
} | |
}); | |
}); | |
if (ready) { | |
ready = false; | |
req.send(); | |
} | |
}; | |
cache = {}; | |
setTitle = function(vid, data){ | |
vid.title = data.statistics.viewCount + " views.\n\n" + truncate(data.snippet.description, 200); | |
vid.dataset.title = data.snippet.title; | |
}; | |
pendingVideos = []; | |
loadInfo = debounce(2000, function(){ | |
var toFetch, i$, ref$, len$, vid, that, batches, batch, id, b; | |
toFetch = {}; | |
for (i$ = 0, len$ = (ref$ = pendingVideos).length; i$ < len$; ++i$) { | |
vid = ref$[i$]; | |
vid.addEventListener('click', onclick); | |
if (that = cache[vid.dataset.id]) { | |
setTitle(vid, that); | |
} else { | |
toFetch[vid.dataset.id] = true; | |
} | |
} | |
pendingVideos = []; | |
batches = []; | |
batch = []; | |
for (id in toFetch) { | |
batch.push(id); | |
if (batch.length === batchSize) { | |
batches.push(batch); | |
batch = []; | |
} | |
} | |
batches.push(batch); | |
if (batch.length > 0) { | |
for (i$ = 0, len$ = batches.length; i$ < len$; ++i$) { | |
b = batches[i$]; | |
(fn$.call(this, new XMLHttpRequest, b)); | |
} | |
} | |
function fn$(req, b){ | |
req.open('GET', "https://www.googleapis.com/youtube/v3/videos?id=" + encodeURIComponent(b) + "&part=snippet%2C+statistics&fields=items(id%2Csnippet%2Cstatistics)&key=" + apiKey); | |
req.addEventListener('load', function(){ | |
var ref$, data, i$, len$, v, j$, ref1$, len1$, vid; | |
if (200 <= (ref$ = this.status) && ref$ < 400) { | |
data = JSON.parse(this.response); | |
for (i$ = 0, len$ = (ref$ = data.items).length; i$ < len$; ++i$) { | |
v = ref$[i$]; | |
cache[v.id] = v; | |
for (j$ = 0, len1$ = (ref1$ = $$(".youtube[data-id=\"" + v.id + "\"]")).length; j$ < len1$; ++j$) { | |
vid = ref1$[j$]; | |
setTitle(vid, v); | |
} | |
} | |
} else { | |
console.error("error fetching youtube info!", this); | |
} | |
}); | |
req.addEventListener('error', function(){ | |
console.error("what happen", this); | |
}); | |
queue(req); | |
} | |
}); | |
onclick = function(e){ | |
var x$; | |
if (!(e.altKey || e.ctrlKey || e.shiftKey || e.metaKey)) { | |
e.preventDefault(); | |
this.replace((x$ = L('iframe'), x$.width = 560, x$.height = 315, x$.src = "//www.youtube.com/embed/" + this.dataset.id + "?" + (this.dataset.params || '') + "&autoplay=1&wmode=transparent", x$.frameborder = 0, x$.allowfullscreen = '', x$)); | |
} | |
}; | |
onpostinsert(function(it){ | |
pendingVideos.push.apply(pendingVideos, it.detail.post.querySelectorAll('.youtube')); | |
loadInfo(); | |
}); | |
}.call(this)); | |
(function(){ | |
var highlighting, highlight, toggleHighlight; | |
highlighting = sget('highlighting') || { | |
admin: false, | |
mod: false | |
}; | |
highlight = function(it){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$(it)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
post.classList.add('highlighted'); | |
} | |
}; | |
toggleHighlight = function(klass){ | |
return function(){ | |
var i$, ref$, len$, post; | |
for (i$ = 0, len$ = (ref$ = $$("." + klass)).length; i$ < len$; ++i$) { | |
post = ref$[i$]; | |
highlighting[klass] = !highlighting[klass]; | |
sset('highlighting', highlighting); | |
post.classList.toggle('highlighted'); | |
} | |
}; | |
}; | |
onPosts({ | |
'.admin .capcode': { | |
click: toggleHighlight('admin') | |
}, | |
'.mod .capcode': { | |
click: toggleHighlight('mod') | |
} | |
}); | |
onready(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight(klass); | |
} | |
} | |
}); | |
onupdate(function(){ | |
var klass, ref$, hl; | |
for (klass in ref$ = highlighting) { | |
hl = ref$[klass]; | |
if (hl) { | |
highlight("new." + klass); | |
} | |
} | |
}); | |
}.call(this)); | |
(function(){ | |
var ref$, markScroll, scroll, toggleOff, onclick, follow; | |
ref$ = (function(){ | |
var last, el; | |
return { | |
markScroll: function(it){ | |
el = it; | |
return last = el.getBoundingClientRect().top; | |
}, | |
scroll: function(){ | |
return window.scrollBy(0, el.getBoundingClientRect().top - last); | |
} | |
}; | |
}.call(this)), markScroll = ref$.markScroll, scroll = ref$.scroll; | |
toggleOff = function(link, inlined){ | |
var no, ref$, i$, x$, len$, pid, ref1$, that; | |
no = link.hash.substring(2); | |
link.hidden = false; | |
markScroll(link); | |
link.classList.remove('inlinedlink'); | |
link.parentNode.classList.remove('inlinedquote'); | |
if ($$(".inline[data-no=\"" + no + "\"]").length === 1) { | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.remove('inlined'); | |
} | |
} | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll('.post.inline')).length; i$ < len$; ++i$) { | |
x$ = ref$[i$]; | |
pid = (split$.call(x$.no, '-')).pop(); | |
if ($$(".inline[data-no=\"" + pid + "\"]").length === 1) { | |
if ((ref1$ = $("p" + pid)) != null) { | |
ref1$.classList.remove('inlined'); | |
} | |
} | |
} | |
inlined.remove(); | |
if (that = link.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
link.nextElementSibling.hidden = false; | |
} | |
if (that = link.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
link.nextElementSibling.nextElementSibling.hidden = false; | |
} | |
} | |
} | |
scroll(); | |
}; | |
onclick = function(e){ | |
var post, no, host, hostid, inlinedId, stubId, inlined, isBacklink, wrapper, x$, i$, y$, ref$, len$, z$, z1$, ref1$, that, this$ = this; | |
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { | |
return; | |
} | |
if (!(post = Post[no = this.hash.substring(2)])) { | |
return; | |
} | |
e.preventDefault(); | |
host = closest('.post', this).id; | |
hostid = (split$.call(host, '-')).pop(); | |
inlinedId = host + "-p" + no; | |
stubId = no + "-inlined-stub"; | |
if (inlined = $(inlinedId)) { | |
toggleOff(this, inlined); | |
} else { | |
isBacklink = this.classList.contains('backlink'); | |
inlined = post.element('article', "inline hovered", inlinedId); | |
wrapper = this; | |
while (wrapper.parentElement.matchesSelector('a,span')) { | |
wrapper = wrapper.parentElement; | |
} | |
markScroll(this); | |
wrapper[isBacklink ? 'after' : 'before'](inlined); | |
if (isBacklink) { | |
inlined.prepend((x$ = L('a'), x$.textContent = post.idx, x$.className = 'inlined-idx', x$.addEventListener('click', function(){ | |
toggleOff(this$, inlined); | |
}), x$)); | |
this.hidden = true; | |
} | |
document.dispatchEvent(new CustomEvent('html5chan-postinsert', { | |
detail: { | |
post: inlined | |
} | |
})); | |
for (i$ = 0, len$ = (ref$ = inlined.querySelectorAll("a.quotelink[href$=\"" + hostid + "\"]")).length; i$ < len$; ++i$) { | |
y$ = ref$[i$]; | |
y$.className = 'recursivelink'; | |
y$.removeAttribute('href'); | |
} | |
z$ = inlined.querySelector('.comment'); | |
if (z$.querySelectorAll('.quotelink').length === 0) { | |
z1$ = z$.firstElementChild; | |
if ((z1$ != null ? z1$.className : void 8) === 'recursivelink') { | |
while (((ref$ = z1$.nextSibling) != null ? ref$.tagName : void 8) === 'BR' || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedquote'))) || ((ref$ = z1$.nextSibling) != null && ((ref1$ = ref$.classList) != null && ref1$.contains('forcedimage')))) { | |
z1$.nextSibling.remove(); | |
} | |
z1$.remove(); | |
} | |
} | |
this.classList.add('inlinedlink'); | |
this.parentNode.classList.add('inlinedquote'); | |
if ((ref$ = $('postpreview')) != null) { | |
ref$.remove(); | |
} | |
if ((ref$ = $("p" + no)) != null) { | |
ref$.classList.add('inlined'); | |
} | |
if (!isBacklink) { | |
if (that = this.nextElementSibling) { | |
if (that.classList.contains('forcedquote') || that.classList.contains('forcedimage')) { | |
this.nextElementSibling.hidden = true; | |
} | |
if (that = this.nextElementSibling.nextElementSibling) { | |
if (that.classList.contains('forcedquote')) { | |
this.nextElementSibling.nextElementSibling.hidden = true; | |
} | |
} | |
} | |
} | |
if (!isBacklink) { | |
scroll(); | |
} | |
} | |
}; | |
follow = function(){ | |
var that; | |
if (that = this.hash) { | |
window.location.hash = that; | |
} | |
}; | |
onPosts({ | |
'.quotelink:not(.hiddenlink)': { | |
click: onclick, | |
dblclick: follow | |
} | |
}); | |
onbacklink(function(arg$){ | |
var detail; | |
detail = arg$.detail; | |
defer(100, function(){ | |
var x$; | |
x$ = detail.post.querySelector(".backlink[href$=p" + detail.no + "]"); | |
x$.addEventListener('click', onclick); | |
x$.addEventListener('dblclick', follow); | |
}); | |
}); | |
}.call(this)); | |
(function(){ | |
console.timeEnd("init"); | |
onready(function(){ | |
console.timeEnd("onready handlers"); | |
console.timeEnd("interactive"); | |
console.timeStamp("html5chan-loaded"); | |
console.groupEnd(); | |
}); | |
}.call(this)); | |
}).call(this) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment