Yunod

/*
followed manga updates list

to see/try how this works :
open this link
yunodBio()
then replace first "https" with "javascript" in adress bar and Enter


Copy all text in spoiler to your Biography,
Add next lines (javascript - 0) as bookmark to browser(mobile or PC)

javascript
:if(location.origin==='https://api.mangadex.org'){fetch('https://api.mangadex.org/v2/user/me').then(d=>d.json()).then(d=>{textArea=document.createElement('textarea');document.body.appendChild(textArea).innerHTML=d.data.biography;document.body.appendChild(document.createElement('script')).innerHTML=textArea.value;})} else open('https://api.mangadex.org/v2/user/me');0

or drag&drop this link to your bookmarks on browser
apiMangaDex()
then replace first "https" with "javascript"

open https://api.mangadex.org then click bookmarklet(on mobile give name like 'mmmmanga' type url bar 'mmm' it shows your bookmarklet then click)

you can also copy&paste or drag&drop all text in spoiler (starting with "javascript") as bookmark to your browser


list refresh every 5 minutes,
click ⦾ to hide/show read chapters,
double click ⦾ export followed-manga list as bookmark HTML file
click ---------------- or ++++++++++++++++ for page navigation
click ⦾ before chapter to mark as read


note : don't try if you have no idea how the code in spoiler work, this code might be something trying steal your info

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?')) {/* " : " & : & < : < > : > \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

Trophies

  1. 100

    Survived v3

    Awarded to all Forum users that posted at least one time and were present before the v5 forums were released
  2. 1

    First message

    Post a message somewhere on the site to receive this.
Back
Top