MediaWiki:Gadget-skinTogglesNew.js

From Old School Near-Reality Wiki
Revision as of 20:04, 4 October 2022 by Bawolff (talk | contribs) (try and set the body class manually to emulate whatever extension powers the other wikis dark mode)
Jump to navigation Jump to search

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));