MediaWiki:Gadget-skinTogglesNew.js
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.
1 /**
2 * Toggles for skin cookies
3 *
4 * @author Gaz Lloyd
5 * @author JaydenKieran
6 *
7 */
8 ;(function($, mw, rs){
9 var READER_COOKIE = 'readermode',
10 DARK_COOKIE = 'darkmode',
11 DARK_COOKIE_CONDITIONAL = 'darkmode_conditional',
12 STICKY_HEADER_COOKIE = 'stickyheader',
13 FLOORNUMBER_LS = 'floornumber_display',
14 currentReader = $.cookie(READER_COOKIE) === 'true',
15 currentDark = $.cookie(DARK_COOKIE) === 'true',
16 currentDarkConditional = $.cookie(DARK_COOKIE_CONDITIONAL) === 'true',
17 currentSticky = $.cookie(STICKY_HEADER_COOKIE) === 'true',
18 currentFloornumber = '_auto',
19 prompt = 'dark_prompt',
20 now = new Date(),
21 hour = now.getHours(),
22 conditionalCheck = (hour >= 19 || hour < 7),
23 popupButton,
24 readerSwitch,
25 darkConditionalSwitch,
26 darkSwitch,
27 stickySwitch,
28 floorSelect,
29 floorSelectAuto,
30 floorSelectUK,
31 floorSelectUS,
32 applyButton,
33 cancelButton,
34 portletLink,
35 $content,
36 formMade = false,
37 userLocale = 'UK',
38 flsetting,
39 browserLocale,
40 floorSelectHelp;
41
42 var self = {
43 init: function () {
44 if (rs.hasLocalStorage()) {
45 currentFloornumber = window.localStorage.getItem(FLOORNUMBER_LS);
46 if (currentFloornumber == null) {
47 currentFloornumber = '_auto';
48 }
49 }
50 flsetting = currentFloornumber;
51 if (window.navigator.languages && window.navigator.languages.length) {
52 browserLocale = window.navigator.languages[0];
53 } else {
54 browserLocale = navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
55 }
56 switch (browserLocale) {
57 // all langs in -US or -CA
58 case 'en-US':
59 case 'es-US':
60 case 'en-CA':
61 case 'fr-CA':
62 userLocale = 'US';
63 break;
64 }
65 if (currentFloornumber == '_auto') {
66 flsetting = userLocale;
67 }
68 switch (flsetting) {
69 case 'US':
70 flsetting = 'floornumber-setting-us';
71 break;
72 case 'UK':
73 default:
74 flsetting = 'floornumber-setting-gb';
75 break;
76 }
77 $('body').addClass(flsetting);
78
79 portletLink = mw.util.addPortletLink(
80 'p-personal',
81 '',
82 '',
83 'pt-skin-toggles',
84 'Your appearance settings',
85 null,
86 $('#pt-userpage, #pt-anonuserpage')
87 );
88
89 $(portletLink).find('a').addClass('oo-ui-icon-advanced').add('.floor-convention').click(function(e) {
90 e.preventDefault();
91 if (!formMade) {
92 mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.initForm);
93 } else {
94 window.OOUIWindowManager.openWindow('skin');
95 }
96 });
97
98
99 if (currentReader) {
100 mw.util.addPortletLink(
101 'p-namespaces',
102 '/',
103 'Menu',
104 'ca-reader-menu'
105 );
106
107 // can't use the nextnode parameter in addPortletLink
108 // because the id of the first tab varies
109 $('#ca-reader-menu')
110 .prependTo('#p-namespaces ul');
111
112 // move sidebar
113 $('#mw-panel')
114 .attr('id', 'ca-reader-dropdown')
115 .appendTo('#ca-reader-menu');
116 }
117
118 if (CurrentDark ) {
119 $('body').addClass('wgl-darkmode');
120 }
121 if (currentDarkConditional) {
122 var reloadRequired = (conditionalCheck != currentDark)
123 $.cookie(DARK_COOKIE, conditionalCheck, {expires: 365, path: '/'});
124 if (reloadRequired === true) {
125 window.location.reload(true);
126 }
127 }
128
129 if (currentSticky) {
130 window.addEventListener("scroll", function() {
131 var personal = $('#p-personal');
132 if (mw.config.get('wgAction') === 'edit' || window.location.search.includes('veaction')) {
133 // We're on an edit page, do nothing and reset all the stuff
134 if (personal.is(":hidden")) {
135 personal.show();
136 head.removeClass('sticky-hidden');
137 }
138 } else {
139 var targetEle = document.getElementById("mw-head");
140 var head = $('#mw-head');
141 if (window.scrollY > (targetEle.offsetTop + targetEle.offsetHeight)) {
142 if (personal.is(":visible")) {
143 personal.hide();
144 head.addClass('sticky-hidden');
145 }
146 } else {
147 if (personal.is(":hidden")) {
148 personal.show();
149 head.removeClass('sticky-hidden');
150 }
151 }
152 }
153 });
154 // hidden by css when sticky-hidden is not on
155 if (mw.config.get('wgIsMainPage') !== true) {
156 mw.util.addPortletLink(
157 'p-namespaces',
158 '/',
159 'Main Page',
160 'ca-nstab-mainpage',
161 'Visit the main page'
162 );
163 }
164 }
165
166 /**
167 * Used for prompting users who have prefers-color-scheme set to dark
168 * to switch to dark mode (because doing this automatically would
169 * require setting a cookie, prompting this is best for privacy/
170 * legal reasons)
171 **/
172
173 if (rs.hasLocalStorage()) {
174 // This should always be true anyway because browsers that
175 // support prefers-color-scheme have LocalStorage API support
176 if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
177 if (!currentDark) {
178 // Only show if they're not currently using dark mode
179 var alreadyPrompted = localStorage.getItem(prompt)
180 if (alreadyPrompted === null) {
181 // Only show if the localStorage key doesn't exist
182 mw.loader.load( 'ext.gadget.skinTogglesNew-prompt' )
183 }
184 }
185 }
186 }
187 },
188 initForm: function() {
189 // Treat opening the form as having seen the dark mode prompt.
190 if (rs.hasLocalStorage() && localStorage.getItem(prompt) === null) {
191 localStorage.setItem(prompt, 'true')
192 }
193
194 readerSwitch = new OO.ui.ToggleSwitchWidget({
195 value: currentReader,
196 classes: ['reader-toggle'],
197 align: 'right'
198 });
199
200 stickySwitch = new OO.ui.ToggleSwitchWidget({
201 value: currentSticky,
202 classes: ['reader-toggle'],
203 align: 'right'
204 });
205
206 darkConditionalSwitch = new OO.ui.ToggleSwitchWidget({
207 value: currentDarkConditional,
208 classes: ['reader-toggle'],
209 align: 'right'
210 })
211
212 darkSwitch = new OO.ui.ButtonSelectWidget({
213 classes: ['appearance-buttons'],
214 items: [
215 new OO.ui.ButtonOptionWidget({
216 classes: ['light-mode-button'],
217 data: false,
218 label: new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Light</div><div class="button-text-selected"></div>'),
219 }),
220 new OO.ui.ButtonOptionWidget({
221 classes: ['dark-mode-button'],
222 data: true,
223 label:new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Dark</div><div class="button-text-selected"></div>'),
224 //disabled: true
225 }),
226 ]
227 });
228
229 floorSelectAuto = new OO.ui.RadioOptionWidget({
230 data: '_auto',
231 label: 'Auto-detect: '+userLocale
232 });
233 floorSelectUK = new OO.ui.RadioOptionWidget({
234 data: 'UK',
235 label: 'UK'
236 });
237 floorSelectUS = new OO.ui.RadioOptionWidget({
238 data: 'US',
239 label: 'US'
240 });
241
242 floorSelect = new OO.ui.RadioSelectWidget({
243 classes: ['floornumber-select'],
244 items: [
245 floorSelectAuto,
246 floorSelectUK,
247 floorSelectUS
248 ]
249 });
250 floorSelect.selectItemByData(currentFloornumber);
251 floorSelectHelp = 'Changes how floor numbers are displayed on the wiki - whether the numbering begins at 0 (ground) or 1.';
252 if (!rs.hasLocalStorage()) {
253 floorSelect.setDisabled(true);
254 floorSelectHelp = 'This option requires local storage to be supported and enabled in your browser.';
255 }
256 floorSelectAuto.$element.attr('title', 'Automatically detect the type to use from your browser.');
257 floorSelectUK.$element.attr('title', 'The numbering used in the UK, Europe, and many Commonwealth countries: entrance on the ground floor, then above that is 1st floor, 2nd floor, etc.');
258 floorSelectUS.$element.attr('title', 'The numbering used in the US and Canada: entrance on the 1st floor, then above that is 2nd floor, 3rd floor, etc.');
259
260 darkSwitch.setDisabled(darkConditionalSwitch.getValue())
261
262 darkConditionalSwitch.on('change', function() {
263 darkSwitch.setDisabled(darkConditionalSwitch.getValue())
264 })
265
266 stickySwitch.setDisabled(readerSwitch.getValue())
267 readerSwitch.setDisabled(stickySwitch.getValue())
268
269 readerSwitch.on('change', function() {
270 if (readerSwitch.getValue() === true) {
271 stickySwitch.setValue(false)
272 }
273 stickySwitch.setDisabled(readerSwitch.getValue())
274 })
275
276 stickySwitch.on('change', function() {
277 if (stickySwitch.getValue() === true) {
278 readerSwitch.setValue(false)
279 }
280 readerSwitch.setDisabled(stickySwitch.getValue())
281 })
282
283 darkSwitch.selectItemByData(currentDark);
284
285 applyButton = new OO.ui.ButtonInputWidget({
286 label: 'Save',
287 flags: ['primary', 'progressive'],
288 classes: ['skin-save-button']
289 });
290
291 applyButton.on('click', function(){
292 $.cookie(READER_COOKIE, readerSwitch.getValue(), {expires: 365, path: '/'});
293 $.cookie(DARK_COOKIE_CONDITIONAL, darkConditionalSwitch.getValue(), {expires: 365, path: '/'});
294 $.cookie(STICKY_HEADER_COOKIE, stickySwitch.getValue(), {expires: 365, path: '/'});
295
296 var darkval = darkSwitch.findSelectedItem(),
297 darkc = false,
298 requireReload = false;
299
300 if ((readerSwitch.getValue() !== currentReader) || (stickySwitch.getValue() !== currentSticky)) {
301 requireReload = true;
302 }
303
304 if (darkConditionalSwitch.getValue() === false) {
305 if (darkval !== null) {
306 darkc = darkval.getData();
307 }
308 } else if (darkConditionalSwitch.getValue() === true) {
309 darkc = conditionalCheck
310 }
311
312 $.cookie(DARK_COOKIE, darkc, {expires: 365, path: '/'});
313
314 if (rs.hasLocalStorage()) {
315 window.localStorage.setItem(FLOORNUMBER_LS, floorSelect.findSelectedItem().getData());
316 }
317
318 if (darkc === true) {
319 mw.loader.using(['wg.darkmode']).then(function() {
320 $('body').addClass('wgl-darkmode')
321 $('body').removeClass('wgl-lightmode')
322 });
323 } else {
324 $('body').addClass('wgl-lightmode')
325 $('body').removeClass('wgl-darkmode')
326 }
327
328 if (requireReload === true) {
329 window.location.reload(true);
330 } else {
331 window.OOUIWindowManager.closeWindow('skin')
332 }
333 });
334
335 cancelButton = new OO.ui.ButtonInputWidget({ label: 'Cancel', flags: 'destructive'});
336
337 $content = $('<div>');
338 $content
339 .addClass('appearance-modal tile')
340 .append(
341 $('<h2>').text('Appearance'),
342 $('<div>')
343 .addClass('appearance-buttons')
344 .append(darkSwitch.$element),
345 $('<div>')
346 .addClass('reader-mode')
347 .append(
348 darkConditionalSwitch.$element,
349 $('<div>').addClass('setting-header dark-conditional-header').text('Automatic dark mode'),
350 $('<p>').addClass('dark-conditional-desc').text('Automatically switch to dark mode from 19:00 to 7:00 local time. Disables the manual setting above.'),
351 readerSwitch.$element,
352 $('<div>').addClass('setting-header reader-mode-header').text('Reader mode'),
353 $('<p>').addClass('reader-mode-desc').text('Increase the font size and switch the wiki to a fixed-width layout. Incompatible with sticky headers.'),
354 stickySwitch.$element,
355 $('<div>').addClass('setting-header sticky-header-header').text('Sticky header'),
356 $('<p>').addClass('sticky-header-desc').text('Pin the navigation bar and search to the top when scrolling. Incompatible with reader mode.'),
357 floorSelect.$element,
358 $('<div>').addClass('setting-header floornumber-header').text('Floor numbering'),
359 $('<p>').addClass('floornumber-desc').text(floorSelectHelp)
360 ),
361 $('<div>')
362 .addClass('appearance-save')
363 .append(
364 $('<p>').addClass('save-button-desc').html('Saving these changes will reload the page and set <a href="https://weirdgloop.org/privacy">personalisation cookies</a>.'),
365 $('<div>').addClass('save-button-container')
366 .append(applyButton.$element)
367 .append(cancelButton.$element)
368 )
369 );
370
371 var initModal = function (modal) {
372 modal.$body.append( $content );
373 cancelButton.on('click', function(modal){window.OOUIWindowManager.closeWindow(modal);}, [modal]);
374 };
375
376 rs.createOOUIWindow('skin', 'Appearance settings', {size: 'large', classes: ['rsw-skin-toggle-popup']}, initModal, true);
377
378 formMade = true;
379 }
380 }
381
382 mw.loader.using(['ext.gadget.rsw-util'], function () {
383 $(self.init);
384 })
385
386 }(jQuery, mediaWiki, rswiki));