// ==UserScript==
// @name F95zone marker
// @namespace http://tampermonkey.net/
// @version 0.1.7
// @description Marks F95Zone threads for future reference
// @author agreg
// @license MIT
// @match https://f95zone.to/*
// @icon https://www.google.com/s2/favicons?domain=f95zone.to
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==
/* jshint esversion: 6 */
(function() {
'use strict';
const THREADS = /^(https:\/\/f95zone.to)?\/threads\//;
const OP = '.message-threadStarterPost';
const OP_ICONS = `${OP} header .message-attribution-opposite`;
var bookmarks = GM_getValue('bookmarks', {});
let urlId = s => (!THREADS.test(s) ? null : s.replace(THREADS, '').match(/^([^\/]+\.)?([0-9]+)/)[2]);
let $find = (s, e=document) => e.querySelector(s);
let $find_ = (s, e=document) => Array.from( e.querySelectorAll(s) );
let $parent = (e, check=()=>true) => (check(e.parentNode) ? e.parentNode : $parent(e.parentNode, check));
let $e = (tag, options={}, ...children) => {var e = Object.assign(document.createElement(tag), options);
children.forEach(x => e.append(x));
return e}
let $assign = (o, vals) => Object.assign(o, ...Object.entries(vals).map(([k, v]) => ({[k]: v||undefined})));
let $bookmark = e => {
e.prepend( $e('i', {className: "bookmarkLink bookmarkLink--highlightable is-bookmarked"}) );
e.classList.add('-marker-bookmark');
};
let $updData = (id, e, data=GM_getValue(id), upd={}) => {if (data) {
GM_setValue(id, $assign(data, upd));
e.classList[data.fade ? 'add' : 'remove']('-marker-fade');
e.classList[data.mark ? 'add' : 'remove']('-marker-mark');
e.title = data.info || "";
} else {
GM_deleteValue(id);
e.classList.remove('-marker-fade', '-marker-mark');
e.title = "";
}};
let $editIcon = (id, e) => {
var icon = $e('i', {className: "fas fa-star"});
var data = GM_getValue(id), edit = null;
let _$updData = (upd={}) => {$updData(id, e, data, upd);
icon.classList.add(`-marker-${data ? "" : "un"}marked`);
icon.classList.remove(`-marker-${data ? "un" : ""}marked`)}
let $deleteMark = () => {if (confirm("Delete mark?")) {
$toggleEdit();
data = null;
_$updData();
}};
let $toggleEdit = () => {if (edit) {
edit.remove();
edit = null;
} else {
data = data || {};
document.body.append(edit = $e('div', {className: '-marker-dialog'},
$e('textarea', {placeholder: "Tooltip", value: data.info||"", oninput () {_$updData({info: this.value})}}),
$e('div', {className: '-marker-row'},
$e('label', {},
$e('input', {type: 'checkbox', checked: data.fade, onchange () {_$updData({fade: this.checked})}}),
$e('span', {innerText: "Fade"})),
$e('label', {},
$e('input', {type: 'checkbox', checked: data.mark, onchange () {_$updData({mark: this.checked})}}),
$e('span', {innerText: "Mark"}))),
$e('div', {className: '-marker-row'},
$e('button', {innerText: "OK", onclick: $toggleEdit}),
$e('button', {innerText: "Delete", onclick: $deleteMark}))));
}};
_$updData();
return $e('a', {href: location.href, onclick () {$toggleEdit(); return false}}, icon);
};
GM_addStyle(`.-marker-unmarked {opacity: 0.5; color: rgb(147, 152, 160)} .-marker-marked {color: rgb(193, 88, 88)}
.-marker-fade:not(:hover) {opacity: 0.25} .-marker-fade:hover {outline: 2px solid black} .-marker-mark {outline: 2px solid grey}
.-marker-bookmark {font-weight: bold; text-shadow: 0 0, 0 0 7px !important} .-marker-bookmark .bookmarkLink {padding-right: 1ex}
.-marker-dialog {position: fixed; top: 30%; left: 30%; width: 40%; background: rgb(29, 31, 33); border-radius: 5px; z-index:1000}
.-marker-dialog > * {margin: 1em} .-marker-dialog textarea {width: calc(100% - 2em)}
.-marker-dialog .-marker-row > * {margin-left: .5em; margin-right: .5em}`);
$find_(".structItem-title").forEach(e => { // forum, similar threads
let _id = urlId(e.getAttribute('uix-data-href')||"");
_id && $updData(_id, $parent(e, x => x.classList.contains('structItem--thread')));
bookmarks[_id] && $bookmark(e);
});
if (['/search/', '/tags/'].some(s => location.pathname.startsWith(s))) { // search results
$find_(".contentRow-title a").forEach(e => {
let _id = urlId(e.href||"");
console.log(e.href, _id)
_id && $updData(_id, $parent(e, x => /^li$/i.test(x.tagName)));
bookmarks[_id] && $bookmark(e);
});
}
if (['/latest', '/sam/latest_alpha'].some(s => location.pathname.startsWith(s))) { // latest updates
GM_addStyle(`.-marker-mark {display: block;/* Chrome bug */}`);
new MutationObserver(mutations => mutations.forEach(m => Array.from(m.addedNodes).filter(e => e.tagName).forEach(node => {
console.warn(node, node.children);
$find_("a.resource-tile_link", node).forEach(e => {
let _id = urlId(e.href||"");
console.log(e.href, _id)
_id && $updData(_id, e);
bookmarks[_id] && $bookmark($find('.resource-tile_info-header_title', e));
});
}))).observe(document.body, {subtree: true, childList: true});
}
if (location.pathname === '/account/bookmarks') {
$find_("li.block-row").forEach(e => {
let _id = urlId($find(".contentRow-title a", e).href||"");
if (_id) {
let toolbar = $find(".contentRow-extra", e);
toolbar.insertBefore($editIcon(_id, e), toolbar.firstChild);
bookmarks[_id] = true;
}
});
GM_setValue('bookmarks', bookmarks);
}
if (location.pathname.startsWith('/threads/') && $find(OP)) { // first page of a thread
let _icons = $find(OP_ICONS), _id = urlId(location.href), _bookmarked = Boolean($find("a.is-bookmarked", _icons));
if ( $find_('a', _icons).some(e => e.innerText == "#1") ) {
_icons.insertBefore($e('li', {}, $editIcon(_id, $find(OP))),
_icons.firstChild);
}
(_bookmarked || bookmarks[_id]) && GM_setValue('bookmarks', $assign(bookmarks, {[_id]: _bookmarked}));
}
})();