// ==UserScript==
// @name iichan catalog
// @namespace http://your.homepage/
// @version 0.4
// @description Add catalog to http://iichan.hk/; Добавляет на ычан каталог с подрузкой.
// @author You
// @match http://iichan.hk/*
// @grant none
// @run-at document-end
// ==/UserScript==
/*jshint multistr: true */
"use strict";
var catalog = null;
var scrollBuffer = 300;
var timeToFailure = 15000;
var backgroundColor = '#F0E0D6';
var noNextPage = false;
var activeDoc;
var pending;
var threadMap= {};
var bookmarkMap;
var styleCSS = '<style>\
.thread_wrap{text-align:center; display:inline-block; overflow: hidden; height: 400px; position:relative; vertical-align: top; margin:5px; }\
.thread_wrap:hover{overflow: visible; z-index: 900;}\
.thread_wrap_1:after {\
content: "";\
position: absolute;\
width: 100%;\
height: 30px;\
background: #111111;\
bottom: 0;\
left: 0;\
background: linear-gradient(to bottom, rgba(240,224,214,0.02) 0%,rgba(240,224,214,0.13)\
30%,rgba(240,224,214,0.62) 52%,rgba(240,224,214,0.94) 74%,rgba(240,224,214,1) 100%);} \
.thread_wrap:hover:after {\
content: "";\
width: 0;\
height: 0;}\
.thread_face{width:220px; min-height:400px; border:1px solid #800000; overflow:hidden;\
-webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box;\
padding:5px; text-align:center; display:inline-block;word-wrap: break-word; }\
.bookmark{margin:3px; opacity:0.3; }\
.bookmark:hover{opacity:0.7;}\
.bookmark.activeBtn{opacity:1;}\
</style>';
var sheet = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack :(
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
return style.sheet;
})();
init();
function init(){
scrollBuffer += window.innerHeight;
pending = false;
activeDoc = document;
if(!isMainFrame()){
return;
}
var adminbar = qs('.adminbar')[0];
if(!adminbar){
return;
}
makeCatalogButton(adminbar);
loadBookmarkMap();
catalog = makeCatalog();
loadFontAwesome();
window.addEventListener("scroll", testScrollPosition, false);
//testScrollPosition();
}
function getBoardName(){
var path = window.location.href;
var parser = document.createElement('a');
parser.href = path;
path = parser.pathname;
var reg = /^(?:\/)(\w*)(?:\/)/;
var res = path.match(reg);
var boardName = (res.length)?res[0]:'';
return boardName;
}
function nextDoc(doc){
activeDoc = doc;
}
function appendPage(href){
if(!href){
return;
}
function addNextPage(doc){
catalog.buildCatalog(getFormFromDoc(doc));
nextDoc(doc);
pending = false;
}
handleXHRDoc(href, addNextPage);
}
function testScrollPosition(){
if( noNextPage ){
return;
}
if(isCatalogMode()){
//Take the max of the two heights for browser compatibility
if( !pending && window.pageYOffset + scrollBuffer > Math.max( document.documentElement.scrollHeight, document.documentElement.offsetHeight ) )
{
pending = true;
var timeout = setTimeout( function(){pending=false;testScrollPosition();}, timeToFailure );
var nextPage = getNextPage();
//console.log('Next page', nextPage);
//debugger;
if(!!nextPage){
appendPage(nextPage);
}
else{
noNextPage = true;
}
}
}
}
function showCatalog(){
catalog.show();
}
function makeCatalogButton(adminbar){
var catalogLink = document.createElement('span');
catalogLink.innerHTML = '[<a href="#" id="id_catalog">Каталог</a>]';
//console.log(catalogLink);
adminbar.appendChild(catalogLink);
catalogLink.onclick = function(e){
//console.log(document.querySelector('form#delform'));
if (catalog.shown) {
resetCatalog();
}
showCatalog();
e.preventDefault();
};
}
function getThreadsListFromForm(form){
// var form = document.querySelector('form#delform');
var tempList = form.querySelectorAll('div');
var threadList = [];
var thread = null;
for(var i =0; i<tempList.length;++i){
if(tempList[i].querySelector('.reflink')){
thread = makeThread(tempList[i]);
//threadList.push(tempList[i]);
threadList.push(thread);
}
}
return threadList;
}
function makeCatalog(){
var catalogElt = document.createElement('div');
var formElt = getFormFromDoc(document);
var formParent = formElt.parentElement;
var newForm = formElt.cloneNode(true);
newForm.setAttribute('id', '');
formParent.insertBefore(newForm, formElt.nextSibling);
newForm.innerHTML ='';
newForm.setAttribute('id', 'catalog-form');
//var catalog = new Catalog(catalogElt, formElt);
catalog = new Catalog(catalogElt, newForm, formElt);
catalog.reset();
return catalog;
}
function makeThread(threadElt){
return new Thread(threadElt);
}
function getFormFromDoc(doc){
var form = doc.querySelectorAll('form#delform')[0];
if(!form){
throw new Error('Form element not found');
}
return form;
}
function getPathFromURL(url){
var parser = document.createElement('a');
parser.href = url;
var path = parser.pathname;
return path;
}
function getByRegex(text, regex){
var match = text.match(regex);
if(match)
{
if(match.length){
return match;
}
}
}
//Get number of post/img from text by regexp.
function parseCount(text, regex){
var result = getByRegex(text, regex);
var count = 0;
if(result){
count = result[1];
}
if(!count){
return 0;
}
return count;
}
function Thread (thread){
if (!thread) {
return;
}
this.element = undefined;
this.threadLink = thread.querySelector('.reflink a');
this.threadLink = this.threadLink.getAttribute('href');
this.threadLink = getPathFromURL(this.threadLink);
this.imgSrc = thread.querySelector('a img');
this.imgSrc = this.imgSrc.getAttribute('src');
this.threadName = thread.querySelector('.filetitle');
this.threadName = this.threadName.textContent;
this.threadText = thread.querySelector('blockquote');
this.threadText = this.threadText.innerHTML;
this.postNum = 0;
this.imgNum = 0;
this.lastPost ='';
var omitted = thread.querySelector('.omittedposts');
if(omitted){
omitted= omitted.textContent;
if(!!omitted){
this.postNum = parseInt(parseCount(omitted, /([0-9]*)(?:\s)*(?:сообщений)/),10);
this.imgNum = parseInt(parseCount(omitted, /([0-9]*)(?:\s)*(?:изображений)/),10);
}
}
var posts = thread.querySelectorAll('.reply');
var date = '';
var post = null;
if (!!posts) {
this.postNum+=posts.length;
for (var i = 0; i < posts.length; i++) {
post = posts[i];
if (!!post.querySelector('img')) {
this.imgNum+=1;
}
if (i === posts.length-1) {
date = post.querySelector('label').textContent;
date = date.replace(/(\D*)/,'');
this.lastPost = date;
}
}
}
}
Thread.threadFromThread = function(threadElt, href) {
var thread = new Thread();
thread.threadLink = href;
thread.imgSrc = threadElt.querySelector('a img').getAttribute('src');
thread.threadName = threadElt.querySelector('label .filetitle').textContent;
thread.threadText = threadElt.querySelector('blockquote').innerHTML;
thread.postNum = 0;
thread.imgNum = 0;
var post = null;
var posts = threadElt.querySelectorAll('.reply');
var date;
if (!!posts) {
thread.postNum+=posts.length;
for (var i = 0; i < posts.length; i++) {
post = posts[i];
if (!!post.querySelector('img')) {
thread.imgNum+=1;
}
if (i === posts.length-1) {
date = post.querySelector('label').textContent;
date = date.replace(/(\D*)/,'');
thread.lastPost = date;
}
}
}
thread.element = buildCatalogPost(thread).element;
return thread;
};
function buildCatalogPost(thread){
var template = '<div class = "thread_wrap"><div class = "thread_face reply">\
<a href="'+ thread.threadLink+ '"><img src="' + thread.imgSrc+'" class="thumb" style=" display: block; margin: 0 auto; float:none;"></a>\
<span class="filetitle thread-title" style=" display:inline-block">'+thread.threadName+'</span>\
<p style= "margin-top:0.6em;">О:'+thread.postNum+' И:'+thread.imgNum+ '<div style="margin-top:-10px;"></div>\
<span>L: ' + thread.lastPost +'</p></span>\
<span>' + thread.threadText + '</span></div></div>';
//console.log(template);
var post = document.createElement('div');
post.innerHTML = template;
post = post.firstChild;
thread.element = post;
addBookmarkBtn(thread);
return thread;
}
//<p>О: ' + thread.lastPost + '</p>\
function isBookmark(name){
if (bookmarkMap[name]!==undefined) {
return true;
}
else{
return false;
}
}
function loadBookmarkMap (){
var ls = getBoardName() + '-'+'bookmarks';
//console.log(ls);
var entry = localStorage.getItem(ls);
if (!!entry) {
bookmarkMap = JSON.parse(entry);
}
else{
bookmarkMap = {};
}
}
function updateBookmarkStorage(){
var ls = getBoardName() + '-'+'bookmarks';
//console.log(bookmarkMap);
var value = JSON.stringify(bookmarkMap);
localStorage.setItem(ls, value);
}
function setBookmarkThread(btn, val){
var threadLink = btn.bindedElt.getAttribute('date-thread');
if (val){
bookmarkMap[threadLink]=true;
}
else{
delete bookmarkMap[threadLink];
}
updateBookmarkStorage();
}
function removeBookmarkFromThread(threadLink){
delete bookmarkMap[threadLink];
updateBookmarkStorage();
}
function bookmarkThread(btn){
setBookmarkThread(btn, true);
}
function unbookmarkThread(btn){
setBookmarkThread(btn, false);
}
function addBookmarkBtn(thread){
var post =thread.element;
var nameElt = post.querySelector('.thread-title');
if (!!nameElt) {
var bookmark = elementFromText('<span class="bookmark"></span>');
nameElt.insertBefore(bookmark, nameElt.firstChild);
bookmark.setAttribute('date-thread',thread.threadLink);
var boolBookmark = isBookmark(thread.threadLink);
var settings = {
name:'bookmark',
active:boolBookmark,
glyphOn:'fa-bookmark-o',
glyphOff:'fa-bookmark-o',
callbackOn:bookmarkThread,
callbackOff:unbookmarkThread};
makeToggleButton(bookmark,settings);
}
}
function resetCatalog(){
function reset (doc){
//console.log(' reset ');
activeDoc = doc;
//var catalog = makeCatalog();
catalog.reset();
}
//console.log(' reset Catalog');
handleXHRDoc(getBoardName(), reset);
}
function isMainFrame(){
if(qs('form#delform').length){
//console.log('iichan main frame');
return true;
}
return false;
}
function getNextPage(pagePane){
pagePane = getPagePane();
var pageList = pagePane.querySelectorAll('td')[2];
var next = pageList.querySelectorAll('td form')[0];
if(!next){
return false;
}
var href = next.getAttribute('action');
//console.log(href);
return href;
}
function getFirstPage(pagePane){
pagePane = getPagePane();
if(!pagePane){
return false;
}
var pageList = pagePane.querySelectorAll('td')[1];
var first = pageList.querySelectorAll('a')[0];
var href = first.getAttribute('href');
//console.log(href);
return href;
}
function getPagePane(doc){
doc = activeDoc;
var paginator = doc.querySelector('body>table>tbody>tr');
//console.log(pagePane);
return paginator;
}
function isCatalogMode(){
return catalog.isShown();
}
function Catalog (catalogElt, formElt, oldForm){
this.threadList = [];
this.catalogElt = catalogElt;
this.shown = false;
this.formElt = formElt;
this.oldForm = oldForm;
this.threadMap = {};
this.isShown = function(){
return this.shown;
};
this.bookmarkShown = false;
this.makeBookmarks = function(){
var self = this;
this.bookmarkShown = true;
var keys = [];
var index;
for(index in bookmarkMap) {
if (bookmarkMap.hasOwnProperty(index)) {
var attr = bookmarkMap[index];
keys.push(index);
}
}
index = 0;
if (keys.length) {
handleXHRDoc(keys[index], testSuccess, testFail);
index++;
}
function testSuccess(doc, reqString){
var threadElt = doc.querySelector('#delform>div');
var thread = Thread.threadFromThread(threadElt,reqString);
//console.log(thread);
self.appendBookmarkPost(thread.element);
//console.log(thread.element);
if (index<keys.length) {
handleXHRDoc(keys[index], testSuccess, testFail);
index++;
}
}
function testFail(doc, reqString){
console.log('fail xhr');
removeBookmarkFromThread(reqString);
if (index<keys.length) {
handleXHRDoc(keys[index], testSuccess, testFail);
index++;
}
}
};
this.show = function (){
formElt.innerHTML ='';
formElt.appendChild(catalog.catalogElt);
//Hide dollchan forms when catalog is shown
//oldForm.style.display='none';
sheet.insertRule("body>form:not(#catalog-form) { display:none; }",0);
sheet.insertRule("#de-main + .de-parea{ display:none; }",0);
var postArea = document.querySelector('.postarea');
if(!!postArea){
console.log(postArea);
postArea.parentElement.removeChild(postArea);
}
// var newTreadForms = document.querySelectorAll('.de-parea');
// if(newTreadForms.lenght==2){
// var first = newTreadForms[0];
// first.parentElement.removeChild(first);
// }
this.shown = true;
};
this.reset = function(){
var style = styleCSS;
//console.log(style);
threadMap ={};
this.bookmarkShown = false;
this.catalogElt.innerHTML = style;
appendPage(getBoardName());
};
this.appendBookmarkPost = function(post){
this.catalogElt.insertBefore(post,this.catalogElt.firstChild);
};
this.buildCatalog = function (form){
var catalogElement = this.catalogElt;
var threadList = getThreadsListFromForm(form);
var threadLink = '';
var post = null;
var thread =null;
for(var i =0; i< threadList.length;++i){
thread = buildCatalogPost(threadList[i]);
threadLink = thread.threadLink;
if ((threadMap[threadLink])===undefined) {
threadMap[threadLink] = thread;
post = thread.element;
if (isBookmark(threadLink)) {
//this.appendBookmarkPost(post);
}
else{
catalogElement.appendChild(post);
}
}
else{
console.log('duble thread '+ threadLink);
}
}
if (!this.bookmarkShown) {
this.makeBookmarks();
}
//console.log(catalogElement);
return catalogElement;
};
}
function loadFontAwesome(){
var fontAwesomeLoader = elementFromText('<link rel="stylesheet" type="text/css" media="screen" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css" />');
document.head.appendChild(fontAwesomeLoader);
}
function handleXHRDoc(reqString, callback, errorCallback){
var doc = document.implementation.createHTMLDocument("example");
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', reqString, true);
xmlhttp.send(null);
xmlhttp.onreadystatechange = handle;
function handle(){
if (xmlhttp.readyState == 4) {
if(xmlhttp.status == 200) {
doc.documentElement.innerHTML = xmlhttp.responseText;
//console.log(doc);
callback(doc, reqString);
}
else{
if (!!errorCallback) {
errorCallback(xmlhttp.status, reqString);
}
else{
console.log('Error xhr of ' + reqString);
}
}
}
}
}
function elementFromText(text){
var div = document.createElement('div');
div.innerHTML = text;
var element = div.firstChild;
return element;
}
function qs(selector){
return document.querySelectorAll(selector);
}
function makeToggleButton(elem, settings){
var btn = new ButtonToggle(settings);
btn.bindElement(elem);
btn.load();
elem.addEventListener('click', btn.handlerClick, false);
//console.log(btn);
return btn;
}
function ButtonToggle(settings) {
var self = this;
var modGliph = '';
this.name = settings.name;
this.active = !!settings.active;
this.bindedElt = null;
this.glyphElt = document.createElement('i');
this.bindElement = function (elem) {
self.bindedElt = elem;
if (self.bindedElt.length > 0) {
elem.insertChildBefore(self.glyphElt, elem.FirstChild);
}
else {
elem.appendChild(self.glyphElt);
}
};
this.glyphOn = 'fa ' + settings.glyphOn + ' ' + modGliph;
this.glyphOff = 'fa ' + settings.glyphOff + ' ' + modGliph;
this.callbackOn = settings.callbackOn;
this.callbackOff = settings.callbackOff;
this.save = function () {
};
this.load = function () {
self.setActive(self.active);
};
this.handlerClick = function (e) {
if (self.active) {
self.setActive(false);
}
else {
self.setActive(true);
}
};
this.setActive = function (val) {
val = !!val;
self.active = val;
self.save(val);
if (val) {
if (!!self.bindedElt) {
self.bindedElt.classList.add('activeBtn');
self.glyphElt.className = self.glyphOn;
}
if (!!self.callbackOn) {
self.callbackOn(self);
}
}
else {
if (!!self.bindedElt) {
self.bindedElt.classList.remove('activeBtn');
self.glyphElt.className = self.glyphOff;
}
if (!!self.callbackOff) {
self.callbackOff(self);
}
}
};
}