PageSourceSearch

https://joplinapp.org/js/script.js?h=11f6c6dd6cfa71e94d03d3d38fb71afd

js joplinapp.org collected 2026-09-24 08:41:41 UTC 6,940 bytes, 198 lines download raw bytes

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.