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 (currentDarkConditional) {
119 var reloadRequired = (conditionalCheck != currentDark)
120 $.cookie(DARK_COOKIE, conditionalCheck, {expires: 365, path: '/'});
121 if (reloadRequired === true) {
122 window.location.reload(true);
123 }
124 }
125
126 if (currentSticky) {
127 window.addEventListener("scroll", function() {
128 var personal = $('#p-personal');
129 if (mw.config.get('wgAction') === 'edit' || window.location.search.includes('veaction')) {
130 // We're on an edit page, do nothing and reset all the stuff
131 if (personal.is(":hidden")) {
132 personal.show();
133 head.removeClass('sticky-hidden');
134 }
135 } else {
136 var targetEle = document.getElementById("mw-head");
137 var head = $('#mw-head');
138 if (window.scrollY > (targetEle.offsetTop + targetEle.offsetHeight)) {
139 if (personal.is(":visible")) {
140 personal.hide();
141 head.addClass('sticky-hidden');
142 }
143 } else {
144 if (personal.is(":hidden")) {
145 personal.show();
146 head.removeClass('sticky-hidden');
147 }
148 }
149 }
150 });
151 // hidden by css when sticky-hidden is not on
152 if (mw.config.get('wgIsMainPage') !== true) {
153 mw.util.addPortletLink(
154 'p-namespaces',
155 '/',
156 'Main Page',
157 'ca-nstab-mainpage',
158 'Visit the main page'
159 );
160 }
161 }
162
163 /**
164 * Used for prompting users who have prefers-color-scheme set to dark
165 * to switch to dark mode (because doing this automatically would
166 * require setting a cookie, prompting this is best for privacy/
167 * legal reasons)
168 **/
169
170 if (rs.hasLocalStorage()) {
171 // This should always be true anyway because browsers that
172 // support prefers-color-scheme have LocalStorage API support
173 if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
174 if (!currentDark) {
175 // Only show if they're not currently using dark mode
176 var alreadyPrompted = localStorage.getItem(prompt)
177 if (alreadyPrompted === null) {
178 // Only show if the localStorage key doesn't exist
179 mw.loader.load( 'ext.gadget.skinTogglesNew-prompt' )
180 }
181 }
182 }
183 }
184 },
185 initForm: function() {
186 // Treat opening the form as having seen the dark mode prompt.
187 if (rs.hasLocalStorage() && localStorage.getItem(prompt) === null) {
188 localStorage.setItem(prompt, 'true')
189 }
190
191 readerSwitch = new OO.ui.ToggleSwitchWidget({
192 value: currentReader,
193 classes: ['reader-toggle'],
194 align: 'right'
195 });
196
197 stickySwitch = new OO.ui.ToggleSwitchWidget({
198 value: currentSticky,
199 classes: ['reader-toggle'],
200 align: 'right'
201 });
202
203 darkConditionalSwitch = new OO.ui.ToggleSwitchWidget({
204 value: currentDarkConditional,
205 classes: ['reader-toggle'],
206 align: 'right'
207 })
208
209 darkSwitch = new OO.ui.ButtonSelectWidget({
210 classes: ['appearance-buttons'],
211 items: [
212 new OO.ui.ButtonOptionWidget({
213 classes: ['light-mode-button'],
214 data: false,
215 label: new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Light</div><div class="button-text-selected"></div>'),
216 }),
217 new OO.ui.ButtonOptionWidget({
218 classes: ['dark-mode-button'],
219 data: true,
220 label:new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Dark</div><div class="button-text-selected"></div>'),
221 //disabled: true
222 }),
223 ]
224 });
225
226 floorSelectAuto = new OO.ui.RadioOptionWidget({
227 data: '_auto',
228 label: 'Auto-detect: '+userLocale
229 });
230 floorSelectUK = new OO.ui.RadioOptionWidget({
231 data: 'UK',
232 label: 'UK'
233 });
234 floorSelectUS = new OO.ui.RadioOptionWidget({
235 data: 'US',
236 label: 'US'
237 });
238
239 floorSelect = new OO.ui.RadioSelectWidget({
240 classes: ['floornumber-select'],
241 items: [
242 floorSelectAuto,
243 floorSelectUK,
244 floorSelectUS
245 ]
246 });
247 floorSelect.selectItemByData(currentFloornumber);
248 floorSelectHelp = 'Changes how floor numbers are displayed on the wiki - whether the numbering begins at 0 (ground) or 1.';
249 if (!rs.hasLocalStorage()) {
250 floorSelect.setDisabled(true);
251 floorSelectHelp = 'This option requires local storage to be supported and enabled in your browser.';
252 }
253 floorSelectAuto.$element.attr('title', 'Automatically detect the type to use from your browser.');
254 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.');
255 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.');
256
257 darkSwitch.setDisabled(darkConditionalSwitch.getValue())
258
259 darkConditionalSwitch.on('change', function() {
260 darkSwitch.setDisabled(darkConditionalSwitch.getValue())
261 })
262
263 stickySwitch.setDisabled(readerSwitch.getValue())
264 readerSwitch.setDisabled(stickySwitch.getValue())
265
266 readerSwitch.on('change', function() {
267 if (readerSwitch.getValue() === true) {
268 stickySwitch.setValue(false)
269 }
270 stickySwitch.setDisabled(readerSwitch.getValue())
271 })
272
273 stickySwitch.on('change', function() {
274 if (stickySwitch.getValue() === true) {
275 readerSwitch.setValue(false)
276 }
277 readerSwitch.setDisabled(stickySwitch.getValue())
278 })
279
280 darkSwitch.selectItemByData(currentDark);
281
282 applyButton = new OO.ui.ButtonInputWidget({
283 label: 'Save',
284 flags: ['primary', 'progressive'],
285 classes: ['skin-save-button']
286 });
287
288 applyButton.on('click', function(){
289 $.cookie(READER_COOKIE, readerSwitch.getValue(), {expires: 365, path: '/'});
290 $.cookie(DARK_COOKIE_CONDITIONAL, darkConditionalSwitch.getValue(), {expires: 365, path: '/'});
291 $.cookie(STICKY_HEADER_COOKIE, stickySwitch.getValue(), {expires: 365, path: '/'});
292
293 var darkval = darkSwitch.findSelectedItem(),
294 darkc = false,
295 requireReload = false;
296
297 if ((readerSwitch.getValue() !== currentReader) || (stickySwitch.getValue() !== currentSticky)) {
298 requireReload = true;
299 }
300
301 if (darkConditionalSwitch.getValue() === false) {
302 if (darkval !== null) {
303 darkc = darkval.getData();
304 }
305 } else if (darkConditionalSwitch.getValue() === true) {
306 darkc = conditionalCheck
307 }
308
309 $.cookie(DARK_COOKIE, darkc, {expires: 365, path: '/'});
310
311 if (rs.hasLocalStorage()) {
312 window.localStorage.setItem(FLOORNUMBER_LS, floorSelect.findSelectedItem().getData());
313 }
314
315 if (darkc === true) {
316 mw.loader.using(['ext.gadget.darkmode']).then(function() {
317 $('body').addClass('wgl-darkmode')
318 $('body').removeClass('wgl-lightmode')
319 });
320 } else {
321 $('body').addClass('wgl-lightmode')
322 $('body').removeClass('wgl-darkmode')
323 }
324
325 if (requireReload === true) {
326 window.location.reload(true);
327 } else {
328 window.OOUIWindowManager.closeWindow('skin')
329 }
330 });
331
332 cancelButton = new OO.ui.ButtonInputWidget({ label: 'Cancel', flags: 'destructive'});
333
334 $content = $('<div>');
335 $content
336 .addClass('appearance-modal tile')
337 .append(
338 $('<h2>').text('Appearance'),
339 $('<div>')
340 .addClass('appearance-buttons')
341 .append(darkSwitch.$element),
342 $('<div>')
343 .addClass('reader-mode')
344 .append(
345 darkConditionalSwitch.$element,
346 $('<div>').addClass('setting-header dark-conditional-header').text('Automatic dark mode'),
347 $('<p>').addClass('dark-conditional-desc').text('Automatically switch to dark mode from 19:00 to 7:00 local time. Disables the manual setting above.'),
348 readerSwitch.$element,
349 $('<div>').addClass('setting-header reader-mode-header').text('Reader mode'),
350 $('<p>').addClass('reader-mode-desc').text('Increase the font size and switch the wiki to a fixed-width layout. Incompatible with sticky headers.'),
351 stickySwitch.$element,
352 $('<div>').addClass('setting-header sticky-header-header').text('Sticky header'),
353 $('<p>').addClass('sticky-header-desc').text('Pin the navigation bar and search to the top when scrolling. Incompatible with reader mode.'),
354 floorSelect.$element,
355 $('<div>').addClass('setting-header floornumber-header').text('Floor numbering'),
356 $('<p>').addClass('floornumber-desc').text(floorSelectHelp)
357 ),
358 $('<div>')
359 .addClass('appearance-save')
360 .append(
361 $('<p>').addClass('save-button-desc').html('Saving these changes will reload the page and set <a href="https://weirdgloop.org/privacy">personalisation cookies</a>.'),
362 $('<div>').addClass('save-button-container')
363 .append(applyButton.$element)
364 .append(cancelButton.$element)
365 )
366 );
367
368 var initModal = function (modal) {
369 modal.$body.append( $content );
370 cancelButton.on('click', function(modal){window.OOUIWindowManager.closeWindow(modal);}, [modal]);
371 };
372
373 rs.createOOUIWindow('skin', 'Appearance settings', {size: 'large', classes: ['rsw-skin-toggle-popup']}, initModal, true);
374
375 formMade = true;
376 }
377 }
378
379 mw.loader.using(['ext.gadget.rsw-util'], function () {
380 $(self.init);
381 })
382
383 }(jQuery, mediaWiki, rswiki));