Amana by Bayuti. Halal property investing from £50
Don't invest unless you're prepared to lose all the money you invest. This is a high risk investment and you are unlikely to be protected if something goes wrong. Take 2 mins to learn more.

Amana is live. Halal property investing, in your pocket.

Your deposit is sitting still. Make it work.

Amana is open in the Bayuti app today. An indirect investment built on real UK property, with any returns paid as dividends, never interest. Shariah compliant, from £50.

Download the app How it works

Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, regulated by the FCA (FRN 822039). Capital at risk.

£50Minimum investment
DividendsHow any returns are paid
100%Shariah compliant
3 daysTypical withdrawal below £50k, in working days

Why Amana

Most halal apps sell you the market. Amana is built on real UK property.

AmanaHalal stock appsCash savings
What you ownAn indirect investment in UK propertyFund units in screened stocksA bank balance
Where income comes fromDividends funded by rental incomeMarket performanceOften interest based
Backed by a physical assetYes, real UK propertyNoNo
Shariah compliantIndependent Shariah boardUsuallyRarely
Daily market swingsNot traded on stock marketsYesNo

Illustrative comparison of typical product categories, not any specific provider. Capital at risk with all investments.

How it works

Three steps. No riba, no grey areas.

Step 1

Open your account

Download Amana and open your account in minutes, straight from your phone.

Step 2

Invest from £50

Start from £50. Your money is invested indirectly in real UK property, structured to stay Shariah compliant.

Step 3

Earn dividends

Any returns are paid to you as dividends, funded by the property income. Never from interest.

See how it grows

The deposit calculator.

Tell us what you have saved so far, what you save each month and for how long. Then compare it with the same money sitting still in the bank.

Illustrative value after 5 years £0
Same amount in the bank, sitting still £0 Difference: £0

Illustrative only, based on an assumed rate of 4% a year for illustration purposes. The bank figure assumes your money sits still with no growth. This is not a projection, forecast or promise of returns. The value of your investment can go down as well as up and you may get back less than you put in. Capital at risk.

FAQs

Straight answers.

What do I actually own with Amana?

Amana is an indirect investment. You do not buy or own property directly. Your money is invested through Amana into real UK property, and any returns are paid to you as dividends.

Is Amana regulated by the FCA?

Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, which is regulated by the Financial Conduct Authority (FRN 822039).

How is Amana Shariah compliant?

The product is structured to be Shariah compliant and is overseen by an independent Shariah board. Any returns come as dividends funded by property income, never from interest, so there is no riba at any point.

How do returns work?

Any returns are paid as dividends, funded by the income the underlying properties generate. Dividends are not guaranteed and the value of your investment can go down as well as up.

How much do I need to start?

You can start from £50.

How quickly can I withdraw?

Withdrawals below £50,000 are typically processed within 3 working days. Withdrawals of £50,000 or above are processed within 30 calendar days.

Get started today

Download the app and start growing your home deposit.

Amana is open and active in the Bayuti app. Download it free, open your account in minutes and start from £50. Capital at risk.

Download on the App Store Get it on Google Play
Amana.by Bayuti

Risk summary. This is a high risk investment. You could lose all the money you invest and you are unlikely to be protected if something goes wrong. Investments are illiquid compared with cash savings and returns are not guaranteed. Past performance is not a reliable indicator of future results. Do not invest more than you can afford to lose. If you are unsure whether investing is right for you, seek independent financial advice.

Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, authorised and regulated by the Financial Conduct Authority (FRN 822039). Amana is an indirect investment built on real UK property. Any returns are paid as dividends and are not guaranteed. Capital at risk.

© 2026 Elite Capital and Management Services Limited. All rights reserved.

<!DOCTYPE html>
<html lang=”en-GB”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Amana by Bayuti. Halal property investing from £50</title>
<meta name=”description” content=”Amana by Bayuti is an indirect investment built on real UK property. Returns paid as dividends, never interest. Shariah compliant, from £50. Capital at risk.”>
<link rel=”preconnect” href=”https://fonts.googleapis.com”>
<link rel=”preconnect” href=”https://fonts.gstatic.com” crossorigin>
<link href=”https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap” rel=”stylesheet”>
<style>
:root{
–ink:#07082B; /* Amana navy */
–ink-2:#101235;
–lime:#00A672; /* Amana green */
–sage:#EEF0F8; /* Amana pale section */
–sage-2:#DCE0F0;
–paper:#FFFFFF;
–muted:#5F6488;
–muted-on-dark:#9BA1C4;
–display:’Space Grotesk’,Arial,sans-serif;
–body:’Inter’,Arial,sans-serif;
–mono:’IBM Plex Mono’,monospace;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion: reduce){html{scroll-behavior:auto}*{animation:none!important;transition:none!important}}
body{font-family:var(–body);color:var(–ink);background:var(–paper);line-height:1.6}
.wrap{max-width:1120px;margin:0 auto;padding:0 24px}
.kicker{font-family:var(–mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(–muted)}
.on-dark .kicker{color:var(–lime)}
h1,h2,h3{font-family:var(–display);line-height:1.12;font-weight:700}
a{color:inherit}

/* FCA risk banner */
.risk{background:#1A1A1A;color:#fff;font-size:13px;text-align:center;padding:10px 16px;line-height:1.5}
.risk a{color:var(–lime);text-decoration:underline}

/* Nav */
nav{background:var(–ink);position:sticky;top:0;z-index:50;border-bottom:1px solid rgba(255,255,255,.08)}
.nav-in{display:flex;align-items:center;justify-content:space-between;height:64px}
.wordmark{font-family:var(–display);font-weight:700;font-size:22px;color:#fff;text-decoration:none;letter-spacing:.01em}
.wordmark span{color:var(–lime)}
.wordmark small{display:block;font-family:var(–mono);font-weight:400;font-size:10px;letter-spacing:.18em;color:var(–muted-on-dark);text-transform:uppercase}
.nav-links{display:flex;gap:28px;align-items:center}
.nav-links a{color:#E6EFE9;text-decoration:none;font-size:15px}
.nav-links a:hover,.nav-links a:focus-visible{color:var(–lime)}
.btn-nav{background:var(–lime);color:#fff!important;padding:9px 18px;border-radius:999px;font-weight:600;font-size:14px!important}
@media(max-width:720px){.nav-links{display:none}}

/* Hero */
header.hero{background:var(–ink);color:#fff;padding:96px 0 72px;position:relative;overflow:hidden}
.hero h1{font-size:clamp(40px,6.5vw,76px);letter-spacing:-.02em;max-width:15ch}
.hero h1 .still{color:var(–muted-on-dark)}
.hero h1 .work{color:var(–lime)}
.hero p.sub{margin:28px 0 36px;max-width:56ch;font-size:19px;color:#D7E2DB}
.btn{display:inline-block;font-family:var(–display);font-weight:500;font-size:17px;text-decoration:none;padding:15px 30px;border-radius:999px;transition:transform .15s ease}
.btn:hover,.btn:focus-visible{transform:translateY(-2px)}
.btn-lime{background:var(–lime);color:#fff}
.btn-ghost{border:1px solid rgba(255,255,255,.35);color:#fff;margin-left:12px}
.hero .fine{margin-top:44px;font-family:var(–mono);font-size:11.5px;letter-spacing:.06em;color:var(–muted-on-dark);text-transform:uppercase;max-width:80ch}

/* Stats */
section.stats{background:var(–sage);padding:64px 0}
.stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:32px}
@media(max-width:860px){.stat-grid{grid-template-columns:repeat(2,1fr)}}
.stat b{font-family:var(–display);font-size:clamp(30px,3.6vw,46px);font-weight:700;display:block;letter-spacing:-.01em}
.stat span{font-size:15px;color:var(–muted)}

section{padding:88px 0}
.sec-head{max-width:60ch;margin-bottom:48px}
.sec-head h2{font-size:clamp(28px,4vw,44px);letter-spacing:-.015em;margin-top:10px}
.sec-head p{margin-top:14px;color:var(–muted);font-size:17px}

/* Comparison table */
.compare{width:100%;border-collapse:collapse;font-size:15.5px}
.compare th,.compare td{padding:16px 14px;text-align:left;border-bottom:1px solid var(–sage-2);vertical-align:top}
.compare thead th{font-family:var(–mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(–muted)}
.compare td:first-child{font-weight:600;width:24%}
.compare .amana{background:var(–sage);font-weight:600}
.compare thead .amana{color:var(–ink)}
@media(max-width:720px){.compare{font-size:13.5px}.compare th,.compare td{padding:11px 8px}}

/* Steps */
.steps{background:var(–ink);color:#fff}
.step-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:40px}
@media(max-width:860px){.step-grid{grid-template-columns:1fr}}
.step h3{font-size:24px;margin:14px 0 10px}
.step p{color:#C9D6CE;font-size:16px}
.steps .sec-head h2{color:#fff}
.steps .sec-head p{color:var(–muted-on-dark)}

/* Calculator */
.calc{background:var(–sage)}
.calc-card{background:#fff;border-radius:20px;padding:40px;display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center}
@media(max-width:860px){.calc-card{grid-template-columns:1fr;padding:28px}}
.calc label{display:block;font-family:var(–mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(–muted);margin:22px 0 6px}
.calc input[type=range]{width:100%;accent-color:var(–ink)}
.calc output{font-family:var(–display);font-weight:700;font-size:20px}
.calc-result b{font-family:var(–display);font-size:clamp(38px,5vw,58px);display:block;letter-spacing:-.02em}
.calc-result span{color:var(–muted);font-size:15px}
.calc .fine{margin-top:26px;font-size:12.5px;color:var(–muted);line-height:1.55}
.vs-bank{margin-top:22px;padding-top:18px;border-top:1px solid var(–sage-2)}
.vs-bank b{font-family:var(–display);font-size:clamp(24px,3vw,34px);display:block;color:var(–muted);letter-spacing:-.01em}
.vs-bank span{display:block;font-size:14px;color:var(–muted)}
#result{color:var(–lime)}
#diff{font-weight:600;color:var(–lime)}

/* FAQ */
details{border-bottom:1px solid var(–sage-2)}
summary{cursor:pointer;font-family:var(–display);font-weight:500;font-size:19px;padding:22px 0;list-style:none;display:flex;justify-content:space-between;align-items:center}
summary::-webkit-details-marker{display:none}
summary::after{content:”+”;font-size:24px;color:var(–muted)}
details[open] summary::after{content:”–”}
details p{padding:0 0 22px;color:var(–muted);max-width:70ch}

/* Register */
.register{background:var(–ink);color:#fff}
.register .sec-head h2{color:#fff}
.register .sec-head p{color:var(–muted-on-dark)}
.store-badges{display:flex;gap:18px;justify-content:center;align-items:center;margin-top:8px;flex-wrap:wrap}
.store-badges a{display:block;line-height:0}
#iosBadge img{height:56px;width:auto;display:block}
#playBadge img{height:66px;width:auto;display:block}
.get-link{display:none;margin:40px auto 0;background:#fff;border-radius:20px;padding:32px;max-width:560px;color:var(–ink);grid-template-columns:1fr auto 1fr;gap:24px;align-items:center;text-align:center}
@media(hover:hover) and (pointer:fine){.get-link{display:grid}}
.get-link-label{font-family:var(–mono);font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;color:var(–muted);margin-bottom:14px}
.get-link-col #qrcode{display:flex;justify-content:center}
.get-link-or{font-family:var(–display);font-weight:700;color:var(–muted)}
.get-link input[type=tel]{width:100%;padding:13px 16px;border:1.5px solid var(–sage-2);border-radius:12px;font-family:var(–body);font-size:16px;margin-bottom:10px}
.get-link input[type=tel]:focus{outline:2px solid var(–lime);border-color:var(–lime)}
.get-link .btn{width:100%;padding:13px 20px;font-size:15.5px;border:none;cursor:pointer}
.get-link-consent{font-size:11px;color:var(–muted);margin-top:10px;line-height:1.45}
.get-link-msg{font-size:13px;margin-top:8px;min-height:18px;font-weight:600}
.get-link-msg.ok{color:var(–lime)}
.get-link-msg.err{color:#C0392B}
@media(max-width:720px){.get-link{grid-template-columns:1fr}.get-link-or{display:none}}
.qr-modal{position:fixed;inset:0;background:rgba(7,8,43,.72);display:flex;align-items:center;justify-content:center;z-index:100;padding:24px}
.qr-modal[hidden]{display:none}
.qr-modal-card{background:#fff;border-radius:20px;padding:34px;max-width:320px;text-align:center;color:var(–ink);position:relative}
.qr-modal-card h3{font-size:22px;margin-bottom:16px}
.qr-modal-card #qrcodeModal{display:flex;justify-content:center}
.qr-modal-card p{font-size:13px;color:var(–muted);margin-top:14px;line-height:1.5}
.qr-modal-links{margin-top:14px;display:flex;gap:16px;justify-content:center}
.qr-modal-links a{font-size:13.5px;color:var(–lime);font-weight:600}
.qr-close{position:absolute;top:10px;right:14px;background:none;border:none;font-size:26px;color:var(–muted);cursor:pointer;line-height:1}

footer{background:#07130E;color:var(–muted-on-dark);font-size:13px;padding:56px 0;line-height:1.7}
footer .wordmark{font-size:18px;margin-bottom:18px;display:inline-block}
footer p{max-width:90ch;margin-bottom:14px}
</style>
</head>
<body>

<div class=”risk”>
Don’t invest unless you’re prepared to lose all the money you invest. This is a high risk investment and you are unlikely to be protected if something goes wrong. <a href=”#risk-summary”>Take 2 mins to learn more</a>.
</div>

<nav>
<div class=”wrap nav-in”>
<a class=”wordmark” href=”#”>Amana<span>.</span><small>by Bayuti</small></a>
<div class=”nav-links”>
<a href=”#how”>How it works</a>
<a href=”#why”>Why Amana</a>
<a href=”#grow”>See how it grows</a>
<a href=”#faq”>FAQs</a>
<a class=”btn-nav” href=”#download”>Download the app</a>
</div>
</div>
</nav>

<header class=”hero on-dark”>
<div class=”wrap”>
<p class=”kicker”>Amana is live. Halal property investing, in your pocket.</p>
<h1 style=”margin-top:18px”>Your deposit is <span class=”still”>sitting still.</span> <span class=”work”>Make it work.</span></h1>
<p class=”sub”>Amana is open in the Bayuti app today. An indirect investment built on real UK property, with any returns paid as dividends, never interest. Shariah compliant, from £50.</p>
<a class=”btn btn-lime” id=”heroDownload” href=”#download”>Download the app</a>
<a class=”btn btn-ghost” href=”#how”>How it works</a>
<p class=”fine”>Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, regulated by the FCA (FRN 822039). Capital at risk.</p>
</div>
</header>

<section class=”stats”>
<div class=”wrap stat-grid”>
<div class=”stat”><b>£50</b><span>Minimum investment</span></div>
<div class=”stat”><b>Dividends</b><span>How any returns are paid</span></div>
<div class=”stat”><b>100%</b><span>Shariah compliant</span></div>
<div class=”stat”><b>3 days</b><span>Typical withdrawal below £50k, in working days</span></div>
</div>
</section>

<section id=”why”>
<div class=”wrap”>
<div class=”sec-head”>
<p class=”kicker”>Why Amana</p>
<h2>Most halal apps sell you the market. Amana is built on real UK property.</h2>
</div>
<table class=”compare”>
<thead>
<tr><th></th><th class=”amana”>Amana</th><th>Halal stock apps</th><th>Cash savings</th></tr>
</thead>
<tbody>
<tr><td>What you own</td><td class=”amana”>An indirect investment in UK property</td><td>Fund units in screened stocks</td><td>A bank balance</td></tr>
<tr><td>Where income comes from</td><td class=”amana”>Dividends funded by rental income</td><td>Market performance</td><td>Often interest based</td></tr>
<tr><td>Backed by a physical asset</td><td class=”amana”>Yes, real UK property</td><td>No</td><td>No</td></tr>
<tr><td>Shariah compliant</td><td class=”amana”>Independent Shariah board</td><td>Usually</td><td>Rarely</td></tr>
<tr><td>Daily market swings</td><td class=”amana”>Not traded on stock markets</td><td>Yes</td><td>No</td></tr>
</tbody>
</table>
<p style=”margin-top:18px;font-size:12.5px;color:var(–muted)”>Illustrative comparison of typical product categories, not any specific provider. Capital at risk with all investments.</p>
</div>
</section>

<section id=”how” class=”steps on-dark”>
<div class=”wrap”>
<div class=”sec-head”>
<p class=”kicker”>How it works</p>
<h2>Three steps. No riba, no grey areas.</h2>
</div>
<div class=”step-grid”>
<div class=”step”><p class=”kicker”>Step 1</p><h3>Open your account</h3><p>Download Amana and open your account in minutes, straight from your phone.</p></div>
<div class=”step”><p class=”kicker”>Step 2</p><h3>Invest from £50</h3><p>Start from £50. Your money is invested indirectly in real UK property, structured to stay Shariah compliant.</p></div>
<div class=”step”><p class=”kicker”>Step 3</p><h3>Earn dividends</h3><p>Any returns are paid to you as dividends, funded by the property income. Never from interest.</p></div>
</div>
</div>
</section>

<section id=”grow” class=”calc”>
<div class=”wrap”>
<div class=”sec-head”>
<p class=”kicker”>See how it grows</p>
<h2>The deposit calculator.</h2>
<p>Tell us what you have saved so far, what you save each month and for how long. Then compare it with the same money sitting still in the bank.</p>
</div>
<div class=”calc-card”>
<div>
<label for=”saved”>Saved so far: <output id=”savedOut”>£5,000</output></label>
<input type=”range” id=”saved” min=”0″ max=”50000″ step=”500″ value=”5000″>
<label for=”monthly”>Saving per month: <output id=”monthlyOut”>£100</output></label>
<input type=”range” id=”monthly” min=”0″ max=”1000″ step=”25″ value=”100″>
<label for=”years”>Duration of savings: <output id=”yearsOut”>5 years</output></label>
<input type=”range” id=”years” min=”1″ max=”15″ step=”1″ value=”5″>
</div>
<div class=”calc-result”>
<span>Illustrative value after <output id=”yearsOut2″>5 years</output></span>
<b id=”result”>£0</b>
<div class=”vs-bank”>
<span>Same amount in the bank, sitting still</span>
<b id=”bank”>£0</b>
<span>Difference: <output id=”diff”>£0</output></span>
</div>
<p class=”fine”>Illustrative only, based on an assumed rate of 4% a year for illustration purposes. The bank figure assumes your money sits still with no growth. This is not a projection, forecast or promise of returns. The value of your investment can go down as well as up and you may get back less than you put in. Capital at risk.</p>
</div>
</div>
</div>
</section>

<section id=”faq”>
<div class=”wrap”>
<div class=”sec-head”>
<p class=”kicker”>FAQs</p>
<h2>Straight answers.</h2>
</div>
<details open>
<summary>What do I actually own with Amana?</summary>
<p>Amana is an indirect investment. You do not buy or own property directly. Your money is invested through Amana into real UK property, and any returns are paid to you as dividends.</p>
</details>
<details>
<summary>Is Amana regulated by the FCA?</summary>
<p>Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, which is regulated by the Financial Conduct Authority (FRN 822039).</p>
</details>
<details>
<summary>How is Amana Shariah compliant?</summary>
<p>The product is structured to be Shariah compliant and is overseen by an independent Shariah board. Any returns come as dividends funded by property income, never from interest, so there is no riba at any point.</p>
</details>
<details>
<summary>How do returns work?</summary>
<p>Any returns are paid as dividends, funded by the income the underlying properties generate. Dividends are not guaranteed and the value of your investment can go down as well as up.</p>
</details>
<details>
<summary>How much do I need to start?</summary>
<p>You can start from £50.</p>
</details>
<details>
<summary>How quickly can I withdraw?</summary>
<p>Withdrawals below £50,000 are typically processed within 3 working days. Withdrawals of £50,000 or above are processed within 30 calendar days.</p>
</details>
</div>
</section>

<section class=”register on-dark” id=”download”>
<div class=”wrap” style=”text-align:center”>
<div class=”sec-head” style=”margin-left:auto;margin-right:auto”>
<p class=”kicker”>Get started today</p>
<h2>Download the app and start growing your home deposit.</h2>
<p>Amana is open and active in the Bayuti app. Download it free, open your account in minutes and start from £50. Capital at risk.</p>
</div>
<div class=”store-badges”>
<a href=”https://apps.apple.com/app/id6739215991″ id=”iosBadge” aria-label=”Download on the App Store”><img src=”https://tools.applemediaservices.com/api/badges/download-on-the-app-store/black/en-gb” alt=”Download on the App Store”></a>
<a href=”https://play.google.com/store/apps/details?id=com.workable.bayuti” id=”playBadge” aria-label=”Get it on Google Play”><img src=”https://play.google.com/intl/en_gb/badges/static/images/badges/en_badge_web_generic.png” alt=”Get it on Google Play”></a>
</div>
<div class=”get-link” id=”qrShell”>
<div class=”get-link-col”>
<p class=”get-link-label”>Scan with your phone camera</p>
<div id=”qrcode”></div>
</div>
<div class=”get-link-or” aria-hidden=”true”>or</div>
<div class=”get-link-col”>
<p class=”get-link-label”>Text me the link</p>
<form id=”smsForm” novalidate>
<input type=”tel” id=”smsPhone” inputmode=”tel” autocomplete=”tel” placeholder=”07123 456789″ aria-label=”UK mobile number”>
<button type=”submit” class=”btn btn-lime” id=”smsSend”>Send the link</button>
<p class=”get-link-consent”>We will send one text with the download link. See our privacy policy for how we use your number.</p>
<p class=”get-link-msg” id=”smsMsg” role=”status”></p>
</form>
</div>
</div>
</div>
</section>

<footer id=”risk-summary”>
<div class=”wrap”>
<a class=”wordmark” href=”#”>Amana<span>.</span><small>by Bayuti</small></a>
<p><strong>Risk summary.</strong> This is a high risk investment. You could lose all the money you invest and you are unlikely to be protected if something goes wrong. Investments are illiquid compared with cash savings and returns are not guaranteed. Past performance is not a reliable indicator of future results. Do not invest more than you can afford to lose. If you are unsure whether investing is right for you, seek independent financial advice.</p>
<p>Amana by Bayuti is a trading name of Elite Capital and Management Services Limited, authorised and regulated by the Financial Conduct Authority (FRN 822039). Amana is an indirect investment built on real UK property. Any returns are paid as dividends and are not guaranteed. Capital at risk.</p>
<p>© 2026 Elite Capital and Management Services Limited. All rights reserved.</p>
</div>
</footer>

<script>
const fmt = n => “£” + Math.round(n).toLocaleString(“en-GB”);
const saved = document.getElementById(“saved”),
monthly = document.getElementById(“monthly”),
years = document.getElementById(“years”);
const RATE = 0.04; // illustrative annual rate
function update(){
const p = +saved.value, m = +monthly.value, y = +years.value,
r = RATE/12, n = y*12;
const growth = r === 0
? p + m*n
: p*Math.pow(1+r,n) + m*((Math.pow(1+r,n)-1)/r);
const bank = p + m*n;
document.getElementById(“savedOut”).textContent = fmt(p);
document.getElementById(“monthlyOut”).textContent = fmt(m);
document.getElementById(“yearsOut”).textContent = y + (y===1?” year”:” years”);
document.getElementById(“yearsOut2″).textContent = y + (y===1?” year”:” years”);
document.getElementById(“result”).textContent = fmt(growth);
document.getElementById(“bank”).textContent = fmt(bank);
document.getElementById(“diff”).textContent = fmt(growth – bank);
}
[saved,monthly,years].forEach(el=>el.addEventListener(“input”,update));
update();
</script>
<div class=”qr-modal” id=”qrModal” role=”dialog” aria-modal=”true” aria-label=”Get the Bayuti app” hidden>
<div class=”qr-modal-card”>
<button class=”qr-close” id=”qrClose” aria-label=”Close”>&times;</button>
<h3>Scan to get the app</h3>
<div id=”qrcodeModal”></div>
<p>Point your phone camera at the code. It takes you straight to the App Store or Google Play.</p>
<div class=”qr-modal-links”>
<a href=”https://apps.apple.com/app/id6739215991″>App Store</a>
<a href=”https://play.google.com/store/apps/details?id=com.workable.bayuti”>Google Play</a>
</div>
</div>
</div>
<script>
//———————————————————————
//
// QR Code Generator for JavaScript
//
// Copyright (c) 2009 Kazuhiko Arase
//
// URL: http://www.d-project.com/
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license.php
//
// The word ‘QR Code’ is registered trademark of
// DENSO WAVE INCORPORATED
// http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//———————————————————————

var qrcode = function() {

//———————————————————————
// qrcode
//———————————————————————

/**
* qrcode
* @param typeNumber 1 to 40
* @param errorCorrectionLevel ‘L’,’M’,’Q’,’H’
*/
var qrcode = function(typeNumber, errorCorrectionLevel) {

var PAD0 = 0xEC;
var PAD1 = 0x11;

var _typeNumber = typeNumber;
var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
var _modules = null;
var _moduleCount = 0;
var _dataCache = null;
var _dataList = [];

var _this = {};

var makeImpl = function(test, maskPattern) {

_moduleCount = _typeNumber * 4 + 17;
_modules = function(moduleCount) {
var modules = new Array(moduleCount);
for (var row = 0; row < moduleCount; row += 1) {
modules[row] = new Array(moduleCount);
for (var col = 0; col < moduleCount; col += 1) {
modules[row][col] = null;
}
}
return modules;
}(_moduleCount);

setupPositionProbePattern(0, 0);
setupPositionProbePattern(_moduleCount – 7, 0);
setupPositionProbePattern(0, _moduleCount – 7);
setupPositionAdjustPattern();
setupTimingPattern();
setupTypeInfo(test, maskPattern);

if (_typeNumber >= 7) {
setupTypeNumber(test);
}

if (_dataCache == null) {
_dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
}

mapData(_dataCache, maskPattern);
};

var setupPositionProbePattern = function(row, col) {

for (var r = -1; r <= 7; r += 1) {

if (row + r <= -1 || _moduleCount <= row + r) continue;

for (var c = -1; c <= 7; c += 1) {

if (col + c <= -1 || _moduleCount <= col + c) continue;

if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
_modules[row + r][col + c] = true;
} else {
_modules[row + r][col + c] = false;
}
}
}
};

var getBestMaskPattern = function() {

var minLostPoint = 0;
var pattern = 0;

for (var i = 0; i < 8; i += 1) {

makeImpl(true, i);

var lostPoint = QRUtil.getLostPoint(_this);

if (i == 0 || minLostPoint > lostPoint) {
minLostPoint = lostPoint;
pattern = i;
}
}

return pattern;
};

var setupTimingPattern = function() {

for (var r = 8; r < _moduleCount – 8; r += 1) {
if (_modules[r][6] != null) {
continue;
}
_modules[r][6] = (r % 2 == 0);
}

for (var c = 8; c < _moduleCount – 8; c += 1) {
if (_modules[6][c] != null) {
continue;
}
_modules[6][c] = (c % 2 == 0);
}
};

var setupPositionAdjustPattern = function() {

var pos = QRUtil.getPatternPosition(_typeNumber);

for (var i = 0; i < pos.length; i += 1) {

for (var j = 0; j < pos.length; j += 1) {

var row = pos[i];
var col = pos[j];

if (_modules[row][col] != null) {
continue;
}

for (var r = -2; r <= 2; r += 1) {

for (var c = -2; c <= 2; c += 1) {

if (r == -2 || r == 2 || c == -2 || c == 2
|| (r == 0 && c == 0) ) {
_modules[row + r][col + c] = true;
} else {
_modules[row + r][col + c] = false;
}
}
}
}
}
};

var setupTypeNumber = function(test) {

var bits = QRUtil.getBCHTypeNumber(_typeNumber);

for (var i = 0; i < 18; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
_modules[Math.floor(i / 3)][i % 3 + _moduleCount – 8 – 3] = mod;
}

for (var i = 0; i < 18; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
_modules[i % 3 + _moduleCount – 8 – 3][Math.floor(i / 3)] = mod;
}
};

var setupTypeInfo = function(test, maskPattern) {

var data = (_errorCorrectionLevel << 3) | maskPattern;
var bits = QRUtil.getBCHTypeInfo(data);

// vertical
for (var i = 0; i < 15; i += 1) {

var mod = (!test && ( (bits >> i) & 1) == 1);

if (i < 6) {
_modules[i][8] = mod;
} else if (i < 8) {
_modules[i + 1][8] = mod;
} else {
_modules[_moduleCount – 15 + i][8] = mod;
}
}

// horizontal
for (var i = 0; i < 15; i += 1) {

var mod = (!test && ( (bits >> i) & 1) == 1);

if (i < 8) {
_modules[8][_moduleCount – i – 1] = mod;
} else if (i < 9) {
_modules[8][15 – i – 1 + 1] = mod;
} else {
_modules[8][15 – i – 1] = mod;
}
}

// fixed module
_modules[_moduleCount – 8][8] = (!test);
};

var mapData = function(data, maskPattern) {

var inc = -1;
var row = _moduleCount – 1;
var bitIndex = 7;
var byteIndex = 0;
var maskFunc = QRUtil.getMaskFunction(maskPattern);

for (var col = _moduleCount – 1; col > 0; col -= 2) {

if (col == 6) col -= 1;

while (true) {

for (var c = 0; c < 2; c += 1) {

if (_modules[row][col – c] == null) {

var dark = false;

if (byteIndex < data.length) {
dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
}

var mask = maskFunc(row, col – c);

if (mask) {
dark = !dark;
}

_modules[row][col – c] = dark;
bitIndex -= 1;

if (bitIndex == -1) {
byteIndex += 1;
bitIndex = 7;
}
}
}

row += inc;

if (row < 0 || _moduleCount <= row) {
row -= inc;
inc = -inc;
break;
}
}
}
};

var createBytes = function(buffer, rsBlocks) {

var offset = 0;

var maxDcCount = 0;
var maxEcCount = 0;

var dcdata = new Array(rsBlocks.length);
var ecdata = new Array(rsBlocks.length);

for (var r = 0; r < rsBlocks.length; r += 1) {

var dcCount = rsBlocks[r].dataCount;
var ecCount = rsBlocks[r].totalCount – dcCount;

maxDcCount = Math.max(maxDcCount, dcCount);
maxEcCount = Math.max(maxEcCount, ecCount);

dcdata[r] = new Array(dcCount);

for (var i = 0; i < dcdata[r].length; i += 1) {
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
}
offset += dcCount;

var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() – 1);

var modPoly = rawPoly.mod(rsPoly);
ecdata[r] = new Array(rsPoly.getLength() – 1);
for (var i = 0; i < ecdata[r].length; i += 1) {
var modIndex = i + modPoly.getLength() – ecdata[r].length;
ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
}
}

var totalCodeCount = 0;
for (var i = 0; i < rsBlocks.length; i += 1) {
totalCodeCount += rsBlocks[i].totalCount;
}

var data = new Array(totalCodeCount);
var index = 0;

for (var i = 0; i < maxDcCount; i += 1) {
for (var r = 0; r < rsBlocks.length; r += 1) {
if (i < dcdata[r].length) {
data[index] = dcdata[r][i];
index += 1;
}
}
}

for (var i = 0; i < maxEcCount; i += 1) {
for (var r = 0; r < rsBlocks.length; r += 1) {
if (i < ecdata[r].length) {
data[index] = ecdata[r][i];
index += 1;
}
}
}

return data;
};

var createData = function(typeNumber, errorCorrectionLevel, dataList) {

var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel);

var buffer = qrBitBuffer();

for (var i = 0; i < dataList.length; i += 1) {
var data = dataList[i];
buffer.put(data.getMode(), 4);
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
data.write(buffer);
}

// calc num max data.
var totalDataCount = 0;
for (var i = 0; i < rsBlocks.length; i += 1) {
totalDataCount += rsBlocks[i].dataCount;
}

if (buffer.getLengthInBits() > totalDataCount * 8) {
throw ‘code length overflow. (‘
+ buffer.getLengthInBits()
+ ‘>’
+ totalDataCount * 8
+ ‘)’;
}

// end code
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
buffer.put(0, 4);
}

// padding
while (buffer.getLengthInBits() % 8 != 0) {
buffer.putBit(false);
}

// padding
while (true) {

if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD0, 8);

if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD1, 8);
}

return createBytes(buffer, rsBlocks);
};

_this.addData = function(data, mode) {

mode = mode || ‘Byte’;

var newData = null;

switch(mode) {
case ‘Numeric’ :
newData = qrNumber(data);
break;
case ‘Alphanumeric’ :
newData = qrAlphaNum(data);
break;
case ‘Byte’ :
newData = qr8BitByte(data);
break;
case ‘Kanji’ :
newData = qrKanji(data);
break;
default :
throw ‘mode:’ + mode;
}

_dataList.push(newData);
_dataCache = null;
};

_this.isDark = function(row, col) {
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
throw row + ‘,’ + col;
}
return _modules[row][col];
};

_this.getModuleCount = function() {
return _moduleCount;
};

_this.make = function() {
if (_typeNumber < 1) {
var typeNumber = 1;

for (; typeNumber < 40; typeNumber++) {
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel);
var buffer = qrBitBuffer();

for (var i = 0; i < _dataList.length; i++) {
var data = _dataList[i];
buffer.put(data.getMode(), 4);
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
data.write(buffer);
}

var totalDataCount = 0;
for (var i = 0; i < rsBlocks.length; i++) {
totalDataCount += rsBlocks[i].dataCount;
}

if (buffer.getLengthInBits() <= totalDataCount * 8) {
break;
}
}

_typeNumber = typeNumber;
}

makeImpl(false, getBestMaskPattern() );
};

_this.createTableTag = function(cellSize, margin) {

cellSize = cellSize || 2;
margin = (typeof margin == ‘undefined’)? cellSize * 4 : margin;

var qrHtml = ”;

qrHtml += ‘<table style=”‘;
qrHtml += ‘ border-width: 0px; border-style: none;’;
qrHtml += ‘ border-collapse: collapse;’;
qrHtml += ‘ padding: 0px; margin: ‘ + margin + ‘px;’;
qrHtml += ‘”>’;
qrHtml += ‘<tbody>’;

for (var r = 0; r < _this.getModuleCount(); r += 1) {

qrHtml += ‘<tr>’;

for (var c = 0; c < _this.getModuleCount(); c += 1) {
qrHtml += ‘<td style=”‘;
qrHtml += ‘ border-width: 0px; border-style: none;’;
qrHtml += ‘ border-collapse: collapse;’;
qrHtml += ‘ padding: 0px; margin: 0px;’;
qrHtml += ‘ width: ‘ + cellSize + ‘px;’;
qrHtml += ‘ height: ‘ + cellSize + ‘px;’;
qrHtml += ‘ background-color: ‘;
qrHtml += _this.isDark(r, c)? ‘#000000’ : ‘#ffffff’;
qrHtml += ‘;’;
qrHtml += ‘”/>’;
}

qrHtml += ‘</tr>’;
}

qrHtml += ‘</tbody>’;
qrHtml += ‘</table>’;

return qrHtml;
};

_this.createSvgTag = function(cellSize, margin, alt, title) {

var opts = {};
if (typeof arguments[0] == ‘object’) {
// Called by options.
opts = arguments[0];
// overwrite cellSize and margin.
cellSize = opts.cellSize;
margin = opts.margin;
alt = opts.alt;
title = opts.title;
}

cellSize = cellSize || 2;
margin = (typeof margin == ‘undefined’)? cellSize * 4 : margin;

// Compose alt property surrogate
alt = (typeof alt === ‘string’) ? {text: alt} : alt || {};
alt.text = alt.text || null;
alt.id = (alt.text) ? alt.id || ‘qrcode-description’ : null;

// Compose title property surrogate
title = (typeof title === ‘string’) ? {text: title} : title || {};
title.text = title.text || null;
title.id = (title.text) ? title.id || ‘qrcode-title’ : null;

var size = _this.getModuleCount() * cellSize + margin * 2;
var c, mc, r, mr, qrSvg=”, rect;

rect = ‘l’ + cellSize + ‘,0 0,’ + cellSize +
‘ -‘ + cellSize + ‘,0 0,-‘ + cellSize + ‘z ‘;

qrSvg += ‘<svg version=”1.1″ xmlns=”http://www.w3.org/2000/svg”‘;
qrSvg += !opts.scalable ? ‘ width=”‘ + size + ‘px” height=”‘ + size + ‘px”‘ : ”;
qrSvg += ‘ viewBox=”0 0 ‘ + size + ‘ ‘ + size + ‘” ‘;
qrSvg += ‘ preserveAspectRatio=”xMinYMin meet”‘;
qrSvg += (title.text || alt.text) ? ‘ role=”img” aria-labelledby=”‘ +
escapeXml([title.id, alt.id].join(‘ ‘).trim() ) + ‘”‘ : ”;
qrSvg += ‘>’;
qrSvg += (title.text) ? ‘<title id=”‘ + escapeXml(title.id) + ‘”>’ +
escapeXml(title.text) + ‘</title>’ : ”;
qrSvg += (alt.text) ? ‘<description id=”‘ + escapeXml(alt.id) + ‘”>’ +
escapeXml(alt.text) + ‘</description>’ : ”;
qrSvg += ‘<rect width=”100%” height=”100%” fill=”white” cx=”0″ cy=”0″/>’;
qrSvg += ‘<path d=”‘;

for (r = 0; r < _this.getModuleCount(); r += 1) {
mr = r * cellSize + margin;
for (c = 0; c < _this.getModuleCount(); c += 1) {
if (_this.isDark(r, c) ) {
mc = c*cellSize+margin;
qrSvg += ‘M’ + mc + ‘,’ + mr + rect;
}
}
}

qrSvg += ‘” stroke=”transparent” fill=”black”/>’;
qrSvg += ‘</svg>’;

return qrSvg;
};

_this.createDataURL = function(cellSize, margin) {

cellSize = cellSize || 2;
margin = (typeof margin == ‘undefined’)? cellSize * 4 : margin;

var size = _this.getModuleCount() * cellSize + margin * 2;
var min = margin;
var max = size – margin;

return createDataURL(size, size, function(x, y) {
if (min <= x && x < max && min <= y && y < max) {
var c = Math.floor( (x – min) / cellSize);
var r = Math.floor( (y – min) / cellSize);
return _this.isDark(r, c)? 0 : 1;
} else {
return 1;
}
} );
};

_this.createImgTag = function(cellSize, margin, alt) {

cellSize = cellSize || 2;
margin = (typeof margin == ‘undefined’)? cellSize * 4 : margin;

var size = _this.getModuleCount() * cellSize + margin * 2;

var img = ”;
img += ‘<img’;
img += ‘\u0020src=”‘;
img += _this.createDataURL(cellSize, margin);
img += ‘”‘;
img += ‘\u0020width=”‘;
img += size;
img += ‘”‘;
img += ‘\u0020height=”‘;
img += size;
img += ‘”‘;
if (alt) {
img += ‘\u0020alt=”‘;
img += escapeXml(alt);
img += ‘”‘;
}
img += ‘/>’;

return img;
};

var escapeXml = function(s) {
var escaped = ”;
for (var i = 0; i < s.length; i += 1) {
var c = s.charAt(i);
switch(c) {
case ‘<‘: escaped += ‘&lt;’; break;
case ‘>’: escaped += ‘&gt;’; break;
case ‘&’: escaped += ‘&amp;’; break;
case ‘”‘: escaped += ‘&quot;’; break;
default : escaped += c; break;
}
}
return escaped;
};

var _createHalfASCII = function(margin) {
var cellSize = 1;
margin = (typeof margin == ‘undefined’)? cellSize * 2 : margin;

var size = _this.getModuleCount() * cellSize + margin * 2;
var min = margin;
var max = size – margin;

var y, x, r1, r2, p;

var blocks = {
‘██’: ‘█’,
‘█ ‘: ‘▀’,
‘ █’: ‘▄’,
‘ ‘: ‘ ‘
};

var blocksLastLineNoMargin = {
‘██’: ‘▀’,
‘█ ‘: ‘▀’,
‘ █’: ‘ ‘,
‘ ‘: ‘ ‘
};

var ascii = ”;
for (y = 0; y < size; y += 2) {
r1 = Math.floor((y – min) / cellSize);
r2 = Math.floor((y + 1 – min) / cellSize);
for (x = 0; x < size; x += 1) {
p = ‘█’;

if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x – min) / cellSize))) {
p = ‘ ‘;
}

if (min <= x && x < max && min <= y+1 && y+1 < max && _this.isDark(r2, Math.floor((x – min) / cellSize))) {
p += ‘ ‘;
}
else {
p += ‘█’;
}

// Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square.
ascii += (margin < 1 && y+1 >= max) ? blocksLastLineNoMargin[p] : blocks[p];
}

ascii += ‘\n’;
}

if (size % 2 && margin > 0) {
return ascii.substring(0, ascii.length – size – 1) + Array(size+1).join(‘▀’);
}

return ascii.substring(0, ascii.length-1);
};

_this.createASCII = function(cellSize, margin) {
cellSize = cellSize || 1;

if (cellSize < 2) {
return _createHalfASCII(margin);
}

cellSize -= 1;
margin = (typeof margin == ‘undefined’)? cellSize * 2 : margin;

var size = _this.getModuleCount() * cellSize + margin * 2;
var min = margin;
var max = size – margin;

var y, x, r, p;

var white = Array(cellSize+1).join(‘██’);
var black = Array(cellSize+1).join(‘ ‘);

var ascii = ”;
var line = ”;
for (y = 0; y < size; y += 1) {
r = Math.floor( (y – min) / cellSize);
line = ”;
for (x = 0; x < size; x += 1) {
p = 1;

if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x – min) / cellSize))) {
p = 0;
}

// Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square.
line += p ? white : black;
}

for (r = 0; r < cellSize; r += 1) {
ascii += line + ‘\n’;
}
}

return ascii.substring(0, ascii.length-1);
};

_this.renderTo2dContext = function(context, cellSize) {
cellSize = cellSize || 2;
var length = _this.getModuleCount();
for (var row = 0; row < length; row++) {
for (var col = 0; col < length; col++) {
context.fillStyle = _this.isDark(row, col) ? ‘black’ : ‘white’;
context.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}
}
}

return _this;
};

//———————————————————————
// qrcode.stringToBytes
//———————————————————————

qrcode.stringToBytesFuncs = {
‘default’ : function(s) {
var bytes = [];
for (var i = 0; i < s.length; i += 1) {
var c = s.charCodeAt(i);
bytes.push(c & 0xff);
}
return bytes;
}
};

qrcode.stringToBytes = qrcode.stringToBytesFuncs[‘default’];

//———————————————————————
// qrcode.createStringToBytes
//———————————————————————

/**
* @param unicodeData base64 string of byte array.
* [16bit Unicode],[16bit Bytes], …
* @param numChars
*/
qrcode.createStringToBytes = function(unicodeData, numChars) {

// create conversion map.

var unicodeMap = function() {

var bin = base64DecodeInputStream(unicodeData);
var read = function() {
var b = bin.read();
if (b == -1) throw ‘eof’;
return b;
};

var count = 0;
var unicodeMap = {};
while (true) {
var b0 = bin.read();
if (b0 == -1) break;
var b1 = read();
var b2 = read();
var b3 = read();
var k = String.fromCharCode( (b0 << 8) | b1);
var v = (b2 << 8) | b3;
unicodeMap[k] = v;
count += 1;
}
if (count != numChars) {
throw count + ‘ != ‘ + numChars;
}

return unicodeMap;
}();

var unknownChar = ‘?’.charCodeAt(0);

return function(s) {
var bytes = [];
for (var i = 0; i < s.length; i += 1) {
var c = s.charCodeAt(i);
if (c < 128) {
bytes.push(c);
} else {
var b = unicodeMap[s.charAt(i)];
if (typeof b == ‘number’) {
if ( (b & 0xff) == b) {
// 1byte
bytes.push(b);
} else {
// 2bytes
bytes.push(b >>> 8);
bytes.push(b & 0xff);
}
} else {
bytes.push(unknownChar);
}
}
}
return bytes;
};
};

//———————————————————————
// QRMode
//———————————————————————

var QRMode = {
MODE_NUMBER : 1 << 0,
MODE_ALPHA_NUM : 1 << 1,
MODE_8BIT_BYTE : 1 << 2,
MODE_KANJI : 1 << 3
};

//———————————————————————
// QRErrorCorrectionLevel
//———————————————————————

var QRErrorCorrectionLevel = {
L : 1,
M : 0,
Q : 3,
H : 2
};

//———————————————————————
// QRMaskPattern
//———————————————————————

var QRMaskPattern = {
PATTERN000 : 0,
PATTERN001 : 1,
PATTERN010 : 2,
PATTERN011 : 3,
PATTERN100 : 4,
PATTERN101 : 5,
PATTERN110 : 6,
PATTERN111 : 7
};

//———————————————————————
// QRUtil
//———————————————————————

var QRUtil = function() {

var PATTERN_POSITION_TABLE = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170]
];
var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);

var _this = {};

var getBCHDigit = function(data) {
var digit = 0;
while (data != 0) {
digit += 1;
data >>>= 1;
}
return digit;
};

_this.getBCHTypeInfo = function(data) {
var d = data << 10;
while (getBCHDigit(d) – getBCHDigit(G15) >= 0) {
d ^= (G15 << (getBCHDigit(d) – getBCHDigit(G15) ) );
}
return ( (data << 10) | d) ^ G15_MASK;
};

_this.getBCHTypeNumber = function(data) {
var d = data << 12;
while (getBCHDigit(d) – getBCHDigit(G18) >= 0) {
d ^= (G18 << (getBCHDigit(d) – getBCHDigit(G18) ) );
}
return (data << 12) | d;
};

_this.getPatternPosition = function(typeNumber) {
return PATTERN_POSITION_TABLE[typeNumber – 1];
};

_this.getMaskFunction = function(maskPattern) {

switch (maskPattern) {

case QRMaskPattern.PATTERN000 :
return function(i, j) { return (i + j) % 2 == 0; };
case QRMaskPattern.PATTERN001 :
return function(i, j) { return i % 2 == 0; };
case QRMaskPattern.PATTERN010 :
return function(i, j) { return j % 3 == 0; };
case QRMaskPattern.PATTERN011 :
return function(i, j) { return (i + j) % 3 == 0; };
case QRMaskPattern.PATTERN100 :
return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
case QRMaskPattern.PATTERN101 :
return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
case QRMaskPattern.PATTERN110 :
return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
case QRMaskPattern.PATTERN111 :
return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };

default :
throw ‘bad maskPattern:’ + maskPattern;
}
};

_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
var a = qrPolynomial([1], 0);
for (var i = 0; i < errorCorrectLength; i += 1) {
a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
}
return a;
};

_this.getLengthInBits = function(mode, type) {

if (1 <= type && type < 10) {

// 1 – 9

switch(mode) {
case QRMode.MODE_NUMBER : return 10;
case QRMode.MODE_ALPHA_NUM : return 9;
case QRMode.MODE_8BIT_BYTE : return 8;
case QRMode.MODE_KANJI : return 8;
default :
throw ‘mode:’ + mode;
}

} else if (type < 27) {

// 10 – 26

switch(mode) {
case QRMode.MODE_NUMBER : return 12;
case QRMode.MODE_ALPHA_NUM : return 11;
case QRMode.MODE_8BIT_BYTE : return 16;
case QRMode.MODE_KANJI : return 10;
default :
throw ‘mode:’ + mode;
}

} else if (type < 41) {

// 27 – 40

switch(mode) {
case QRMode.MODE_NUMBER : return 14;
case QRMode.MODE_ALPHA_NUM : return 13;
case QRMode.MODE_8BIT_BYTE : return 16;
case QRMode.MODE_KANJI : return 12;
default :
throw ‘mode:’ + mode;
}

} else {
throw ‘type:’ + type;
}
};

_this.getLostPoint = function(qrcode) {

var moduleCount = qrcode.getModuleCount();

var lostPoint = 0;

// LEVEL1

for (var row = 0; row < moduleCount; row += 1) {
for (var col = 0; col < moduleCount; col += 1) {

var sameCount = 0;
var dark = qrcode.isDark(row, col);

for (var r = -1; r <= 1; r += 1) {

if (row + r < 0 || moduleCount <= row + r) {
continue;
}

for (var c = -1; c <= 1; c += 1) {

if (col + c < 0 || moduleCount <= col + c) {
continue;
}

if (r == 0 && c == 0) {
continue;
}

if (dark == qrcode.isDark(row + r, col + c) ) {
sameCount += 1;
}
}
}

if (sameCount > 5) {
lostPoint += (3 + sameCount – 5);
}
}
};

// LEVEL2

for (var row = 0; row < moduleCount – 1; row += 1) {
for (var col = 0; col < moduleCount – 1; col += 1) {
var count = 0;
if (qrcode.isDark(row, col) ) count += 1;
if (qrcode.isDark(row + 1, col) ) count += 1;
if (qrcode.isDark(row, col + 1) ) count += 1;
if (qrcode.isDark(row + 1, col + 1) ) count += 1;
if (count == 0 || count == 4) {
lostPoint += 3;
}
}
}

// LEVEL3

for (var row = 0; row < moduleCount; row += 1) {
for (var col = 0; col < moduleCount – 6; col += 1) {
if (qrcode.isDark(row, col)
&& !qrcode.isDark(row, col + 1)
&& qrcode.isDark(row, col + 2)
&& qrcode.isDark(row, col + 3)
&& qrcode.isDark(row, col + 4)
&& !qrcode.isDark(row, col + 5)
&& qrcode.isDark(row, col + 6) ) {
lostPoint += 40;
}
}
}

for (var col = 0; col < moduleCount; col += 1) {
for (var row = 0; row < moduleCount – 6; row += 1) {
if (qrcode.isDark(row, col)
&& !qrcode.isDark(row + 1, col)
&& qrcode.isDark(row + 2, col)
&& qrcode.isDark(row + 3, col)
&& qrcode.isDark(row + 4, col)
&& !qrcode.isDark(row + 5, col)
&& qrcode.isDark(row + 6, col) ) {
lostPoint += 40;
}
}
}

// LEVEL4

var darkCount = 0;

for (var col = 0; col < moduleCount; col += 1) {
for (var row = 0; row < moduleCount; row += 1) {
if (qrcode.isDark(row, col) ) {
darkCount += 1;
}
}
}

var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount – 50) / 5;
lostPoint += ratio * 10;

return lostPoint;
};

return _this;
}();

//———————————————————————
// QRMath
//———————————————————————

var QRMath = function() {

var EXP_TABLE = new Array(256);
var LOG_TABLE = new Array(256);

// initialize tables
for (var i = 0; i < 8; i += 1) {
EXP_TABLE[i] = 1 << i;
}
for (var i = 8; i < 256; i += 1) {
EXP_TABLE[i] = EXP_TABLE[i – 4]
^ EXP_TABLE[i – 5]
^ EXP_TABLE[i – 6]
^ EXP_TABLE[i – 8];
}
for (var i = 0; i < 255; i += 1) {
LOG_TABLE[EXP_TABLE[i] ] = i;
}

var _this = {};

_this.glog = function(n) {

if (n < 1) {
throw ‘glog(‘ + n + ‘)’;
}

return LOG_TABLE[n];
};

_this.gexp = function(n) {

while (n < 0) {
n += 255;
}

while (n >= 256) {
n -= 255;
}

return EXP_TABLE[n];
};

return _this;
}();

//———————————————————————
// qrPolynomial
//———————————————————————

function qrPolynomial(num, shift) {

if (typeof num.length == ‘undefined’) {
throw num.length + ‘/’ + shift;
}

var _num = function() {
var offset = 0;
while (offset < num.length && num[offset] == 0) {
offset += 1;
}
var _num = new Array(num.length – offset + shift);
for (var i = 0; i < num.length – offset; i += 1) {
_num[i] = num[i + offset];
}
return _num;
}();

var _this = {};

_this.getAt = function(index) {
return _num[index];
};

_this.getLength = function() {
return _num.length;
};

_this.multiply = function(e) {

var num = new Array(_this.getLength() + e.getLength() – 1);

for (var i = 0; i < _this.getLength(); i += 1) {
for (var j = 0; j < e.getLength(); j += 1) {
num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
}
}

return qrPolynomial(num, 0);
};

_this.mod = function(e) {

if (_this.getLength() – e.getLength() < 0) {
return _this;
}

var ratio = QRMath.glog(_this.getAt(0) ) – QRMath.glog(e.getAt(0) );

var num = new Array(_this.getLength() );
for (var i = 0; i < _this.getLength(); i += 1) {
num[i] = _this.getAt(i);
}

for (var i = 0; i < e.getLength(); i += 1) {
num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
}

// recursive call
return qrPolynomial(num, 0).mod(e);
};

return _this;
};

//———————————————————————
// QRRSBlock
//———————————————————————

var QRRSBlock = function() {

var RS_BLOCK_TABLE = [

// L
// M
// Q
// H

// 1
[1, 26, 19],
[1, 26, 16],
[1, 26, 13],
[1, 26, 9],

// 2
[1, 44, 34],
[1, 44, 28],
[1, 44, 22],
[1, 44, 16],

// 3
[1, 70, 55],
[1, 70, 44],
[2, 35, 17],
[2, 35, 13],

// 4
[1, 100, 80],
[2, 50, 32],
[2, 50, 24],
[4, 25, 9],

// 5
[1, 134, 108],
[2, 67, 43],
[2, 33, 15, 2, 34, 16],
[2, 33, 11, 2, 34, 12],

// 6
[2, 86, 68],
[4, 43, 27],
[4, 43, 19],
[4, 43, 15],

// 7
[2, 98, 78],
[4, 49, 31],
[2, 32, 14, 4, 33, 15],
[4, 39, 13, 1, 40, 14],

// 8
[2, 121, 97],
[2, 60, 38, 2, 61, 39],
[4, 40, 18, 2, 41, 19],
[4, 40, 14, 2, 41, 15],

// 9
[2, 146, 116],
[3, 58, 36, 2, 59, 37],
[4, 36, 16, 4, 37, 17],
[4, 36, 12, 4, 37, 13],

// 10
[2, 86, 68, 2, 87, 69],
[4, 69, 43, 1, 70, 44],
[6, 43, 19, 2, 44, 20],
[6, 43, 15, 2, 44, 16],

// 11
[4, 101, 81],
[1, 80, 50, 4, 81, 51],
[4, 50, 22, 4, 51, 23],
[3, 36, 12, 8, 37, 13],

// 12
[2, 116, 92, 2, 117, 93],
[6, 58, 36, 2, 59, 37],
[4, 46, 20, 6, 47, 21],
[7, 42, 14, 4, 43, 15],

// 13
[4, 133, 107],
[8, 59, 37, 1, 60, 38],
[8, 44, 20, 4, 45, 21],
[12, 33, 11, 4, 34, 12],

// 14
[3, 145, 115, 1, 146, 116],
[4, 64, 40, 5, 65, 41],
[11, 36, 16, 5, 37, 17],
[11, 36, 12, 5, 37, 13],

// 15
[5, 109, 87, 1, 110, 88],
[5, 65, 41, 5, 66, 42],
[5, 54, 24, 7, 55, 25],
[11, 36, 12, 7, 37, 13],

// 16
[5, 122, 98, 1, 123, 99],
[7, 73, 45, 3, 74, 46],
[15, 43, 19, 2, 44, 20],
[3, 45, 15, 13, 46, 16],

// 17
[1, 135, 107, 5, 136, 108],
[10, 74, 46, 1, 75, 47],
[1, 50, 22, 15, 51, 23],
[2, 42, 14, 17, 43, 15],

// 18
[5, 150, 120, 1, 151, 121],
[9, 69, 43, 4, 70, 44],
[17, 50, 22, 1, 51, 23],
[2, 42, 14, 19, 43, 15],

// 19
[3, 141, 113, 4, 142, 114],
[3, 70, 44, 11, 71, 45],
[17, 47, 21, 4, 48, 22],
[9, 39, 13, 16, 40, 14],

// 20
[3, 135, 107, 5, 136, 108],
[3, 67, 41, 13, 68, 42],
[15, 54, 24, 5, 55, 25],
[15, 43, 15, 10, 44, 16],

// 21
[4, 144, 116, 4, 145, 117],
[17, 68, 42],
[17, 50, 22, 6, 51, 23],
[19, 46, 16, 6, 47, 17],

// 22
[2, 139, 111, 7, 140, 112],
[17, 74, 46],
[7, 54, 24, 16, 55, 25],
[34, 37, 13],

// 23
[4, 151, 121, 5, 152, 122],
[4, 75, 47, 14, 76, 48],
[11, 54, 24, 14, 55, 25],
[16, 45, 15, 14, 46, 16],

// 24
[6, 147, 117, 4, 148, 118],
[6, 73, 45, 14, 74, 46],
[11, 54, 24, 16, 55, 25],
[30, 46, 16, 2, 47, 17],

// 25
[8, 132, 106, 4, 133, 107],
[8, 75, 47, 13, 76, 48],
[7, 54, 24, 22, 55, 25],
[22, 45, 15, 13, 46, 16],

// 26
[10, 142, 114, 2, 143, 115],
[19, 74, 46, 4, 75, 47],
[28, 50, 22, 6, 51, 23],
[33, 46, 16, 4, 47, 17],

// 27
[8, 152, 122, 4, 153, 123],
[22, 73, 45, 3, 74, 46],
[8, 53, 23, 26, 54, 24],
[12, 45, 15, 28, 46, 16],

// 28
[3, 147, 117, 10, 148, 118],
[3, 73, 45, 23, 74, 46],
[4, 54, 24, 31, 55, 25],
[11, 45, 15, 31, 46, 16],

// 29
[7, 146, 116, 7, 147, 117],
[21, 73, 45, 7, 74, 46],
[1, 53, 23, 37, 54, 24],
[19, 45, 15, 26, 46, 16],

// 30
[5, 145, 115, 10, 146, 116],
[19, 75, 47, 10, 76, 48],
[15, 54, 24, 25, 55, 25],
[23, 45, 15, 25, 46, 16],

// 31
[13, 145, 115, 3, 146, 116],
[2, 74, 46, 29, 75, 47],
[42, 54, 24, 1, 55, 25],
[23, 45, 15, 28, 46, 16],

// 32
[17, 145, 115],
[10, 74, 46, 23, 75, 47],
[10, 54, 24, 35, 55, 25],
[19, 45, 15, 35, 46, 16],

// 33
[17, 145, 115, 1, 146, 116],
[14, 74, 46, 21, 75, 47],
[29, 54, 24, 19, 55, 25],
[11, 45, 15, 46, 46, 16],

// 34
[13, 145, 115, 6, 146, 116],
[14, 74, 46, 23, 75, 47],
[44, 54, 24, 7, 55, 25],
[59, 46, 16, 1, 47, 17],

// 35
[12, 151, 121, 7, 152, 122],
[12, 75, 47, 26, 76, 48],
[39, 54, 24, 14, 55, 25],
[22, 45, 15, 41, 46, 16],

// 36
[6, 151, 121, 14, 152, 122],
[6, 75, 47, 34, 76, 48],
[46, 54, 24, 10, 55, 25],
[2, 45, 15, 64, 46, 16],

// 37
[17, 152, 122, 4, 153, 123],
[29, 74, 46, 14, 75, 47],
[49, 54, 24, 10, 55, 25],
[24, 45, 15, 46, 46, 16],

// 38
[4, 152, 122, 18, 153, 123],
[13, 74, 46, 32, 75, 47],
[48, 54, 24, 14, 55, 25],
[42, 45, 15, 32, 46, 16],

// 39
[20, 147, 117, 4, 148, 118],
[40, 75, 47, 7, 76, 48],
[43, 54, 24, 22, 55, 25],
[10, 45, 15, 67, 46, 16],

// 40
[19, 148, 118, 6, 149, 119],
[18, 75, 47, 31, 76, 48],
[34, 54, 24, 34, 55, 25],
[20, 45, 15, 61, 46, 16]
];

var qrRSBlock = function(totalCount, dataCount) {
var _this = {};
_this.totalCount = totalCount;
_this.dataCount = dataCount;
return _this;
};

var _this = {};

var getRsBlockTable = function(typeNumber, errorCorrectionLevel) {

switch(errorCorrectionLevel) {
case QRErrorCorrectionLevel.L :
return RS_BLOCK_TABLE[(typeNumber – 1) * 4 + 0];
case QRErrorCorrectionLevel.M :
return RS_BLOCK_TABLE[(typeNumber – 1) * 4 + 1];
case QRErrorCorrectionLevel.Q :
return RS_BLOCK_TABLE[(typeNumber – 1) * 4 + 2];
case QRErrorCorrectionLevel.H :
return RS_BLOCK_TABLE[(typeNumber – 1) * 4 + 3];
default :
return undefined;
}
};

_this.getRSBlocks = function(typeNumber, errorCorrectionLevel) {

var rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel);

if (typeof rsBlock == ‘undefined’) {
throw ‘bad rs block @ typeNumber:’ + typeNumber +
‘/errorCorrectionLevel:’ + errorCorrectionLevel;
}

var length = rsBlock.length / 3;

var list = [];

for (var i = 0; i < length; i += 1) {

var count = rsBlock[i * 3 + 0];
var totalCount = rsBlock[i * 3 + 1];
var dataCount = rsBlock[i * 3 + 2];

for (var j = 0; j < count; j += 1) {
list.push(qrRSBlock(totalCount, dataCount) );
}
}

return list;
};

return _this;
}();

//———————————————————————
// qrBitBuffer
//———————————————————————

var qrBitBuffer = function() {

var _buffer = [];
var _length = 0;

var _this = {};

_this.getBuffer = function() {
return _buffer;
};

_this.getAt = function(index) {
var bufIndex = Math.floor(index / 8);
return ( (_buffer[bufIndex] >>> (7 – index % 8) ) & 1) == 1;
};

_this.put = function(num, length) {
for (var i = 0; i < length; i += 1) {
_this.putBit( ( (num >>> (length – i – 1) ) & 1) == 1);
}
};

_this.getLengthInBits = function() {
return _length;
};

_this.putBit = function(bit) {

var bufIndex = Math.floor(_length / 8);
if (_buffer.length <= bufIndex) {
_buffer.push(0);
}

if (bit) {
_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
}

_length += 1;
};

return _this;
};

//———————————————————————
// qrNumber
//———————————————————————

var qrNumber = function(data) {

var _mode = QRMode.MODE_NUMBER;
var _data = data;

var _this = {};

_this.getMode = function() {
return _mode;
};

_this.getLength = function(buffer) {
return _data.length;
};

_this.write = function(buffer) {

var data = _data;

var i = 0;

while (i + 2 < data.length) {
buffer.put(strToNum(data.substring(i, i + 3) ), 10);
i += 3;
}

if (i < data.length) {
if (data.length – i == 1) {
buffer.put(strToNum(data.substring(i, i + 1) ), 4);
} else if (data.length – i == 2) {
buffer.put(strToNum(data.substring(i, i + 2) ), 7);
}
}
};

var strToNum = function(s) {
var num = 0;
for (var i = 0; i < s.length; i += 1) {
num = num * 10 + chatToNum(s.charAt(i) );
}
return num;
};

var chatToNum = function(c) {
if (‘0’ <= c && c <= ‘9’) {
return c.charCodeAt(0) – ‘0’.charCodeAt(0);
}
throw ‘illegal char :’ + c;
};

return _this;
};

//———————————————————————
// qrAlphaNum
//———————————————————————

var qrAlphaNum = function(data) {

var _mode = QRMode.MODE_ALPHA_NUM;
var _data = data;

var _this = {};

_this.getMode = function() {
return _mode;
};

_this.getLength = function(buffer) {
return _data.length;
};

_this.write = function(buffer) {

var s = _data;

var i = 0;

while (i + 1 < s.length) {
buffer.put(
getCode(s.charAt(i) ) * 45 +
getCode(s.charAt(i + 1) ), 11);
i += 2;
}

if (i < s.length) {
buffer.put(getCode(s.charAt(i) ), 6);
}
};

var getCode = function(c) {

if (‘0’ <= c && c <= ‘9’) {
return c.charCodeAt(0) – ‘0’.charCodeAt(0);
} else if (‘A’ <= c && c <= ‘Z’) {
return c.charCodeAt(0) – ‘A’.charCodeAt(0) + 10;
} else {
switch (c) {
case ‘ ‘ : return 36;
case ‘$’ : return 37;
case ‘%’ : return 38;
case ‘*’ : return 39;
case ‘+’ : return 40;
case ‘-‘ : return 41;
case ‘.’ : return 42;
case ‘/’ : return 43;
case ‘:’ : return 44;
default :
throw ‘illegal char :’ + c;
}
}
};

return _this;
};

//———————————————————————
// qr8BitByte
//———————————————————————

var qr8BitByte = function(data) {

var _mode = QRMode.MODE_8BIT_BYTE;
var _data = data;
var _bytes = qrcode.stringToBytes(data);

var _this = {};

_this.getMode = function() {
return _mode;
};

_this.getLength = function(buffer) {
return _bytes.length;
};

_this.write = function(buffer) {
for (var i = 0; i < _bytes.length; i += 1) {
buffer.put(_bytes[i], 8);
}
};

return _this;
};

//———————————————————————
// qrKanji
//———————————————————————

var qrKanji = function(data) {

var _mode = QRMode.MODE_KANJI;
var _data = data;

var stringToBytes = qrcode.stringToBytesFuncs[‘SJIS’];
if (!stringToBytes) {
throw ‘sjis not supported.’;
}
!function(c, code) {
// self test for sjis support.
var test = stringToBytes(c);
if (test.length != 2 || ( (test[0] << 8) | test[1]) != code) {
throw ‘sjis not supported.’;
}
}(‘\u53cb’, 0x9746);

var _bytes = stringToBytes(data);

var _this = {};

_this.getMode = function() {
return _mode;
};

_this.getLength = function(buffer) {
return ~~(_bytes.length / 2);
};

_this.write = function(buffer) {

var data = _bytes;

var i = 0;

while (i + 1 < data.length) {

var c = ( (0xff & data[i]) << 8) | (0xff & data[i + 1]);

if (0x8140 <= c && c <= 0x9FFC) {
c -= 0x8140;
} else if (0xE040 <= c && c <= 0xEBBF) {
c -= 0xC140;
} else {
throw ‘illegal char at ‘ + (i + 1) + ‘/’ + c;
}

c = ( (c >>> 8) & 0xff) * 0xC0 + (c & 0xff);

buffer.put(c, 13);

i += 2;
}

if (i < data.length) {
throw ‘illegal char at ‘ + (i + 1);
}
};

return _this;
};

//=====================================================================
// GIF Support etc.
//

//———————————————————————
// byteArrayOutputStream
//———————————————————————

var byteArrayOutputStream = function() {

var _bytes = [];

var _this = {};

_this.writeByte = function(b) {
_bytes.push(b & 0xff);
};

_this.writeShort = function(i) {
_this.writeByte(i);
_this.writeByte(i >>> 8);
};

_this.writeBytes = function(b, off, len) {
off = off || 0;
len = len || b.length;
for (var i = 0; i < len; i += 1) {
_this.writeByte(b[i + off]);
}
};

_this.writeString = function(s) {
for (var i = 0; i < s.length; i += 1) {
_this.writeByte(s.charCodeAt(i) );
}
};

_this.toByteArray = function() {
return _bytes;
};

_this.toString = function() {
var s = ”;
s += ‘[‘;
for (var i = 0; i < _bytes.length; i += 1) {
if (i > 0) {
s += ‘,’;
}
s += _bytes[i];
}
s += ‘]’;
return s;
};

return _this;
};

//———————————————————————
// base64EncodeOutputStream
//———————————————————————

var base64EncodeOutputStream = function() {

var _buffer = 0;
var _buflen = 0;
var _length = 0;
var _base64 = ”;

var _this = {};

var writeEncoded = function(b) {
_base64 += String.fromCharCode(encode(b & 0x3f) );
};

var encode = function(n) {
if (n < 0) {
// error.
} else if (n < 26) {
return 0x41 + n;
} else if (n < 52) {
return 0x61 + (n – 26);
} else if (n < 62) {
return 0x30 + (n – 52);
} else if (n == 62) {
return 0x2b;
} else if (n == 63) {
return 0x2f;
}
throw ‘n:’ + n;
};

_this.writeByte = function(n) {

_buffer = (_buffer << 8) | (n & 0xff);
_buflen += 8;
_length += 1;

while (_buflen >= 6) {
writeEncoded(_buffer >>> (_buflen – 6) );
_buflen -= 6;
}
};

_this.flush = function() {

if (_buflen > 0) {
writeEncoded(_buffer << (6 – _buflen) );
_buffer = 0;
_buflen = 0;
}

if (_length % 3 != 0) {
// padding
var padlen = 3 – _length % 3;
for (var i = 0; i < padlen; i += 1) {
_base64 += ‘=’;
}
}
};

_this.toString = function() {
return _base64;
};

return _this;
};

//———————————————————————
// base64DecodeInputStream
//———————————————————————

var base64DecodeInputStream = function(str) {

var _str = str;
var _pos = 0;
var _buffer = 0;
var _buflen = 0;

var _this = {};

_this.read = function() {

while (_buflen < 8) {

if (_pos >= _str.length) {
if (_buflen == 0) {
return -1;
}
throw ‘unexpected end of file./’ + _buflen;
}

var c = _str.charAt(_pos);
_pos += 1;

if (c == ‘=’) {
_buflen = 0;
return -1;
} else if (c.match(/^\s$/) ) {
// ignore if whitespace.
continue;
}

_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
_buflen += 6;
}

var n = (_buffer >>> (_buflen – 8) ) & 0xff;
_buflen -= 8;
return n;
};

var decode = function(c) {
if (0x41 <= c && c <= 0x5a) {
return c – 0x41;
} else if (0x61 <= c && c <= 0x7a) {
return c – 0x61 + 26;
} else if (0x30 <= c && c <= 0x39) {
return c – 0x30 + 52;
} else if (c == 0x2b) {
return 62;
} else if (c == 0x2f) {
return 63;
} else {
throw ‘c:’ + c;
}
};

return _this;
};

//———————————————————————
// gifImage (B/W)
//———————————————————————

var gifImage = function(width, height) {

var _width = width;
var _height = height;
var _data = new Array(width * height);

var _this = {};

_this.setPixel = function(x, y, pixel) {
_data[y * _width + x] = pixel;
};

_this.write = function(out) {

//———————————
// GIF Signature

out.writeString(‘GIF87a’);

//———————————
// Screen Descriptor

out.writeShort(_width);
out.writeShort(_height);

out.writeByte(0x80); // 2bit
out.writeByte(0);
out.writeByte(0);

//———————————
// Global Color Map

// black
out.writeByte(0x00);
out.writeByte(0x00);
out.writeByte(0x00);

// white
out.writeByte(0xff);
out.writeByte(0xff);
out.writeByte(0xff);

//———————————
// Image Descriptor

out.writeString(‘,’);
out.writeShort(0);
out.writeShort(0);
out.writeShort(_width);
out.writeShort(_height);
out.writeByte(0);

//———————————
// Local Color Map

//———————————
// Raster Data

var lzwMinCodeSize = 2;
var raster = getLZWRaster(lzwMinCodeSize);

out.writeByte(lzwMinCodeSize);

var offset = 0;

while (raster.length – offset > 255) {
out.writeByte(255);
out.writeBytes(raster, offset, 255);
offset += 255;
}

out.writeByte(raster.length – offset);
out.writeBytes(raster, offset, raster.length – offset);
out.writeByte(0x00);

//———————————
// GIF Terminator
out.writeString(‘;’);
};

var bitOutputStream = function(out) {

var _out = out;
var _bitLength = 0;
var _bitBuffer = 0;

var _this = {};

_this.write = function(data, length) {

if ( (data >>> length) != 0) {
throw ‘length over’;
}

while (_bitLength + length >= 8) {
_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
length -= (8 – _bitLength);
data >>>= (8 – _bitLength);
_bitBuffer = 0;
_bitLength = 0;
}

_bitBuffer = (data << _bitLength) | _bitBuffer;
_bitLength = _bitLength + length;
};

_this.flush = function() {
if (_bitLength > 0) {
_out.writeByte(_bitBuffer);
}
};

return _this;
};

var getLZWRaster = function(lzwMinCodeSize) {

var clearCode = 1 << lzwMinCodeSize;
var endCode = (1 << lzwMinCodeSize) + 1;
var bitLength = lzwMinCodeSize + 1;

// Setup LZWTable
var table = lzwTable();

for (var i = 0; i < clearCode; i += 1) {
table.add(String.fromCharCode(i) );
}
table.add(String.fromCharCode(clearCode) );
table.add(String.fromCharCode(endCode) );

var byteOut = byteArrayOutputStream();
var bitOut = bitOutputStream(byteOut);

// clear code
bitOut.write(clearCode, bitLength);

var dataIndex = 0;

var s = String.fromCharCode(_data[dataIndex]);
dataIndex += 1;

while (dataIndex < _data.length) {

var c = String.fromCharCode(_data[dataIndex]);
dataIndex += 1;

if (table.contains(s + c) ) {

s = s + c;

} else {

bitOut.write(table.indexOf(s), bitLength);

if (table.size() < 0xfff) {

if (table.size() == (1 << bitLength) ) {
bitLength += 1;
}

table.add(s + c);
}

s = c;
}
}

bitOut.write(table.indexOf(s), bitLength);

// end code
bitOut.write(endCode, bitLength);

bitOut.flush();

return byteOut.toByteArray();
};

var lzwTable = function() {

var _map = {};
var _size = 0;

var _this = {};

_this.add = function(key) {
if (_this.contains(key) ) {
throw ‘dup key:’ + key;
}
_map[key] = _size;
_size += 1;
};

_this.size = function() {
return _size;
};

_this.indexOf = function(key) {
return _map[key];
};

_this.contains = function(key) {
return typeof _map[key] != ‘undefined’;
};

return _this;
};

return _this;
};

var createDataURL = function(width, height, getPixel) {
var gif = gifImage(width, height);
for (var y = 0; y < height; y += 1) {
for (var x = 0; x < width; x += 1) {
gif.setPixel(x, y, getPixel(x, y) );
}
}

var b = byteArrayOutputStream();
gif.write(b);

var base64 = base64EncodeOutputStream();
var bytes = b.toByteArray();
for (var i = 0; i < bytes.length; i += 1) {
base64.writeByte(bytes[i]);
}
base64.flush();

return ‘data:image/gif;base64,’ + base64;
};

//———————————————————————
// returns qrcode function.

return qrcode;
}();

// multibyte support
!function() {

qrcode.stringToBytesFuncs[‘UTF-8’] = function(s) {
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
function toUTF8Array(str) {
var utf8 = [];
for (var i=0; i < str.length; i++) {
var charcode = str.charCodeAt(i);
if (charcode < 0x80) utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6),
0x80 | (charcode & 0x3f));
}
else if (charcode < 0xd800 || charcode >= 0xe000) {
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
// surrogate pair
else {
i++;
// UTF-16 encodes 0x10000-0x10FFFF by
// subtracting 0x10000 and splitting the
// 20 bits of 0x0-0xFFFFF into two halves
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
| (str.charCodeAt(i) & 0x3ff));
utf8.push(0xf0 | (charcode >>18),
0x80 | ((charcode>>12) & 0x3f),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
return toUTF8Array(s);
};

}();

(function (factory) {
if (typeof define === ‘function’ && define.amd) {
define([], factory);
} else if (typeof exports === ‘object’) {
module.exports = factory();
}
}(function () {
return qrcode;
}));

</script>
<script>
(function(){
var APP_STORE = “https://apps.apple.com/app/id6739215991”;
var PLAY = “https://play.google.com/store/apps/details?id=com.workable.bayuti”;
var ua = navigator.userAgent || “”;
var isIOS = /iPhone|iPad|iPod/i.test(ua);
var isAndroid = /Android/i.test(ua);
var isMobile = isIOS || isAndroid;

// ?app=1 auto-redirect (QR route)
if (new URLSearchParams(location.search).has(“app”)) {
if (isIOS) location.replace(APP_STORE);
else if (isAndroid) location.replace(PLAY);
}

var qrTarget = location.origin + location.pathname + “?app=1”;
function drawQR(el){
if (!el || el.hasChildNodes() || typeof qrcode !== “function”) return;
var qr = qrcode(0, “M”);
qr.addData(qrTarget);
qr.make();
el.innerHTML = qr.createSvgTag({ cellSize: 4, margin: 2, scalable: true });
var svg = el.querySelector(“svg”);
if (svg) {
svg.setAttribute(“width”, “168”);
svg.setAttribute(“height”, “168”);
var path = svg.querySelector(“path”);
if (path) path.setAttribute(“fill”, “#07082B”);
}
}

var modal = document.getElementById(“qrModal”);
function openModal(){
drawQR(document.getElementById(“qrcodeModal”));
modal.hidden = false;
document.getElementById(“qrClose”).focus();
}
function closeModal(){ modal.hidden = true; }
document.getElementById(“qrClose”).addEventListener(“click”, closeModal);
modal.addEventListener(“click”, function(e){ if (e.target === modal) closeModal(); });
document.addEventListener(“keydown”, function(e){ if (e.key === “Escape”) closeModal(); });

// Download CTAs: store on mobile, QR modal on web
document.querySelectorAll(‘#heroDownload, .btn-nav’).forEach(function(el){
if (isIOS) { el.href = APP_STORE; }
else if (isAndroid) { el.href = PLAY; }
else {
el.addEventListener(“click”, function(e){ e.preventDefault(); openModal(); });
}
});

// Store badges: on web, open the QR instead of the store page
[“iosBadge”,”playBadge”].forEach(function(id){
var el = document.getElementById(id);
if (el && !isMobile) el.addEventListener(“click”, function(e){ e.preventDefault(); openModal(); });
});

// Inline QR card in the download section (desktop only)
if (!isMobile) drawQR(document.getElementById(“qrcode”));

// Text-me-the-link: posts the number to a HubSpot form; a HubSpot
// workflow on that form sends the SMS with the download link.
var SMS_FORM_ID = “REPLACE-WITH-SMS-FORM-ID”; // HubSpot form GUID, portal 7221343
var smsForm = document.getElementById(“smsForm”);
if (smsForm) smsForm.addEventListener(“submit”, function(e){
e.preventDefault();
var input = document.getElementById(“smsPhone”);
var msg = document.getElementById(“smsMsg”);
var btn = document.getElementById(“smsSend”);
var raw = (input.value || “”).replace(/[\s()-]/g, “”);
var ok = /^(\+447\d{9}|07\d{9})$/.test(raw);
if (!ok) {
msg.textContent = “Enter a UK mobile number, like 07123 456789.”;
msg.className = “get-link-msg err”;
input.focus();
return;
}
btn.disabled = true; btn.textContent = “Sending…”;
fetch(“https://api.hsforms.com/submissions/v3/integration/submit/7221343/” + SMS_FORM_ID, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
fields: [{ objectTypeId: “0-1”, name: “mobilephone”, value: raw }],
context: { pageUri: location.href, pageName: document.title }
})
}).then(function(r){
if (!r.ok) throw new Error();
msg.textContent = “Done. Your link is on its way.”;
msg.className = “get-link-msg ok”;
smsForm.reset();
}).catch(function(){
msg.textContent = “That didn’t send. Try again, or scan the QR code instead.”;
msg.className = “get-link-msg err”;
}).finally(function(){
btn.disabled = false; btn.textContent = “Send the link”;
});
});
})();
</script>
</body>
</html>