// ==UserScript== // @name Instagram - Add notes to the user // @name:zh-CN Instagram - 为用户添加备注(别名/标签) // @name:zh-TW Instagram - 為使用者新增備註(別名/標籤) // @namespace https://greasyfork.org/zh-CN/users/193133-pana // @homepage https://greasyfork.org/zh-CN/users/193133-pana // @icon data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+ // @version 6.0.5 // @description Add notes (aliases/tags) for users to help identify and search // @description:zh-CN 为用户添加备注(别名/标签)功能,以帮助识别和搜索 // @description:zh-TW 為使用者新增備註(別名/標籤)功能,以幫助識別和搜尋 // @license GNU General Public License v3.0 or later // @compatible chrome // @compatible firefox // @author pana // @match *://*.instagram.com/* // @require https://gcore.jsdelivr.net/npm/arrive@2.4.1/minified/arrive.min.js // @require https://gcore.jsdelivr.net/gh/LightAPIs/greasy-fork-library@1b596ab3b97d13e3aa41dcdad1870b65944fda4d/Note_Obj.js // @noframes // @grant GM_info // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_openInTab // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @downloadURL none // ==/UserScript== (function () { 'use strict'; const UPDATED = '2023-02-27'; const INS_ICON = { NOTE_BLACK: 'url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2IoMzgsIDM4LCAzOCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYigzOCwgMzgsIDM4KSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)', }; const INS_STYLE = ` .note-obj-ins-background-box { display: inline-block; align-items: center; white-space: nowrap; border-radius: 50px; padding: 0px 10px; background-color: #336699; color: #fff; } .note-obj-ins-add-btn { background-image: ${INS_ICON.NOTE_BLACK}; background-size: 24px; background-repeat: no-repeat; background-position: center; margin-left: 5px; cursor: pointer; width: 24px; height: 24px; } .note-obj-ins-homepage-btn { margin: 6px !important; } .note-obj-ins-homepage-btn:hover { opacity: 0.5; } .note-obj-ins-userpage-btn { margin-top: 2px; } .note-obj-ins-userpage-tag { display: block; font-size: 20px; margin-bottom: 20px; white-space: nowrap; } .note-obj-ins-font-bold { font-weight: bold; } .note-obj-veryins-blue-tag { background-color: #3c81df; color: #fff; display: inline-flex; align-items: center; padding: 0px 10px; white-space: nowrap; line-height: 100%; border-radius: 50px; padding: 2px 10px; } .note-obj-veryins-userpage-btn { display: inline-block; vertical-align: middle; } .note-obj-settings-frame-card label { color: #2f2f2f; } .note-obj-interface-dark .note-obj-settings-frame-card label { color: #fff; } .note-obj-group-frame-tbody input { color: #000; }`; const selector = { homepage: { article: '[role="main"] article', id: '._aaqt a', icon: 'span._aamz', commentId: '._ab8x .xt0psk2 a.notranslate', commentAt: '._ab8x ._aacl a.notranslate', }, homepageStories: { id: '._aad6', idShell: 'li [role="menuitem"]', }, homepageRecommend: { id: '._aak3 ._ab8x .xt0psk2 a', }, userPage: { frame: '._aa_y', id: 'h2', bar: '.x8j4wrb', box: 'ul', common: 'span._aaai', suggest: '._acj1 a.notranslate', infoAt: '.notranslate', userName: '._aa_c > span', }, watchList: { initialItem: '[role="dialog"] [aria-labelledby]', laterItem: '._aaei', id: '._ab8w a.notranslate', }, stories: { id: 'a.notranslate', idShell: '._ac0o', cardId: '._afgd ._aacw', }, dialog: { frame: '[role="dialog"] article', commentId: '._a9zc ._ab8w a, ._aacx._aacu a', commentAt: '._a9zs .notranslate', }, request: { follow: '._aajc ._ab8x .xt0psk2 a', }, suggest: { user: '._aa0- ._ab8x .xt0psk2 a', }, }; const noteObj = new Note_Obj({ id: 'myInstagramNote', script: { author: { name: 'pana', homepage: 'https://greasyfork.org/zh-CN/users/193133-pana', }, url: 'https://greasyfork.org/scripts/387871', updated: UPDATED, library: [ { name: 'arrive.js', version: '2.4.1', url: 'https://github.com/uzairfarooq/arrive', }, ], }, style: INS_STYLE, primaryColor: '#336699', settings: { replaceHomepageID: { type: 'checkbox', lang: { en: 'Allow replacing user IDs on the home page', zhHans: '允许替换首页上的用户 ID', zhHant: '允許替換首頁上的使用者 ID', }, default: true, event: instagramHomepageEvent, }, }, changeEvent: instagramChangeEvent, }); function homepageNote(ele, changeId) { const user = Note_Obj.fn.queryAnchor(ele, selector.homepage.id); if (user) { const replaceHomepageID = noteObj.getOtherConfig().replaceHomepageID === true; const eleId = Note_Obj.fn.getIdFromUrl(user.href); if (!changeId || changeId === eleId) { noteObj.handler(eleId, user, undefined, { add: replaceHomepageID ? undefined : 'sapn', className: replaceHomepageID ? undefined : ['note-obj-ins-background-box'], }); } } } function homepageCommentNote(ele, changeId) { for (const comment of Note_Obj.fn.queryAllAnchor(ele, selector.homepage.commentId, 'info')) { const commentId = Note_Obj.fn.getIdFromUrl(comment.href); if (!changeId || changeId === commentId) { noteObj.handler(commentId, comment); } } } function homepageCommentAtNote(ele, changeId) { const commentAtId = Note_Obj.fn.getIdFromUrl(ele.href); if (!changeId || changeId === commentAtId) { noteObj.handler(commentAtId, ele, undefined, { prefix: '@', title: true, }); } } function dialogCommentNote(ele, chagneId) { const picCommentId = Note_Obj.fn.getIdFromUrl(ele.href); if (!chagneId || chagneId === picCommentId) { noteObj.handler(picCommentId, ele); } } function dialogCommentAtNote(ele, changeId) { if (!ele.classList.contains(selector.homepage.commentId.replace(/^\.|\s+.*$/g, ''))) { const picCommentAtId = Note_Obj.fn.getIdFromUrl(ele.href); if (!changeId || changeId === picCommentAtId) { noteObj.handler(picCommentAtId, ele, undefined, { prefix: '@', title: true, }); } } } function homepageStoriesNote(ele, changeId) { const homepageStoriesId = Note_Obj.fn.getText(ele, selector.homepageStories.id); if (!changeId || changeId === homepageStoriesId) { ele.title = noteObj.getUserTag(homepageStoriesId); } } function anchorElementNote(ele, changeId) { const itemId = Note_Obj.fn.getIdFromUrl(ele.href); if (!changeId || changeId === itemId) { noteObj.handler(itemId, ele); } } function userPageNote(ele, changeId) { const userPageId = Note_Obj.fn.getText(ele, selector.userPage.id); const userPageBox = Note_Obj.fn.query(ele, selector.userPage.box); if (userPageId && userPageBox) { if (changeId) { if (changeId === userPageId) { noteObj.handler(userPageId, ele, undefined, { add: 'div', after: userPageBox, maskSecondaryColor: true, offsetWidth: -20, className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold'], }); } } else { const userNameText = Note_Obj.fn.getText(ele, selector.userPage.userName, 'warn'); noteObj.handler(userPageId, ele, undefined, { add: 'div', after: userPageBox, maskSecondaryColor: true, offsetHeight: -20, className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold'], }, userNameText); } } } function userPageCommonNote(ele, changeId) { for (const commonUser of Note_Obj.fn.queryAll(ele, selector.userPage.common, 'info')) { const commonUserId = commonUser.textContent?.trim(); if (commonUserId) { if (!changeId || changeId === commonUserId) noteObj.handler(commonUserId, commonUser, undefined, { title: true, notModify: true, }); } } } function userPageInfoAtNote(ele, changeId) { for (const infoAtUser of Note_Obj.fn.queryAllAnchor(ele, selector.userPage.infoAt, 'info')) { const infoAtUserId = Note_Obj.fn.getIdFromUrl(infoAtUser.href); if (!changeId || changeId === infoAtUserId) { noteObj.handler(infoAtUserId, infoAtUser, undefined, { prefix: '@', title: true, }); } } } function storiesNote(ele, changeId) { itemNote(ele, selector.stories.id, changeId); Note_Obj.fn.docQueryAll(selector.stories.cardId).forEach(item => { const itemId = item.textContent?.trim() || ''; if (!changeId || changeId === itemId) { noteObj.handler(itemId, item, undefined, { notModify: true, title: true, }); } }); } function watchListItemNote(ele, changeId) { itemNote(ele, selector.watchList.id, changeId); } function itemNote(ele, idSelector, changeId) { const item = Note_Obj.fn.queryAnchor(ele, idSelector); if (item) { const itemId = Note_Obj.fn.getIdFromUrl(item.href); if (!changeId || changeId === itemId) { noteObj.handler(itemId, item); } } } function instagramChangeEvent(changeId) { for (const article of Note_Obj.fn.docQueryAll(selector.homepage.article, 'none')) { homepageNote(article, changeId); homepageCommentNote(article, changeId); for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.homepage.commentAt, 'none')) { homepageCommentAtNote(commentAt, changeId); } for (const picCommentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) { dialogCommentNote(picCommentUser, changeId); } for (const picCommentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) { dialogCommentAtNote(picCommentAt, changeId); } } for (const homepageStories of Note_Obj.fn.docQueryAll(selector.homepageStories.idShell, 'none')) { homepageStoriesNote(homepageStories, changeId); } for (const homepageRecommend of Note_Obj.fn.docQueryAllAnchor(selector.homepageRecommend.id, 'none')) { anchorElementNote(homepageRecommend, changeId); } for (const userPage of Note_Obj.fn.docQueryAll(selector.userPage.frame, 'none')) { userPageNote(userPage, changeId); userPageCommonNote(userPage, changeId); userPageInfoAtNote(userPage, changeId); } for (const storiesShell of Note_Obj.fn.docQueryAll(selector.stories.idShell, 'none')) { storiesNote(storiesShell, changeId); } for (const initial of Note_Obj.fn.docQueryAll(selector.watchList.initialItem, 'none')) { watchListItemNote(initial, changeId); } for (const later of Note_Obj.fn.docQueryAll(selector.watchList.laterItem, 'none')) { watchListItemNote(later, changeId); } for (const dialog of Note_Obj.fn.docQueryAll(selector.dialog.frame, 'none')) { homepageNote(dialog, changeId); homepageCommentNote(dialog, changeId); for (const commentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) { dialogCommentNote(commentUser, changeId); } for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) { dialogCommentAtNote(commentAt, changeId); } } for (const follow of Note_Obj.fn.docQueryAllAnchor(selector.request.follow, 'none')) { anchorElementNote(follow, changeId); } for (const suggestUser of Note_Obj.fn.docQueryAllAnchor(selector.suggest.user, 'none')) { anchorElementNote(suggestUser, changeId); } for (const suggest of Note_Obj.fn.docQueryAllAnchor(selector.userPage.suggest, 'none')) { anchorElementNote(suggest, changeId); } } function instagramHomepageEvent(newValue, oldValue) { for (const article of Note_Obj.fn.docQueryAll(selector.homepage.article)) { const articleUser = Note_Obj.fn.queryAnchor(article, selector.homepage.id); if (articleUser) { const articleUserId = Note_Obj.fn.getIdFromUrl(articleUser.href); noteObj.handler(articleUserId, articleUser, undefined, { add: oldValue ? undefined : 'span', className: oldValue ? undefined : ['note-obj-ins-background-box'], title: oldValue, restore: true, }); noteObj.handler(articleUserId, articleUser, undefined, { add: newValue ? undefined : 'span', className: newValue ? undefined : ['note-obj-ins-background-box'], title: newValue, }); } } } function initInstagram() { const arriveOption = { fireOnAttributesModification: true, existing: true, }; document.body.arrive(selector.homepage.article, arriveOption, article => { const homepageIcon = Note_Obj.fn.query(article, selector.homepage.icon); const articleUserId = Note_Obj.fn.getUrlId(article, selector.homepage.id); if (homepageIcon && articleUserId) { homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(articleUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span')); } homepageNote(article); homepageCommentNote(article); article.arrive(selector.homepage.commentAt, arriveOption, commentAt => { homepageCommentAtNote(commentAt); }); article.arrive(selector.dialog.commentId, arriveOption, picCommentUser => { dialogCommentNote(picCommentUser); }); article.arrive(selector.dialog.commentAt, arriveOption, picCommentAt => { dialogCommentAtNote(picCommentAt); }); }); document.body.arrive(selector.homepageStories.idShell, arriveOption, homepageStories => { homepageStoriesNote(homepageStories); }); document.body.arrive(selector.homepageRecommend.id, arriveOption, homepageRecommend => { anchorElementNote(homepageRecommend); }); document.body.arrive(selector.userPage.frame, arriveOption, userPage => { const userPageBar = Note_Obj.fn.query(userPage, selector.userPage.bar); const userPageId = Note_Obj.fn.getText(userPage, selector.userPage.id); if (userPageBar && userPageId) { const userNameText = Note_Obj.fn.getText(userPage, selector.userPage.userName, 'info'); userPageBar.after(noteObj.createNoteBtn(userPageId, userNameText, ['note-obj-ins-add-btn', 'note-obj-ins-userpage-btn'])); } userPageNote(userPage); userPageCommonNote(userPage); userPageInfoAtNote(userPage); }); document.body.arrive(selector.stories.idShell, arriveOption, storiesShell => { storiesNote(storiesShell); const stories = Note_Obj.fn.queryAnchor(storiesShell, selector.stories.id); if (stories) { const userIdChange = new MutationObserver(() => { const newUserId = Note_Obj.fn.getIdFromUrl(stories.href); if (noteObj.judgeUsers(newUserId)) { noteObj.handler(newUserId, stories); } else { noteObj.handler(newUserId, stories, undefined, { restore: true, }); } }); userIdChange.observe(stories, { attributeFilter: ['href'], }); } }); document.body.arrive(selector.watchList.initialItem, arriveOption, initial => { watchListItemNote(initial); }); document.body.arrive(selector.watchList.laterItem, arriveOption, later => { watchListItemNote(later); }); document.body.arrive(selector.dialog.frame, arriveOption, dialog => { const homepageIcon = Note_Obj.fn.query(dialog, selector.homepage.icon); const dialogUserId = Note_Obj.fn.getUrlId(dialog, selector.homepage.id); if (homepageIcon && dialogUserId) { homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(dialogUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span')); } homepageNote(dialog); homepageCommentNote(dialog); dialog.arrive(selector.dialog.commentId, arriveOption, commentUser => { dialogCommentNote(commentUser); }); dialog.arrive(selector.dialog.commentAt, arriveOption, commentAt => { dialogCommentAtNote(commentAt); }); }); document.body.arrive(selector.request.follow, arriveOption, follow => { anchorElementNote(follow); }); document.body.arrive(selector.suggest.user, arriveOption, suggestUser => { anchorElementNote(suggestUser); }); document.body.arrive(selector.userPage.suggest, arriveOption, suggest => { anchorElementNote(suggest); }); } initInstagram(); })();