MediaWiki:Gadget-skinTogglesNew.js: Difference between revisions

From EverVoid Wiki
Jump to navigation Jump to search
(Undo revision 263 by Carmin (talk))
Tag: Undo
(New updated for 139.6)
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
/**
/**
  * Toggles for skin cookies
  * Toggles for various appearance-related cookies, including the current theme.
  *  
  *  
  * @author Gaz Lloyd
  * @author Gaz Lloyd
  * @author JaydenKieran
  * @author Jayden
  *  
  *  
  */
  */
;(function($, mw, rs){
;(function($, mw, rs){
var READER_COOKIE = 'readermode',
var DARK_COOKIE = 'darkmode',
DARK_COOKIE = 'darkmode',
DARK_COOKIE_CONDITIONAL = 'darkmode_conditional',
STICKY_HEADER_COOKIE = 'stickyheader',
STICKY_HEADER_COOKIE = 'stickyheader',
THEME_COOKIE = 'theme',
FLOORNUMBER_LS = 'floornumber_display',
FLOORNUMBER_LS = 'floornumber_display',
currentReader = $.cookie(READER_COOKIE) === 'true',
theme = ($.cookie('theme') !== null) ? $.cookie('theme') : (($.cookie(DARK_COOKIE) === 'true') ? 'dark' : 'light'),
currentDark = $.cookie(DARK_COOKIE) === 'true',
currentDarkConditional = $.cookie(DARK_COOKIE_CONDITIONAL) === 'true',
currentSticky = $.cookie(STICKY_HEADER_COOKIE) === 'true',
currentSticky = $.cookie(STICKY_HEADER_COOKIE) === 'true',
currentFloornumber = '_auto',
currentFloornumber = '_auto',
prompt = 'dark_prompt',
themeSwitch,
now = new Date(),
hour = now.getHours(),
conditionalCheck = (hour >= 19 || hour < 7),
popupButton,
readerSwitch,
darkConditionalSwitch,
darkSwitch,
stickySwitch,
stickySwitch,
floorSelect,
floorSelect,
Line 30: Line 20:
floorSelectUK,
floorSelectUK,
floorSelectUS,
floorSelectUS,
applyButton,
closeButton,
cancelButton,
themePortletLink,
portletLink,
$content,
$content,
formMade = false,
userLocale = 'UK',
userLocale = 'UK',
flsetting,
flsetting,
browserLocale,
browserLocale,
floorSelectHelp;
themePopup;


var self = {
var self = {
init: function () {
init: function () {
// Add the theme selector
self.createThemePortletLink();
// Add the appearance settings button
self.createSettingsPortletLink();
// Perform skin overrides if required
self.doFloorNumberOverrides();
if (currentSticky) self.doStickyHeaderOverrides();
// Transition to the theme cookie
if ( $.cookie('theme') == null ) {
$.cookie(THEME_COOKIE, theme, {expires: 365, path: '/'});
}
},
/**
* Perform specific overrides to the skin if the user is currently
* using the sticky header option.
*/
doStickyHeaderOverrides: function() {
$('body').addClass('wgl-stickyheader');
function onScroll() {
var personal = $('#p-personal');
var targetEle = document.getElementById("mw-head");
var head = $('#mw-head');
if (mw.config.get('wgAction') === 'edit' || window.location.search.includes('veaction')) {
// We're on an edit page, do nothing and reset all the stuff
if (personal.is(":hidden")) {
personal.show();
head.removeClass('sticky-hidden');
}
} else {
if (window.scrollY > (targetEle.offsetTop + targetEle.offsetHeight)) {
if (personal.is(":visible")) {
personal.hide();
head.addClass('sticky-hidden');
}
} else {
if (personal.is(":hidden")) {
personal.show();
head.removeClass('sticky-hidden');
}
}
}
}
onScroll();
$(window).scroll(onScroll);
// hidden by css when sticky-hidden is not on
if (mw.config.get('wgIsMainPage') !== true) {
mw.util.addPortletLink('p-namespaces', '/', 'Main Page', 'ca-nstab-mainpage', 'Visit the main page');
}
},
/**
* Perform specific overrides to the skin based on the selected
* floor number preference.
*/
doFloorNumberOverrides: function() {
if (rs.hasLocalStorage()) {
if (rs.hasLocalStorage()) {
currentFloornumber = window.localStorage.getItem(FLOORNUMBER_LS);
currentFloornumber = window.localStorage.getItem(FLOORNUMBER_LS);
Line 76: Line 161:
}
}
$('body').addClass(flsetting);
$('body').addClass(flsetting);
},
portletLink = mw.util.addPortletLink(
 
'p-personal',
/**
'',
* Adds the theme select portlet link to the page
'',
*/
'pt-skin-toggles',
createThemePortletLink: function() {
'Your appearance settings',
themePortletLink = mw.util.addPortletLink('p-personal', '', '', 'pt-theme-toggles', 'Change theme', null, $('#pt-userpage, #pt-anonuserpage'));
null,
$(themePortletLink).find('a').addClass('oo-ui-icon-advanced').click(function(e) {
$('#pt-userpage, #pt-anonuserpage')
e.preventDefault();
);
if (!themePopup) {
mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.createThemePopup);
$(portletLink).find('a').addClass('oo-ui-icon-advanced').add('.floor-convention').click(function(e) {
} else {
themePopup.toggle();
}
});
},
/**
* Adds the appearance settings portlet link to the page
*/
createSettingsPortletLink: function() {
settingsPortletLink = mw.util.addPortletLink('p-personal', '', '', 'pt-skin-toggles', 'Appearance settings', null, $('#pt-userpage, #pt-anonuserpage'));
$(settingsPortletLink).find('a').addClass('oo-ui-icon-advanced').add('.floor-convention').click(function(e) {
e.preventDefault();
e.preventDefault();
if (!formMade) {
if (!window.OOUIWindowManager || !window.OOUIWindowManager.hasWindow('skin')) {
mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.initForm);
mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.createAppearanceModal);
} else {
} else {
window.OOUIWindowManager.openWindow('skin');
window.OOUIWindowManager.openWindow('skin');
}
}
});
});
},
/**
* Loads a theme by its name
*/
loadTheme: function(themeName) {
var removeExistingTheme = function () {
// Remove any existing theme class
$('body').removeClass(function (i, className) {
return (className.match (/(^|\s)wgl-theme-\S+/g) || []).join(' ')
})
}
 
// Add new theme class
if (currentReader) {
if (themeName === 'light') {
mw.util.addPortletLink(
removeExistingTheme();
'p-namespaces',
$('body').addClass('wgl-theme-light');
mw.util.getUrl(mw.config.get('wgMainPageTitle')),
} else {
'Menu',
mw.loader.using(['wgl.theme.' + themeName]).then(function () {
'ca-reader-menu'
removeExistingTheme();
);
$('body').addClass('wgl-theme-' + themeName);
 
// can't use the nextnode parameter in addPortletLink
// because the id of the first tab varies
$('#ca-reader-menu')
.prependTo('#p-namespaces ul');
 
// move sidebar
$('#mw-panel')
.attr('id', 'ca-reader-dropdown')
.appendTo('#ca-reader-menu');
}
 
if (currentDarkConditional) {
var reloadRequired = (conditionalCheck != currentDark)
$.cookie(DARK_COOKIE, conditionalCheck, {expires: 365, path: '/'});
if (reloadRequired === true) {
window.location.reload(true);
}
}
 
if (currentSticky) {
window.addEventListener("scroll", function() {
var personal = $('#p-personal');
    if (mw.config.get('wgAction') === 'edit' || window.location.search.includes('veaction')) {
  // We're on an edit page, do nothing and reset all the stuff
if (personal.is(":hidden")) {
personal.show();
head.removeClass('sticky-hidden');
}
    } else {
  var targetEle = document.getElementById("mw-head");
  var head = $('#mw-head');
  if (window.scrollY > (targetEle.offsetTop + targetEle.offsetHeight)) {
if (personal.is(":visible")) {
personal.hide();
head.addClass('sticky-hidden');
}
  } else {
if (personal.is(":hidden")) {
personal.show();
head.removeClass('sticky-hidden');
}
  }
  }
});
});
// hidden by css when sticky-hidden is not on
if (mw.config.get('wgIsMainPage') !== true) {
mw.util.addPortletLink(
'p-namespaces',
mw.util.getUrl(mw.config.get('wgMainPageTitle')),
'Main Page',
'ca-nstab-mainpage',
'Visit the main page'
);
}
}
/**
* Used for prompting users who have prefers-color-scheme set to dark
* to switch to dark mode (because doing this automatically would
* require setting a cookie, prompting this is best for privacy/
* legal reasons)
**/
if (rs.hasLocalStorage()) {
// This should always be true anyway because browsers that
// support prefers-color-scheme have LocalStorage API support
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
if (!currentDark) {
// Only show if they're not currently using dark mode
var alreadyPrompted = localStorage.getItem(prompt)
if (alreadyPrompted === null) {
// Only show if the localStorage key doesn't exist
mw.loader.using(['oojs-ui-core','oojs-ui-widgets']).then(function () {
var popup = new OO.ui.PopupWidget( {
  $content: $( '<p style="font-size: 0.75em;">Your device is using dark mode. You can click here to enable the wiki\'s dark mode!<br /><a id="rsw-color-scheme-opt-out" href="#">Don\'t show this again</a></p>' ),
  padded: true,
  width: 300,
  $floatableContainer: $('#pt-skin-toggles'),
  autoClose: true,
  id: 'rsw-color-scheme-prompt'
} );
$( 'body' ).append( popup.$element );
popup.toggle( true );
$( '#rsw-color-scheme-opt-out' ).click( function() {
// Set localStorage key so we don't prompt them again
localStorage.setItem(prompt, 'true')
popup.toggle( false )
} )
});
}
}
}
}
}
},
},
initForm: function() {
readerSwitch = new OO.ui.ToggleSwitchWidget({
/**
value: currentReader,
* Initialises the creation of the theme toggle widget
classes: ['reader-toggle'],
*/
align: 'right'
createThemeToggle: function() {
});
// Create the theme toggle
 
themeSwitch = new OO.ui.ButtonSelectWidget({
stickySwitch = new OO.ui.ToggleSwitchWidget({
value: currentSticky,
classes: ['reader-toggle'],
align: 'right'
});
 
darkConditionalSwitch = new OO.ui.ToggleSwitchWidget({
value: currentDarkConditional,
classes: ['reader-toggle'],
align: 'right'
})
 
darkSwitch = new OO.ui.ButtonSelectWidget({
classes: ['appearance-buttons'],
classes: ['appearance-buttons'],
items: [
items: [
new OO.ui.ButtonOptionWidget({
new OO.ui.ButtonOptionWidget({
classes: ['light-mode-button'],
classes: ['light-mode-button'],
data: false,
data: 'light',
label: new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Light</div><div class="button-text-selected"></div>'),
title: 'Light',
framed: false,
label: new OO.ui.HtmlSnippet('<div class="button-img"></div>'),
}),
}),
new OO.ui.ButtonOptionWidget({
new OO.ui.ButtonOptionWidget({
classes: ['dark-mode-button'],
classes: ['dark-mode-button'],
data: true,
data: 'dark',
label:new OO.ui.HtmlSnippet('<div class="button-img"></div><div class="button-text">Dark</div><div class="button-text-selected"></div>'),
title: 'Dark',
//disabled: true
framed: false,
label: new OO.ui.HtmlSnippet('<div class="button-img"></div>'),
}),
}),
]
]
});
});
// Set the toggle to whatever theme is currently active
themeSwitch.selectItemByData(theme);
themeSwitch.on('choose', function() {
// Change the theme instantly without needing a refresh
theme = themeSwitch.findSelectedItem().getData();
$.cookie(THEME_COOKIE, theme, {expires: 365, path: '/'});
self.loadTheme(theme);
})
},
/**
* Initialises the creation of the theme popup window, which appears
* when the moon icon is clicked at the top right of the page.
*/
createThemePopup: function() {
self.createThemeToggle();
// Create the popup
themePopup = new OO.ui.PopupWidget( {
classes: ['wgl-theme-popup'],
$content: themeSwitch.$element,
$floatableContainer: $(themePortletLink),
width: null,
autoClose: true,
} );
themePopup.on('toggle', function (visible) {
// When the popup is opened, change the moon icon to an X
if (visible) {
$(themePortletLink).find('a').addClass('wgl-theme-popup-opened');
} else {
$(themePortletLink).find('a').removeClass('wgl-theme-popup-opened');
}
})
$(document.body).append(themePopup.$element);
// Open the popup, since we'll have only created the popup if the
// user tried to interact with it in the first place.
themePopup.toggle(true);
},
/**
* Initialises the creation of the gear modal, for other non-theme
* related appearance settings.
*/
createAppearanceModal: function() {
stickySwitch = new OO.ui.ToggleSwitchWidget({
value: currentSticky,
classes: ['reader-toggle'],
align: 'right'
});
floorSelectAuto = new OO.ui.RadioOptionWidget({
floorSelectAuto = new OO.ui.RadioOptionWidget({
data: '_auto',
data: '_auto',
Line 266: Line 328:
floorSelectAuto.$element.attr('title', 'Automatically detect the type to use from your browser.');
floorSelectAuto.$element.attr('title', 'Automatically detect the type to use from your browser.');
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.');
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.');
floorSelectUS.$element.attr('title', 'The numbering used in the USA and Canada: entrance on the 1st floor, then above that is 2nd floor, 3rd floor, etc.');
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.');
 
floorSelect.on('choose', function () {
darkSwitch.setDisabled(darkConditionalSwitch.getValue())
 
darkConditionalSwitch.on('change', function() {
darkSwitch.setDisabled(darkConditionalSwitch.getValue())
})
 
stickySwitch.setDisabled(readerSwitch.getValue())
readerSwitch.setDisabled(stickySwitch.getValue())
 
readerSwitch.on('change', function() {
if (readerSwitch.getValue() === true) {
stickySwitch.setValue(false)
}
stickySwitch.setDisabled(readerSwitch.getValue())
})
 
stickySwitch.on('change', function() {
if (stickySwitch.getValue() === true) {
readerSwitch.setValue(false)
}
readerSwitch.setDisabled(stickySwitch.getValue())
})
 
darkSwitch.selectItemByData(currentDark);
 
applyButton = new OO.ui.ButtonInputWidget({
label: 'Save',
flags: ['primary', 'progressive'],
classes: ['skin-save-button']
});
 
applyButton.on('click', function(){
$.cookie(READER_COOKIE, readerSwitch.getValue(), {expires: 365, path: '/'});
$.cookie(DARK_COOKIE_CONDITIONAL, darkConditionalSwitch.getValue(), {expires: 365, path: '/'});
$.cookie(STICKY_HEADER_COOKIE, stickySwitch.getValue(), {expires: 365, path: '/'});
var darkval = darkSwitch.findSelectedItem(),
darkc = false,
requireReload = false;
if ((readerSwitch.getValue() !== currentReader) || (stickySwitch.getValue() !== currentSticky)) {
requireReload = true;
}
 
if (darkConditionalSwitch.getValue() === false) {
if (darkval !== null) {
darkc = darkval.getData();
}
} else if (darkConditionalSwitch.getValue() === true) {
darkc = conditionalCheck
}
$.cookie(DARK_COOKIE, darkc, {expires: 365, path: '/'});
if (rs.hasLocalStorage()) {
if (rs.hasLocalStorage()) {
window.localStorage.setItem(FLOORNUMBER_LS, floorSelect.findSelectedItem().getData());
window.localStorage.setItem(FLOORNUMBER_LS, floorSelect.findSelectedItem().getData());
}
if (darkc === true) {
mw.loader.using(['wg.darkmode']).then(function() {
  $('body').addClass('wgl-darkmode')
});
} else {
$('body').removeClass('wgl-darkmode')
}
if (requireReload === true) {
window.location.reload(true);
} else {
window.OOUIWindowManager.closeWindow('skin')
}
}
});
});
 
cancelButton = new OO.ui.ButtonInputWidget({ label: 'Cancel', flags: 'destructive'});
stickySwitch.on('change', function() {
$.cookie(STICKY_HEADER_COOKIE, stickySwitch.getValue(), {expires: 365, path: '/'});
})
closeButton = new OO.ui.ButtonInputWidget({ label: 'Close', flags: 'destructive'});


$content = $('<div>');
$content = $('<div>');
$content
$content
.addClass('appearance-modal tile')
.addClass('appearance-modal')
.append(
.append(
$('<h2>').text('Appearance'),
$('<div>')
.addClass('appearance-buttons')
.append(darkSwitch.$element),
$('<div>')
$('<div>')
.addClass('reader-mode')
.addClass('reader-mode')
.append(
.append(
darkConditionalSwitch.$element,
$('<div>').addClass('dark-conditional-header').text('Automatic dark mode'),
$('<p>').addClass('dark-conditional-desc').text('Automatically switch to dark mode from 19:00 to 7:00 local time. Disables the manual setting above.'),
readerSwitch.$element,
$('<div>').addClass('reader-mode-header').text('Reader mode'),
$('<p>').addClass('reader-mode-desc').text('Increase the font size and switch the wiki to a fixed-width layout. Incompatible with sticky headers.'),
stickySwitch.$element,
stickySwitch.$element,
$('<div>').addClass('sticky-header-header').text('Sticky header'),
$('<div>').addClass('setting-header sticky-header-header').text('Sticky header'),
$('<p>').addClass('sticky-header-desc').text('Pin the navigation bar and search to the top when scrolling. Incompatible with reader mode.'),
$('<p>').addClass('sticky-header-desc').text('Pin the navigation bar and search to the top when scrolling.'),
floorSelect.$element,
floorSelect.$element,
$('<div>').addClass('floornumber-header').text('Floor numbering'),
$('<div>').addClass('setting-header floornumber-header').text('Floor numbering'),
$('<p>').addClass('floornumber-desc').text(floorSelectHelp)
$('<p>').addClass('floornumber-desc').text(floorSelectHelp)
),
),
Line 370: Line 360:
.addClass('appearance-save')
.addClass('appearance-save')
.append(
.append(
$('<p>').addClass('save-button-desc').html('Saving these changes will reload the page and set <a href="https://weirdgloop.org/privacy">personalisation cookies</a>.'),
$('<p>').addClass('save-button-desc').html('We use <a href="https://weirdgloop.org/privacy">cookies</a> to personalise the wiki.'),
$('<div>').addClass('save-button-container')
$('<div>').addClass('save-button-container')
.append(applyButton.$element)
.append(closeButton.$element)
.append(cancelButton.$element)
)
)
);
);
Line 379: Line 368:
var initModal = function (modal) {
var initModal = function (modal) {
modal.$body.append( $content );
modal.$body.append( $content );
cancelButton.on('click', function(modal){window.OOUIWindowManager.closeWindow(modal);}, [modal]);
closeButton.on('click', function(modal){window.OOUIWindowManager.closeWindow(modal);}, [modal]);
};
};


rs.createOOUIWindow('skin', 'Appearance settings', {size: 'medium', classes: ['rsw-skin-toggle-popup']}, initModal, true);
rs.createOOUIWindow('skin', 'Appearance settings', {size: 'large', classes: ['rsw-skin-toggle-popup']}, initModal, true, true).then(function () {
formMade = true;
window.OOUIWindowManager.on('closing', function (win, closed, data) {
if (win.$element.hasClass('rsw-skin-toggle-popup')) {
// If the window that closed is this one, finalise some stuff (mostly reader mode etc).
var requireReload = false;
if ((stickySwitch.getValue() !== currentSticky)) {
requireReload = true;
}
if (requireReload === true) {
window.location.reload(true);
return;
}
}
})
})
}
}
}
}

Latest revision as of 11:49, 28 December 2023

/**
 * Toggles for various appearance-related cookies, including the current theme.
 * 
 * @author Gaz Lloyd
 * @author Jayden
 * 
 */
;(function($, mw, rs){
	var DARK_COOKIE = 'darkmode',
		STICKY_HEADER_COOKIE = 'stickyheader',
		THEME_COOKIE = 'theme',
		FLOORNUMBER_LS = 'floornumber_display',
		theme = ($.cookie('theme') !== null) ? $.cookie('theme') : (($.cookie(DARK_COOKIE) === 'true') ? 'dark' : 'light'),
		currentSticky = $.cookie(STICKY_HEADER_COOKIE) === 'true',
		currentFloornumber = '_auto',
		themeSwitch,
		stickySwitch,
		floorSelect,
		floorSelectAuto,
		floorSelectUK,
		floorSelectUS,
		closeButton,
		themePortletLink,
		$content,
		userLocale = 'UK',
		flsetting,
		browserLocale,
		themePopup;

	var self = {
		init: function () {
			// Add the theme selector
			self.createThemePortletLink();
			
			// Add the appearance settings button
			self.createSettingsPortletLink();
			
			// Perform skin overrides if required
			self.doFloorNumberOverrides();
			
			if (currentSticky) self.doStickyHeaderOverrides();
			
			// Transition to the theme cookie
			if ( $.cookie('theme') == null ) {
				$.cookie(THEME_COOKIE, theme, {expires: 365, path: '/'});
			}
	
		},
	
		/**
	
		 * Perform specific overrides to the skin if the user is currently
	
		 * using the sticky header option.
	
		 */
	
		doStickyHeaderOverrides: function() {
			$('body').addClass('wgl-stickyheader');
			function onScroll() {
	
				var personal = $('#p-personal');
	
				var targetEle = document.getElementById("mw-head");
	
				var head = $('#mw-head');
	
				if (mw.config.get('wgAction') === 'edit' || window.location.search.includes('veaction')) {
	
					// We're on an edit page, do nothing and reset all the stuff
	
					if (personal.is(":hidden")) {
	
						personal.show();
	
						head.removeClass('sticky-hidden');
	
					}
	
				} else {
	
					if (window.scrollY > (targetEle.offsetTop + targetEle.offsetHeight)) {
	
						if (personal.is(":visible")) {
	
							personal.hide();
	
							head.addClass('sticky-hidden');
	
						}
	
					} else {
	
						if (personal.is(":hidden")) {
	
							personal.show();
	
							head.removeClass('sticky-hidden');
	
						}
	
					}
	
				}
	
			}
	
			
	
			onScroll();
	
			$(window).scroll(onScroll);
	
			
	
			// hidden by css when sticky-hidden is not on
	
			if (mw.config.get('wgIsMainPage') !== true) {
	
				mw.util.addPortletLink('p-namespaces', '/', 'Main Page', 'ca-nstab-mainpage', 'Visit the main page');
			}
		},
		
		/**
		 * Perform specific overrides to the skin based on the selected
		 * floor number preference.
		 */
		doFloorNumberOverrides: function() {
			if (rs.hasLocalStorage()) {
				currentFloornumber = window.localStorage.getItem(FLOORNUMBER_LS);
				if (currentFloornumber == null) {
					currentFloornumber = '_auto';
				}
			}
			flsetting = currentFloornumber;
			if (window.navigator.languages && window.navigator.languages.length) {
				browserLocale = window.navigator.languages[0];
			} else {
				browserLocale = navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
			}
			switch (browserLocale) {
				// all langs in -US or -CA
				case 'en-US':
				case 'es-US':
				case 'en-CA':
				case 'fr-CA':
					userLocale = 'US';
					break;
			}
			if (currentFloornumber == '_auto') {
				flsetting = userLocale;
			}
			switch (flsetting) {
				case 'US':
					flsetting = 'floornumber-setting-us';
					break;
				case 'UK':
				default:
					flsetting = 'floornumber-setting-gb';
					break;
			}
			$('body').addClass(flsetting);
		},

		/**
		 * Adds the theme select portlet link to the page
		 */
		createThemePortletLink: function() {
			themePortletLink = mw.util.addPortletLink('p-personal', '', '', 'pt-theme-toggles', 'Change theme', null, $('#pt-userpage, #pt-anonuserpage'));
			$(themePortletLink).find('a').addClass('oo-ui-icon-advanced').click(function(e) {
				e.preventDefault();
				if (!themePopup) {
					mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.createThemePopup);
				} else {
					themePopup.toggle();
				}
			});
		},
		
		/**
		 * Adds the appearance settings portlet link to the page
		 */
		createSettingsPortletLink: function() {
			settingsPortletLink = mw.util.addPortletLink('p-personal', '', '', 'pt-skin-toggles', 'Appearance settings', null, $('#pt-userpage, #pt-anonuserpage'));
			$(settingsPortletLink).find('a').addClass('oo-ui-icon-advanced').add('.floor-convention').click(function(e) {
				e.preventDefault();
				if (!window.OOUIWindowManager || !window.OOUIWindowManager.hasWindow('skin')) {
					mw.loader.using(['oojs-ui-core','oojs-ui-windows','oojs-ui-widgets']).then(self.createAppearanceModal);
				} else {
					window.OOUIWindowManager.openWindow('skin');
				}
			});
		},
		
		/**
		 * Loads a theme by its name
		 */
		loadTheme: function(themeName) {
			var removeExistingTheme = function () {
				// Remove any existing theme class
				$('body').removeClass(function (i, className) {
					return (className.match (/(^|\s)wgl-theme-\S+/g) || []).join(' ')
				})
			}
			
			// Add new theme class
			if (themeName === 'light') {
				removeExistingTheme();
				$('body').addClass('wgl-theme-light');
			} else {
				mw.loader.using(['wgl.theme.' + themeName]).then(function () {
					removeExistingTheme();
					$('body').addClass('wgl-theme-' + themeName);
				});
			}
		},
		
		/**
		 * Initialises the creation of the theme toggle widget
		 */
		createThemeToggle: function() {
			// Create the theme toggle
			themeSwitch = new OO.ui.ButtonSelectWidget({
				classes: ['appearance-buttons'],
				items: [
					new OO.ui.ButtonOptionWidget({
						classes: ['light-mode-button'],
						data: 'light',
						title: 'Light',
						framed: false,
						label: new OO.ui.HtmlSnippet('<div class="button-img"></div>'),
					}),
					new OO.ui.ButtonOptionWidget({
						classes: ['dark-mode-button'],
						data: 'dark',
						title: 'Dark',
						framed: false,
						label: new OO.ui.HtmlSnippet('<div class="button-img"></div>'),
					}),
				]
			});

			// Set the toggle to whatever theme is currently active
			themeSwitch.selectItemByData(theme);
			
			themeSwitch.on('choose', function() {
				// Change the theme instantly without needing a refresh
				theme = themeSwitch.findSelectedItem().getData();
				$.cookie(THEME_COOKIE, theme, {expires: 365, path: '/'});
				self.loadTheme(theme);
			})
		},
		
		/**
		 * Initialises the creation of the theme popup window, which appears
		 * when the moon icon is clicked at the top right of the page.
		 */
		createThemePopup: function() {
			self.createThemeToggle();
			
			// Create the popup
			themePopup = new OO.ui.PopupWidget( {
				classes: ['wgl-theme-popup'],
				$content: themeSwitch.$element,
				$floatableContainer: $(themePortletLink),
				width: null,
				autoClose: true,
			} );
			
			themePopup.on('toggle', function (visible) {
				// When the popup is opened, change the moon icon to an X
				if (visible) {
					$(themePortletLink).find('a').addClass('wgl-theme-popup-opened');
				} else {
					$(themePortletLink).find('a').removeClass('wgl-theme-popup-opened');
				}
			})
			
			$(document.body).append(themePopup.$element);
			
			// Open the popup, since we'll have only created the popup if the
			// user tried to interact with it in the first place.
			themePopup.toggle(true);
		},
		
		/**
		 * Initialises the creation of the gear modal, for other non-theme
		 * related appearance settings.
		 */
		 
		createAppearanceModal: function() {
			stickySwitch = new OO.ui.ToggleSwitchWidget({
	
				value: currentSticky,
	
				classes: ['reader-toggle'],
	
				align: 'right'
	
			});
			floorSelectAuto = new OO.ui.RadioOptionWidget({
						data: '_auto',
						label: 'Auto-detect: '+userLocale
					});
			floorSelectUK = new OO.ui.RadioOptionWidget({
						data: 'UK',
						label: 'UK'
					});
			floorSelectUS = new OO.ui.RadioOptionWidget({
						data: 'US',
						label: 'US'
					});
			
			floorSelect = new OO.ui.RadioSelectWidget({
				classes: ['floornumber-select'],
				items: [
					floorSelectAuto,
					floorSelectUK,
					floorSelectUS
					]
			});
			floorSelect.selectItemByData(currentFloornumber);
			floorSelectHelp = 'Changes how floor numbers are displayed on the wiki - whether the numbering begins at 0 (ground) or 1.';
			if (!rs.hasLocalStorage()) {
				floorSelect.setDisabled(true);
				floorSelectHelp = 'This option requires local storage to be supported and enabled in your browser.';
			}
			floorSelectAuto.$element.attr('title', 'Automatically detect the type to use from your browser.');
			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.');
			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.');
			floorSelect.on('choose', function () {
				if (rs.hasLocalStorage()) {
					window.localStorage.setItem(FLOORNUMBER_LS, floorSelect.findSelectedItem().getData());
				}
			});
	
	
			stickySwitch.on('change', function() {
	
				$.cookie(STICKY_HEADER_COOKIE, stickySwitch.getValue(), {expires: 365, path: '/'});
			})
			
			closeButton = new OO.ui.ButtonInputWidget({ label: 'Close', flags: 'destructive'});

			$content = $('<div>');
			$content
				.addClass('appearance-modal')
				.append(
					$('<div>')
						.addClass('reader-mode')
						.append(
							stickySwitch.$element,
							$('<div>').addClass('setting-header sticky-header-header').text('Sticky header'),
							$('<p>').addClass('sticky-header-desc').text('Pin the navigation bar and search to the top when scrolling.'),
							floorSelect.$element,
							$('<div>').addClass('setting-header floornumber-header').text('Floor numbering'),
							$('<p>').addClass('floornumber-desc').text(floorSelectHelp)
						),
					$('<div>')
						.addClass('appearance-save')
						.append(
							$('<p>').addClass('save-button-desc').html('We use <a href="https://weirdgloop.org/privacy">cookies</a> to personalise the wiki.'),
							$('<div>').addClass('save-button-container')
								.append(closeButton.$element)
						)
				);

			var initModal = function (modal) {
				modal.$body.append( $content );
				closeButton.on('click', function(modal){window.OOUIWindowManager.closeWindow(modal);}, [modal]);
			};

			rs.createOOUIWindow('skin', 'Appearance settings', {size: 'large', classes: ['rsw-skin-toggle-popup']}, initModal, true, true).then(function () {
	
				window.OOUIWindowManager.on('closing', function (win, closed, data) {
	
					if (win.$element.hasClass('rsw-skin-toggle-popup')) {
	
						// If the window that closed is this one, finalise some stuff (mostly reader mode etc).
	
						var requireReload = false;
	
							
	
						if ((stickySwitch.getValue() !== currentSticky)) {
	
							requireReload = true;
	
						}
	
						
	
						if (requireReload === true) {
	
							window.location.reload(true);
	
							return;
	
						}
	
					}
	
				})
	
			})
		}
	}

	mw.loader.using(['ext.gadget.rsw-util'], function () {
		$(self.init);
	})

}(jQuery, mediaWiki, rswiki));