// ==UserScript==
// @name JavDB 跳轉 MissAv
// @namespace https://javdb.com/
// @license MIT
// @version 2.0
// @description Combine waterfall layout and jump button for JavDB
// @author YourName
// @match https://javdb.com/*
// @match https://missav.ws/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_notification
// @grant GM_setClipboard
// @grant GM_getResourceURL
// @grant GM_registerMenuCommand
// @grant GM_info
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @connect *
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// JavDB Waterfall Layout Code
const EMPTY_IMAGE_DATA = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAB4CAMAAABsOSjPAAAAn1BMVEUAAAD/lgD/x3f/zoj/nRH/3Kr/+e7/sD//68z/1Zj/qzP/skT/uVX/9eX/pCL/oh3/wGb/8d3/4LL/5Lv/2aL/tUv/kAD/0o//9uj/v2L/mQn//vv//Pf/rjf/+vL/1pn/2pT/mAX/kwD/+/X/9OH/4rb/qzD/qSX/mAH/6sn/6cL/26f/y4D/nxr/vV7/uU//wnH/7tb/1YX/wEL/nypWdfcGAAAAAXRSTlMAQObYZgAABOdJREFUeNrs2F1TgkAYhmGeRMRWLbDlS5BAUPysqf7/b6sYt600dQ8a3nW4T3Teo2uWHRYw2tquMOiU5mhDl64ADR36jdZhsVv0X7XoFn2iFt2iT9Si/wG93AxY73Q8fLAooW2GizI7MRm0j4tLCiJoBTPyN4sE2s6h0o4EmkMprySAXmaK6CcC6A0UG1nNowdQLFo2j2aQmTxMKpyrbB7dk5rd54VfLXAmlxI6NOqKrUZocygOG43QXEycSB90KCZTUx90Mvk6bvRBV+P95F6jPY1RUQ9SrW55uPOdqR3id3kUEUYDkZkdmu8da731KrLoI3mPxkfDV486Ovthrps800aPUrs7l2ZR6uV00XPrc2GZMMtWC7Lo26Aez7gwyyxGFJ0MxWHOhVlmlW7pugE1dL03hPpl/y/wA2KfxX6gk++62cSoixkShxba9fuywjgsfgKwHZNCHyuYfVtvjjqfOHqdSWHMsG8XU0Z3I6Av98ZXi5vOR5uNQxDtVoBQzzhk+f63Sw+9juQenjKiz9Pj/oNsnQ4qYeNpZ0H1JYBBsWzVPPoZim2L5tElFOsRuOXFc6iVEkAbXSiVTCigjRAKmTaNE/G9nfNtbRsGwnjudBKW5Gk2ZtCtsJcb7N2+/5ebHv1JXROEkkFxih6IkSr57ufrnaQ2Ia93UP/4c5pt3Ar16dvfE509XpT/Hb62FX593852YHp9+dLWz3MfTfc686cQbmpAD+iGBvSAbmhAD+iGBvSAbmhAD+iGBvSAbuhjoDdKErYPmDSBttISWRvzhJI899m1tDShFRW5B6AnomBqSzXn3fWWANPchDbMMxF74segq3khf/h3X3g3b+Ek02V3aUFX+2QuD2l9e6vFH35VimjaQaPTr7mVHtW+XB6TAXRO5uVgRP8fNPdCM81WyCsp0dOeiBaDGY4o28GgqB00C7l8s5RhHx8iUFK4BW0dkdMpoyY493E4TJ5ydFUgCoFUL/RMQkViriXKGKgIS26pN2ht4a1Az7WkpZg5FuIGs8XBlrocp2uC59LNsv3QlO/2uH9DgwEINws7kmSdWWqZAE7jQRSMlGGPJULzgnv0EVqn1W9B6WP2CqcuuaJkZioOSN8BHVZHHoZtZIELXEL8cQ7JQrIiVXgPfXGYBCOeJKWGOmYEoANHpUBbuPKANrUFCy6aVWkJNdIPzYBzIArx4hBPQJucDj72HYVdduThtMXQpsjF4SVX0RFaJ9LqJLl0FTqatQgDPNdlowNaXyPtk5EKyPl2kzyuggdBytj96pGjKiTLFdqRugFt3kFzju+a-Sm0MnTOvP5IAzVlAeotxF6YLlYigaDAHfh8gpw8rhU6Q0U5pIdGKekMvd2ABqCCWcYMC9S5ACAxVS38diEaR1kqBwoGcImvrAAbUC7J2ny/eVtAgw1yZUjfgjZCZXE3lOUL9BxdrVIdNKEnKuIYX1egLZy4sphMBq0ggAB13bprOCu1zwsWCqORHmiXM8hSn9HCZ/aspdC000Nx1npZGVXFKl0AwmwvVuU5a4lcXgOqlLqy8ARUZnUcgnjXMdcjiI4ty7q4W9kWHjXx2oDul+rdiU/yR4C28SV9B5SzQJtr0nfoLNDlIOFNz9zTQKfi031TzwN9hwb0gG5oQA/oDxR9iq80ehI9PfQT6amhh4Y+k/4BYXA+w/0JM4sAAAAASUVORK5CYII=';
const JAVDB_ITEM_SELECTOR = '.movie-list.v.cols-4.vcols-8 .item, .movie-list.v.cols-4.vcols-5 .item, .movie-list.h.cols-4.vcols-8 .item, .movie-list.h.cols-4.vcols-5 .item';
function Queue(n) {
n = parseInt(n, 10);
return new Queue.prototype.init((n && n > 0) ? n : 1)
}
Queue.prototype = {
init: function (n) {
this.threads = [];
this.taskList = [];
while (n--) {
this.threads.push(new this.Thread)
}
},
push: function (callback) {
if (typeof callback !== 'function') return;
var index = this.indexOfIdle();
if (index != -1) {
this.threads[index].idle(callback);
} else {
this.taskList.push(callback);
for (var i = 0, l = this.threads.length; i < l; i++) {
((thread, self, id) => {
thread.idle(() => {
if (self.taskList.length > 0) {
let promise = self.taskList.shift()();
return promise.promise ? promise : $.Deferred().resolve().promise();
} else {
return $.Deferred().resolve().promise();
}
})
})(this.threads[i], this, i);
}
}
},
indexOfIdle: function () {
var threads = this.threads,
thread = null,
index = -1;
for (var i = 0, l = threads.length; i < l; i++) {
thread = threads[i];
if (thread.promise.state() === 'resolved') {
index = i;
break;
}
}
return index;
},
Thread: function () {
this.promise = $.Deferred().resolve().promise();
this.idle = (callback) => {
this.promise = this.promise.then(callback)
}
}
};
Queue.prototype.init.prototype = Queue.prototype;
var thirdparty = {
waterfallButton: null,
waterfallScrollInit: () => {
var w = new thirdparty.waterfall({});
var $pages4 = $(JAVDB_ITEM_SELECTOR);
if ($pages4.length) {
GM_addStyle(`
.container {max-width: inherit !important;}
.tags{display: block !important;}
.tag.hobby{display: block;float: right;color: #fff;line-height: 2em;}
`);
$pages4[0].parentElement.id = "waterfall";
w = new thirdparty.waterfall({
next: '.pagination .pagination-next',
item: JAVDB_ITEM_SELECTOR,
cont: '#waterfall',
pagi: '.pagination',
});
}
w.setSecondCallback((cont, elems) => {
cont.append(elems);
});
},
waterfall: (() => {
function waterfall(selectorcfg = {}) {
class Lock {
constructor(d = false) {
this.locked = d;
}
lock() {
this.locked = true;
}
unlock() {
this.locked = false;
}
}
this.queue = new Queue(1);
this.page_queue = new Queue(1);
this.lock = new Lock();
this.baseURI = this.getBaseURI();
this.selector = {
next: '.pagination .pagination-next',
item: JAVDB_ITEM_SELECTOR,
cont: '#waterfall',
pagi: '.pagination',
};
Object.assign(this.selector, selectorcfg);
this.pagegen = this.fetchSync(location.href);
this.anchor = $(this.selector.pagi)[0];
this._count = 0;
this._1func = (cont, elems) => {
cont.empty().append(elems);
};
this._2func = (cont, elems) => {
cont.append(elems);
};
this._3func = (elems) => { };
if ($(this.selector.item).length) {
document.addEventListener('scroll', this.scroll.bind(this));
document.addEventListener('wheel', this.wheel.bind(this));
this.appendElems(this._1func);
}
}
waterfall.prototype.getBaseURI = () => {
let _ = location;
return `${_.protocol}//${_.hostname}${(_.port && `:${_.port}`)}`;
};
waterfall.prototype.getNextURL = function (href) {
let a = document.createElement('a');
a.href = href;
return `${this.baseURI}${a.pathname}${a.search}`;
};
waterfall.prototype.fetchURL = function (url) {
console.log(`fetchUrl = ${url}`);
let status = 404;
const fetchwithcookie = fetch(url, { credentials: 'same-origin' });
return fetchwithcookie.then(response => {
status = response.status;
return response.text();
}).then(html => new DOMParser().parseFromString(html, 'text/html'))
.then(doc => {
let $doc = $(doc);
let elems = [];
let nextURL;
if(status < 300){
let href = $doc.find(this.selector.next).attr('href');
nextURL = href ? this.getNextURL(href) : undefined;
elems = $doc.find(this.selector.item);
for (const elem of elems) {
const links = elem.getElementsByTagName('a');
for (const link of links) {
link.target = "_blank";
}
}
if ($(JAVDB_ITEM_SELECTOR).length && (this._count !== 0) && url === nextURL) {
if ($(`#waterfall>div>a[href="${$(elems[0]).find('a.box')[0].attr('href')}"]`).length > 0) {
nextURL = undefined;
elems = [];
}
}
} else {
nextURL = $doc.url;
}
return {
nextURL,
elems
};
});
};
waterfall.prototype.fetchSync = function* (urli) {
let url = urli;
do {
yield new Promise((resolve, reject) => {
if (this.lock.locked) {
reject();
}
else {
this.lock.lock();
resolve();
}
}).then(() => {
return this.fetchURL(url).then(info => {
url = info.nextURL;
return info.elems;
});
}).then(elems => {
this.lock.unlock();
return elems;
}).catch((err) => {
// Locked!
});
} while (url);
};
waterfall.prototype.appendElems = function () {
let nextpage = this.pagegen.next();
if (!nextpage.done) {
nextpage.value.then(elems => {
const cb = (this._count === 0) ? this._1func : this._2func;
cb($(this.selector.cont), elems);
this._count += 1;
});
}
return nextpage.done;
};
waterfall.prototype.end = function () {
document.removeEventListener('scroll', this.scroll.bind(this));
document.removeEventListener('wheel', this.wheel.bind(this));
let $end = $(`<h1>The End</h1>`);
$(this.anchor).replaceWith($end);
};
waterfall.prototype.reachBottom = function (elem, limit) {
return (elem.getBoundingClientRect().top - $(window).height()) < limit;
};
waterfall.prototype.scroll = function () {
this.pageQueuePush();
};
waterfall.prototype.wheel = function () {
this.pageQueuePush();
};
waterfall.prototype.pageQueuePush = function () {
this.page_queue.push(() => {
let defer = $.Deferred();
new Promise(resolve => {
if (this.reachBottom(this.anchor, 1200) && this.appendElems(this._2func)) {
this.end();
}
resolve();
}).then(() => {
defer.resolve();
});
return defer.promise();
});
};
waterfall.prototype.setFirstCallback = function (f) {
this._1func = f;
};
waterfall.prototype.setSecondCallback = function (f) {
this._2func = f;
};
waterfall.prototype.setJavlibCallback = function (f) {
this._3func = f;
};
waterfall.prototype.setFourthCallback = function (f) {
this._4func = f;
};
return waterfall;
})(),
};
function mainRun() {
thirdparty.waterfallScrollInit();
}
// Add JQuery
(function(callback) {
var script = document.createElement("script");
script.setAttribute("src", "//cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js");
script.addEventListener('load', function() {
var script = document.createElement("script");
script.textContent = '(' + callback.toString() + ')();';
document.body.appendChild(script);
}, false);
document.body.appendChild(script);
})(mainRun);
// JavDB Jump Button Code
var jumpButton = document.createElement('button');
jumpButton.style.backgroundColor = 'green';
jumpButton.style.color = 'white';
jumpButton.style.position = 'fixed';
jumpButton.style.bottom = '10px';
jumpButton.style.left = '10px';
jumpButton.style.zIndex = '9999';
jumpButton.style.padding = '16px 24px';
jumpButton.style.borderRadius = '8px';
jumpButton.style.fontSize = '24px';
jumpButton.style.border = 'none';
jumpButton.style.cursor = 'pointer';
jumpButton.style.display = 'none';
jumpButton.addEventListener('click', function() {
let 番號 = '';
let url = '';
if (window.location.href.includes('javdb.com')) {
var link = document.querySelector('.panel-block.first-block a.button.is-white.copy-to-clipboard');
if (link) {
番號 = link.getAttribute('data-clipboard-text');
if (番號) {
url = "https://missav.ws/" + 番號.toLowerCase();
}
}
} else if (window.location.href.includes('missav.ws')) {
var element = document.querySelector('.text-secondary span.font-medium');
if (element) {
番號 = element.innerText;
番號 = 番號.replace(/-UNCENSORED-LEAK|-CHINESE-SUBTITLE/g, '');
url = "https://javdb.com/search?f=all&q=" + 番號;
}
}
if (番號 && url) {
var a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
});
let 番號 = '';
if (window.location.href.includes('javdb.com')) {
var link = document.querySelector('.panel-block.first-block a.button.is-white.copy-to-clipboard');
if (link) {
番號 = link.getAttribute('data-clipboard-text');
}
} else if (window.location.href.includes('missav.ws')) {
var element = document.querySelector('.text-secondary span.font-medium');
if (element) {
番號 = element.innerText;
番號 = 番號.replace(/-UNCENSORED-LEAK|-CHINESE-SUBTITLE/g, '');
}
}
if (番號) {
jumpButton.innerHTML = 番號;
jumpButton.style.display = 'block';
} else {
jumpButton.style.display = 'none';
}
document.body.appendChild(jumpButton);
mainRun(); // 確保 mainRun 在跳轉按鈕初始化後運行
})();