- // ==UserScript==
- // @name Asurascans Bookmarking System
- // @version 3.4
- // @author longkidkoolstar
- // @namespace https://github.com/longkidkoolstar
- // @icon https://images.vexels.com/media/users/3/223055/isolated/preview/eb3fcec56c95c2eb7ded9201e51550a2-bookmark-icon-flat-by-vexels.png
- // @description A bookmarking system for Manga reading sites with signup/login functionality to save your info(Bookmarks e.t.c) Across ALL Devices. Keep Track of your BookMarks in AsuraScans amongst other websites.
- // @require https://greasyfork.org/scripts/468394-itsnotlupus-tiny-utilities/code/utils.js
- // @match https://www.asurascans.com/*
- // @match https://asura.gg/*
- // @match https://asuracomics.gg/*
- // @match https://asura.nacm.xyz/*
- // @match https://asuracomics.com/*
- // @match https://asuratoon.com/*
- // @match https://asuracomic.net/*
- // @match https://void-scans.com/*
- // @match https://lunarscan.org/*
- // @match https://flamescans.org/*
- // @match https://luminousscans.com/*
- // @match https://luminousscans.net/*
- // @match https://cosmic-scans.com/*
- // @match https://nightscans.org/*
- // @match https://nightscans.net/*
- // @match https://freakscans.com/*
- // @match https://reaperscans.fr/*
- // @match https://manhwafreak.com/*
- // @match https://manhwa-freak.com/*
- // @match https://manhwafreak-fr.com/*
- // @match https://realmscans.to/*
- // @match https://mangastream.themesia.com/*
- // @match https://manga-scans.com/*
- // @match https://mangakakalot.so/*
- // @match https://reaperscans.com/*
- // @match https://flamecomics.com/*
- // @match https://hivetoon.com/*
- // @match https://night-scans.com/*
- // @license MIT
- // @grant GM.setValue
- // @grant GM.getValue
- // @grant GM.xmlHttpRequest
- // ==/UserScript==
-
- console.log('User script started');
-
- const scriptVersion = '3.4';
-
- // Check if user is logged in
- (async () => {
- const username = await GM.getValue('username');
- const password = await GM.getValue('password');
-
- if (username && password) {
- console.log('User is logged in');
- window.addEventListener('load', saveDeviceType);
- } else {
- console.log('User is not logged in');
- // Prompt user to enter username and password
- const username = prompt('Enter your username:');
- const password = prompt('Enter your password:');
- // Save username and password to Tampermonkey
- await GM.setValue('username', username);
- await GM.setValue('password', password);
- // Save username and password to JSONBin
- saveUserCredentialsToJSONBin(username, password);
- }
- })();
-
-
- // Function to get a standardized hostname for Asura-related domains
- function getStandardizedHostname(hostname) {
- if (hostname.includes('asura') || hostname.includes('asuratoon')) {
- return 'asurascans.com';
- }
- return hostname;
- }
-
- // Function to get the current Asura domain
- function getCurrentAsuraDomain() {
- const hostname = window.location.hostname;
- if (hostname.includes('asura') || hostname.includes('asuratoon')) {
- return hostname;
- }
- return null;
- }
-
- // Below is to delete the new ads that Asura has been implementing.
- //--------------
- (function() {
- // Function to delete the element
- function deleteElement() {
- var element = document.getElementById('noktaplayercontainer');
- if (element) {
- element.remove();
- }
- }
-
- // DOM listener to check for element existence
- var observer = new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- if (mutation.type === 'childList' || mutation.type === 'subtree') {
- deleteElement();
- }
- });
- });
-
- // Start observing the document body for changes
- observer.observe(document.body, { childList: true, subtree: true });
- })();
-
- // Function to detect and save the device type to JSONBin and local storage
- async function saveDeviceType() {
- const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
-
- // Get the saved device type from local storage
- const savedDeviceType = localStorage.getItem('deviceType');
-
- if (savedDeviceType !== isMobile.toString()) {
- // Device type has changed, update JSONBin and local storage
- const username = await GM.getValue('username');
- const password = await GM.getValue('password');
- const deviceData = { deviceType: isMobile.toString() };
-
- // Save device data to JSONBin
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const existingData = JSON.parse(response.responseText);
- if (existingData.users[username].password === password) {
- existingData.users[username].variables.deviceType = isMobile.toString();
- GM.xmlHttpRequest({
- method: 'PUT',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(existingData),
- });
- }
- }
- });
-
- // Save device data to local storage
- localStorage.setItem('deviceType', isMobile.toString());
-
- // Update variables in local storage
- const jsonBinData = getJSONBinData();
- if (jsonBinData) {
- saveVariablesFromJSONBin(jsonBinData);
- updateLocalStorageVariables(jsonBinData);
- }
- }
-
- // Log the device type to the console
- console.log('Device Type:', isMobile ? 'Mobile' : 'Computer');
- }
-
- //------------------------------------------------------------------------ Uncomment if Asura changes Domain name again.
-
- function updateLastChaptersDomain() {
- const oldDomain = "https://asuracomics.com/";
- const newDomain = "https://asuracomics.gg/";
-
- const lastChapters = JSON.parse(localStorage.getItem("last-chapter"));
- if (lastChapters) {
- for (const mangaId in lastChapters) {
- if (lastChapters.hasOwnProperty(mangaId)) {
- const oldUrl = lastChapters[mangaId];
- if (oldUrl.startsWith(oldDomain)) {
- const newUrl = oldUrl.replace(oldDomain, newDomain);
- lastChapters[mangaId] = newUrl;
- }
- }
- }
- localStorage.setItem("last-chapter", JSON.stringify(lastChapters));
- console.log("Last chapters updated to the new domain.");
- } else {
- console.log("No last chapters found.");
- }
- }
-
- // Call the function to update the URLs
- updateLastChaptersDomain();
-
- function updateLastChaptersDomainif() {
- const oldDomain = "https://asuracomics.gg/";
- const newDomain = "https://asuratoon.com/";
-
- const lastChapters = JSON.parse(localStorage.getItem("last-chapter"));
- if (lastChapters) {
- for (const mangaId in lastChapters) {
- if (lastChapters.hasOwnProperty(mangaId)) {
- const oldUrl = lastChapters[mangaId];
- if (oldUrl.startsWith(oldDomain)) {
- const newUrl = oldUrl.replace(oldDomain, newDomain);
- lastChapters[mangaId] = newUrl;
- }
- }
- }
- localStorage.setItem("last-chapter", JSON.stringify(lastChapters));
- console.log("Last chapters updated to the new domain.");
- } else {
- console.log("No last chapters found.");
- }
- }
-
- // Call the function to update the URLs
- updateLastChaptersDomainif();
- //-----------------------------------------------------------------------------
-
- // Wait for the page to load
- window.addEventListener('load', async function () {
- // Fetch JSONBin data and save variables
- const jsonBinData = getJSONBinData();
- if (jsonBinData) {
- saveVariablesFromJSONBin(jsonBinData);
- }
-
- // Check if it's a manga page and display the last read chapter if available
- if (window.location.pathname.startsWith('/manga/')) {
- displayLastReadChapter();
- }
- });
-
- // Function to fetch JSONBin data
- function getJSONBinData() {
- const request = new XMLHttpRequest();
- request.open('GET', `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`, false);
-
- request.send(null);
- if (request.status === 200) {
- return JSON.parse(request.responseText);
- }
- return null;
- }
-
- // Save variables from JSONBin to GM storage
- async function saveVariablesFromJSONBin(data) {
- const userData = data.users[await GM.getValue('username')];
- if (userData) {
- const variables = userData.variables;
- if (variables) {
- for (const key in variables) {
- if (variables.hasOwnProperty(key)) {
- await GM.setValue(key, variables[key]);
- }
- }
- }
- }
- }
-
- // Modify the saveVariablesToJSONBin function
- async function saveVariablesToJSONBin() {
- const username = await GM.getValue('username');
- const password = await GM.getValue('password');
- const originalWebsite = window.location.hostname;
- const standardizedWebsite = getStandardizedHostname(originalWebsite);
- const newVariables = {};
-
- // Add specific variables to the newVariables object
- const requiredVariables = ['last-chapter', 'bookmark', 'deviceType', 'scriptVersion'];
-
- for (const key of requiredVariables) {
- const value = localStorage.getItem(key);
- if (value !== null) {
- newVariables[key] = value;
- }
- }
-
- // Fetch existing data from JSONBin
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const responseData = JSON.parse(response.responseText);
- const userData = responseData.users[username];
- if (userData && userData.password === password) {
- // Merge newVariables with the existing variables for the standardized website
- userData.variables[standardizedWebsite] = {
- ...(userData.variables[standardizedWebsite] || {}),
- ...newVariables
- };
-
- // Save the merged data back to JSONBin
- saveUserDataToJSONBin(userData);
- } else {
- console.log('Invalid credentials or user data not found.');
- }
- }
- });
- }
-
-
- function checkAndUpdateScriptVersion() {
- const storedScriptVersion = localStorage.getItem('scriptVersion');
- if (storedScriptVersion !== scriptVersion) {
- // Script version has changed, update the stored version and save variables to JSONBin
- localStorage.setItem('scriptVersion', scriptVersion);
- }
- }
-
- // Call the function on page load
- checkAndUpdateScriptVersion();
-
- // Save user data to JSONBin
- function saveUserDataToJSONBin(userData) {
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const existingData = JSON.parse(response.responseText);
-
- // Create an empty users object if it doesn't exist
- existingData.users = existingData.users || {};
-
- // Store the user data within the users object using the username as the key
- existingData.users[userData.username] = userData;
-
- GM.xmlHttpRequest({
- method: 'PUT',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(existingData),
- });
- }
- });
- }
-
- // Save variables from the local website storage to Tampermonkey storage
- async function saveVariablesToTampermonkeyStorage() {
- const variables = await GM.getValue('variables') || {};
-
- let hasChanges = false;
- for (const key in localStorage) {
- if (localStorage.hasOwnProperty(key)) {
- if (!variables.hasOwnProperty(key) || variables[key] !== localStorage[key]) {
- variables[key] = localStorage[key];
- hasChanges = true;
- }
- }
- }
- if (hasChanges) {
- await GM.setValue('variables', variables);
- saveVariablesToJSONBin();
- }
- }
-
- // Add event listener to the bookmark <div> elements
- const bookmarkDivs = document.querySelectorAll('div.bookmark');
- bookmarkDivs.forEach(function (bookmarkDiv) {
- bookmarkDiv.addEventListener('click', function () {
- setTimeout(saveVariablesToTampermonkeyStorage, 1000); // Delay execution by 1000 milliseconds (1 second)
- });
- });
-
- //----------------------------------------------------------------------------------------------------------------------------------------------------All the Buttons
-
- // Check for element before changing the z index to 0
- const bgelement = document.querySelector("#post-225070 > div.bixbox.animefull > div.bigcontent.nobigcover");
- if (bgelement) {
- bgelement.style.zIndex = "0";
- }
-
- // Autosaving functionality
- let isAutosaveEnabled = GM.getValue('isAutosaveEnabled', false);
- let autosaveInterval = GM.getValue('autosaveInterval', null);
-
- // Function to toggle autosaving with persistence across webpages
- async function toggleAutosave() {
- const button = document.getElementById('autosaveButton');
- if (isAutosaveEnabled) {
- clearInterval(autosaveInterval);
- isAutosaveEnabled = false;
- await GM.setValue('isAutosaveEnabled', false);
- await GM.setValue('autosaveInterval', null); // Clear the autosaveInterval on disable
- button.textContent = 'Toggle Autosave (Off)';
- console.log('Autosaving disabled.');
- } else {
- await GM.setValue('autosaveInterval', autosaveInterval);
- const currentTime = new Date().getTime();
- localStorage.setItem('autosaveStartTime', currentTime);
- autosaveInterval = setInterval(async function() {
- const startTime = parseInt(localStorage.getItem('autosaveStartTime'));
- const elapsedTime = (new Date().getTime() - startTime) / 1000;
- const remainingTime = 180 - elapsedTime; // Change autosave interval to 3 minutes
- localStorage.setItem('autosaveCountdown', remainingTime);
- if (remainingTime <= 0) {
- saveVariablesToJSONBin();
- saveVariablesToTampermonkeyStorage();
- localStorage.removeItem('autosaveStartTime');
- localStorage.removeItem('autosaveCountdown');
- localStorage.setItem('autosaveStartTime', new Date().getTime()); // Start another countdown from the last autosave
- console.log('Autosaved at: ' + new Date().toLocaleString()); // Log the time of the last save
- }
- }, 1000); // Update countdown every second
- isAutosaveEnabled = true;
- await GM.setValue('isAutosaveEnabled', true); // Save the state to Tampermonkey storage
- button.textContent = 'Toggle Autosave (On)';
- console.log('Autosaving enabled.');
- }
- }
-
- // Create a container for the buttons and style it
- const menuContainer = document.createElement('div');
- menuContainer.id = 'menuContainer';
- menuContainer.classList.add('menu-container'); // Add a CSS class for styling
- // Function to check if autosave is enabled and return a string for button text
- async function checkAutosaveStatus() {
- const isAutosaveEnabled = await GM.getValue('isAutosaveEnabled', false);
- return isAutosaveEnabled ? 'Toggle Autosave (On)' : 'Toggle Autosave (Off)';
- }
-
- // Create "Save" button
- const saveButton = document.createElement('button');
- saveButton.id = 'saveButton';
- saveButton.textContent = 'Save';
- saveButton.classList.add('normal-button');
- saveButton.addEventListener('click', handleSaveButtonClick);
- saveButton.addEventListener('mouseover', function() {
- saveButton.style.opacity = '0.5'; // Dim the button when hovered over
- });
- saveButton.addEventListener('mouseout', function() {
- saveButton.style.opacity = '1'; // Restore the button's normal opacity
- });
- menuContainer.appendChild(saveButton);
-
- // Create "Get Bookmarks" button
- const getBookmarksButton = document.createElement('button');
- getBookmarksButton.id = 'getBookmarksButton';
- getBookmarksButton.textContent = 'Get Bookmarks';
- getBookmarksButton.classList.add('normal-button');
- getBookmarksButton.addEventListener('click', handleGetBookmarksButtonClick);
- getBookmarksButton.addEventListener('mouseover', function() {
- getBookmarksButton.style.opacity = '0.5'; // Dim the button when hovered over
- });
- getBookmarksButton.addEventListener('mouseout', function() {
- getBookmarksButton.style.opacity = '1'; // Restore the button's normal opacity
- });
- menuContainer.appendChild(getBookmarksButton);
-
- // Create "Autosave" button
- const autosaveButton = document.createElement('button');
- autosaveButton.id = 'autosaveButton';
- (async () => {
- autosaveButton.textContent = await checkAutosaveStatus();
- })();
- autosaveButton.classList.add('normal-button');
- autosaveButton.addEventListener('click', toggleAutosave);
- autosaveButton.addEventListener('mouseover', function() {
- autosaveButton.style.opacity = '0.5'; // Dim the button when hovered over
- });
- autosaveButton.addEventListener('mouseout', function() {
- autosaveButton.style.opacity = '1'; // Restore the button's normal opacity
- });
- menuContainer.appendChild(autosaveButton);
- // Append the menu container to the document body
- document.body.appendChild(menuContainer);
-
- // Create a dropdown button
- const dropdownButton = document.createElement('button');
- dropdownButton.id = 'dropdownButton';
- dropdownButton.textContent = 'Menu ▼'; // ▼ is a down arrow character
- dropdownButton.classList.add('menu-button', 'fixed-button'); // Add CSS classes for styling
-
- // Append the dropdown button to the menu container
- menuContainer.appendChild(dropdownButton);
-
- // Toggle the visibility of the menu when the dropdown button is clicked
- dropdownButton.addEventListener('click', function () {
- if (menuContainer.style.display === 'none') {
- menuContainer.style.display = 'block';
- } else {
- menuContainer.style.display = 'none';
- }
- });
-
- // Position the menu container in the bottom right corner
- menuContainer.style.position = 'fixed';
- menuContainer.style.bottom = '20px';
- menuContainer.style.right = '20px';
-
- const logoutButton = document.createElement('button');
- logoutButton.id = 'logoutButton';
- logoutButton.textContent = 'Logout';
- logoutButton.style.cssText = 'background-color: rgb(145,63,226); border: 1px solid #ccc; border-radius: 3px; color: #fff; cursor: pointer; font-size: 12px; font-weight: bold; padding: 5px; position: fixed; bottom: 10px; right: 10px;';
- logoutButton.classList.add('normal-button');
- logoutButton.addEventListener('click', logout);
- logoutButton.addEventListener('mouseover', () => logoutButton.style.opacity = '0.5');
- logoutButton.addEventListener('mouseout', () => logoutButton.style.opacity = '1');
-
- // Append the logout button to the menu container
- menuContainer.appendChild(logoutButton);
-
- // Append the dropdown button to the document body
- document.body.appendChild(dropdownButton);
-
- // Apply CSS styles to the buttons and container
- const style = document.createElement('style');
- style.textContent = `
- .menu-container {
- position: fixed;
- bottom: 20px;
- left: 20px;
- display: none;
- flex-direction: column;
- align-items: flex-start;
- }
-
- .menu-button {
- background-color: rgb(145, 63, 226);
- border: 1px solid #ccc;
- border-radius: 3px;
- color: #fff;
- opacity: 0.33;
- cursor: pointer;
- font-family: 'Open Sans, sans-serif';
- font-size: 9.15px;
- font-weight: bold;
- padding: 5px;
- margin: 5px 0;
- }
-
- .normal-button {
- background-color: rgb(145, 63, 226);
- border: 1px solid #ccc;
- border-radius: 3px;
- color: #fff;
- cursor: pointer;
- font-family: 'Open Sans, sans-serif';
- font-size: 12px;
- font-weight: bold;
- padding: 5px;
- margin: 5px 0;
- transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
- }
-
- .menu-button:hover {
- opacity: 0.8;
- }
- .fixed-button {
- position: fixed;
- top: 20px; /* Adjust top position as needed */
- left: 20px; /* Adjust left position as needed */
- }
-
- /* Additional styles for the fixed button */
- .fixed-button {
- z-index: 999; /* Ensure the button is on top of other elements */
- }
- `;
-
- document.head.appendChild(style);
-
- // Event handler for "Save" button
- async function handleSaveButtonClick() {
- const username = await GM.getValue('username');
- const password = await GM.getValue('password');
-
- if (username && password) {
- // Save the script version along with other variables
- localStorage.setItem('scriptVersion', scriptVersion);
- saveVariablesToTampermonkeyStorage();
- saveVariablesToJSONBin(username, password);
- } else {
- console.log('User is not logged in.');
- }
- }
-
- async function handleGetBookmarksButtonClick() {
- const jsonBinData = getJSONBinData();
- if (jsonBinData) {
- const originalWebsite = window.location.hostname;
- const standardizedWebsite = getStandardizedHostname(originalWebsite);
- const userData = jsonBinData.users[await GM.getValue('username')];
- if (userData && userData.variables && userData.variables[standardizedWebsite]) {
- const variables = userData.variables[standardizedWebsite];
- updateLocalStorageVariables(variables);
- }
- }
- }
-
- function updateLocalStorageVariables(variables) {
- for (const key in variables) {
- if (variables.hasOwnProperty(key)) {
- // Check if the value is an object and convert it to a string
- const value = typeof variables[key] === 'object' ? JSON.stringify(variables[key]) : variables[key];
- localStorage.setItem(key, value);
- }
- }
- }
-
- function displayLastReadChapter() {
- const mangaNameElement = document.querySelector('h1.entry-title');
- if (!mangaNameElement) {
- console.log('Manga name element not found.');
- return;
- }
-
- const displayedMangaName = mangaNameElement.innerText.trim().toLowerCase().replace(/[^a-z0-9]/g, '');
- const lastChapter = JSON.parse(localStorage.getItem('last-chapter'));
- const currentAsuraDomain = getCurrentAsuraDomain();
-
- if (lastChapter && currentAsuraDomain) {
- for (const mangaId in lastChapter) {
- let storedChapterUrl = lastChapter[mangaId];
- const storedMangaNameMatch = storedChapterUrl.match(/\/(\d+-)?([^/]+)-chapter-\d+\//);
- if (storedMangaNameMatch) {
- const storedMangaName = storedMangaNameMatch[2].replace(/-/g, '').toLowerCase();
-
- console.log('Displayed Manga Name:', displayedMangaName);
- console.log('Stored Manga Name:', storedMangaName);
-
- if (displayedMangaName === storedMangaName || isCloseMatch(displayedMangaName, storedMangaName)) {
- console.log(`Match found: Displayed Manga Name - ${displayedMangaName}, Stored Manga Name - ${storedMangaName}`);
-
- // Update the stored URL with the current Asura domain
- const storedUrlObj = new URL(storedChapterUrl);
- storedUrlObj.hostname = currentAsuraDomain;
- storedChapterUrl = storedUrlObj.toString();
-
- const lastChapterElement = document.createElement('div');
- lastChapterElement.textContent = 'Last Read Chapter: ';
- const lastChapterLink = document.createElement('a');
- lastChapterLink.href = storedChapterUrl;
- lastChapterLink.textContent = 'Chapter ' + storedChapterUrl.split('/').pop().replace(/[^0-9]/g, '');
- lastChapterElement.appendChild(lastChapterLink);
- lastChapterElement.style.position = 'fixed';
- lastChapterElement.style.top = '50%';
- lastChapterElement.style.right = '10px';
- lastChapterElement.style.transform = 'translateY(-50%)';
- lastChapterElement.style.backgroundColor = 'rgb(145,63,226)';
- lastChapterElement.style.color = '#fff';
- lastChapterElement.style.padding = '5px';
- lastChapterElement.style.fontFamily = 'Open Sans, sans-serif';
- lastChapterElement.style.fontSize = '12px';
- lastChapterElement.style.fontWeight = 'bold';
- document.body.appendChild(lastChapterElement);
-
- return; // Stop looping after finding a match
- }
- }
- }
- }
- console.log('No last read chapter found for the manga.');
- }
-
-
- function levenshteinDistance(a, b) {
- const matrix = [];
-
- let i;
- for (i = 0; i <= b.length; i++) {
- matrix[i] = [i];
- }
-
- let j;
- for (j = 0; j <= a.length; j++) {
- matrix[0][j] = j;
- }
-
- for (i = 1; i <= b.length; i++) {
- for (j = 1; j <= a.length; j++) {
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
- matrix[i][j] = matrix[i - 1][j - 1];
- } else {
- matrix[i][j] = Math.min(
- matrix[i - 1][j - 1] + 1,
- Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)
- );
- }
- }
- }
-
- return matrix[b.length][a.length];
- }
-
- function isCloseMatch(displayedMangaName, storedMangaName) {
- const similarityThreshold = 5; // Adjust this threshold as needed
- const distance = levenshteinDistance(displayedMangaName.toLowerCase(), storedMangaName.toLowerCase());
-
- return distance <= similarityThreshold;
- }
-
- // Test the function
- const displayedName = "One Piece";
- const storedName = "One Pece";
- console.log(isCloseMatch(displayedName, storedName)); // Should return true or false based on the threshold
-
- async function logout() {
- // Clear Tampermonkey storage
- await GM.setValue('username', '');
- await GM.setValue('password', '');
- await GM.setValue('variables', {});
-
- // Clear website's local storage
- localStorage.clear();
-
- // Refresh the page to log out
- location.reload();
- }
-
- function saveUserCredentialsToJSONBin(username, password) {
- // Validate that both username and password are not null or empty
- if (!username || !password) {
- console.log('Username and password cannot be empty.');
- alert('Username and Password cannot be empty. Please refresh the page or click the logout button to try again.');
- return; // Exit the function if either is empty
- }
-
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: async function (response) {
- const existingData = JSON.parse(response.responseText);
- let isUsernameFound = false;
- let isPasswordCorrect = false;
- let isUsernameTaken = false;
- for (const user in existingData.users) {
- if (existingData.users.hasOwnProperty(user)) {
- if (existingData.users[user].username === username) {
- isUsernameFound = true;
- if (existingData.users[user].password === password) {
- isPasswordCorrect = true;
- }
- break;
- }
- }
- }
- if (isUsernameFound && isPasswordCorrect) {
- console.log('User logged in successfully.');
-
- // Replace variables in local storage with the ones from JSONBin
- const jsonBinData = getJSONBinData();
- if (jsonBinData) {
- const website = window.location.hostname;
- const userData = jsonBinData.users[await GM.getValue('username')];
- if (userData && userData.variables && userData.variables[website]) {
- const variables = userData.variables[website];
- updateLocalStorageVariables(variables);
-
- //-------------
- // Select the element you want to refresh
- const elementToRefresh = document.querySelector("#bookmark-pool");
-
- // Create a new XMLHttpRequest object
- const xhr = new XMLHttpRequest();
-
- // Set up the AJAX request
- xhr.open("GET", "https://asuratoon.com/bookmark/ #bookmark-pool", true);
-
- // Define the callback function to handle the AJAX response
- xhr.onload = function () {
- if (xhr.status === 200) {
- // Create a temporary element to hold the response
- const tempElement = document.createElement("div");
- tempElement.innerHTML = xhr.responseText;
-
- // Find the specific part of the response
- const refreshedElement = tempElement.querySelector("#bookmark-pool");
-
- // Replace the content of the element with the refreshed content and its children
- elementToRefresh.innerHTML = refreshedElement.innerHTML;
-
- // Append the refreshed children to the element
- while (refreshedElement.firstChild) {
- elementToRefresh.appendChild(refreshedElement.firstChild);
- }
- }
- };
-
- // Send the AJAX request
- xhr.send();
- //------------
- }
- }
- } else if (!isUsernameFound || !isPasswordCorrect) {
- for (const user in existingData.users) {
- if (existingData.users.hasOwnProperty(user)) {
- if (existingData.users[user].username === username) {
- isUsernameTaken = true;
- break;
- }
- }
- }
- if (!isUsernameTaken) {
- const userData = {
- username: username,
- password: password,
- variables: {} // Initialize variables object
- };
- const mergedData = {
- ...existingData,
- users: {
- ...existingData.users,
- [username]: userData
- }
- };
- GM.xmlHttpRequest({
- method: 'PUT',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(mergedData),
- onload: async function (response) {
- // Update Tampermonkey storage with the variables from the local website storage
- const variables = {};
- for (const key in localStorage) {
- if (localStorage.hasOwnProperty(key)) {
- variables[key] = localStorage[key];
- }
- }
- await GM.setValue('username', username);
- await GM.setValue('password', password);
- await GM.setValue('variables', variables);
- saveVariablesToJSONBin();
- }
- });
- } else {
- const newUsername = prompt('That username is already taken. Please enter a new username:');
- saveUserCredentialsToJSONBin(newUsername, password);
- }
- }
- }
- });
- }
-
- function checkUserCredentialsInJSONBin(username, password) {
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const existingData = JSON.parse(response.responseText);
- let isUserFound = false;
- let isPasswordCorrect = false;
- let userData = {};
- for (const user in existingData.users) {
- if (existingData.users[user].username === username) {
- isUserFound = true;
- userData = existingData.users[user];
- if (userData.password === password) {
- isPasswordCorrect = true;
- }
- break;
- }
- }
- if (isUserFound && isPasswordCorrect) {
- console.log('User logged in successfully.');
- // Insert your login code here
- } else if (isUserFound && !isPasswordCorrect) {
- const newPassword = prompt('Incorrect password. Please enter a new password:');
- checkUserCredentialsInJSONBin(username, newPassword);
- } else {
- const newUsername = prompt('That username is not found. Please enter a new username:');
- checkUserCredentialsInJSONBin(newUsername, password);
- }
- }
- });
- }
-
- function saveBookmarkToJSONBin(username, password, title) {
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const existingData = JSON.parse(response.responseText);
- const userData = existingData.users[username];
- if (userData && userData.password === password) {
- // Add the bookmark to the user's bookmarks
- if (!userData.bookmarks) {
- userData.bookmarks = [];
- }
- userData.bookmarks.push(title);
- const mergedData = {
- ...existingData,
- users: {
- ...existingData.users,
- [username]: userData
- }
- };
- GM.xmlHttpRequest({
- method: 'PUT',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(mergedData),
-
- });
- } else {
- console.log('Invalid credentials or user data not found.');
- }
- }
- });
- }
-
- function removeBookmarkFromJSONBin(username, password, title) {
- GM.xmlHttpRequest({
- method: 'GET',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {},
- onload: function (response) {
- const existingData = JSON.parse(response.responseText);
- const userData = existingData.users[username];
- if (userData.password === password) {
- // Remove the bookmark from the user's bookmarks
- userData.bookmarks = userData.bookmarks.filter(bookmark => bookmark !== title);
- const mergedData = {
- ...existingData,
- users: {
- ...existingData.users,
- [username]: userData
- }
- };
- GM.xmlHttpRequest({
- method: 'PUT',
- url: `https://api.jsonstorage.net/v1/json/d206ce58-9543-48db-a5e4-997cfc745ef3/b4e88aaa-780e-4e6f-9500-c7fbf655aa67?apiKey=a5e587f8-a940-42e4-bc68-cc17f8f0468c`,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(mergedData),
-
- });
- }
- }
- });
- }
-
- function handleClick() {
- const navElement = document.getElementById('main-menu');
-
- if (navElement.style.display === 'none') {
- navElement.style.display = 'block';
- } else {
- navElement.style.display = 'none';
- }
- }
-
- const iconElement = document.querySelector('.fa-bars');
-
- // Add your own click event listener with useCapture set to true
- iconElement.addEventListener('click', function() {
- if (iconElement.style.display === 'none') {
- handleClick();
- }
- }, true);
-
- // Code below is derived from 'Itsnotlupus' as they are using the MIT license I am allowed to appropriate their code into my system. Thank you for understanding this and if you have any issue with this email me at 'kidkoolstar@GMail.com' or comment on the script.
-
- addStyles(`
- /* remove ads and blank space between images were ads would have been */
- [class^="ai-viewport"], .code-block, .blox, .kln, [id^="teaser"] {
- display: none !important;
- }
-
- /* hide various header and footer content. */
- .socialts, .chdesc, .chaptertags, .postarea >#comments, .postbody>article>#comments {
- display: none;
- }
-
- /* style a custom button to expand collapsed footer areas */
- button.expand {
- float: right;
- border: 0;
- border-radius: 20px;
- padding: 2px 15px;
- font-size: 13px;
- line-height: 25px;
- background: #333;
- color: #888;
- font-weight: bold;
- cursor: pointer;
- }
- button.expand:hover {
- background: #444;
- }
-
- /* disable builtin drag behavior to allow drag scrolling */
- * {
- user-select: none;
- -webkit-user-drag: none;
- }
- body.drag {
- cursor: grabbing;
- }
-
- /* add a badge on bookmark items showing the number of unread chapters */
- .unread-badge {
- position: absolute;
- top: 0;
- right: 0;
- z-index: 9999;
- display: block;
- padding: 2px;
- margin: 5px;
- border: 1px solid #0005b1;
- border-radius: 12px;
- background: #ffc700;
- color: #0005b1;
- font-weight: bold;
- font-family: cursive;
- transform: rotate(10deg);
- width: 24px;
- height: 24px;
- line-height: 18px;
- text-align: center;
- }
- .soralist .unread-badge {
- position: initial;
- display: inline-block;
- zoom: 0.8;
- }
- `);
-
- function makeCollapsedFooter({ label, section }) {
- const elt = crel('div', {
- className: 'bixbox',
- style: 'padding: 8px 15px'
- }, crel('button', {
- className: 'expand',
- textContent: label,
- onclick() {
- section.style.display = 'block';
- elt.style.display = 'none';
- }
- }));
- section.parentElement.insertBefore(elt, section);
- }
- // 1. collapse related series.
- const related = $$$("//span[text()='Related Series']/../../..")[0];
- if (related) {
- makeCollapsedFooter({label: 'Show Related Series', section: related});
- related.style.display = 'none';
- }
- // 2. collapse comments.
- const comments = $`#comments`;
- if (comments) makeCollapsedFooter({label: 'Show Comments', section: comments});
-
- //------------- What I wrote resumes Here
-
- (function () {
- 'use strict';
-
- // Function to store chapter data
- function storeChapterData() {
- var chapter = window.location.href.split('/').pop().split('-').pop();
- var chapter_id = document.querySelector('link[rel="shortlink"]').href.split('=').pop();
- var manga_id = JSON.parse(localStorage.getItem('bm_history'))[chapter_id].manga_ID;
-
- var last_chapter = JSON.parse(localStorage.getItem('last-chapter')) || {};
-
- // Store the standardized URL
- const standardizedUrl = new URL(window.location.href);
- standardizedUrl.hostname = getStandardizedHostname(standardizedUrl.hostname);
- last_chapter[manga_id] = standardizedUrl.toString();
-
- localStorage.setItem('last-chapter', JSON.stringify(last_chapter));
- }
- // Return if url is not a chapter
- if (
- window.location.href === 'https://asuracomics.com//bookmarks/' ||
- window.location.href.startsWith('https://asuracomics.com//manga/')
- ) {
- return;
- }
-
- // Create a mutation observer to watch for changes in the URL
- const observer = new MutationObserver(() => {
- storeChapterData();
- });
-
- // Observe changes in the URL
- observer.observe(document.documentElement, { childList: true, subtree: true });
-
- // Call the function on page load
- storeChapterData();
- })();