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