// ==UserScript==
// @name Search posts by user
// @name:ru Поиск постов пользователя
// @description Search for posts by user(-s) in current topic
// @description:ru Найти посты пользователя(-ей) в текущей теме
// @version 1.3.1
// @date 17.12.2017
// @author Halibut
// @namespace https://greasyfork.org/en/users/145947-halibut
// @homepageURL https://greasyfork.org/en/scripts/36319-search-posts-by-user
// @supportURL https://forum.ru-board.com/topic.cgi?forum=2&topic=5673&glp
// @license HUG-WARE
// @include http*://forum.ru-board.com/topic.cgi?forum=*&topic=*
// @noframes
// @grant none
// ==/UserScript==
/******************************************************************************
* "THE HUG-WARE LICENSE" (Revision 2): As long as you retain this notice you *
* can do whatever you want with this stuff. If we meet some day, and you *
* think this stuff is worth it, you can give me/us a hug. *
******************************************************************************/
((body, img, listener) => {
'use strict';
if (listener.isFF) {
const context = body.getAttribute("contextmenu"),
menu = context
? document.getElementById(context)
: body.appendChild(document.createElement("menu")),
mitem = menu.appendChild(document.createElement("menuitem"));
if (!context) {
menu.id = "GM_page-actions";
menu.type = "context";
body.setAttribute("contextmenu", "GM_page-actions");
};
mitem.label = "Найти собщения пользователя(-ей)";
mitem.addEventListener("click", listener)
};
const button = listener.actnBox.getElementsByTagName("td")[0].appendChild(document.createTextNode(" "))
&& listener.actnBox.getElementsByTagName("td")[0].appendChild(document.createElement("a")),
imgNode = button.appendChild(document.createElement("img"));
imgNode.src = img;
button.addEventListener("click", listener)
body.addEventListener("contextmenu", e => {
let target = e.target.closest("a.m") || e.target.closest("a[href*='?action=show&member=']");
if (!target || e.altKey || e.shiftKey || e.ctrlKey || e.metaKey || !!listener.getSel() || target.closest("#spu-spoiler-body")) return;
listener.setName = target.className == "m" ? target.textContent : target.search.split("&member=")[1];
listener.handleEvent(e)
})
})(
document.body
, "data:image/gif;base64,R0lGODlhXgAPAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAABeAA8AAAKUhI+py+0PIwq02ouz3rz7HxjgSJamJ57qym5plcAvAMs0dYfuhL/hzPvlgrLLEFfT/ZDMWy5zVCqjzKozWbREt7qrsQpNcsNYr7T3bYq74N6TPF3Dpc4Dxke3ImlmuWYcNwf4hjeIFhiIp+Vn9uTIaERYNjNJ9bZo5WOHaWNTeNkSKgqiOGp6Gomquuom4foKGwtRAAA7"
, {
options: {
// !!!ОПЦИИ СКРИПТА!!! (пока неполные)
showAlerts: false // Показывать алерты по окончании поиска
, scrollToSearchResults: true // Автоматически прокручивать к рещультатам поиска
, autoOpenSpoilerWithResult: false // Раскрывать спойлер с результатами автоматически
, reversPostSorting: true // обратить сортировку по дате для найденных постов (от новых к старым)
}
, name: []
, ttlShow: '\u25BA Показать результаты поиска'
, ttlHide: '\u25BC Скрыть результаты поиска'
, ttlLdng: '\u23F3 Поиск...'
, ttlNotFnd: '\u26A0 Постов не найдено!'
, set setName(str) {
this.name.push(str)
}
, get win() {
delete this.win;
return this.win = window.top
}
, get loc() {
delete this.loc;
return this.loc = this.win.location.href
}
, get actnBox() {
delete this.actnBox;
return this.actnBox = [...document.getElementsByTagName("table")].find(el => el.querySelector("a[href^='post.cgi?action=new']")
|| !el.getElementsByTagName('td')[0].children.length)
}
, get isFF() {
delete this.isFF;
return this.isFF = this.win.navigator && this.win.navigator.userAgent && this.win.navigator.userAgent.toLowerCase().includes("firefox")
}
, get spHd() {
delete this.spHd;
return this.spHd = this.getSpHd()
}
, get spBd() {
delete this.spBd;
return this.spBd = this.getSpBd()
}
, get spTTl() {
delete this.spTTl;
return this.spTTl = this.spHd && this.spHd.getElementsByTagName('td')[0]
}
, getSel() {
return this.win.getSelection && this.win.getSelection().toString() || ""
}
, prompt() {
const prompt = this.win.prompt('Задайте имя для поиска', '')
return prompt || undefined
}
, getPosts(url, name) {
return new Promise((res, rej) => {
let posts;
if (url == this.loc) {
posts = document.getElementsByClassName('tb');
if (posts && !!posts.length) {
posts = this.filterPosts(posts, name);
return res(posts.map(el => el.cloneNode(true)));
}
};
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = re => {
if (!(xhr.readyState == 4 && xhr.status == 200)) return rej("error");
posts = xhr.responseXML.getElementsByClassName('tb');
if (posts && !!posts.length) {
posts = this.filterPosts(posts, name);
return res(posts);
}
};
xhr.onerror = xhr.onabort = xhr.ontimeout = () => rej("error");
xhr.responseType = "document";
xhr.send();
})
}
, procesPosts(posts) {
return new Promise((res, rej) => {
posts = this.flatPosts(posts);
posts = this.stylePosts(posts);
if (posts && !!posts.length) {
this.options.reversPostSorting && (posts = posts.reverse());
for (const post of posts)
this.spBd.appendChild(post);
res(posts.length)
}
else
rej("not found")
})
}
, filterPosts(posts, name) {
return [...posts].filter(post => !post.querySelector('a.tpc[href$="&postno=1"]')
&& name.includes((post.querySelector("td.dats > a.m > b") || post.querySelector("td.dats > b"))
.textContent.toLowerCase().replace(/\s/g, '_')));
}
, stylePosts(posts) {
const color1 = "#EEEEEE",
color2 = "#FFFFFF"
return posts.map((post,indx) => {
const tdEls = post.getElementsByTagName("td");
for (const td of tdEls)
td.bgColor && (td.bgColor = !(indx % 2) ? color1 : color2);
return post
})
}
, flatPosts(posts) {
return posts.reduce((a, b) => a.concat(b), []);
}
, getUsrName() {
const name = this.name.join(",") || this.getSel() || this.prompt()
return name && name.trim().toLowerCase().replace(/\s/g, "_").split(",");
}
, getPages() {
const paginator = [...document.getElementsByTagName("p")].find(el => el.textContent && el.textContent.startsWith("Страницы: "));
if (paginator)
return [...paginator.getElementsByTagName("td")[0].children].map(el => el.href || this.loc);
else
return [this.loc]
}
, getSpHd() {
let spoilerHead = document.getElementById("spu-spoiler-head");
if (!spoilerHead) {
const dummyNode = this.actnBox.parentNode.insertBefore(document.createElement('div'), this.actnBox.nextElementSibling);
dummyNode.outerHTML = '<table id="spu-spoiler-head" style="-moz-user-select: none ! important; cursor: pointer ! important;" width="95%" cellspacing="1" cellpadding="3" bgcolor="#999999" align="center" border="0"><tbody><tr><td valign="middle" bgcolor="#dddddd" align="left"></td></tr><td class="small" bgcolor="#dddddd" align="center">Найдено: <span id="spu-fndd"></span> Ошибок: <span id="spu-errs"></span></td></tbody></table>';
spoilerHead = this.actnBox.nextElementSibling;
spoilerHead.id = "spu-spoiler-head";
spoilerHead.hidden = true;
const spTitle = spoilerHead.getElementsByTagName('td')[0];
spoilerHead.style.cssText = 'font-family: Segoe UI Emoji, bitstreamcyberbit, quivira, Verdana, Arial, Helvetica, sans-serif; -moz-user-select: none !important;-webkit-user-select: none !important; -ms-user-select: none !important; user-select: none !important; cursor: pointer !important';
spoilerHead.onclick = e => {
const spoilerBody = this.spBd;
if (e.button != 0 || !spoilerBody || !(spoilerBody && spoilerBody.ready)) return;
e.preventDefault(); e.stopPropagation();
spTitle.textContent = spoilerBody.hidden ? this.ttlHide : this.ttlShow;
spoilerBody.hidden = !spoilerBody.hidden;
}
}
return spoilerHead;
}
, getSpBd() {
let spoilerBody = document.getElementById("spu-spoiler-body");
if (!spoilerBody) {
const spoilerHead = this.spHd;
spoilerBody = spoilerHead.parentNode.insertBefore(document.createElement("table"), spoilerHead.nextElementSibling);
spoilerBody.id = "spu-spoiler-body";
spoilerBody.align = "center";
spoilerBody.width = "95%";
spoilerBody.style.border = "1px solid black";
spoilerBody.style.padding = "1em .5em"
spoilerBody.hidden = true
}
return spoilerBody;
}
, endNotify(fndd,errs,ntfnd) {
if (fndd) {
const spoilerHead = this.spHd,
spoilerBody = this.spBd,
confirm = this.win.confirm("Поиск окончен!\nНайдено: " + fndd + (errs ? "\nОшибок: " + errs : "") + "\nПерейти к резульататам?");
if (confirm) {
spoilerHead.scrollIntoView({behavior:"smooth"});
if (this.options.autoOpenSpoilerWithResult) {
this.spTTl.textContent = this.ttlHide;
spoilerBody.hidden = false;
}
}
}
else
this.win.alert("Постов не найдено!" + (errs ? "\nОшибок: " + errs : ""))
}
, handleEvent(e) {
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
const name = this.getUsrName();
if (!name || name && !name.length) return;
const pageLinks = this.getPages(),
spoilerHead = this.spHd,
spoilerBody = this.spBd,
spoilerTitle = this.spTTl,
errsLbl = document.getElementById("spu-errs"),
findedLbl = document.getElementById("spu-fndd");
spoilerHead.hidden = false; spoilerTitle.textContent = this.ttlLdng;
spoilerBody.hidden = true; spoilerBody.ready = false;
this.options.scrollToSearchResults && !this.options.showAlerts && spoilerHead.scrollIntoView({behavior:"smooth"});
let errorsCntr = 0;
errsLbl.textContent = findedLbl.textContent = 0;
if (spoilerBody.children && !!spoilerBody.children.length)
for (const node of [...spoilerBody.children])
node.remove();
Promise.all(pageLinks.map(link => {
return this.getPosts(link, name)
})).then(
posts => {
return this.procesPosts(posts)
}
, error => {
errorsCntr += 1;
errsLbl.textContent = errorsCntr;
}
).then(
founded => {
findedLbl.textContent = founded;
spoilerTitle.textContent = this.ttlShow;
spoilerBody.ready = true;
if (this.options.showAlerts)
this.endNotify(founded, errorsCntr)
else if (this.options.autoOpenSpoilerWithResult) {
spoilerBody.hidden = false
spoilerTitle.textContent = this.ttlHide;
};
return
}
, notfound => {
spoilerTitle.textContent = this.ttlNotFnd;
this.options.showAlerts && this.endNotify(null, errorsCntr, notfound);
return
}
).catch(err => {
errorsCntr += 1; errsLbl.textContent = errorsCntr
})
this.name.length = 0;
}
}
);