javascript
:/**/
(function(apiCallOrigin = 'https://api.mangadex.org'){
if(window.location.origin === 'https://api.mangadex.org'){
document.head.appendChild(document.createElement('title')).innerHTML = 'MangaDex()';
document.head.appendChild(document.createElement('link')).outerHTML = '<link rel="icon" type="image/png" sizes="16x16" href="https://mangadex.org/favicon-16x16.png">'
document.head.appendChild(document.createElement('meta')).outerHTML = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
document.head.appendChild(document.createElement('style')).innerHTML = 'body{background:black}';
/* window.onbeforeunload = e => '?';
*/
}
/*
document.body.appendChild(document.createElement('div'))
.attachShadow({mode:'open'})
.appendChild(document.createElement('div'))
.innerHTML='<div style="position:fixed;top:1px;left:66px;z-index:99999">iii</div>';
*/
div = document.createElement('div');
div.id = 'mangaUpdatesList';
document.body.appendChild(div);
(div.appendChild(document.createElement('style')).innerHTML = `
body > pre {
max-height: 90vh;
overflow: hidden;
}
#mangaUpdatesList {
z-index: 9999;
position: fixed;
top: 0px;
background: black;
color: DarkOrange;
max-width: 99vw;
max-height: 90vh;
}
#mangaUpdatesList > details {
max-height: 85vh;
}
#mangaUpdatesList > details > div {
display: flex;
flex-wrap: wrap;
min-width: 99%;
max-width: 99%;
max-height: 80vh;
overflow-x: hidden;
overflow-y: scroll;
/* scrollbar-width: thin;
scrollbar-color: DarkOrange black;
*/
}
::-webkit-scrollbar {
width: 2px;
height: 2px;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px grey;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: DarkOrange;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #b30000;
}
#mangaUpdatesList > details > div > div {
/* height: 110px;
*/ flex: 250px;
border:1px solid DarkOrange;
border-radius:6px;
margin:2px;
white-space:nowrap;
overflow-x:scroll;
}
#mangaUpdatesList > details > div > div > div:nth-child(2) {
height: 90px;
white-space:nowrap;
overflow-y:scroll;
display:inline-block;
direction: rtl;
text-align: left;
}
#mangaUpdatesList > details > div > div > div:nth-child(2) * {
direction: ltr;
}
#mangaUpdatesList > details > div > p {
width: 99%;
white-space: nowrap;
font-size:300%;
margin:-5px 0;
/* display:none
*/
}
#mangaUpdatesList > details > div > div > div > img {
width: 60px;
height: 90px;
border-radius: 4px;
}
#mangaUpdatesList > details > div > div > div:first-child {
float:left;
margin-top: 4px;
}
#mangaUpdatesList > details > summary {
font-size: 150%;
padding-right: 60px;
}
#mangaUpdatesList > details > summary > a.settings {
background: black;
/* transition: margin-left 1s;
*/
}
#mangaUpdatesList > details > summary > a.settings:hover,
#mangaUpdatesList > details > summary > a.settings:active,
#mangaUpdatesList > details > summary > a.settings:focus,
#mangaUpdatesList > details > summary > a.settings:focus-within {
margin-left: -200px;
}
#mangaUpdatesList > details > summary > a.settings > * {
display:none;
}
#mangaUpdatesList > details > summary > a.settings:active > * ,
#mangaUpdatesList > details > summary > a.settings:hover > * ,
#mangaUpdatesList > details > summary > a.settings:focus > * ,
#mangaUpdatesList > details > summary > a.settings:focus-within > * {
display: inherit;
}
#mangaUpdatesList details.hideReaded > div > div.read,
#mangaUpdatesList details.hideReaded > div > div > div > div.readChapter,
#mangaUpdatesList details.hideReaded > div > div > div > div.unavailableChapter {
display: none !important;
}
#mangaUpdatesList > details > summary > a.settings:after {
content: "

";
}
#mangaUpdatesList.ok > details > summary > a.settings:after {
content: "

";
}
#mangaUpdatesList.error > details > summary > a.settings:after {
content: "

";
text-decoration: line-through double red;
}
#mangaUpdatesList.loading > details > summary > a.settings:after {
content: url("data:image/gif;base64,"

;
content: "◐";
margin: -4px 0;
animation: clock 2s linear infinite;
}
@keyframes clock {
0% { content: "◐"; }
25% { content: "◓"; }
50% { content: "◑"; }
75% { content: "◒"; }
100% { content: "◐"; }
}
#mangaUpdatesList > details > summary > a.settings > input.toggleReaded:after {
content: "⦾";/*⦾⦿❍👁*/
color: white;
background: black;
font-size: 200%;
position: fixed;
margin-top: -8px;
}
#mangaUpdatesList > details.hideReaded > summary > a.settings > input.toggleReaded:after {
color: gray;
text-decoration: line-through;
}
#mangaUpdatesList > details > summary > a.settings > input.toggleGroupBy:after {
content: "

";
background: black;
font-size: 200%;
position: fixed;
margin: -12px 0 0 -8px;
}
#mangaUpdatesList > details > summary > a.settings > input.toggleGroupBy:checked:after {
content: "

";
}
#mangaUpdatesList > details > div > div > div > div > a:nth-child(2):after {
content: attr(title);
}
#mangaUpdatesList div.unreadChapter > a:nth-child(1) {
text-decoration: line-through;
color: gray;
}
#mangaUpdatesList div.readChapter > a:nth-child(1) {
color: white;
}
#mangaUpdatesList a {
color: SaddleBrown;
}
#mangaUpdatesList div.readChapter > a {
color: gray;
}
#mangaUpdatesList div.mangaPlus.unreadChapter > a {
color: red;
}
#mangaUpdatesList a.mangaPlus + a {
color: red;
}
#mangaUpdatesList div.unavailableChapter > a {
color: gray;
text-decoration: line-through double red;
}
#mangaUpdatesList a.unavailableChapter + a {
color: gray;
opacity: 0.1;
}
#mangaUpdatesList a:visited {
color: gray;
}
`);
details = document.createElement('details');
details.className = 'hideReaded';
div.appendChild(details);
summary = document.createElement('summary');
summary.title = 'Double click/tap to download followed manga list as Bookmarks';
summary.innerHTML = `MangaDex.org<a target="_blank" href="https://mangadex.org/follows">/follows</a>
<a class="settings">
<input type="number" id="pageNumberInput" style="width:32px" value="1" min="1" oninput="if(!event.data){}" onfocusout="this.title=(this.value-1)+'00-'+this.value+'00';Window.detailsUpdate()">
<select id="followTypeSelect" onchange="Window.detailsUpdate()">
<option value="0">ALL</option>
<option value="1" selected>Reading</option>
<option value="6">ReReading</option>
<option value="4">PlanToRead</option>
<option value="3">OnHold</option>
<option value="2">Completed</option>
<option value="5">Dropped</option>
</select>
<input type="checkbox" class="toggleGroupBy" id="groupByMangaTitleInput" checked onchange="Window.detailsUpdate()">
<input type="checkbox" class="toggleReaded" id="" value="title" checked onchange="this.parentNode.parentNode.parentNode.className=this.checked?'hideReaded':'showReaded'">
</a>`;
details.appendChild(summary);
detailsDiv = document.createElement('div');
details.appendChild(detailsDiv);
/* let headers = new Headers();
headers.append();
fetch(sign_in, {headers: headers})
*/
header = {
/* method: 'GET',
mode:'cors',
credentials:'include',
credentials:'same-origin'
headers: {
'Access-Control-Allow-Origin' : '*',
'Origin': location.origin,
'Content-Type': 'application/json',
'Authorization', 'Basic ' + base64.encode(username + ":" + password)
},
queryParameters:{
saver:true,
mark_read:true,
p:1
}
*/
};
Window.markAsRead = function markAsRead(t, id, open = false) {
div.className = 'loading';
fetch(apiCallOrigin + `/v2/chapter/${id}?markread`, header)
.then(function(d){
if (!d.ok) {
div.className = 'error';
div.title = 'error (' + d.status + ') : '+ d.statusText;
console.log(d);
/* throw Error(d.statusText);
*/} else {
div.className = 'ok';
div.title = '';
};
return d.json();
})
.then(function(d){
if (d.status === 'error') {
if (open && confirm('This chapter is unavailable.\nStill wanna try?')) {
window.open('https://mangadex.org/chapter/' + id);
} else {
};
console.log(d);
div.className = 'error';
} else {
t.parentNode.className = t.parentNode.className.replaceAll('unreadChapter', 'readChapter');
if(t.parentNode.parentNode.querySelectorAll('.unreadChapter').length === 0) t.parentNode.parentNode.parentNode.className = 'read';
if(d.data.status === 'external' && open/* || confirm(d.data.mangaTitle + ' : ' + d.data.title)*/) window.open(d.data.pages);
}
})
.catch(function(error) {
console.log(error);
div.className = 'error';
});
};
var page = 1;
var updateCount = 0;
Window.detailsUpdate = function detailsUpdate(pagination = 0){
fetchFollowType = document.getElementById('followTypeSelect').value;
groupByMangaId = document.getElementById('groupByMangaTitleInput').checked;
pageNumber = document.getElementById('pageNumberInput').value * 1;
page = pageNumber + pagination;
if(page < 1) page = 1;
document.getElementById('pageNumberInput').value = page;
div.className = 'loading';
fetch(apiCallOrigin + '/v2/user/me/followed-updates?p=' + page + '&type=' + fetchFollowType, header)
.then(function(d){
if (!d.ok) {
div.className = 'error';
div.title = 'error (' + d.status + ') : '+ d.statusText;
console.log(d);
throw Error(d.statusText, d);
} else {
div.className = 'ok';
div.title = '';
};
return d.json();
console.log(d);
})
.then(function(d){
var bookmarks = '';
if(!Array.isArray(d.data)) {
if(groupByMangaId) {
var input = d.data.chapters;
var key = 'mangaId';
var groupByMangaIdArray = [];
for (var ii = 0; ii < input.length; ii++) {
var data = input[ii];
data.arrayIndex = ii;
var found = false;
for(var j=0; j < groupByMangaIdArray.length; j++) {
if(groupByMangaIdArray[j][0][key] === data[key]) {
found = true;
groupByMangaIdArray[j].push(data);
break;
}
}
if(!found) {
groupByMangaIdArray.push([data]);
}
}
for(var k = 0; k < groupByMangaIdArray.length; k++) {
mangaArrayMaker(groupByMangaIdArray[k]);
};
} else {
mangaArrayMakerChapter(d.data.chapters);
};
} else {
bookmarks = '<div><img loading="lazy" src="https://mangadex.org/images/misc/navbar.svg">No manga found!!</div>';
};
function mangaArrayMaker(mangaArray) {
bookmarks +='';
chapters = '';
isTitleAllRead = true;
for({arrayIndex, id, hash, mangaId, mangaTitle, volume, chapter, title, language, groups, uploader, timestamp, threadId, comments, views, read} of mangaArray) {
if(!read) isTitleAllRead = false;
time = (Date.now() / 1000 - timestamp) / 3600;
time = time > 365 * 30 * 24 ? ~~(time / 365 / 30 / 24) + 'Y' : time > 30 * 24 ? ~~(time / 30 / 24) + 'M' : time > 168 ? ~~(time / 168) + 'W' : time > 24 ? ~~(time / 24) + 'D' : time > 1 ? ~~time + '

' : '

';
views = views > 999999 ? ~~(views/1000000) + 'KK' :views > 999 ? ~~(views/1000) + 'K' : views;
chapters += `<div class="${(groups.indexOf(9097) > -1) ? 'mangaPlus' : ''} ${read ? '' : 'un'}readChapter${views < 4 ? ' unavailableChapter' : ''}">
<a onclick="Window.markAsRead(this, ${id});" data-read="${read ? 1 : 0}">⦿</a>
<a target="_blank" ${(groups.indexOf(9097) > -1) ? 'onclick="Window.markAsRead(this, ' + id + ', true);" ' : 'href="https://mangadex.org/chapter/' + id}" title=" (${(groups.indexOf(9097) > -1) ? 'm+ ' : ''}${time} ${views})">${chapter} : ${title}</a>
</div>`;
};
const uriHolder = document.createElement("P"

;
function titleToURI(mT) {
uriHolder.innerHTML = mT;
mT = uriHolder.innerText;
return mT.toLowerCase().replace(/"&|×/g, '').replace(/[^a-zA-Z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
};
bookmarks += `\n
<div class="${isTitleAllRead ? '' : 'un'}read">
<div>
<img loading="lazy" src="https://mangadex.org/images/manga/${mangaId}.thumb.jpg">
</div>
<div>
<div>
<a target="_blank" href="https://mangadex.org/title/${mangaId}/${titleToURI(mangaTitle)}" title="${mangaTitle}">${mangaTitle}</a><br>
</div>
${chapters}
</div>
</div>`;
};
function mangaArrayMakerChapter(mangaArray) {
bookmarks +='';
chapters = '';
isTitleAllRead = true;
for({arrayIndex, id, hash, mangaId, mangaTitle, volume, chapter, title, language, groups, uploader, timestamp, threadId, comments, views, read} of mangaArray) {
if(views > 0) {
if(!read) isTitleAllRead = false;
time = (Date.now() / 1000 - timestamp) / 3600;
time = time > 30 * 24 ? ~~(time / 30 / 24) + 'M' : time > 168 ? ~~(time / 168) + 'W' : time > 24 ? ~~(time / 24) + 'D' : time > 1 ? ~~time + '

' : '

';
views = views > 999999 ? ~~(views/1000000) + 'KK' :views > 999 ? ~~(views/1000) + 'K' : views;
chapters = `<div class="${uploader === 1 ? 'mangaPlus' : ''} ${read ? '' : 'un'}readChapter">
<a onclick="Window.markAsRead(this, ${id});" data-read="${read ? 1 : 0}">⦿</a>
<a target="_blank" ${uploader === 1 ? 'onclick="Window.markAsRead(this, ' + id + ', true);" ' : 'href="https://mangadex.org/chapter/' + id}" title=" (${uploader === 1 ? 'm+ ' : ''}${time} ${views})">${chapter} : ${title}</a>
</div>`;
const uriHolder = document.createElement("P"

;
function titleToURI(mT) {
uriHolder.innerHTML = mT;
mT = uriHolder.innerText;
return mT.toLowerCase().replace(/"&|×/g, '').replace(/[^a-zA-Z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
};
bookmarks += `\n
<div class="${read ? '' : 'un'}read">
<div>
<img loading="lazy" src="https://mangadex.org/images/manga/${mangaId}.thumb.jpg">
</div>
<div>
<div>
<a target="_blank" href="https://mangadex.org/title/${mangaId}/${titleToURI(mangaTitle)}" title="${mangaTitle}">${mangaTitle}</a><br>
</div>
${chapters}
</div>
</div>`;
}
};
}
if (pagination === 1) {
detailsDiv.firstChild.nextElementSibling.scrollIntoView();
} else if (pagination === -1) {
detailsDiv.lastChild.scrollIntoView();
};
a = detailsDiv.innerText;
detailsDiv.innerHTML = `
<p id="firstPage" onclick="Window.detailsUpdate(-1)" style="${page === 1 ? 'display:none' : ''}">- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</p>
${bookmarks}
<p id="lastPage" onclick="Window.detailsUpdate(1);">++++++++++++++++++++++++++++++++++++++++++++++++++</p>`;
console.log( 'update count : ' + (updateCount++) + '\nupdate time : ' + new Date().toLocaleTimeString() + '\npage : ' + page + '\npage updated : ' + (a != detailsDiv.innerText));
details.open = 'true';
div.className = '';
})
.catch(function(error) {
console.log(error);
});
};
document.onvisibilitychange = function(){
if(!document.hidden) {
Window.detailsUpdate();
}
};
setInterval(Window.detailsUpdate(), 5 * 60 * 1000);
summary.ondblclick = function(){
if (confirm('Download followed manga list as Bookmarks?')) {/* " : &quot; & : &amp; < : &lt; > : &gt; \n > %0A*/
fetch(apiCallOrigin + '/v2/user/me/followed-manga', header).then((d) => {if(d.ok){return d.json()} else throw new Error('errrrr!!11!')})
.then(function(d){
const time = Date.now();
const icon = "data:image/png;base64,"
const FollowTypes = ['Unfollow', 'Reading', 'Completed', 'OnHold', 'PlanToRead', 'Dropped', 'ReReading', '7', '8'];
const mangaLinks = ['','','','','','','','',''];
const uriHolder = document.cre