User:SuperHamster/view-it-full.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
function viewItEmbedded() {
	var VI_API = new mw.Api();
	var VI_NUM_RESULTS = 20;
	var VI_TRANSLATIONS = {
		"bn": {
			"label": "দেখুন",
			"description": "এই নিবন্ধের সাথে সম্পর্কিত চিত্রগুলি দেখুন"
		},
		"cs": {
			"label": "Zobrazení",
			"description": "Zobrazit média související s tímto tématem"
		},
		"en": {
			"label": "View",
			"description": "View images related to this article"
		},
		"eo": {
			"label": "Rigardu",
			"description": "Rigardu bildojn rilatajn al ĉi tiu artikolo"
		},
		"es": {
			"label": "Ver",
			"description": "Ver imágenes relacionadas a este artículo"
		},
		"fi": {
			"label": "Kuvia",
			"description": "Katso tähän artikkeliin liittyviä kuvia"
		},
		"ha": {
			"label": "Duba",
			"description": "Duba hotuna masu alaƙa da wannan labarin"
		},
		"hu": {
			"label": "Képek",
			"description": "Ehhez a szócikkhez kapcsolódó képek mutatása"
		},
		"it": {
			"label": "Guarda",
			"description": "Guarda immagini collegate a questa pagina"
		},
		"nl": {
			"label": "Afbeeldingen",
			"description": "Bekijk gerelateerde afbeeldingen bij dit artikel"
		},
		"pa": {
			"label": "ਝਾਤ ਮਾਰੋ",
			"description": "ਮਤਾਲਕ ਤਸਵੀਰਾਂ ਨੂੰ ਵੇਖੋ"
		},
		"pnb": {
			"label": "جھات مارو",
			"description": "متعلق تصویراں نوں ویکھو"
		},
		"pt": {
			"label": "Visualizar",
			"description": "Veja imagens relacionadas a este artigo"
		},
		"ro": {
			"label": "Vezi",
			"description": "Vezi imagini legate de acest articol"
		},
		"sv": {
			"label": "Visa",
			"description": "Se bilder relaterade till den här artikeln"
		},
		"tl": {
			"label": "Tingnan",
			"description": "Tingnan ang mga larawang may kinalaman sa artikulong ito"
		},
		"uk": {
			"label": "Див.",
			"description": "Див. зображення, які пов'язані з цією статтею"
		},
		"vi": {
			"label": "Xem",
			"description": "Xem hình ảnh có liên quan đến bài viết"
		}
	};
	var VI_LABEL = '';
	var VI_DESCRIPTION = '';
	var VI_SKIN = mw.config.get('skin');
	var VI_PORTLET = 'p-views';
	if (VI_SKIN === 'minerva') {
		VI_PORTLET = 'p-tb';
	}
	var VI_LANG = mw.config.get('wgContentLanguage');
	if (VI_LANG in VI_TRANSLATIONS) {
		VI_LABEL = VI_TRANSLATIONS[VI_LANG]['label'];
		VI_DESCRIPTION = VI_TRANSLATIONS[VI_LANG]['description'];
	} else {
		VI_LABEL = VI_TRANSLATIONS['en']['label'];
		VI_DESCRIPTION = VI_TRANSLATIONS['en']['description'];
	}
	var VI_EMBEDDEDSTYLESHEET = '.view-it-control-container{display:none;}.action-edit .view-it-control-container,.ve-active .view-it-control-container, .wb-itempage .view-it-control-container{display:flex;}.ve-active #view-it-container{position:sticky;z-index:1;}.ve-active .skin-timeless #view-it-container{top:6.5em;}.ve-active .skin-vector #view-it-container{top:3em;}.ve-active .skin-vector-2022 #view-it-container{top:2em;}.skin-vector-2022 #view-it-container{padding-top: 15px;}.skin-minerva #view-it-container{width:90%;margin:auto;}.skin-minerva #view-it-close-button + *{clear:both;}.ve-active .mw-indicators{display:block !important;}#view-it-container{pointer-events:auto;opacity:1;background-color:white;}#view-it-expand{padding:0px 5px;font-size:1.2em;}.view-it-link{padding:10px 5px;font-size:1.2em }#view-it-close-button, #view-it-container button{padding:5px;background:#36c;color:#fff;border:0;border-radius:3px;cursor:pointer }#view-it-container button{margin: 5px;}#view-it-container button[disabled]{background:#c8ccd1;cursor:not-allowed }#view-it-close-button{z-index:2;position:relative;pointer-events:auto;opacity:1;font-size:.8em;line-height:.8em;border-radius:25px;}#view-it-browse-buttons{padding-top:5px }#view-it-gallery{white-space:nowrap;overflow:scroll;scrollbar-width:thin;width:0px;min-width:100%;}#view-it-gallery a,#view-it-gallery-full a{display:inline-block;padding-right:3px;padding-bottom:3px }#view-it-gallery a{height:100px }#view-it-gallery-full a{height:200px }#view-it-gallery a>img,#view-it-gallery-full a>img{cursor:pointer;min-width:60px;max-height:200px;}#view-it-expand{margin:0 auto;display:table;cursor:pointer;color:#36c }.view-it-link{width:100%;display:inline-block;text-align:center }.view-it-carousel-element{width:fit-content;display:inline-block;position:relative }.view-it-control-container{position:absolute;bottom:3px;right:3px;}.view-it-control-element{cursor:pointer;padding:3px;background:#000;opacity:50%;margin-left:3px;}.view-it-control-element:hover{transform:scale(1.1,1.1);}.view-it-control-element:active{transform:scale(1.4,1.4);}';
	var VI_DISABLEDLINKCSS = '.skin-timeless #ca-viewitgallery>span,.skin-vector-2022 #ca-viewitgallery>span{color:#abc3ed;display:block;padding:18px 0 7px;margin-bottom:-1px}.skin-vector-legacy #ca-viewitgallery>span{background-position:right bottom;color:#abc3ed;box-sizing:border-box;display:block;float:left;height:3.07692308em;position:relative;padding-top:1.25em;padding-left:8px;padding-right:8px;font-size:.8125em;background-image:linear-gradient(to bottom,rgba(167,215,249,0) 0,#c4c4cc 100%);background-repeat:no-repeat;background-size:1px 100%}';
	var VI_SKIN = mw.config.get('skin');
	var VI_NAMESPACE = mw.config.get('wgNamespaceNumber');
	var VI_PAGENAME = mw.config.get('wgPageName');
	var VI_XHR = new XMLHttpRequest();
	var VI_QNUMBLACKLIST = ['Q5296'];
	var VI_OUTPUTP18CLAIM = true;
	document.body.insertAdjacentHTML("beforeend", '<style>' + VI_EMBEDDEDSTYLESHEET + '</style>');

	var viRetrievedImages = [];

	function copyToClipboard(text) {
		navigator.clipboard.writeText(text);
	}

	function createClaim(qNum, property, value) {
		if (confirm('Would you like to add a P18 (image) statement with value ' + value.replace(' ', '_') + '?')) {
			VI_API.postWithEditToken({
				action: "wbcreateclaim",
				entity: qNum,
				property: property,
				snaktype: "value",
				value: JSON.stringify(value),
				summary: 'using #view-it',
				tags: 'view-it'
			}).done(function(data){
				if (data.success && data.success === 1) {
					location.reload(); 
				} else {
					alert('View it! failed to update the P18 property.');
				}
			});
			}
	}

	function viewItRecreateNode(el) {
		el.parentNode.replaceChild(el.cloneNode(true), el);
	}

	async function viewItGetImages(qNum, offset) {
		return new Promise((resolve, reject) => {
			VI_XHR.onreadystatechange = (e) => {
				if (VI_XHR.readyState !== 4) {
					return;
				}

				if (VI_XHR.status === 200) {
					var data = JSON.parse(VI_XHR.responseText);
					viRetrievedImages = viRetrievedImages.concat(data.results);
					resolve(data);
				} else {
					console.warn('Failed to fetch images from View it! API');
				}
				if (this.readyState == 4 && this.status == 200) {
					return JSON.parse(VI_XHR.responseText);
				}
			}
			VI_XHR.open('GET', 'https://view-it.toolforge.org/api/' + qNum + '?offset=' + offset + '&captions=true');
			VI_XHR.send();
		});
	}

	async function viewItUpdateCarousel(qNum, offset, total) {
		var viewItGallery = document.getElementById('view-it-gallery');
		viewItGallery.style.height = viewItGallery.clientHeight + 'px';
		viewItGallery.innerHTML = '';
		var imagesToDisplay = [];
		if (viRetrievedImages.length < (offset + VI_NUM_RESULTS) && viRetrievedImages.length !== total) {
			await viewItGetImages(qNum, offset).then(data => {
				imagesToDisplay = data.results;
			});
		} else {
			imagesToDisplay = viRetrievedImages.slice(offset, offset + VI_NUM_RESULTS);
		}

		imagesToDisplay.forEach(image => {
			var linkElement = document.createElement('a');
			linkElement.href = image.image;
			linkElement.target = '_blank';
			var imageElement = document.createElement('img');
			imageElement.src = image.thumb;
			imageElement.alt = image.title;
			imageElement.title = image.title;
			imageElement.height = '100';
			linkElement.appendChild(imageElement);
			var carouselElement = document.createElement('div');
			carouselElement.classList = 'view-it-carousel-element';
			carouselElement.appendChild(linkElement);
			var controlElement = document.createElement('div');
			controlElement.classList = 'view-it-control-container';
			if (mw.config.get('wgSiteName') === 'Wikidata') {
				// If on Wikidata, show icons to copy image title + insert P18 claim:
				var copyElement = document.createElement('div');
				copyElement.classList = 'view-it-control-element';
				var copyIcon = document.createElement('img');
				copyIcon.src = 'https://upload.wikimedia.org/wikipedia/commons/3/34/Copy_%2889528%29_-_The_Noun_Project.svg';
				copyIcon.title = 'Click to copy filename';
				copyIcon.width = '20';
				copyIcon.style.filter = 'brightness(0) invert(1)';
				copyElement.appendChild(copyIcon);
				controlElement.appendChild(copyElement);
				copyElement.addEventListener("click", copyToClipboard.bind(this, image.title));

				
				if (VI_OUTPUTP18CLAIM) {
					var addClaimElement = document.createElement('div');
					addClaimElement.classList = 'view-it-control-element';
					var addClaimIcon = document.createElement('img');
					addClaimIcon.src = 'https://upload.wikimedia.org/wikipedia/commons/9/9d/Plus_%2889613%29_-_The_Noun_Project.svg';
					addClaimIcon.title = 'Click to add in P18 (image) claim';
					addClaimIcon.width = '20';
					addClaimIcon.style.filter = 'brightness(0) invert(1)';
					addClaimElement.appendChild(addClaimIcon);
					controlElement.appendChild(addClaimElement);
					addClaimElement.addEventListener("click", createClaim.bind(this, qNum, 'P18', image.title));
				}
			} else {
				// If not on Wikidata, show icon to copy thumbnail syntax:
				var copyElement = document.createElement('div');
				copyElement.classList = 'view-it-control-element';
				var copyIcon = document.createElement('img');
				copyIcon.src = 'https://upload.wikimedia.org/wikipedia/commons/3/34/Copy_%2889528%29_-_The_Noun_Project.svg';
				copyIcon.title = 'Click to copy thumbnail syntax';
				copyIcon.width = '20';
				copyIcon.style.filter = 'brightness(0) invert(1)';
				copyElement.appendChild(copyIcon);
				controlElement.appendChild(copyElement);
				if (image.captions && image.captions[VI_LANG]) {
					copyElement.addEventListener("click", copyToClipboard.bind(this, '[[File:' + image.title + '|thumb|' + image.captions[VI_LANG].value + ']]'));
				} else {
					copyElement.addEventListener("click", copyToClipboard.bind(this, '[[File:' + image.title + '|thumb|Insert caption here.]]'));
				}
			}
			carouselElement.appendChild(controlElement);
			viewItGallery.appendChild(carouselElement);
		});
		viewItGallery.style.height = 'auto';
		var previousButton = document.getElementById('view-it-previous-button');
		var nextButton = document.getElementById('view-it-next-button');
		viewItRecreateNode(previousButton);
		viewItRecreateNode(nextButton);
		var previousButton = document.getElementById('view-it-previous-button');
		var nextButton = document.getElementById('view-it-next-button');
		if (total > (offset + VI_NUM_RESULTS)) {
			nextButton.addEventListener("click", viewItUpdateCarousel.bind(this, qNum, offset + VI_NUM_RESULTS, total));
			nextButton.disabled = false;
		} else {
			nextButton.disabled = true;
		}
		if (offset >= VI_NUM_RESULTS) {
			previousButton.addEventListener("click", viewItUpdateCarousel.bind(this, qNum, offset - VI_NUM_RESULTS, total));
			previousButton.disabled = false;
		} else {
			previousButton.disabled = true;
		}
	}

	function viewItPopulateGallery(qNum, offset) {
		var scrollNotice = document.getElementById('view-it-scroll-notice');
		if (scrollNotice) {
			scrollNotice.remove();
		}
		viewItGetImages(qNum, offset).then(data => {
			if (data.total > 0) {
				if(document.getElementById('#ca-viewitgallery')) {
					document.querySelector('#ca-viewitgallery > a').innerHTML = '<span>' + VI_LABEL + '</span>';
				}

				var viewItGallery = document.getElementById('view-it-gallery-full');
				if (offset === 0) {
					var viewItLink = document.createElement('a');
					viewItLink.classList = 'view-it-link';
					viewItLink.href = 'https://view-it.toolforge.org/?q=' + qNum;
					viewItLink.target = '_blank';
					viewItLink.innerHTML = 'View all ' + data.total.toLocaleString() + ' images in View it!';
					viewItGallery.insertAdjacentElement('beforebegin', viewItLink);

					var clearDiv = document.createElement("div");
					clearDiv.style.clear = 'both';
					viewItLink.insertAdjacentElement('afterend', clearDiv);
				}

				var results = data.results;
				for (var i in results) {
					var linkElement = document.createElement('a');
					linkElement.href = results[i].image;
					linkElement.target = '_blank';
					var imageElement = document.createElement('img');
					imageElement.src = results[i].thumb;
					imageElement.alt = results[i].title;
					imageElement.title = results[i].title;
					imageElement.height = '200';
					linkElement.appendChild(imageElement);
					viewItGallery.appendChild(linkElement);
				}

				if (data.total > (offset + VI_NUM_RESULTS)) {
					offset += VI_NUM_RESULTS;

					var resultsContainer = document.getElementById('view-it-gallery-full');

					var scrollNotice = document.createElement('div');
					scrollNotice.id = 'view-it-scroll-notice';
					scrollNotice.innerHTML = '<br/>Scroll to bottom to load more...';
					scrollNotice.display = '';
					resultsContainer.appendChild(scrollNotice);

					function scrollListener(e) {
						var elementOffset = resultsContainer.getBoundingClientRect().top - resultsContainer.offsetParent.getBoundingClientRect().top;
						var top = window.pageYOffset + window.innerHeight - elementOffset;
						if (top > resultsContainer.scrollHeight) {
							window.removeEventListener('scroll', scrollListener);
							viewItPopulateGallery(qNum, offset);
						}
					}
					window.addEventListener("scroll", scrollListener, {
						passive: false
					});
				}
			} else {
				document.body.insertAdjacentHTML("beforeend", '<style>' + VI_DISABLEDLINKCSS + '</style>');
				document.querySelector('#ca-viewitgallery').innerHTML = '<span>' + VI_LABEL + '</span>';
			}
		});
	}

	if (VI_NAMESPACE === 0 || VI_NAMESPACE > 99) {
		var qNum = '';
		var title = mw.config.get('wgTitle').replace(new RegExp(' ', "g"), '_');
		if (mw.config.get('wgSiteName') === 'Wikidata') {
			qNum = mw.config.get('wbEntityId');
		} else {
			if (mw.config.get('wgAction') === 'edit' && document.querySelector('#t-wikibase>a')) {
				// Grabbing qNum from Wikidata link in sidebar
				// Quick solution; should be calling API but will need to account for async
				// Ticket is open to have qNum available on edit pages: T185437
				var wikidataLink = document.querySelector('#t-wikibase>a').href;
				if (wikidataLink) {
					qNum = wikidataLink.substring(wikidataLink.lastIndexOf('/') + 1);
				}
			} else {
				qNum = mw.config.get('wgWikibaseItemId');
			}
		}

		var url = new URL(window.location);
		var gallery = url.searchParams.has('view-it');
		if (qNum && !VI_QNUMBLACKLIST.includes(qNum) && gallery !== true) {
			// Add portlet link to gallery:
			mw.util.addPortletLink(VI_PORTLET, '/wiki/' + title + '?view-it=' + qNum, VI_LABEL, 'ca-viewitgallery', VI_DESCRIPTION, null, document.querySelector('#p-views>div>ul>li:nth-child(2)'));

			viewItGetImages(qNum, 0).then(data => {
				if (data.total > 0) {
					var results = data.results;

					// Create carousel container:
					var viewItContainer = document.createElement('div');
					viewItContainer.id = 'view-it-container';
					if (document.querySelector('#contentSub')) {
						document.querySelector('#contentSub').insertAdjacentElement('afterend', viewItContainer);
					} else {
						document.querySelector('#bodyContent').insertAdjacentElement('beforebegin', viewItContainer);
					}

					// Create carousel gallery:
					var viewItGallery = document.createElement('div');
					viewItGallery.id = 'view-it-gallery';
					viewItGallery.style.whiteSpace = 'nowrap';
					viewItGallery.style.overflow = 'scroll';
					viewItGallery.appendChild(document.createTextNode('Loading images...'));
					var viewItContainer = document.getElementById('view-it-container');
					viewItContainer.appendChild(viewItGallery);
					viewItGallery.innerHTML = '';

					if (data.total > 20) {
						var viewItExpand = document.createElement('span');
						viewItExpand.id = 'view-it-expand';
						viewItExpand.innerHTML = '▼▼▼▼';
						viewItExpand.addEventListener("click", function() {
							var viewItGallery = document.getElementById('view-it-gallery');
							var viewItLink = document.getElementById('view-it-link');
							var viewItBrowseButtons = document.getElementById('view-it-browse-buttons');
							if (this.innerHTML === '▼▼▼▼') {
								viewItGallery.style.whiteSpace = 'initial';
								viewItGallery.style.textAlign = 'center';
								viewItLink.style.display = 'table';
								viewItBrowseButtons.style.display = 'table';
								viewItExpand.innerHTML = '▲▲▲▲';
							} else {
								viewItGallery.style.whiteSpace = 'nowrap';
								viewItGallery.style.textAlign = 'initial';
								viewItLink.style.display = 'none';
								viewItBrowseButtons.style.display = 'none';
								viewItExpand.innerHTML = '▼▼▼▼';
							}
						});
						viewItContainer.appendChild(viewItExpand);
					}

					// Output and control close button
					var closeButton = document.createElement('button');
					closeButton.id = 'view-it-close-button';
					closeButton.classList = 'mw-indicator';
					if (document.getElementsByClassName('mw-indicators').length > 0) {
						document.getElementsByClassName('mw-indicators')[0].appendChild(closeButton);
					} else {
						document.querySelector('#content').prepend(closeButton);
						closeButton.style.float = 'inline-end';
						closeButton.style.margin = '5px';
					}
					const closedStatus = localStorage.getItem('view-it-closed');
					if (closedStatus === null) {
						localStorage.setItem('view-it-closed', 'open');
						closeButton.innerHTML = 'Close View it!';
						document.getElementById('view-it-container').style.display = 'block';
					} else if (closedStatus === 'closed') {
						closeButton.innerHTML = 'Open View it!';
						document.getElementById('view-it-container').style.display = 'none';
					} else {
						closeButton.innerHTML = 'Close View it!';
						document.getElementById('view-it-container').style.display = 'block';
					}
					closeButton.addEventListener("click", function() {
						const closedStatus = localStorage.getItem('view-it-closed');
						if (closedStatus === 'open') {
							localStorage.setItem('view-it-closed', 'closed');
							document.getElementById('view-it-container').style.display = 'none';
							this.innerHTML = 'Open View it!';
						} else {
							localStorage.setItem('view-it-closed', 'open');
							document.getElementById('view-it-container').style.display = 'block';
							this.innerHTML = 'Close View it!';
						}
					});

					// Output 'view all' button
					var viewItLink = document.createElement('a');
					viewItLink.id = 'view-it-link';
					viewItLink.style.display = 'none';
					viewItLink.style.margin = '0 auto';
					viewItLink.href = window.location.origin + window.location.pathname + '?view-it=' + qNum;
					var viewItLinkButton = document.createElement('button');
					viewItLinkButton.id = 'view-it-link-button';
					viewItLinkButton.innerHTML = 'View all ' + data.total.toLocaleString() + ' images';
					viewItLink.appendChild(viewItLinkButton);
					viewItContainer.appendChild(viewItLink);
					if (data.total > 20) {
						viewItContainer.appendChild(viewItExpand);
					}

					// Output previous and next buttons
					var viewItBrowseButtons = document.createElement('div');
					viewItBrowseButtons.id = 'view-it-browse-buttons';
					viewItBrowseButtons.style.display = 'none';
					viewItBrowseButtons.style.margin = '0 auto';
					var previousButton = document.createElement('button');
					previousButton.id = 'view-it-previous-button';
					previousButton.innerHTML = 'Previous Page';
					previousButton.disabled = true;
					var nextButton = document.createElement('button');
					nextButton.id = 'view-it-next-button';
					nextButton.innerHTML = 'Next Page';
					if (data.total <= VI_NUM_RESULTS) {
						nextButton.disabled = true;
					} else {
						nextButton.addEventListener("click", viewItUpdateCarousel.bind(this, qNum, VI_NUM_RESULTS, data.total));
					}
					viewItBrowseButtons.appendChild(previousButton);
					viewItBrowseButtons.appendChild(nextButton);
					viewItLink.insertAdjacentElement('beforebegin', viewItBrowseButtons);

					async function initialCarouselCall() {
						// If on Wikidata item, check if P18 exists:
						if (mw.config.get('wgSiteName') === 'Wikidata') {
							await VI_API.get({
								"action": "wbgetclaims",
								"format": "json",
								"entity": qNum,
								"property": "P18",
								"formatversion": "2"
							}).done(function(data) {
								if (data['claims']['P18']) {
									VI_OUTPUTP18CLAIM = false;
								}
							});
						}

						// Add images to carousel:
						viewItUpdateCarousel(qNum, 0, data.total);
					}

					initialCarouselCall();

					var clearDiv = document.createElement("div");
					clearDiv.style.clear = 'both';
					viewItContainer.appendChild(clearDiv);
				} else {
					document.body.insertAdjacentHTML("beforeend", '<style>' + VI_DISABLEDLINKCSS + '</style>');
					document.querySelector('#ca-viewitgallery').innerHTML = '<span>' + VI_LABEL + '</span>';
				}
			});

		} else if (qNum && !VI_QNUMBLACKLIST.includes(qNum) && gallery) {
			mw.util.addPortletLink(VI_PORTLET, '/wiki/' + title + '?view-it=' + qNum, VI_LABEL, 'ca-viewitgallery', VI_DESCRIPTION, null, document.querySelector('#p-views>div>ul>li:nth-child(2)'));
			if (document.getElementById('ca-view')) {
				document.getElementById('ca-view').classList.remove('selected');
			}
			if (document.getElementById('ca-viewitgallery')) {
				document.getElementById('ca-viewitgallery').classList.add('selected');
			}

			var qNum = url.searchParams.get('view-it');

			// Clear contents
			document.getElementById('mw-content-text').innerHTML = '';

			var viewItGallery = document.createElement('div');
			viewItGallery.id = 'view-it-gallery-full';
			viewItGallery.style.textAlign = 'center';
			viewItGallery.style.overflow = 'scroll';
			document.getElementById('mw-content-text').appendChild(viewItGallery);

			viewItPopulateGallery(qNum, 0);
		}
	}
}

viewItEmbedded();