一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
273 行
7.7 KiB
Plaintext
273 行
7.7 KiB
Plaintext
<div id="plans-section" class="env-{{env}}">
|
|
<style>
|
|
.toggle-container {
|
|
display: flex;
|
|
border: 2px solid black;
|
|
border-radius: 100px;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
margin-top: 20px;
|
|
max-width: 600px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
|
|
.toggle-option {
|
|
flex: 1;
|
|
padding: 10px 20px;
|
|
text-align: center;
|
|
transition: background 0.3s, color 0.3s;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.active {
|
|
background: black;
|
|
color: white;
|
|
}
|
|
|
|
.inactive {
|
|
background: white;
|
|
color: black;
|
|
}
|
|
|
|
/* Replace the Pro plan with the Pro+ plan when requesting increased storage */
|
|
body.-increased-storage .plan-group .account-type-2 {
|
|
display: none !important;
|
|
}
|
|
body:not(.-increased-storage) .plan-group .account-type-4 {
|
|
display: none !important;
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.toggle-container {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
border-radius: 10px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-12 title-box">
|
|
<h1 translate class="text-center">
|
|
Our synchronisation and sharing <span class="frame-bg frame-bg-yellow">solutions</span>
|
|
</h1>
|
|
<p translate class="text-center sub-title">
|
|
Synchronise and share your notes with our range of plans.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-container" id="toggle">
|
|
<div class="toggle-option active toggle-button-managed">Managed hosting</div>
|
|
<div class="toggle-option inactive toggle-button-self">Self-hosting</div>
|
|
</div>
|
|
|
|
<noscript>
|
|
<div class="alert alert-danger alert-env-dev" role="alert" style='text-align: center; margin-top: 10px;'>
|
|
To use this page please enable JavaScript!
|
|
</div>
|
|
</noscript>
|
|
|
|
<div class="row hosting-type-managed">
|
|
<div class="col-12 title-box">
|
|
<h1 translate class="text-center">
|
|
Joplin Cloud
|
|
</h1>
|
|
<p translate class="text-center sub-title">
|
|
<a href="https://joplincloud.com">Joplin Cloud</a> allows you to synchronise your notes across devices. It also lets you publish notes, and collaborate on notebooks with your friends, family or colleagues.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row hosting-type-self">
|
|
<div class="col-12 title-box">
|
|
<h1 translate class="text-center">
|
|
Joplin Server Business
|
|
</h1>
|
|
<p translate class="text-center sub-title">
|
|
Joplin Server Business is the self-hosted solution for businesses and organisations. Install it on your own infrastructure, keeping your data private and secure within your organisation while retaining full control over where it is stored.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display: flex; justify-content: center; margin-top: 1.2em" class="hosting-type-managed">
|
|
<div class="form-check form-check-inline">
|
|
<input id="pay-monthly-radio" class="form-check-input" type="radio" name="pay-radio" checked value="monthly">
|
|
<label translate style="font-weight: bold" class="form-check-label" for="pay-monthly-radio">
|
|
Pay Monthly
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline">
|
|
<input id="pay-yearly-radio" class="form-check-input" type="radio" name="pay-radio" value="yearly">
|
|
<label translate style="font-weight: bold" class="form-check-label" for="pay-yearly-radio">
|
|
Pay Yearly
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row plan-group plan-prices-monthly default-storage">
|
|
{{#plans.basic}}
|
|
{{> plan}}
|
|
{{/plans.basic}}
|
|
|
|
{{#plans.pro}}
|
|
{{> plan}}
|
|
{{/plans.pro}}
|
|
|
|
{{#plans.pro100Gb}}
|
|
{{> plan}}
|
|
{{/plans.pro100Gb}}
|
|
|
|
{{#plans.teams}}
|
|
{{> plan}}
|
|
{{/plans.teams}}
|
|
|
|
{{#plans.joplinServerBusiness}}
|
|
{{> plan}}
|
|
{{/plans.joplinServerBusiness}}
|
|
|
|
<p translate class="joplin-cloud-login-info hosting-type-managed">Already have a Joplin Cloud account? <a href="https://joplincloud.com">Login now</a></p>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div>
|
|
<h1>Feature comparison</h1>
|
|
<div class="joplin-cloud-feature-list">
|
|
{{{featureListHtml}}}
|
|
</div>
|
|
<p> </p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row faq">
|
|
{{{faqHtml}}}
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://js.stripe.com/v3/"></script>
|
|
|
|
<script>
|
|
const urlQuery = new URLSearchParams(location.search);
|
|
let subscriptionPeriod = 'monthly';
|
|
const stripe = Stripe('{{{stripeConfig.publishableKey}}}');
|
|
|
|
let checkoutSessionUser = null;
|
|
|
|
// Temporary setup to allow Beta users to start their subscription.
|
|
function setupBetaHandling(query) {
|
|
let accountType = Number(query.get('account_type'));
|
|
if (isNaN(accountType)) accountType = 1;
|
|
|
|
const email = query.get('email');
|
|
if (!email) return;
|
|
|
|
$('.account-type-3').css('display', 'none');
|
|
$('.subscribeButton').text('Buy now');
|
|
|
|
if (accountType === 2) {
|
|
$('.account-type-1').css('display', 'none');
|
|
}
|
|
|
|
checkoutSessionUser = { email, accountType };
|
|
}
|
|
|
|
var createCheckoutSession = function(priceId) {
|
|
const promotionCode = urlQuery.get('promo') || '';
|
|
|
|
console.info('Creating Stripe session for price:', priceId, 'Promotion code:', promotionCode);
|
|
|
|
const source = localStorage.getItem('source');
|
|
|
|
return fetch("{{{stripeConfig.webhookBaseUrl}}}/stripe/createCheckoutSession", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
priceId,
|
|
promotionCode,
|
|
email: checkoutSessionUser ? checkoutSessionUser.email : '',
|
|
source,
|
|
})
|
|
}).then(async function(result) {
|
|
if (!result.ok) {
|
|
console.error('Could not create Stripe checkout session', await result.text());
|
|
alert('The checkout session could not be created. Please contact us on the forum for support.');
|
|
} else {
|
|
return result.json();
|
|
}
|
|
});
|
|
};
|
|
|
|
const applyPeriod = (period) => {
|
|
subscriptionPeriod = period;
|
|
$('.plan-group').removeClass(period === 'monthly' ? 'plan-prices-yearly' : 'plan-prices-monthly');
|
|
$('.plan-group').addClass('plan-prices-' + period);
|
|
$("#pay-" + period + '-radio').prop('checked', true);
|
|
}
|
|
|
|
$(() => {
|
|
$("input[name='pay-radio']").change(function() {
|
|
const period = $("input[type='radio'][name='pay-radio']:checked").val();
|
|
applyPeriod(period);
|
|
});
|
|
|
|
// Keep the ?source= label so it can be sent to Stripe at checkout.
|
|
// Only overwrite when the param is present, to preserve an earlier one.
|
|
const sourceParam = urlQuery.get('source');
|
|
if (sourceParam) localStorage.setItem('source', sourceParam);
|
|
|
|
setupBetaHandling(urlQuery);
|
|
if (urlQuery.get('period') === 'monthly') {
|
|
// Nothing - this is the default
|
|
} else {
|
|
applyPeriod('yearly');
|
|
}
|
|
|
|
$('.feature-description').hide();
|
|
|
|
$('.feature-title').click((event) => {
|
|
event.preventDefault();
|
|
const featureId = event.currentTarget.getAttribute('data-id');
|
|
$('.feature-description-' + featureId).toggle(200);
|
|
});
|
|
});
|
|
|
|
const setHostingType = (type, updateUrl = true) => {
|
|
const other = type === 'managed' ? 'self' : 'managed';
|
|
$('.toggle-button-' + type).addClass('active');
|
|
$('.toggle-button-' + type).removeClass('inactive');
|
|
$('.toggle-button-' + other).addClass('inactive');
|
|
$('.toggle-button-' + other).removeClass('active');
|
|
|
|
$('.hosting-type-' + type).show();
|
|
$('.hosting-type-' + other).hide();
|
|
|
|
if (updateUrl) {
|
|
const url = new URL(location.href);
|
|
url.searchParams.set('hosting', type);
|
|
history.replaceState(null, '', url);
|
|
}
|
|
}
|
|
|
|
$('.toggle-button-managed').click((event) => {
|
|
event.preventDefault();
|
|
setHostingType('managed');
|
|
});
|
|
|
|
$('.toggle-button-self').click((event) => {
|
|
event.preventDefault();
|
|
// Self-hosting is currently yearly-only
|
|
applyPeriod('yearly');
|
|
setHostingType('self');
|
|
});
|
|
|
|
const initialHostingType = urlQuery.get('hosting') ? urlQuery.get('hosting') : 'managed';
|
|
setHostingType(initialHostingType, false);
|
|
</script>
|
|
</div>
|