Skip to content

Instantly share code, notes, and snippets.

@vsemozhetbyt
Last active February 6, 2018 21:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vsemozhetbyt/f26932dfc20b43dee8540e8a669989e9 to your computer and use it in GitHub Desktop.
Save vsemozhetbyt/f26932dfc20b43dee8540e8a669989e9 to your computer and use it in GitHub Desktop.
/* ************************************************************************** */
// ==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} ")`;
}
})();
@vsemozhetbyt
Copy link
Author

vsemozhetbyt commented Jan 28, 2018

javascript:(() => {
  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} ")`;
  }
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment