// ==UserScript== // @name Damoang Profile Modifier with MutationObserver // @namespace http://tampermonkey.net/ // @version 1.3 // @description Modify profile icon based on member level with MutationObserver and caching // @author ChatGPT // @match https://damoang.net/* // @grant none // ==/UserScript== (function() { 'use strict'; function fetchImage(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'blob'; xhr.onload = function() { if (xhr.status === 200) { const reader = new FileReader(); reader.onloadend = function() { resolve(reader.result); }; reader.readAsDataURL(xhr.response); } else { reject(Error('Image fetch failed')); } }; xhr.onerror = function() { reject(Error('Network error')); }; xhr.send(); }); } function processProfileLinks(profileLinks) { profileLinks.forEach(async (link) => { const xpIcon = link.querySelector('.xp-icon'); if (xpIcon) { const memberLevelIcon = xpIcon.getAttribute('data-member-level-icon'); if (memberLevelIcon) { const cacheKey = `profile_img_${memberLevelIcon}`; let imgSrc = localStorage.getItem(cacheKey); if (!imgSrc) { const imgUrl = `https://damoang.net/plugin/nariya/skin/level/playonly/${memberLevelIcon}.svg?4`; try { imgSrc = await fetchImage(imgUrl); localStorage.setItem(cacheKey, imgSrc); } catch (error) { console.error('Failed to fetch image:', error); return; } } const newImgTag = ``; const profileImg = link.querySelector('.profile_img'); if (!profileImg.previousElementSibling || profileImg.previousElementSibling.tagName !== 'IMG') { profileImg.insertAdjacentHTML('beforebegin', newImgTag); } } } }); } // 초기 프로필 링크 처리 const initialProfileLinks = document.querySelectorAll('a.sv_member'); processProfileLinks(initialProfileLinks); // MutationObserver를 사용하여 DOM 변경 감지 const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' && mutation.addedNodes.length) { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { // ELEMENT_NODE const newProfileLinks = node.querySelectorAll ? node.querySelectorAll('a.sv_member') : []; if (newProfileLinks.length) { processProfileLinks(newProfileLinks); } } }); } } }); observer.observe(document.body, { childList: true, subtree: true }); })();