Skip to content

Instantly share code, notes, and snippets.

@antronic
Last active August 11, 2024 11:42
Show Gist options
  • Save antronic/f1dee102e69d8316bca6b654481d8713 to your computer and use it in GitHub Desktop.
Save antronic/f1dee102e69d8316bca6b654481d8713 to your computer and use it in GitHub Desktop.
Hareshi's Saved list
// ==UserScript==
// @name Hareshi-My_List
// @match https://www.hareshi.net/browse/anime*
// @include https://www.hareshi.net/browse/anime*
// @description Hareshi's Saved list
// @namespace hareshi-my-list
// @version 2024-08-11
// @author Jirachai Chansivanon
// @icon https://www.google.com/s2/favicons?sz=64&domain=hareshi.net
// @downloadURL https://raw.githubusercontent.com/antronic/hareshi-my-list/main/build/hareshi-my-list.tampermonkey.min.js
// @version 0.1
// @grant none
// ==/UserScript==
"use strict";var __classPrivateFieldSet=(this&&this.__classPrivateFieldSet)||function(receiver,state,value,kind,f){if(kind==="m")throw new TypeError("Private method is not writable");if(kind==="a"&&!f)throw new TypeError("Private accessor was defined without a setter");if(typeof state==="function"?receiver!==state||!f:!state.has(receiver))throw new TypeError("Cannot write private member to an object whose class did not declare it");return(kind==="a"?f.call(receiver,value):f?f.value=value:state.set(receiver,value)),value;};var __classPrivateFieldGet=(this&&this.__classPrivateFieldGet)||function(receiver,state,kind,f){if(kind==="a"&&!f)throw new TypeError("Private accessor was defined without a getter");if(typeof state==="function"?receiver!==state||!f:!state.has(receiver))throw new TypeError("Cannot read private member from an object whose class did not declare it");return kind==="m"?f:kind==="a"?f.call(receiver):f?f.value:state.get(receiver);};var _Observable_value;class Observable extends EventTarget{constructor(initialData){super();_Observable_value.set(this,void 0);__classPrivateFieldSet(this,_Observable_value,initialData,"f");}
update(param){if(typeof param!=='function'){__classPrivateFieldSet(this,_Observable_value,param,"f");}
if(typeof param==='function'){const updater=param;__classPrivateFieldSet(this,_Observable_value,updater(__classPrivateFieldGet(this,_Observable_value,"f")),"f");}
this.dispatchEvent(new CustomEvent('dataChanged',{detail:__classPrivateFieldGet(this,_Observable_value,"f")}));}
onChange(callback){if(typeof callback==='function'){switch(callback.length){case 1:this.addEventListener('dataChanged',()=>callback(__classPrivateFieldGet(this,_Observable_value,"f")));break;case 2:this.addEventListener('dataChanged',(event)=>callback(event,__classPrivateFieldGet(this,_Observable_value,"f")));break;default:this.addEventListener('dataChanged',()=>callback());}}}
get(){return __classPrivateFieldGet(this,_Observable_value,"f");}
toString(){return JSON.stringify(__classPrivateFieldGet(this,_Observable_value,"f"));}
toJSON(){return __classPrivateFieldGet(this,_Observable_value,"f");}}
_Observable_value=new WeakMap();const loadMyList=()=>{let animeStorage=null;function validateStructure(storageStructure){if(storageStructure===undefined){return false;}
if(!Array.isArray(storageStructure.savedList)){return false;}
return true;}
function getStorage(){const DEFAULT_STRORAGE={savedList:[]};const storage=localStorage.getItem('hareshi_animate_storage');let parsedStorage=DEFAULT_STRORAGE;if(storage!==null){try{parsedStorage=Object.assign({},JSON.parse(storage));console.log('after parsedStorage',parsedStorage);if(!validateStructure(parsedStorage)){throw new Error('Invalid storage structure');}}
catch(e){console.log(e);parsedStorage=DEFAULT_STRORAGE;}}
console.log('parsedStorage',parsedStorage);const _savedList=new Observable(parsedStorage.savedList);animeStorage=Object.assign(Object.assign({},parsedStorage),{savedList:_savedList});}
function saveStorage(){localStorage.setItem('hareshi_animate_storage',JSON.stringify(animeStorage));}
function createAnimeData(){const coverImage=document.querySelector('.info .img-cover').src;const title=document.querySelector('.info #anipop.title').innerText;const animeId=window.location.pathname.split('/')[3];const host=window.location.hostname;const path=window.location.pathname;return{title,coverImage,animeId,host,path};}
function addToSavedList(anime){if(animeStorage.savedList.get().findIndex((i)=>i.animeId===anime.animeId)>-1){console.log('Already exists');return;}
animeStorage.savedList.update((saved)=>[...saved,anime]);saveStorage();}
function removeFromSaveList(storageIndex){const pageInfo=getPageInfo();const index=storageIndex===undefined?pageInfo.storageIndex:storageIndex;animeStorage.savedList.update((saved)=>[...saved.slice(0,index),...saved.slice(index+1),]);saveStorage();}
const toolbarHeader=document.createElement('div');function injectSaveButton(){const saveToggleButton=document.createElement('BUTTON');saveToggleButton.style.background='var(--primary)';saveToggleButton.style.color='#fff';saveToggleButton.style.borderRadius='.25rem';saveToggleButton.style.marginRight='4px';const pageInfo=getPageInfo();saveToggleButton.innerText=pageInfo.isSamePage?'✅ Saved':'➕ Save';animeStorage===null||animeStorage===void 0?void 0:animeStorage.savedList.onChange(()=>{const pageInfo=getPageInfo();saveToggleButton.innerText=pageInfo.isSamePage?'✅ Saved':'➕ Save';});saveToggleButton.addEventListener('click',()=>{const pageInfo=getPageInfo();if(pageInfo.isSamePage){removeFromSaveList();}
else{addToSavedList(createAnimeData());}});const underCoverMenu=document.createElement('DIV');underCoverMenu.appendChild(saveToggleButton);toolbarHeader.appendChild(saveToggleButton);}
function injectToolbar(){const toolbar=document.createElement('div');toolbar.style.position='fixed';toolbar.style.top='72px';toolbar.style.right='16px';toolbar.style.background='var(--primary)';toolbar.style.borderRadius='.25rem';const title=document.createElement('span');title.innerText='Saved list';toolbarHeader.appendChild(title);toolbarHeader.style.padding='0px 4px';toolbar.appendChild(toolbarHeader);const savedListContainer=document.createElement('DIV');savedListContainer.style.background='#fff';savedListContainer.style.padding='2px';function renderSavedList(){const list=animeStorage.savedList.get();savedListContainer.innerHTML='';list.forEach((item,index)=>{const title=document.createElement('a');title.innerText=item.title;title.href=`${item.path}`;title.style.marginBottom='4px';const delButton=document.createElement('button');delButton.innerText='🗑️';delButton.style.border='none';delButton.style.marginRight='2px';delButton.setAttribute('index',index.toString());delButton.addEventListener('click',()=>{removeFromSaveList(index);});const animeItem=document.createElement('DIV');animeItem.appendChild(delButton);animeItem.appendChild(title);savedListContainer.appendChild(animeItem);});}
renderSavedList();toolbar.appendChild(savedListContainer);animeStorage.savedList.onChange(()=>{renderSavedList();toolbar.appendChild(savedListContainer);});document.body.appendChild(toolbar);console.log(getPageInfo());}
function buildMenu(){}
let PAGE_TYPE;(function(PAGE_TYPE){PAGE_TYPE[PAGE_TYPE["ANIME_PAGE"]=0]="ANIME_PAGE";PAGE_TYPE[PAGE_TYPE["LIST_PAGE"]=1]="LIST_PAGE";PAGE_TYPE[PAGE_TYPE["INVALID"]=2]="INVALID";})(PAGE_TYPE||(PAGE_TYPE={}));function locationChanged(callback){window.addEventListener('locationchange',()=>callback());}
function checkWeb(){if(window.location.pathname.search(/\/browse\/anime\/\d/)===0){return PAGE_TYPE.ANIME_PAGE;}
if(window.location.pathname.search('/browse/anime')===0){return PAGE_TYPE.LIST_PAGE;}
return PAGE_TYPE.INVALID;}
function getPageInfo(){const pageType=checkWeb();const animeId=pageType===PAGE_TYPE.ANIME_PAGE&&window.location.pathname.split('/').length>=3?window.location.pathname.split('/')[3]:null;const storageIndex=pageType===PAGE_TYPE.ANIME_PAGE?animeStorage.savedList.get().findIndex((i)=>i.animeId===animeId):-1;return{type:pageType,path:window.location.pathname,animeId,isSamePage:storageIndex>-1,storageIndex,};}
function injectTools(){if(checkWeb()===PAGE_TYPE.ANIME_PAGE){injectSaveButton();}
injectToolbar();}
getStorage();injectTools();};loadMyList();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment