/* ************************************************************************** */ // ==UserScript== // @name News Bookmark Instagram // @namespace vsemozhetbyt // @description News Reading from Bookmark in Instagram // @version 1 // @include https://www.instagram.com/* // @noframes // @grant none // @nocompat Chrome // ==/UserScript== /* ************************************************************************** */ 'use strict'; (() => { const d = document; const b = d.body; if (d.querySelector('[data-vmb-news-div]')) return; const funcs = [ { name: '\u221e', func: bookmarkFind, }, { name: '\u25b2', func: navToPost, args: 'getPrevPost', }, { name: '\u25bc', func: navToPost, args: 'getNextPost', }, { name: '\u00a0\u222b\u00a0', func: bookmarkSave, }, ]; const opts = { 'www.instagram.com': { additionalCSS: '[data-vmb-news-div] { display: block !important; }', idLinkSelector: 'div._mck9w._gvoze._tn0ps a[href]', getPost: nd => nd.closest('div._mck9w._gvoze._tn0ps'), getIdLink: nd => nd.querySelector('a[href]'), getBookmark: url => d.querySelector(`a[href='${url}']`), getPostgroupAncestor: nd => nd.closest('div._6d3hm._mnav9'), getPrevPost: nd => xp(`./preceding::div[${xpClass('_mck9w _gvoze _tn0ps')}][1]`, nd), getNextPost: nd => xp(`./following::div[${xpClass('_mck9w _gvoze _tn0ps')}]`, nd), scrollTop: -55, storage: IDB, siteFuncs: ['\u221e', '\u25b2', '\u25bc', '\u00a0\u222b\u00a0'], autoload: [bookmarkFind], }, }; const opt = opts[location.hostname]; if (!opt) { alert('Wrong site.'); return; } const newsSt = b.appendChild(d.createElement('style')); newsSt.setAttribute('data-vmb-news-style', ''); newsSt.textContent = ` [data-vmb-news-div] { position: fixed; top: 0px; left: 50%; z-index: 10000; padding: 2px !important; background-color: gainsboro !important; outline: 1px solid black !important; } [data-vmb-news-div] * { margin: 0px 2px !important; } ${opt.additionalCSS || ''} `; if (opt.getBookmark || opt.getPrevPost) { const bmkNavSt = b.appendChild(d.createElement('style')); bmkNavSt.setAttribute('data-vmb-bookmark-nav-style', ''); bmkNavSt.textContent = ` body { counter-reset: vmb-bookmark-nav-counter; } ${opt.idLinkSelector}:before { content: counter(vmb-bookmark-nav-counter, decimal-leading-zero)'. '; counter-increment: vmb-bookmark-nav-counter; } [data-vmb-focus], [data-vmb-focus] ${opt.idLinkSelector} { outline: 1px solid lime !important; } [data-vmb-bookmark], [data-vmb-bookmark] *, [data-vmb-bookmark] ~ *, [data-vmb-bookmark] ~ * * { background-color: gainsboro !important; } ${opt.getPostgroupAncestor ? '[data-vmb-bookmark-postgroup-ancestor] ~ *, [data-vmb-bookmark-postgroup-ancestor] ~ * * { background-color: gainsboro !important; }' : ''} `; b.addEventListener('mouseup', setFocus); } const newsDiv = d.createElement('div'); newsDiv.setAttribute('data-vmb-news-div', ''); funcs.forEach( (func) => { if (opt.siteFuncs.includes(func.name)) { const btn = newsDiv.appendChild(d.createElement('button')); btn.type = 'button'; btn.textContent = func.name; btn.addEventListener('click', () => { func.func(func.args); }); } }, ); b.appendChild(newsDiv); if (opt.autoload) opt.autoload.forEach((func) => { func(); }); function setFocus(evt) { const newFocus = opt.getPost(evt.target); if (newFocus) { const oldFocus = d.querySelector('[data-vmb-focus]'); if (oldFocus) oldFocus.removeAttribute('data-vmb-focus'); newFocus.setAttribute('data-vmb-focus', ''); } } function bookmarkFind() { opt.storage(`vmb_bookmark_${location.href}`, null, (url) => { if (!url) { alert('Bookmark not found. Save a new one.'); return; } const idLink = opt.getBookmark(url); let focus; if (idLink) focus = opt.getPost(idLink); if (focus) { const oldBookmark = d.querySelector('[data-vmb-bookmark]'); if (oldBookmark) oldBookmark.removeAttribute('data-vmb-bookmark'); focus.setAttribute('data-vmb-bookmark', ''); if (opt.getPostgroupAncestor) { const oldBookmarkAncestor = d.querySelector('[data-vmb-bookmark-postgroup-ancestor]'); if (oldBookmarkAncestor) { oldBookmarkAncestor.removeAttribute('data-vmb-bookmark-postgroup-ancestor'); } opt.getPostgroupAncestor(focus).setAttribute('data-vmb-bookmark-postgroup-ancestor', ''); } setFocus({ target: idLink }); focus.scrollIntoView(true); scrollBy(0, opt.scrollTop); } else { scrollTo(0, b.scrollHeight); } }); } function bookmarkSave() { const focus = d.querySelector('[data-vmb-focus]'); if (focus) { const idLink = opt.getIdLink(focus); if (idLink) { const oldBookmark = d.querySelector('[data-vmb-bookmark]'); if (oldBookmark) oldBookmark.removeAttribute('data-vmb-bookmark'); focus.setAttribute('data-vmb-bookmark', ''); if (opt.getPostgroupAncestor) { const oldBookmarkAncestor = d.querySelector('[data-vmb-bookmark-postgroup-ancestor]'); if (oldBookmarkAncestor) oldBookmarkAncestor.removeAttribute('data-vmb-bookmark-postgroup-ancestor'); opt.getPostgroupAncestor(focus).setAttribute('data-vmb-bookmark-postgroup-ancestor', ''); } opt.storage(`vmb_bookmark_${location.href}`, idLink.getAttribute('href')); } else { alert('Sorry. Strange post. Select another one.'); } } else { alert('Click on the post.'); } } function navToPost(dirFunk) { const focus = d.querySelector('[data-vmb-focus]'); if (focus) { const scrollPad = 50; const destPost = opt[dirFunk](focus); if (destPost) { setFocus({ target: destPost.querySelector('a[href]') }); if (opt[dirFunk](destPost)) { destPost.scrollIntoView(true); scrollBy(0, opt.scrollTop); } else if (dirFunk === 'getPrevPost') { ; } else { scrollBy(0, scrollPad); } } else if (dirFunk === 'getPrevPost') { ; } else { scrollBy(0, scrollPad); } } else { const idLink = b.querySelector(opt.idLinkSelector); setFocus({ target: idLink }); opt.getPost(idLink).scrollIntoView(true); scrollBy(0, opt.scrollTop); } } function IDB(key, value, func) { const openRequest = indexedDB.open('vmbIDBNews', 1); openRequest.onerror = (evtErr) => { console.error(evtErr.target.error); }; openRequest.onupgradeneeded = (evt) => { evt.target.result.createObjectStore('news'); }; openRequest.onsuccess = (evt) => { const db = evt.target.result; db.onerror = (evtErr) => { console.error(evtErr.target.error); db.close(); }; if (value) { db.transaction('news', 'readwrite') .objectStore('news') .put(value, key) .onsuccess = () => { db.close(); }; } else { db.transaction('news', 'readonly') .objectStore('news') .get(key) .onsuccess = (evtGet) => { func(evtGet.target.result); db.close(); }; } }; } function xp(expression, contextNode = b) { return d.evaluate( expression, contextNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null, ).singleNodeValue; } function xpClass(clsName) { return `contains(concat(" ", normalize-space(@class), " "), " ${clsName} ")`; } })();