Hi all,
I've been working on enhancing the Theme extension [1] to add a preference option so that registered users can set their preferred theme [2], just like how they can set a skin and other such options.
The theme selector is shown on Special:Preferences, below the list of available skins, and since themes are nothing but a set of CSS modifications on top of a skin's base CSS, previewing them in real time is easy. Or so I thought, anyway. Turns out that this is a bit more complicated than that. I'm loading the theme-specific CSS module via mw.loader.load(), which seems to append a <style> tag to the <head> of the page. To prevent "stacking" of theme CSS and to ensure that themes will display as intended, I'm using $( 'head style' ).last().remove(); to remove the last added <style> tag from head. This has the unintended side-effect that previewing a theme works only once, and after that the theme CSS is no longer applied because ResourceLoader thinks it has already been loaded.
What would be the proper way to signal to ResourceLoader, "actually I need you to load this module again"?
To test out my patch [2], you'll need a skin which supports themes, such as MonoBook or Vector. Then simply git clone the mediawiki/extensions/Theme repository, apply the patch on top of that, load the extension via wfLoadExtension( 'Theme' ); in LocalSettings.php (you may also need to set $wgDefaultTheme = 'default'; in your LocalSettings.php) and then visit Special:Preferences and you should see the new "Theme" drop-down menu there.
[1] https://www.mediawiki.org/wiki/Extension:Theme [2] https://gerrit.wikimedia.org/r/#/c/359643/
Thanks and regards, -- Jack Phoenix MediaWiki developer
Hi,
On 06/18/2017 01:00 PM, Jack Phoenix wrote:
What would be the proper way to signal to ResourceLoader, "actually I need you to load this module again"?
I don't think ResourceLoader was really designed for the concept of "unloading" modules. Instead I'd suggest scoping the CSS to some class like "theme-name" and then using JS to set and remove that class from <body> (or whatever other element) as needed.
-- Legoktm
On Mon, Jun 19, 2017 at 2:14 AM, Legoktm legoktm.wikipedia@gmail.com wrote:
Hi,
On 06/18/2017 01:00 PM, Jack Phoenix wrote:
What would be the proper way to signal to ResourceLoader, "actually I need you to load this module again"?
I don't think ResourceLoader was really designed for the concept of "unloading" modules. Instead I'd suggest scoping the CSS to some class like "theme-name" and then using JS to set and remove that class from
<body> (or whatever other element) as needed.
I note VE has a similar bug in https://phabricator.wikimedia.org/T156414. But I suspect that the part of that task relating to steps 8 and 9 is/will be low priority.
I don't think "unloading" modules will ever be a thing. While ResourceLoader could be made to keep track of modules' CSS to be able to remove it, we can't "unload" JavaScript modules at all (because their code has already executed), so I'm not sure if we'd want to add such special case.
If you want a slightly less horrible hack than you do now – you could generate an appropriate load.php URL yourself (with &only=styles) and add that in a <link> tag to the page, much like we did back in the old days with importStylesheetURI(). ResourceLoader won't even know the module is loaded. And this way, you can remove the <link> tag later without messing with ResourceLoader's internals.
Thanks for the replies, everyone.
On Mon, Jun 19, 2017 at 8:42 PM, Bartosz Dziewoński matma.rex@gmail.com wrote:
I don't think "unloading" modules will ever be a thing. While ResourceLoader could be made to keep track of modules' CSS to be able to remove it, we can't "unload" JavaScript modules at all (because their code has already executed), so I'm not sure if we'd want to add such special case.
I still think it'd make sense to have some sort of official support for unloading modules' CSS, whether we're talking about CSS-only modules or modules which happen to also include CSS. CSS and JS obviously have different functions, and just like how you use $out->addModules( 'some-module-name' ); to load JS modules and $out->addModuleStyles( 'some-module-name' ); to load CSS modules in PHP, why couldn't there be official support for unloading CSS modules? Or failing that, support for a way to tell ResourceLoader, "yes, I *really* want to reload this module again even if you think that it's already loaded and thus there's no need to make another HTTP request".
If you want a slightly less horrible hack than you do now – you could generate an appropriate load.php URL yourself (with &only=styles) and add that in a <link> tag to the page, much like we did back in the old days with importStylesheetURI(). ResourceLoader won't even know the module is loaded. And this way, you can remove the <link> tag later without messing with ResourceLoader's internals.
Thanks for the suggestion. While I didn't implement this at this time -- simply because I managed to get my hacks working before you posted this message -- I'm sure this is the route I'll have to take in the future. For the time being, I solved the problem by using a cache variable and caching loaded CSS there and instead of trying to (unsuccessfully) call mw.loader.load() twice (or thrice etc.) I first check cache and try to use the cached CSS if possible [1].
As always, code review of both the JS and PHP parts of the patchset [2] is welcome, although the most acute problem (with the JS) is now fixed and legoktm gave me a few good pointers on how to make the PHP part saner. :-)
[1] https://gerrit.wikimedia.org/r/#/c/359643/6..7/ext.theme.livepreview.js [2] https://gerrit.wikimedia.org/r/359643
Thanks and regards, -- Jack Phoenix MediaWiki developer
wikitech-l@lists.wikimedia.org