MediaWiki talk:Gadget-visibilityToggling.js

From Wiktionary, the free dictionary
Latest comment: 4 years ago by Tacsipacsi in topic Hooks
Jump to navigation Jump to search

This is the page for an obsolete gadget. Post new discussions on MediaWiki talk:Gadget-defaultVisibilityToggles.js (about particular toggles) and MediaWiki talk:Gadget-VisibilityToggles.js (about the VisibilityToggles object).

Cleanup[edit]

@Erutuon I cleaned and reworked the visibility toggling code a bit to support the "hide synonyms use case" as required by the recent vote. Could you take a look and drop it in to see if everything still works? I've made quite a few changes so something will probably break. User:Jberkel/visibilityToggles.jsJberkel 03:20, 6 January 2019 (UTC)Reply

@Jberkel: Hmm, this complicates things because I also made a new version in User:Erutuon/scripts/sandbox.js. (I copied it to MediaWiki:Gadget-VisibilityToggles.js, but it is not actually used yet.) I've tested my version somewhat by using it with User:Erutuon/scripts/listSwitcher.js. It is longer and more complex but may be more readable than the current version.
What are the main differences in your version? The main thing I can see is the visibleByDefault argument which allows a script to make the element visible to start with, and because of that a different key–value cookie format that allows a user to override this.
I've made a change so that you should be able to test your version of VisibilityToggles now, as long as you define VisibilityToggles before the code on this page runs. — Eru·tuon 04:27, 6 January 2019 (UTC)Reply
@Erutuon: Looks like you rewrote it from scratch and modularized it which is good. You're right, the cookie format is changed so that "don't show this" is now explicit. I tried to clean up the code a bit by moving things into variables / functions, but there's no point continuing with that if the whole thing is going to be replaced. I modified the old User:Ungoliant_MMDCCLXIV/synshide.js to use this new toggle code, User:Jberkel/semhide.js which seems to work well so far. How can I run my code before MediaWiki:common.js? Another thing I wondered, what kind of JS features can be used safely, is this documented somewhere? – Jberkel 09:22, 6 January 2019 (UTC)Reply
@Jberkel: By trial and error I found that my common.js and scripts it imports will run before MediaWiki:Common.js and the scripts it imports if the importing isn't done inside $(function () { ... }), which delays the execution. It would be nice to find some clear explanation of how this works, though.
I was also wondering about what JavaScript version I could use but didn't have success finding any information, and Giorgi Eufshi pointed me to mw:Compatibility. My takeaway was that we should use JavaScript features that are supported by the browsers that a significant number of people are using to visit MediaWiki sites. So I've been removing some stuff related to IE10 or below, and have been avoiding ES6 features except in my own scripts. I look at MDN's browser compatibility tables a lot. — Eru·tuon 10:18, 6 January 2019 (UTC)Reply
@Erutuon: Ah, if wrapped inside $(function () { ... }) it only executes after DOMContentLoaded, by which time MediaWiki:common.js has already done some things (I noticed that some resources are loaded lazily, after the DOM, but not all of them). I can't find documentation about Mediawiki's script loading order. When I use ?debug=true and put breakpoints in both global and local common.js I noticed that the global version gets loaded first. It's also likely that there are no guarantees given at all since most of the load happens asynchronously. Anyway, I have a way to test the code now. Perhaps I should continue with your version? – Jberkel 23:00, 6 January 2019 (UTC)Reply
Yes, you should, because I would like to install my version ultimately. I've attempted to add support for the new cookie format and the extra parameter to register for default status (in User:Erutuon/scripts/sandbox.js, that is). However, the default status parameter functions differently than in your version: it is only used when a call to register creates a new ToggleCategory. The function that ultimately uses the parameter in both versions is currentStatus (getInitialStatus in my version), which I think does not need to be called for every toggle in a toggle category, because it only looks at the location bar and at cookie values, and neither of those should change between calls. So basically the default status parameter is ignored except when registering the first toggle in a category. But that might be counterintuitive to someone who is using the register function. Not sure. — Eru·tuon 23:27, 6 January 2019 (UTC)Reply
@Erutuon: I tried your version and it works well so far, the code is so much nicer to work with. The only confusing aspect (naming) is ToggleCategory.status and Toggle.status which is easily mixed up. I also found and fixed a couple of smaller issues and simplified some parts (User:Jberkel/visibilityToggles2.js). At the moment, when all toggles in a page change the cookie is changed as well, I don't think that's intentional (see comment in ToggleCategory.prototype.updateToggle). – Jberkel 03:08, 8 January 2019 (UTC)Reply
@Jberkel: Hmm, it looks like you're right, in the current version the cookie only changes if the sidebar toggle is clicked. So the cookie-setting should be moved to toggleCategoryOnclick to maintain the same behavior. — Eru·tuon 03:31, 8 January 2019 (UTC)Reply

I was thinking of using ObjectStorage in MediaWiki:Gadget-StorageUtils.js. Then the data would be in JSON format, in either localStorage or cookies depending on which is available. But probably this should wait till I figure out how to test and add my new version of the gadget, which will make ObjectStorage easier to use. — Eru·tuon 20:04, 8 January 2019 (UTC)Reply

@Erutuon: Not sure, the underlying LocalStorage operates on string level as well, so this would just add more code for no extra benefit. I wrote some tests for the visibility toggling (run locally on nodejs), and found another problem with the regexp, but it only affects the "migration" of the cookie: Special:Diff/51221391/51235786. Other than that everything seems to work quite well. I'm done with what I wanted to do, so I'll just do some more testing, and let you handle the integration. (on a second thought, a well-tested object storage would avoid the ad hoc regular expression shenanigans) – Jberkel 13:33, 10 January 2019 (UTC)Reply
Yeah, the main benefit is that we would only have to call JSON.parse and JSON.stringify. I'm also uncertain about the cookie format using a semicolon as separator. The cookie format has presumably worked so far in most cases, but MDN indicates some noncompliant browsers might use just a semicolon (not semicolon and space) to separate cookie entries, in which case weird stuff might happen (as it might if a toggle category name started with whitespace). JSON in localStorage (as most users probably have localStorage available) would not have undefined behavior. — Eru·tuon 22:56, 10 January 2019 (UTC)Reply
Oh, never mind, naturally mw.cookie URL-encodes semicolons and equals signs so there's no problem. I'm sure I looked at document.cookie at one point, but forgot... — Eru·tuon 10:04, 11 January 2019 (UTC)Reply
@Erutuon: It looks like mw.cookie mostly delegates to jquery.cookie (which is not part of jquery core and apparently no longer maintained). Localstorage seems to be widely supported (~94% caniuse.com), I'm wondering if the cookie fallback is really necessary. Let me know if you need help with testing. – Jberkel 11:27, 11 January 2019 (UTC)Reply
@Jberkel: User:Erutuon/scripts/VisibilityToggles.js now converts the old cookie to an object and deletes it, and saves a JSON version to localStorage. I would appreciate your taking a look at it to see if I missed anything. — Eru·tuon 20:51, 13 January 2019 (UTC)Reply
Hmm, now that this is loaded as a gadget, my version of VisibilityToggles is not used by this script. I guess gadgets are executed before user JavaScript. — Eru·tuon 21:26, 13 January 2019 (UTC)Reply
@Erutuon: Looks like the regular expression won't match the old format (without =). BooleanStorage.rawValue returns an empty string if not set, this will fail JSON parsing with SyntaxError, which isn't handled at at all. L198 (cookieValue) should probably be renamed. I'll look a bit more in detail tomorrow. – Jberkel 22:49, 13 January 2019 (UTC)Reply
@Jberkel: Yes, the regular expression is only meant to test for your format with =[01]. I've added an attempt at testing for JSON errors. — Eru·tuon 23:06, 13 January 2019 (UTC)Reply
@Erutuon: ok, i've done some more testing and simplified (I hope) the code a bit. I'm unsure about VisibilityToggles.prototype.addToggleCategory, does this need to be publicly exposed? I also can't get the code to replace the existing VisibilityToggles. – Jberkel 13:35, 16 January 2019 (UTC)Reply
@Jberkel: Okay, it was a simple fix: use global rather than local VisibilityToggles in the functions that set up the toggles. Took me a while for that idea to occur to me unfortunately. Yeah, VisibilityToggles.prototype.addToggleCategory doesn't need to be exposed, but I haven't been enforcing a distinction between public and private functions and variables. — Eru·tuon 22:32, 16 January 2019 (UTC)Reply
@Erutuon: JavaScript and its global variable madness… works better, but sometimes the main code still gets loaded first. – Jberkel 23:45, 16 January 2019 (UTC)Reply
@Jberkel: Huh, VisibilityToggles should be replaced as long as user JS is loaded before the document is ready (before callback in $(callback) is called). But maybe that isn't always true. If it isn't, another technique would be to set user as a dependency for visibilityToggling in MediaWiki:Gadgets-definition. That would be clearer at least. I am concerned it would make visibility toggles take even longer to load. — Eru·tuon 00:38, 17 January 2019 (UTC)Reply
I've split MediaWiki:Gadget-visibilityToggling.js into MediaWiki:Gadget-VisibilityToggles.js, which defines VisibilityToggles, and MediaWiki:Gadget-defaultVisibilityToggles.js, which uses it. I had planned to do that but had to get some familiarity with how gadgets work, and it seemed simpler to do it before introducing a new version of VisibilityToggles. — Eru·tuon 06:02, 17 January 2019 (UTC)Reply
@Erutuon: I spent the last few hours trying to understand MediaWiki's resource loading, and now it's somewhat clearer… The user js gets loaded very early, but any code loaded from there might get executed after the gadget has initialized. The only way around this is to put the visibilityToggle code right into the user js. From the module loading point of view the user and site-wide Common.js are treated as special cases, so it's good to use the gadget mechanism instead. It would be great if users had customization points for gadgets, but that would make the loading logic even more complicated :) I also noticed that the total amount of JS code loaded is in the 2MB range, already minified, no wonder the site feels sluggish. LocalStorage is required for the JS base system to start (startup.js:67), so we can assume that it is present, which means the availability check can probably be removed. MediaWiki:Gadget-visibilityToggling.js is no longer used now? Maybe it should be deleted (+ this page moved). – Jberkel 15:14, 17 January 2019 (UTC)Reply
@Jberkel: Thanks, that is good to know. I've removed the localStorage check from my version. I suppose localStorage could be present but not functioning. At the very least mw.storage protects against errors. I've added user as a dependency for VisibilityToggles, and now my VisibilityToggles seems to be replacing the default one. If this slows things down, it can be reverted. I am guessing that the user module includes the user's common.js and common.css and probably skin JavaScript and CSS files too, but I did not find a clear description on mediawiki.org. — Eru·tuon 19:50, 17 January 2019 (UTC)Reply
@Erutuon: I think the explicit user module dependency is not necessary, it looks like the user module is always loaded, not with the mw.loader dependency mechanism but rather injected into the page (OutputPage.php#L3109). If you look at the head tag of the HTML page, in one of the script tags you'll see a call to mw.loader.load with the user module. That explains why it's loaded so early. The VisibilityToggle replacement might not work, depending on how fast the connection is, and how the requests are queued up by the browser. A blocking/synchronous request would be needed, but I'm not sure that's even possible with JS. (update: it is possible, although deprecated: Special:Diff/51275959/51277380) – Jberkel 21:39, 17 January 2019 (UTC)Reply
@Jberkel: Well, I didn't look at the source code in as much depth as you did, but I assumed that the "user" module was not automatically loaded because I saw it listed as a dependency in a gadget on Wikipedia, and including it as a dependency or loading it with mw.loader.using does not fail as it does for mediawiki.config, which is definitely loaded by default (mw.loader.using("mediawiki.config").done(() => console.log("success")).fail(() => console.log("failure"))). — Eru·tuon 19:57, 28 January 2019 (UTC)Reply
@Erutuon: It's probably needed to avoid a race condition (in case the user code hasn't [fully] loaded when the gadget loads), or the user loading mechanism changed and the dependencies haven't been updated. Why would loading mediawiki.config fail? I don't see anything in the docs/code which indicates that already loaded modules fail that call. – Jberkel 20:31, 28 January 2019 (UTC)Reply
@Jberkel: Well, mediawiki.config isn't in mw.loader.moduleRegistry so I guess it isn't a loadable module. The function that populates it is added to the queue by the inline script at the top of the page (starting with window.RLQ), so it might be loaded before everything else. In the same function the "user" module's status is set to "loading", though I can't glean any more information about it from there. — Eru·tuon 21:25, 28 January 2019 (UTC)Reply
@Erutuon: Some mediawiki configuration is serialized in startup.js, which is probably the global, site-specific MediaWiki config. The inlined part is user/page specific (it contains wgUsername, wgArticleId etc.) The state is set to "loading" because the loading happens just afterwards (why the state isn't set is in the loading function is beyond me). The actual user loading call looks like mw.loader.load("/w/load.php?debug=false\u0026lang=en\u0026modules=user\u0026skin=vector\u0026user=USERNAME\u0026version=HASH");. That resource returns mw.loader.implement("user@HASH", "... actual code as string"); which stores the script and updates the state. A later call to doPropagation() will then execute it. – Jberkel 22:15, 28 January 2019 (UTC)Reply
@Jberkel, Erutuon Hi! I stumbled upon this discussion whilst poking at my own global.js - I was recently helped to fix a code-snippet I use to auto-expand any Enhanced-RC/Watchlist page-clusters (i.e. the grouped show-all-changes setup, which auto-collapse by default). I don't understand most of it (IANAD), but these pointers might possibly be of help or interest to you: Here's a diff between my old version (which often loaded too soon) and the fixed version, and the docs that were consulted to determine that edit are at https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook I don't see that site mentioned above and I thought it might help. -- That is all. :-) Quiddity (talk) 03:02, 5 February 2019 (UTC)Reply
@Quiddity: thanks for the pointer, I noticed the hook mechanism. Perhaps we could use it in the visibility toggling code to provide further customization points (by firing a special event when the toggles get added). – Jberkel 09:09, 5 February 2019 (UTC)Reply
Just a note: User:Yair rand/fastbrowsing.js requires the ability to empty the list of toggles in the sidebar. I doubt it's used anymore because it hasn't been edited in seven and a half years and nobody's JavaScript page imports it (User: insource:"fastbrowsing"). So if anyone wants to revive that script, VisibilityToggles will need a sidebar toggle-emptying method. — Eru·tuon 21:31, 19 February 2019 (UTC)Reply
I've added the new version of VisibilityToggles from User:Erutuon/scripts/VisibilityToggles.js to MediaWiki:Gadget-VisibilityToggles.js. If all goes well, I can also install User:Jberkel/semhide.js. — Eru·tuon 21:46, 26 February 2019 (UTC)Reply

Hooks[edit]

I haven’t read through the above discussion, but using hooks as a consumer should be a must-have: the current version of the script doesn’t work—for example—after VisualEditor page save and with the live preview (Show preview without reloading the page at Preferences / Editing), meaning the boxes’ content cannot be seen (as the CSS collapsing them works just fine). Changing the end to the following worked for me:

/* == Apply three functions defined above == */
mw.loader.using('mediawiki.cookie', function() {
	mw.hook('wikipage.content').add(function($content) {
		// NavToggles
		$('.NavFrame', $content).each(function(){
			createNavToggle(this);
		});

		//quotes
		if (mw.config.get('wgNamespaceNumber') === 0) {
			// First, find all the ordered lists, i.e. all the series of definitions.
			$('ol > li', $content).each(function(){
				setupHiddenQuotes(this);
			});
		}

		//view switching
		$('.vsSwitcher', $content).each(function(){
			viewSwitching($(this));
		});
	});
});

})();
// </nowiki>

(Apart from changing $.ready to mw.hook('wikipage.content'), I also simplified the NavFrame code to make full use of jQuery, and removed the LivePreviewDone listener. I’m not sure what this latter wanted to do, but based on the name, it was a hack for the live preview. Live preview clearly doesn’t work currently.) —Tacsipacsi (talk) 11:30, 15 March 2019 (UTC)Reply

@Tacsipacsi: Thanks you very much for this suggestion. I don't use the Visual Editor or the live preview, so I hadn't considered how the code in MediaWiki:Gadget-defaultVisibilityToggles.js would interact with them. Here's a version with the list-switcher added. I've reduced it to just the hook part.
mw.hook('wikipage.content').add(function($content) {
	// NavToggles
	$('.NavFrame', $content).each(function(){
		createNavToggle(this);
	});

	//quotes
	if (mw.config.get('wgNamespaceNumber') === 0) {
		// First, find all the ordered lists, i.e. all the series of definitions.
		$('ol > li', $content).each(function(){
			setupHiddenQuotes(this);
		});
	}

	//view switching
	$('.vsSwitcher', $content).each(function(){
		viewSwitching($(this));
	});

	// list switching
	$(".list-switcher", $content).each(function () {
		enableListSwitch($(this), window.listSwitcherRowCount);
	});
});
I will test this before enabling it. — Eru·tuon 20:13, 15 March 2019 (UTC)Reply
@Erutuon: Any news on this? I’m still unable to see the navframes’ content on preview without hacking the CSS from the browser developer tools, which is quite far from being user-friendly… —Tacsipacsi (talk) 10:12, 16 April 2020 (UTC)Reply
@Tacsipacsi: Sorry, I didn't figure out a way to test the code and then I forgot about the request. I may be able to look into it soon. — Eru·tuon 04:28, 24 April 2020 (UTC)Reply
@Tacsipacsi: I enabled the new code for myself for a while and didn't notice any problems, so I've enabled it for everyone. — Eru·tuon 18:46, 12 May 2020 (UTC)Reply
@Erutuon: Thanks, it works great! Probably I will edit more here from now on. :) (By the way, I think everyone should give live preview a try. VisualEditor has a quite different philosophy on editing, and doesn’t work well with the translation tables’ architecture here, so it’s a pain to use it on Wiktionary; live preview, however, simply makes preview—both preview of the page and preview of the diff—much faster. Some things need to be updated to be compatible with it, but that update most of the time means such trivial changes like here. And if interface admins use the live preview everyday, broken things will be updated much faster. ;)) —Tacsipacsi (talk) 15:39, 14 May 2020 (UTC)Reply