|
|
| Line 3: |
Line 3: |
|
| |
|
| const CONFIG = { | | const CONFIG = { |
| optionKey: 'gadget-theme',
| | cookieKey: 'mode', |
| cookieKey: 'theme', | |
| className: 'theme-light',
| |
| toggleId: 'pt-theme-toggle' | | toggleId: 'pt-theme-toggle' |
| }; | | }; |
| Line 16: |
Line 14: |
| }; | | }; |
|
| |
|
| function Storage() { | | function getMode() { |
| function loggedIn() { | | return document.documentElement.getAttribute('data-mode') === 'light' |
| return mw.user.isNamed();
| | ? 'light' |
| }
| | : 'dark'; |
| | } |
|
| |
|
| // true = light theme, false = dark (default)
| | function saveMode(mode) { |
| function getState() {
| | mw.cookie.set(CONFIG.cookieKey, mode, { |
| return mw.user.options.get(CONFIG.optionKey) == 1;
| | expires: 365 * 86400, |
| }
| | prefix: '' |
| | }); |
| | } |
|
| |
|
| function save(value) {
| | function iconFor(mode) { |
| if (loggedIn()) {
| | return mode === 'light' ? ICONS.moon : ICONS.sun; |
| return new mw.Api().saveOption(CONFIG.optionKey, value ? 1 : 0);
| | } |
| }
| |
|
| |
|
| mw.cookie.set(CONFIG.cookieKey, value ? '1' : '0', {
| | function labelFor(mode) { |
| expires: 365 * 86400,
| | return mode === 'light' |
| prefix: ''
| | ? 'Switch to dark theme' |
| });
| | : 'Switch to light theme'; |
| | | } |
| return $.Deferred().resolve().promise();
| |
| }
| |
|
| |
|
| function readCookieFallback(defaultValue) {
| | function apply(mode) { |
| if (loggedIn()) return defaultValue;
| | document.documentElement.setAttribute('data-mode', mode); |
|
| |
|
| return mw.cookie.get(CONFIG.cookieKey, '', defaultValue ? '1' : '0') === '1';
| | const a = document.querySelector('#' + CONFIG.toggleId + ' a'); |
| | if (a) { |
| | a.innerHTML = iconFor(mode); |
| | a.setAttribute('aria-label', labelFor(mode)); |
| | a.title = labelFor(mode); |
| } | | } |
| | } |
|
| |
|
| return {
| | function setMode(mode) { |
| loggedIn,
| | apply(mode); |
| getState,
| | saveMode(mode); |
| save,
| |
| readCookieFallback
| |
| }; | |
| } | | } |
|
| |
|
| function View() { | | function toggleMode() { |
| function iconFor(light) { | | setMode(getMode() === 'light' ? 'dark' : 'light'); |
| return light ? ICONS.moon : ICONS.sun;
| | } |
| }
| |
|
| |
|
| function labelFor(light) {
| | function createToggle() { |
| return light ? 'Switch to dark theme' : 'Switch to light theme';
| | const ul = document.querySelector('#p-personal ul'); |
| }
| |
|
| |
|
| function apply(light) { | | if (!ul) { |
| document.documentElement.classList.toggle(CONFIG.className, light); | | setTimeout(createToggle, 300); |
| | | return; |
| const a = document.querySelector('#' + CONFIG.toggleId + ' a');
| |
| if (a) { | |
| a.innerHTML = iconFor(light);
| |
| a.setAttribute('aria-label', labelFor(light));
| |
| a.title = labelFor(light);
| |
| }
| |
| } | | } |
|
| |
|
| function createToggle(onToggle, getState) { | | if (document.getElementById(CONFIG.toggleId)) { |
| function findTarget() {
| | return; |
| return document.querySelector('#p-personal ul');
| |
| }
| |
| | |
| const ul = findTarget();
| |
| | |
| if (!ul) {
| |
| setTimeout(function () {
| |
| createToggle(onToggle, getState);
| |
| }, 300);
| |
| return;
| |
| }
| |
| | |
| if (document.getElementById(CONFIG.toggleId)) return;
| |
| | |
| const light = getState();
| |
| | |
| const li = document.createElement('li');
| |
| li.id = CONFIG.toggleId;
| |
| | |
| const a = document.createElement('a');
| |
| a.href = '#';
| |
| a.innerHTML = iconFor(light);
| |
| a.setAttribute('aria-label', labelFor(light));
| |
| a.title = labelFor(light);
| |
| | |
| a.addEventListener('click', function (e) {
| |
| e.preventDefault();
| |
| onToggle();
| |
| }); | |
| | |
| li.appendChild(a);
| |
| ul.insertBefore(li, ul.firstChild);
| |
| } | | } |
|
| |
|
| return {
| | const mode = getMode(); |
| apply,
| |
| createToggle
| |
| };
| |
| }
| |
| | |
| function Controller() {
| |
| const store = Storage(); | |
| const view = View();
| |
|
| |
|
| let state = store.getState(); | | const li = document.createElement('li'); |
| | li.id = CONFIG.toggleId; |
|
| |
|
| function set(next) { | | const a = document.createElement('a'); |
| state = next;
| | a.href = '#'; |
| | a.innerHTML = iconFor(mode); |
| | a.setAttribute('aria-label', labelFor(mode)); |
| | a.title = labelFor(mode); |
|
| |
|
| view.apply(state);
| | a.addEventListener('click', function (e) { |
| | | e.preventDefault(); |
| store.save(state).fail(function () {
| | toggleMode(); |
| mw.notify('Could not save theme preference.', {
| | }); |
| title: 'Theme toggle',
| |
| type: 'warn',
| |
| tag: 'themeToggle'
| |
| });
| |
| });
| |
| }
| |
| | |
| function toggle() {
| |
| set(!state); | |
| }
| |
| | |
| function init() {
| |
| if (!store.loggedIn()) {
| |
| state = store.readCookieFallback(state);
| |
| } | |
| | |
| view.apply(state);
| |
| view.createToggle(toggle, function () {
| |
| return state;
| |
| });
| |
| }
| |
|
| |
|
| return { | | li.appendChild(a); |
| init
| | ul.insertBefore(li, ul.firstChild); |
| }; | |
| } | | } |
|
| |
|
| $(function () { | | $(function () { |
| new Controller().init(); | | apply(getMode()); |
| | createToggle(); |
| }); | | }); |
| })(); | | })(); |