1/*global AmCharts, storageAvailable*/ 2 3let siteTheme = { 4 5 /** 6 * The current site theme 7 */ 8 currentTheme: 'light', 9 10 /** 11 * Themes available for the site 12 */ 13 availableThemes: ['light', 'dark'], 14 15 /** 16 * Selector for theme switcher 17 */ 18 themeSwitcherSelector: '[data-action="theme-switch"]', 19 20 /** 21 * Theme Switcher object 22 */ 23 $themeSwitcherObject: null, 24 25 /** 26 * The selector the for the twitter objects 27 */ 28 twitterSelector: '.twitter-tweet', 29 30 /** 31 * Sets the value of 'data-theme' based on user preferences. This function is called when no 'theme' variable is found 32 * on local storage. 33 * @param {string}themeSwitcherHtmlId Html ID for the theme switcher 34 * @returns {void} 35 */ 36 initializeSwitcher: function () { 37 // Get the current theme 38 this.currentTheme = jQuery('html')[0].dataset.theme; 39 40 // Set the theme switcher element and object 41 this.$themeSwitcherObject = jQuery(this.themeSwitcherSelector); 42 43 // Check if $themeSwitcherObject is a valid jQuery object 44 if (this.$themeSwitcherObject.length) { 45 // Set up event handlers 46 this.$themeSwitcherObject.on('click', this.switcherClickEvent.bind(this)); 47 // Set up language switcher 48 this.maintainThemeOnLangSwitch(); 49 // Set twitter theme on initialization 50 jQuery(this.twitterSelector).attr('data-theme', this.currentTheme); 51 } 52 }, 53 54 init: function() { 55 document.addEventListener('DOMContentLoaded', function() { 56 siteTheme.initializeSwitcher(); 57 }); 58 }, 59 60 /** 61 * When the switcher is clicked, update the theme and Icon. If an amchart is present, reload the page 62 * @param {event}event Mousedown event when clicking on the switch theme 63 * @returns {void} 64 */ 65 switcherClickEvent: function (event) { 66 event.preventDefault(); 67 68 let newTheme = 'light'; 69 // If it's currently light, then switch to dark 70 if (siteTheme.currentTheme === 'light') { 71 newTheme = 'dark'; 72 } 73 74 // Reload the page if an AmChart is present on the page, otherwise switch on the page 75 if (siteTheme.hasAmchartOnPage()) { 76 // Set the theme mode to the new theme, then refresh by setting the window location 77 let url = new URL(window.location.toString()); 78 url.searchParams.set('theme_mode', newTheme); 79 window.location.href = url.toString(); 80 } else { 81 siteTheme.setTheme(newTheme); 82 siteTheme.switchTweetTheme(); 83 siteTheme.maintainThemeOnLangSwitch(); 84 } 85 }, 86 87 /** 88 * Disable dark mode 89 * @returns {void} 90 */ 91 disableDarkMode: function () { 92 jQuery(this.themeSwitcherSelector).remove(); 93 }, 94 95 /** 96 * Set the main html tag data-theme 97 * @param {string}theme The theme name to use 98 * @returns {void} 99 */ 100 setTheme: function (theme) { 101 this.currentTheme = theme; 102 document.documentElement.setAttribute('data-theme', this.currentTheme); 103 localStorage.setItem('theme', this.currentTheme); 104 }, 105 106 /** 107 * Set the theme by url parameter if the key exists 108 * @returns {string} The theme from url or empty string 109 */ 110 getThemeFromUrl: function () { 111 let theme = ''; 112 // This is assuming that the url is of the form ../../page/?key=value 113 let url = new URL(window.location.toString()); 114 if (url.searchParams.has('theme_mode')) { 115 let themeMode = url.searchParams.get('theme_mode').toLowerCase(); 116 let themes = ['dark', 'light']; 117 if (themes.includes(themeMode)) { 118 theme = themeMode; 119 } 120 } 121 return theme; 122 }, 123 124 /** 125 * Set the theme dark if the user browser preference is dark mode 126 * @returns {string} The theme name by preference (if dark is their default) 127 */ 128 getThemeByUserPref: function () { 129 let theme = ''; 130 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { 131 theme = 'dark'; 132 } 133 return theme; 134 }, 135 136 /** 137 * Change theme of tweets 138 * @returns {void} 139 */ 140 switchTweetTheme: function () { 141 let tweets = document.querySelectorAll('[data-tweet-id]');
142 tweets.forEach(function (tweet) { 143 let src = new URL(tweet.getAttribute("src")); 144 const tweetTheme = src.searchParams.get('theme'); 145 if (tweetTheme !== siteTheme.currentTheme) { 146 src.searchParams.set('theme', siteTheme.currentTheme); 147 tweet.setAttribute("src", src.href); 148 } 149 }); 150 }, 151 152 /** 153 * Checks there are AmChart charts present 154 * @returns {boolean} True if AmChart charts are present, false otherwise 155 */ 156 hasAmchartOnPage: function () { 157 let hasChart = false; 158 if (typeof AmCharts !== 'undefined' || typeof am4core !== 'undefined') { 159 hasChart = true; 160 } 161 return hasChart; 162 }, 163 164 /** 165 * Maintains the users theme when switching languages 166 * @return {void} 167 */ 168 maintainThemeOnLangSwitch: function () { 169 let langSwitch = jQuery('#langSwitch'); 170 // If there's a language switcher present 171 if (langSwitch.length) { 172 // Get the url 173 let langSwitchLink = new URL(langSwitch.attr('href')); 174 // Add a theme mode 175 langSwitchLink.searchParams.set('theme_mode', this.currentTheme); 176 // Update the url 177 langSwitch.attr("href", langSwitchLink.href); 178 // If there's a mobile language switcher update that link as well. 179 let langSwitchMobile = jQuery('#langSwitch.navbar-toggle'); 180 if (langSwitchMobile.length) { 181 langSwitchMobile.attr("href", langSwitchLink.href); 182 } 183 } 184 } 185}; 186 187//if the browser supports multiple themes, and local storage is available set the page theme 188if (!window.navigator.userAgent.match(/Trident.*rv:11\./) && storageAvailable.checkLocalStorage()) { 189 // Get the different storages for themes 190 let themeFromUrl, themeFromStorage, themeFromUserPref; 191 192 // Set the current theme as the default 193 let themeToUse = siteTheme.currentTheme; 194 // Order of which storage to use for determining the theme. URL -> Storage -> User preference -> Default 'light' 195 if (siteTheme.availableThemes.includes(themeFromUrl = siteTheme.getThemeFromUrl())) { 196 themeToUse = themeFromUrl; 197 } else if (siteTheme.availableThemes.includes(themeFromStorage = localStorage.getItem('theme'))) { 198 themeToUse = themeFromStorage; 199 } else if (siteTheme.availableThemes.includes(themeFromUserPref = siteTheme.getThemeByUserPref())) { 200 themeToUse = themeFromUserPref; 201 } 202 siteTheme.setTheme(themeToUse); 203} else { 204 siteTheme.disableDarkMode(); 205} 206 207siteTheme.init();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.