1// Set to true once a Windows ARM64 build is published in a release. 2const windowsArm64Enabled = false; 3 4async function getOs() { 5 6 // The macOS release is available for Intel and Apple silicon processors, 7 // and the only way to get that info is through this new 8 // `getHighEntropyValues` function (which is not available on all browsers). 9 // So here we either return "macOs" for Intel or "macOsM1" for Apple 10 // Silicon. If we don't know which it is, we return "macOsUndefined". 11 // https://stackoverflow.com/a/75177111/561309 12 13 if (navigator.appVersion.indexOf("Mac")!=-1) { 14 let platformInfo = null; 15 try { 16 platformInfo = await navigator.userAgentData.getHighEntropyValues(['architecture']) 17 } catch (error) { 18 console.warn('Failed getting Mac architecture:', error); 19 return 'macOsUndefined'; 20 } 21 22 console.info('Got platform info:', platformInfo); 23 24 if (platformInfo.architecture === 'arm') { 25 return "macOsM1"; 26 } else { 27 return "macOs"; 28 } 29 } 30 31 // Windows on ARM has the same appVersion as x64, so the architecture has to 32 // come from getHighEntropyValues, as for Apple Silicon above. Falling back 33 // to the x64 build is fine - it runs on ARM under emulation. 34 if (navigator.appVersion.indexOf("Win")!=-1) { 35 if (windowsArm64Enabled) { 36 try { 37 const platformInfo = await navigator.userAgentData.getHighEntropyValues(['architecture']); 38 console.info('Got platform info:', platformInfo); 39 if (platformInfo.architecture === 'arm') return "windowsArm64"; 40 } catch (error) { 41 console.warn('Failed getting Windows architecture:', error); 42 } 43 } 44 return "windows"; 45 } 46 if (navigator.appVersion.indexOf("X11")!=-1) return "linux"; 47 if (navigator.appVersion.indexOf("Linux")!=-1) return "linux"; 48 return null; 49} 50 51function getFilename(path) { 52 if (!path) return ''; 53 const s = path.split('/'); 54 const urlWithParams = s.pop(); 55 const s2 = urlWithParams.split('?'); 56 return s2[0]; 57} 58 59function getMobileOs() { 60 var userAgent = navigator.userAgent || navigator.vendor || window.opera; 61 62 // Windows Phone must come first because its UA also contains "Android" 63 if (/windows phone/i.test(userAgent)) { 64 return "windowsPhone"; 65 } 66 67 if (/android/i.test(userAgent)) { 68 return "android"; 69 } 70 71 // iOS detection from: http://stackoverflow.com/a/9039885/177710 72 if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { 73 return "ios"; 74 } 75 76 return ""; 77} 78 79 80function setupMobileMenu() { 81 $("#open-menu-mobile").click(function () { 82 $("#menu-mobile").animate({ "margin-right": "0px" }, 300); 83 }); 84 85 $("#close-menu-mobile").click(function () { 86 $("#menu-mobile").animate({ "margin-right": "-300px" }, 300); 87 }); 88} 89 90async function setupDownloadPage() { 91 if (!$('.page-download').length) return; 92 93 const downloadLinks = {}; 94 95 $('.page-download .get-it-desktop a').each(function() { 96 const href = $(this).attr('href'); 97 98 // The arm64 installer is also named "-Setup", so it must be excluded 99 // from the x64 link even when disabled, or it would overwrite it. 100 if (href.indexOf('-arm64.exe') > 0) { 101 if (windowsArm64Enabled) downloadLinks['windowsArm64'] = href; 102 } 103 else if (href.indexOf('-Setup') > 0) downloadLinks['windows'] = href; 104 if (href.indexOf('.dmg') > 0) downloadLinks['macOs'] = href; 105 if (href.indexOf('arm64.DMG') > 0) downloadLinks['macOsM1'] = href; 106 if (href.indexOf('.AppImage') > 0) downloadLinks['linux'] = href; 107 }); 108 109 $('.page-download .get-it-desktop').hide(); 110 111 $('.page-download .download-click-here').click((event) => { 112 event.preventDefault(); 113 $('.page-download .get-it-desktop').show(500); 114 }); 115 116 const mobileOs = getMobileOs(); 117 118 if (mobileOs) { 119 $('.page-download .intro').hide(); 120 } else { 121 const os = await getOs(); 122 123 console.info('Found OS: ' + os); 124 125 if (os === 'macOsUndefined') { 126 // If we don't know which macOS version it is, we let the user choose. 127 $('.main-content .intro').html('<p class="macos-m1-info">The macOS release is available for Intel processors or for Apple Silicon (M1) processors. Please select your version:</p>');
128 const macOsLink = $('.download-link-macOs'); 129 const macOsM1Link = $('.download-link-macOsM1'); 130 $('.macos-m1-info').after('<p style="font-style: italic; font-size: .8em;">To find out what processor you have, click on the <b>Apple logo</b> in the macOS menu bar, choose <b>About This Mac</b> from the dropdown menu. If you have an Apple silicon it should say"Apple M1" under "Chip". Otherwise you have an Intel processor.</p>'); 131 $('.macos-m1-info').after(macOsM1Link); 132 $('.macos-m1-info').after(macOsLink); 133 } else if (!os || !downloadLinks[os]) { 134 // If we don't know, display the section to manually download the app 135 $('.page-download .get-it-desktop').show(); 136 } else if (os === 'linux') { 137 // If it's Linux, the user should probably install it using the 138 // install script so we redirect to the install section 139 window.location = 'https://joplinapp.org/help/install'; 140 } else { 141 // Otherwise, start the download 142 const downloadLink = downloadLinks[os]; 143 $('.downloaded-filename').text(getFilename(downloadLink)); 144 window.location = downloadLink; 145 } 146 } 147} 148 149// Supported locale path prefixes (language code -> URL path) 150// Most languages use their code directly (fr, de), with exceptions mapped here 151const localePathOverrides = { 152 'zh': 'cn', 153}; 154 155// List of supported language codes 156const supportedLanguages = ['fr', 'de', 'zh']; 157 158function getLocalePath(langCode) { 159 const pathPrefix = localePathOverrides[langCode] || langCode; 160 return '/' + pathPrefix; 161} 162 163function setupLocaleRedirect() { 164 // Only redirect on the front page (root path or index.html) 165 const path = window.location.pathname; 166 const isRootPage = path === '/' || path === '/index.html' || path === ''; 167 if (!isRootPage) return; 168 169 // Check if user has explicitly chosen to stay on current locale 170 const localePreference = (localStorage.getItem('joplin-locale-preference') || '').toLowerCase(); 171 if (localePreference === 'en') return; 172 173 // Get user's preferred language from browser 174 const browserLang = (navigator.language || navigator.userLanguage || '').toLowerCase(); 175 176 // Extract the base language code (e.g., 'fr' from 'fr-ca') 177 const langCode = browserLang.split('-')[0]; 178 179 // Check if we support this language 180 if (!supportedLanguages.includes(langCode)) return; 181 182 window.location.href = getLocalePath(langCode) + '/'; 183} 184 185// Allow users to switch language and remember their preference 186function setLocalePreference(locale, url) { 187 localStorage.setItem('joplin-locale-preference', locale); 188 window.location.href = url; 189} 190 191// Expose globally for language switcher links 192window.setLocalePreference = setLocalePreference; 193 194$(function () { 195 setupMobileMenu(); 196 setupLocaleRedirect(); 197 void setupDownloadPage(); 198});
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.