Instagram - visible images counter

Shows in instagram profile pages how many images out of total (as a number and as a percentage) are currently visible, as you scroll down the page.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name        Instagram - visible images counter
// @namespace   darkred
// @version     2024.2.23
// @description Shows in instagram profile pages how many images out of total (as a number and as a percentage) are currently visible, as you scroll down the page.
// @author      darkred
// @license     MIT
// @include     https://www.instagram.com/*
// @grant       none
// @require     https://code.jquery.com/jquery-3.2.1.min.js
// @require     https://greasyfork.org/scripts/21927-arrive-js/code/arrivejs.js
// @supportURL  https://github.com/darkred/Userscripts/issues
// ==/UserScript==

/* eslint-disable no-console */

const escapeHTMLPolicy = (({ trustedTypes }, policy) =>
	trustedTypes
		? trustedTypes.createPolicy('myEscapePolicy', policy)
		: policy)(window, {
	createHTML: (str) => str,
});


var stylesheet =
`<style>
	.counter {
		color: #D9D9D9 !important;
	}
</style>`;
$('head').append(stylesheet);


// Not needed anymore
/*
// If you scroll down, beyond the first 12 images, then the "LOAD MORE" button(to show more images) will be automatically clicked
$(window).scroll(function() {
	if ($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
		var element = $(`a:contains('Load more')`)[0];
		element.click();
	}
});
*/

var hrefselems = [];
var hrefs = [];
var total;

function showCounter() {

	// var totalString = $(`span:contains('posts'):last-child > span, .g47SY`).html(); 																	// The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de
	// var totalString = document.querySelector(`#react-root > section > main > div > header > section > ul > li:nth-child(1) > * > span`).textContent;	// The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de
	var totalString = document.querySelector(`[id^=mount_0_0_] section > main > div > header > section > ul > li:nth-child(1) > * > span`).textContent;	// The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de
	total = totalString.replace(',', '').replace('.', ''); // strip the thousand comma/dot seperator


	// hrefselems = document.querySelectorAll(`a[href*='taken-by']`);
	// hrefselems = document.querySelectorAll(`._aabd._aa8k._aanf > a`);
	// hrefselems = document.querySelectorAll(`._aabd._aa8k._al3l > a`);
	hrefselems = document.querySelectorAll(`._aabd._aa8k.x2pgyrj  > a`);
	$.each(hrefselems, function(index, value) {
		// hrefs.indexOf(String(value)) === -1 ? hrefs.push(String(value)) : console.log("This item already exists"); // https://stackoverflow.com/a/36683363
		if (hrefs.indexOf(String(value)) === -1) { 		// hrefs.count -below- serves as a counter for the newly added displayed images (on each infinite scrolling event)
			hrefs.push(String(value));
		}
	});

	var visibleCount = hrefs.length;

	var visiblePercent = ((visibleCount / total) * 100).toFixed(1); // Visible images count as percentage
	if (isNaN(visiblePercent)) {
		visiblePercent = 0 ;  // avoid NaN
	}

	var counter = visibleCount + ' / ' + totalString + ' that is ' + visiblePercent + '%';
	return counter;

}




function createDiv() {
	// Creation of the counter element
	document.body.appendChild(div);
	// div.innerHTML = showCounter(); 	// Initial display of the counter
	div.innerHTML = escapeHTMLPolicy.createHTML(showCounter()); 	// Initial display of the counter
	div.style.top = '1px';
	div.style.right = '1px';
	div.style.position = 'fixed';
	div.className = 'counter';
}



function createObserver() {

	// var thePics = document.querySelector('div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]');
	var thePics = document.querySelector('div[style*="flex-direction: column;"], div[style$="padding-top: 0px;"]');  // the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions)   --> https://stackoverflow.com/a/5110337  ("wildcard * in CSS") starting (^=) with x and ending ($=) with y
	if (!thePics){
		return;
	}

	hrefselems.length = 0;  // empty the array (see https://stackoverflow.com/a/1232046, method #2)
	hrefs.length = 0;
	/// ---------------------------------
	/// mutation observer -monitors the Posts grid for infinite scrolling event-.
	/// ---------------------------------
	observer = new MutationObserver(function() {  // --> Callback function to execute when mutations are observed
		if (div.innerHTML.indexOf(total + ' / ' + total) === -1) {
			// div.innerHTML = showCounter(); 	// On each infinite scrolling event, re-calculate counter
			div.innerHTML = escapeHTMLPolicy.createHTML(showCounter()); 	// On each infinite scrolling event, re-calculate counter
		}
	// }).observe(document.querySelector('._havey'), 	// target of the observer: the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions)
	// }).observe(document.querySelector('div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]'), 	// target of the observer: the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions)
	});

	observer.observe(thePics, 	// target of the observer
		{	// attributes: true,
			childList: true,
			// characterData: true,
			// subtree: true,
		}); // config of the observer

}






var div = document.createElement('div');
var observer;

// var avatarSelector = 'span[style="width: 152px; height: 152px;"]';   // the profile's photo/avatar element
// var avatarSelector = 'main > article > header > section > div._ienqf > div > button';                 // the 3-dots icon
// var avatarSelector = 'div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]';   // the 3-dots icon
// var avatarSelector = '._mainc';                                      // the profile's bio area element
// var avatarSelector = 'h1.notranslate';                               // the profile name element
// var avatarSelector = 'h1.rhpdm';                                     // the profile name element
// var avatarSelector = 'span.-nal3';                                   // the 'posts' count element, e.g.  683 posts
// var avatarSelector = 'ul.k9GMp';                                     // the profile's 3 counters container element
// var avatarSelector = '.eC4Dz';                                  // the profile's username container element
// var avatarSelector = '._aa_m';                                  // the profile's username container element
// var avatarSelector = '._aa_c';                                  // the profile's username container element
// var avatarSelector = 'main > article > header > section > div._ienqf > div > button';                                  // the 3-dots icon
// var avatarSelector = 'div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]';                    // the 3-dots icon
var avatarSelector = '.x1q0g3np.x2lah0s.x8j4wrb';                                                                         // the 3-dots icon




if (document.querySelector(avatarSelector) ) {
	if (!document.querySelector('.counter')){
		createDiv();
		createObserver();
	}
} else {
	console.log('ERROR: Cannot create the Counter element, the avatarSelector element ( ' + avatarSelector + ' ) doesn\'t exist !!');
}


document.arrive(avatarSelector, function() { // the avatar in the profile page
	createDiv();
	createObserver();
	// alert()
});



function removeCounter(){
	div.remove();
	hrefselems.length = 0;  // empty the array (see https://stackoverflow.com/a/1232046, method #2)
	hrefs.length = 0;
	total = '';
	observer.disconnect();
	// if (observer) {
	// 	observer.disconnect();
	// }
}


document.leave(avatarSelector, function() {
	if (!document.querySelector(avatarSelector)){
		removeCounter();
	}
});


// when navigating using the browser's back/forth
window.addEventListener('popstate', function () {
	// alert()
	removeCounter();
	console.log('COUNTER IS REMOVED');

	// TODO
	// createDiv();
	// createObserver();

});


// when navigating from a profile to another via searchbox // history API)
// https://stackoverflow.com/questions/56760727/how-to-observe-a-change-in-the-url-using-javascript
(function(){
	var rs = history.pushState;
	history.pushState = function(state, title, url){
		if ( !url.includes('/following/') && !url.includes('/followers/') && !url.includes('/p/') && !url.includes('/direct/') && !window.location.href.includes('/p/') ){   // avoid all possible in-page/popup "pages" (a profile's following/followers, and posts -opened by clicking on a thumb-)
			rs.apply(history, arguments); // preserve normal functionality
			console.log('navigating', arguments); // do something extra here; raise an event
			removeCounter();
			// alert()
		}
	};
}());