Przejdź do zawartości

Wikipedysta:Nux/LongTableFilters.js

Z Wikipedii, wolnej encyklopedii

Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.

  • Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
  • Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
  • Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5.
  • Opera: Naciśnij klawisze Ctrl+F5.
/* global mw, $ */
/**
 * Long table filtering.
 * 
 * Assumes only tables longer then 20 rows are worth filtering.
 * [[Wikipedysta:Nux/ViewFilter.js]]
 */
// for table filter
mw.loader.load("https://pl.wikipedia.org/w/index.php?action=raw&ctype=text/javascript&smaxage=21600&maxage=86400&title=Wikipedysta:Nux/ViewFilter.js");

/* Translatable strings */
mw.messages.set({
	'nuxltf-refresh': 'Odśwież',
	'nuxltf-action-init': 'Filtr tabel',
});
// mw.msg('nuxltf-action-init')

/**
 * Main class.
 */
class LongTableFilters {
	constructor(ViewFilter) {
		this.ViewFilter = ViewFilter;
	}

	/** Init when ready. */
	init() {
		if (!this.validate()) {
			return;
		}
		if (!this.createControlsContainer()) {
			return;
		}
		this.prepareTable();
	}
	
	/** Find all long tables in the article. */
	findLongTables() {
		return Array.from(document.querySelectorAll( "#content table" ))
			.filter(t=>t.querySelectorAll('tr').length > 20)
		;
	}

	/** Check if there are some tables to filter */
	validate() {
		if (this.findLongTables().length < 1) {
			console.log('[table filter]', 'no long tables found');
			return false;
		}
		return true;
	}

	/** Create filtering form container. */
	createControlsContainer() {
		// find controls context
		var context = this.findLongTables();
		if (!context.length) {
			console.warn('[table filter]', 'context not found');
			return false;
		}
		context = context[0];
		// create controls container
		context.insertAdjacentHTML("beforebegin", "<div class='table-filter-controls'/>");

		return true;
	}

	/** Prepare filtering. */
	prepareTable() {
		// define view filter
		const tableFilter = new this.ViewFilter();
		this.tableFilter = tableFilter;

		tableFilter.itemProperty = 'LongTableFilters_text';

		// custom text
		tableFilter.itemToText = (item) => {
			return this.itemToText(item);
		};
		
		// filter for MalarzBOT pages
		let pageName = mw.config.get('wgPageName');
		if (pageName.indexOf(':MalarzBOT') > 0) {
			$('.sortable td small').remove();	// remove replay links etc
		}

		// mark long tables
		let longTables = this.findLongTables();
		longTables.forEach((t) => {
			t.classList.add('nuxlongtable');
		});

		// insert controls and pre-parse items
		// assumes header row(s) are in thead and not in tbody (should be true in MW)
		tableFilter.init(".table-filter-controls", ".nuxlongtable tbody tr");
		
		// refresh button
		this.prepareRefreshButton(this.tableFilter.container);
	}

	/** @private */
	itemToText (item) {
		// construct text from both `::before` (from CSS) and `textContent`
		let text = '';
		const allChildren = this.children(item);
		for (const child of allChildren) {
			if (child.nodeType === Node.ELEMENT_NODE) {
				// append ::before from CSS
				const before = window.getComputedStyle(child, '::before').getPropertyValue('content');
				if (before && before !== 'none') {
					text += this.cssContentToText(before);
				}
			} else if (child.nodeType === Node.TEXT_NODE) {
				text += child.textContent;
			}
		}
		//console.log('[LTF]', {item, text});
		return text;
	}
	/** @private Translate value from ComputedStyle to actual text. */
	cssContentToText(cssValue) {
		return cssValue
			// strip opening and closing quotes
			.replace(/^['"]/, '').replace(/['"]$/, '')
			// should we translate unicode maybe?
		;
	}
	/** @private All children in order. */
	children(node) {
		const ret = [];
		for (let i = 0; i < node.childNodes.length; i++) {
			const child = node.childNodes[i];
			ret.push(child, ...this.children(child));
		}
		return ret;
	}

	/**
	 * Extra refresh button.
	 * @param {Element} container The field container.
	 */
	prepareRefreshButton(container) {
		var _self = this;
		var inputPhrase = document.createElement("input");
		inputPhrase.setAttribute("type", "button");
		inputPhrase.setAttribute("value", mw.msg('nuxltf-refresh'));
		inputPhrase.addEventListener('click', function() {
			_self.refresh();
		});
		container.appendChild(inputPhrase);
		this.inputPhrase = inputPhrase;
	}

	/** Refresh table cache. */
	refresh() {
		this.tableFilter.preParseItems();
	}
}

//
// on load
//
mw.hook('userjs.ViewFilter.loaded').add(function (ViewFilter) {
	$(function(){
		const filters = new LongTableFilters(ViewFilter);
		
		if (filters.validate()) {
			var portletId = mw.config.get('skin') === 'timeless' ? 'p-pagemisc' : 'p-tb';
			var link = mw.util.addPortletLink(portletId, '#', '💧 ' + mw.msg('nuxltf-action-init'), 't-LongTableFilters', null);
	
			var $link = $(link);
			$link.click((e) => {
				e.preventDefault();
				if ($link.data('waiting')) {
					return;
				}
				$link.data('waiting', true);
	
				filters.init();
	
				setTimeout(function() {
					$link.data('waiting', false);
				}, 500);
			});
		}
	});
});