Skip to content

Instantly share code, notes, and snippets.

@gullinbursti
Last active February 6, 2022 03:34
Show Gist options
  • Save gullinbursti/cefefea9db8a44389348070d87c92ed1 to your computer and use it in GitHub Desktop.
Save gullinbursti/cefefea9db8a44389348070d87c92ed1 to your computer and use it in GitHub Desktop.
DOM Manipulation + Injection for Firefox
// overwrite history events for url change
(()=> {
var pushState = history.pushState;
var replaceState = history.replaceState;
history.pushState = function() {
pushState.apply(history, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('locationchange'));
};
history.replaceState = function() {
replaceState.apply(history, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('locationchange'));
};
window.addEventListener('popstate', function() {
window.dispatchEvent(new Event('locationchange'))
});
})();
// remove all added extractor elements
(()=> {
const css = Array.from(document.head.children).filter((el)=> (el.getAttribute('data-id') === 'vsco-extractor')).pop();
if (css) {
css.remove();
}
Array.from(document.querySelectorAll('.vsco-extractor')).forEach((el)=> { el.remove(); });
})();
// add img btn + info to gallery thumbs
(()=> {
const MONTH_NAMES = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
const imgDimSort = (dims)=> ([... new Set(dims.sort((dim1, dim2)=> ((dim1.height - dim2.height))).sort((dim1, dim2)=> ((dim1.width - dim2.width))).map((el)=>(el.toString()))) ].map((el)=>(el.split(','))));
const toggleLarges = ()=> {
btnToggle.selected = !brnToggle.selected;
btnToggle.innerHTML = ``
Array.from(document.querySelectorAll('.MediaThumbnail')).filter((thumb)=> ((!thumb.classList.contains('dim-lg')))).forEach((thumb)=> {
thumb.classList.toggle('is-hidden');
//thumb.querySelector('.MediaImage > div').classList.toggle('shrink-down');
});
};
let scrollLoader = null;
let dimLarges = [];
let gridThumbs = {
load : {
prev : [],
curr : [],
isLoading : false
},
disp : {
img : {
small : [],
orthodox : [],
large : [],
sizes : []
},
vid : {
enabled : [],
disabled : []
}
}
};
const galleryCheck = ()=> {
const hasPanel = (document.querySelector('.vsco-extractor-dash-panel') != null);
if (location.pathname.includes('gallery')) {
let preThumbs = Array.from(document.querySelectorAll('.grid .MediaThumbnail'));
const loadBtn = document.querySelector('main > div > div > section > div > button');
gridThumbs = { ...gridThumbs,
load : {
prev : [ ...preThumbs ],
curr : [ ...preThumbs ]
}
};
if (!hasPanel) {
document.querySelector('.page-wrap').appendChild(dashPanel);
}
if (loadBtn) {
gridThumbs = { ...gridThumbs,
load : { ...gridThumbs.load,
isLoading : true
}
};
loadBtn.scrollIntoView();
loadBtn.click();
scrollLoader = setInterval(()=> {
const spinner = document.querySelector('main > div > div > section > div > svg');
preThumbs = Array.from(document.querySelectorAll('.grid .MediaThumbnail'));
gridThumbs = { ...gridThumbs,
load : { ...gridThumbs.load,
prev : gridThumbs.load.curr,
curr : [ ...preThumbs ]
}
};
const { load } = gridThumbs;
const { prev, curr } = load;
console.log('gridThumbs.load', { ...load }, { loadBtn, spinner });
gridUpdate();
// if (prev === curr && !document.querySelector('main div > svg')) {
if (prev.length === curr.length && (!spinner || !loadBtn)) {
clearInterval(scrollLoader);
scrollLoader = null;
gridThumbs = { ...gridThumbs,
load : { ...gridThumbs.load,
isLoading : true
}
};
}
window.scrollByPages(((prev.length === curr.length) << 0) * -1);
window.scrollBy(0, window.scrollMaxY);
setTimeout(()=> { window.scrollBy(0, window.scrollMaxY); }, (1 + ((prev.length === curr.length) << 0) * 69));
}, 666);
}
} else {
clearInterval(scrollLoader);
scrollLoader = null;
if (hasPanel) {
document.querySelector('.page-wrap').removeChild(dashPanel);
}
}
};
const gridUpdate = ()=> {
const totThumbs = Array.from(document.querySelectorAll('.grid figure.MediaThumbnail')).length;
const totVids = Array.from(document.querySelectorAll('.grid video')).length;
const accVids = Array.from(document.querySelectorAll('.grid video')).filter((v)=> (v.currentSrc.length > 0)).length;
const totImgs = totThumbs - totVids;
//imgDate.innerHTML = `${Date().now().toLocaleString().split(',').shift()}`;
firstDate.innerHTML = `<<-- ${((totThumbs > 0) ? new Date(Array.from(document.querySelectorAll('.MasonryGridLayout-column')).map((col)=> (Object.values(col.lastChild).shift().return.pendingProps.media)).map((media)=> ((((media.captureDate || media.createdDate) || media.uploadDate) || Date.now()))).sort().reverse().pop()) : Date.now()).toLocaleString().split(',').shift()}`;
let imgDims = [];
gridStats.innerHTML = `
<h6 class="vsco-extractor">Total : ${totThumbs}</h6>
<div>Images : ${totImgs}</div><div>Videos : ${accVids}${(totVids > accVids) ? ` (${totVids - accVids})` : ''}</div>`;
// iterate over grid items w/o injection yet
Array.from(document.querySelectorAll('.grid .MediaThumbnail')).filter((thumb)=> ((!thumb.classList.contains('vsco-extractor')))).forEach((thumb)=> {
thumb.classList.add('vsco-extractor');
const { media, mediaIndex } = Object.values(thumb).shift().return.pendingProps;
const { captureDate, createdDate, uploadDate, responsiveUrl, width, height, isVideo, videoUrl } = media;
const url = `https://${(!isVideo) ? responsiveUrl : videoUrl}`;
const size = { width, height };
const pubDate = (((captureDate || createdDate) || uploadDate) || Date.now());
imgDims.push(size);
const imgWrapper = (!isVideo) ? thumb.querySelector('.MediaImage') : thumb.querySelector('div');
const btnFullImg = document.createElement('button');
btnFullImg.classList.add('vsco-extractor-btn');
btnFullImg.classList.add('full-image-btn');
if ((size.width << 0) >= 4032 || (size.height << 0) >= 4032) {
thumb.classList.add('dim-lg');
btnFullImg.classList.add('dim-lg-btn');
imgWrapper.classList.add('vsco-extractor-img-lg');
dimLarges.push(thumb);
}
//console.log('[>> size ]', JSON.stringify(size, null, 2));
btnFullImg.innerHTML = `${size.width} x ${size.height}`;
btnFullImg.disabled = (isVideo && thumb.querySelector('video').currentSrc.length === 0);
btnFullImg.onclick = (e)=> { e.target.blur(); window.open(url, '_blank'); };
thumb.insertAdjacentElement('afterBegin', btnFullImg);
const timestamp = document.createElement('div');
timestamp.classList.add('vsco-extractor');
timestamp.classList.add('vsco-extractor-timestamp');
timestamp.innerHTML = `(#${mediaIndex + 1}) ${(new Date(pubDate)).toLocaleString().split(',').shift()}`;
thumb.insertAdjacentElement('beforeEnd', timestamp);
});
const { prev, curr, isLoading } = gridThumbs.load
const { img } = gridThumbs.disp;
gridThumbs = { ...gridThumbs,
disp : { ...gridThumbs.disp,
img : { ...img, large : dimLarges }
}
};
btnToggle.innerHTML = `Toggle Larges (${dimLarges.length})`;
btnToggle.disabled = (dimLarges.length === 0 && !isLoading);
btnToggle.onclick = (!btnToggle.disabled) ? (e)=> {
e.preventDefault();
e.target.blur();
window.scrollTo(0, 425);
toggleLarges();
} : null;
console.log('img dims ASC w,hd', imgDimSort(imgDims));
};
const css = document.createElement('style');
css.setAttribute('type', 'text/css');
css.setAttribute('media', 'screen');
css.setAttribute('data-id', 'vsco-extractor');
css.innerHTML = `
button:hover {
cursor: pointer;
}
button[disabled] {
cursor: not-allowed;
}
h6.vsco-extractor {
margin-bottom: 3px;
border-bottom: 1px solid #000000;
line-height: normal;
font-weight: bold;
font-size: 1.1em;
}
.is-hidden {
display: none;
}
.shrink-down {
height: 0;
}
.vsco-extractor {
font-family: Consolas, Monaco, "Courier New", Courier, monospace;
font-size: 12px;
}
.vsco-extractor .vsco-extractor-btn {
border: 1px solid #ffffff;
}
.vsco-extractor .vsco-extractor-btn[disabled], .vsco-extractor .vsco-extractor-btn[disabled]:hover {
border: 1px solid #4a4a4a;
opacity: 0.25;
filter: brightness(85%);
cursor: not-allowed;
}
.vsco-extractor-dash-panel {
position: fixed;
top: 0;
right: 0;
width: auto;
padding: 3px;
border: 1px solid rgba(0, 0, 0, 1.0);
background-color: rgba(0, 0, 0, 0.5);
text-align: right;
font-size: 0.9em;
z-index: 1001;
}
.vsco-extractor-grid-stats {
color: rgba(0, 255, 255, 0.875);
font-size: 0.8em;
line-height: 1.333em;
text-shadow: 0px 0px 5px rgba(0, 0, 0, 1.0);
}
.vsco-extractor-first-date {
margin: 4px 3px;
font-size: 0.6em;
color: e0e0e0;
}
.vsco-extractor .toggle-dim-btn {
margin-top: 4px;
padding: 4px 4px 6px 4px;
border: none;
background-color: rgba(255, 255, 255, 0.75);
color: #ffffff;
font-size: 0.7em;
}
.vsco-extractor .grid-img-lg {
border: 2px solid rgba(192, 0, 192, 0.75);
transition: border-color 0.125s linear 0s;
}
.vsco-extractor .grid-img-lg:hover {
border: 2px solid rgba(255, 0, 255, 1);
transition: border-color 0.625s ease-in 0.125;
}
.vsco-extractor .full-image-btn {
position: absolute;
left: 0;
width: 100%;
padding: 3px 0 4px 0;
background-color: rgba(27, 33, 33, 1.0);
opacity: 0.5;
color: rgba(255, 255, 255, 0.875);
font-size: 1em;
font-weight: bold;
line-height: normal;
border: 1px solid rgba(255, 255, 255, 0.25);
text-shadow: 0px 0px 3px rgba(0, 0, 0, 1.0);
/* filter: brightness(100%); */
/* transition: opacity 0.125s ease-in 0s, filter 0.125s, ease-in 0s; */
transition: opacity 0.125s ease-in 0s;
/* transition: filter 0.125s, ease-in 0s; */
z-index: 101;
}
.vsco-extractor .full-image-btn:hover {
opacity: 0.875;
filter: brightness(105%);
/* transition: opacity 0.333s ease-out 0s, filter 0.333s, ease-in 0s; */
}
.vsco-extractor .dim-lg-btn {
background-color: rgba(255, 0, 255, 0.875);
border: 1px solid rgba(255, 0, 255, 0.5);
border-bottom: 1px dotted rgba(0, 0, 0, 1.0);
color: #ffff00;
opacity: 0.75;
}
.vsco-extractor .dim-lg-btn:hover {
opacity: 1.0;
filter: brightness(115%);
}
.vsco-extractor-timestamp {
position: absolute;
right: 0;
bottom: 22px;
padding: 2px;
color: #808080;
font-size: 0.8em;
line-height: normal;
}
.MediaThumbnail {
position: relative;
transition: height 0.5s linear 0s;
}
.MediaThumbnail .MediaImage > div {
height: auto;
transition: background-color 0.2s ease 0s;
}
.MediaThumbnail .MediaImage > div.shrink-down {
height: 0;
/*transition: background-color 0.2s ease 0s; */
transition: height 1s linear 0s;
}
`;
document.head.appendChild(css);
const dashPanel = document.createElement('div');
dashPanel.classList.add('vsco-extractor');
dashPanel.classList.add('vsco-extractor-dash-panel');
const gridStats = document.createElement('div');
gridStats.classList.add('vsco-extractor-grid-stats');
const firstDate = document.createElement('div');
firstDate.classList.add('vsco-extractor-first-date');
const btnToggle = document.createElement('button');
btnToggle.classList.add('vsco-extractor-btn');
btnToggle.classList.add('toggle-dim-btn');
dashPanel.appendChild(gridStats);
dashPanel.appendChild(firstDate);
dashPanel.appendChild(btnToggle);
// dashPanel.insertAdjacentElement('beforeEnd', btnToggle);
//setTimeout(()=> { galleryCheck(); }, 1000);
//galleryCheck();
window.onload = ()=> {
console.log('window.LOAD -->');
setTimeout(galleryCheck, 333);
};
// window.document.addEventListener('load', ()=> {
// console.log('window.LOAD -->';)
// galleryCheck();
// });
window.addEventListener('locationchange', ()=> {
console.log('window.locationChanged', location);
//galleryCheck();
setTimeout(galleryCheck, 333);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment