forked from SomajitDey/EasyForm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
executable file
·369 lines (361 loc) · 26.6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
<!DOCTYPE html>
<html lang="en">
<head>
<title>Formonit</title>
<meta name="description" content="Free, self-hosted, open-source backend for your HTML forms. Free hosted contact pages and website. Copy form submissions to Google Sheets or MS Excel. Reply directly to users. Forms and comments backend or hosting for static websites. Receive form submissions as push notifications and Telegram messages.">
<meta name="google-site-verification" content="hXewG_4pdiMTsH8E2tYvT0xhctgnpwigLug6FXog0hY" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<link rel="manifest" href="site.webmanifest">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/SomajitDey/webtools@0.0.0/prism/prism.min.css">
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" defer></script>
<script defer src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/SomajitDey/webtools@0.0.0/prism/prism.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/SomajitDey/webtools@0.0.0/spa/spa.min.js"></script>
<!-- Module type script loading is deferred automatically -->
<script type="module" src="app/main.js"></script>
<script>
window.OneSignalDeferred = window.OneSignalDeferred || [];
</script>
</head>
<body class="d-flex flex-column min-vh-100">
<header class="container-fluid p-5 text-bg-primary text-center">
<a class="navbar-dark nav-link" href="/" onClick="spaGoTo(spaHomePageID);">
<h1>Welcome to Formonit</h1>
</a>
</header>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark justify-content-center">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="#about" onClick="spaGoTo('about');">About</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/formonit/formonit.github.io">Source</a></li>
<li class="nav-item"><a class="nav-link" href="https://buymeacoffee.com/SomajitDey">Donate</a></li>
<li class="nav-item"><a class="nav-link" href="#contact" onClick="spaGoTo('contact');">Contact</a></li>
<li class="nav-item"><a class="nav-link" href="ToS.txt">Terms</a></li>
<li class="nav-item"><a class="nav-link" href="PRIVACY.txt">Privacy</a></li>
</ul>
</nav>
<div class="container-fluid">
<div class="row">
<nav class="col-sm-1 bg-secondary">
<div class="sticky-top">
<div class="d-flex flex-column min-vh-100 justify-content-start align-items-center py-3">
<a class="btn btn-info my-3" href="#inbox" onclick="spaGoTo('inbox');">Inbox<br><span id ="unread" class="badge bg-danger">0</span></a>
<a class="btn btn-info my-3" href="#forms" onclick="spaGoTo('forms');">Forms</a>
<a class="btn btn-info my-3" href="#settings" onclick="spaGoTo('settings');"><img width="25" height="25" src="https://img.icons8.com/3d-fluency/50/gear--v1.png" alt="gear--v1"/></a>
<a class="btn btn-info my-3" href="#admin" onclick="spaGoTo('admin');"><img width="25" height="25" src="https://img.icons8.com/3d-fluency/50/test-passed.png" alt="test-passed"/></a>
<button type="button" class="btn btn-info my-3" href="#admin" onclick="toggleDarkMode();"><img width="25" height="25" src="https://img.icons8.com/fluency/48/day-and-night.png" alt="day-and-night"/></button>
</div>
</div>
</nav>
<main class="col-sm-11 bg-light pt-5 pb-4">
<div class="text-bg-light">
<p class="alert alert-danger" id="jsAlert">You don't have JavaScript enabled! This page cannot function without it.</p>
<script>document.getElementById('jsAlert').hidden = true;</script>
<div class="spa-page" id="about">
<section>
<h3>About</h3>
<p>
'Formonit' is a portmanteau of 'Form(s)' and 'Monitor'.
But you can also take it literally, as a platform to form your digital presence on.
Basically, we aspire to give you a free and easy, feature-rich, form-backend solution for all your static websites.
</p>
<details class="my-2">
<summary><h3>How to use</h3></summary>
<p>To setup your HTML forms, you have three options, all available in the <a href="#forms" onclick="spaGoTo('forms');">Forms</a> section upon sign up.</p>
<ol>
<li>You can simply use the generated URL in the <code>action</code> attribute of your HTML forms.</li>
<li>Simpler even is to embed our form snippet in your website's HTML.</li>
<li>If you don't require custom forms, or want a no-code solution, then the simplest option is to direct your users
to the contact-page(s) we generate and host for you for free!</li>
</ol>
<p>You can also set up custom redirects for your forms.</p>
<p>
Once your users submit the form(s), your <a href="#inbox" onclick="spaGoTo('inbox');">Inbox</a> here will be populated and your device will get a web-push notification.
If you <a href="#settings" onclick="spaGoTo('settings');">setup Telegram integration</a>, you will also be notified on TG.
</p>
<p>You can also check if you are properly receiving form-submissions from <a href="#admin" onclick="spaGoTo('admin');">here</a>.</p>
</details>
<details class="my-2">
<summary><h3>Pricing</h3></summary>
This service is free. However, if you find this project useful, <a href="https://buymeacoffee.com/somajitdey">please consider donating towards its survival</a>.
No support is too small. Thank you 💚
<br><br>
<p>Note that there are a few tradeoffs for availing this service free of cost 😁:</p>
<ol>
<li>1-day data retention</li>
<li>Data retention till first sync; synced data is lost upon page reload</li>
<li>Only support forms with textual data, of total size less than 10KiB, and POSTed as 'application/x-www-form-urlencoded'</li>
</ol>
</details>
<details class="my-2">
<summary><h3>Features</h3></summary>
Existing features are coded <strong style="color:green">green</strong>.
Upcoming ones <strong style="color:blue">blue</strong>.
And sunsetting/deprecated ones <strong style="color:red">red</strong>.
Implementation of some of the blue-coded features are subject to <a href="#contact" onClick="spaGoTo('contact');">demand/popular-requests</a> and <a href="https://buymeacoffee.com/somajitdey">support</a>.
<ol>
<li><strong style="color:green">Can be installed on any platform that has a standard web-browser</strong></li>
<li><strong style="color:green">Same submission URL for all your forms. Inbox differentiates forms based on their unique FormID parameter</strong></li>
<li><strong style="color:green">Custom redirects upon form submission</strong></li>
<li><strong style="color:green">Push notifications</strong></li>
<li><strong style="color:green">Telegram integration</strong></li>
<li><strong style="color:green">Tabular representation of form submissions in the Inbox. Copy all the data with a single-click and paste in MS Excel or Google Sheets</strong></li>
<li><strong style="color:green">Option to directly reply to the users who contact you</strong></li>
<li><strong style="color:green">Readily shareable contact-pages for different use cases along with their view counts</strong></li>
<li><strong style="color:green">Dedicated section for testing everything is working as desired</strong></li>
<li><strong style="color:green">High-level logs for non-develeopers</strong></li>
<li><strong style="color:blue">Readily shareable profile-page, with its view count. This profile-page contains your brief bio, avatar and links to your socials and website as well as a contact-form. This can serve as your own website</strong></li>
<li><strong style="color:blue">End-to-end encryption</strong></li>
<li><strong style="color:blue">Chatbox</strong></li>
<li><strong style="color:blue">Email-based login</strong></li>
<li><strong style="color:blue">Comments hosting for static sites</strong></li>
<li><strong style="color:blue">Premium plans with larger rate-limits</strong></li>
</ol>
</details>
<details class="my-2">
<summary><h3>Acknowledgements</h3></summary>
Formonit is powered by these 3rd party services/software:
<ul class="nav nav-pills nav-justified">
<li class="nav-item"><a class="nav-link" href="https://securelay.github.io/">SecuRelay</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/nwtgck/piping-server">Piping-Server</a></li>
<li class="nav-item"><a class="nav-link" href="https://core.telegram.org/bots/api">Telegram Bot</a></li>
<li class="nav-item"><a class="nav-link" href="https://getbootstrap.com/">Bootstrap</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/zenorocha/clipboard.js">clipboard.js</a></li>
<li class="nav-item"><a class="nav-link" href="https://jsdelivr.com/">jsdelivr</a></li>
<li class="nav-item"><a class="nav-link" href="https://prismjs.com/">Prism</a></li>
<li class="nav-item"><a class="nav-link" href="https://www.freeformatter.com/html-formatter.html">freeformatter.com</a></li>
<li class="nav-item"><a class="nav-link" href="https://favicon.io/">favicon.io</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/twitter/twemoji/blob/master/assets/svg/1f5e8.svg">Twemoji</a></li>
<li class="nav-item"><a class="nav-link" href="https://icons8.com/icons">icons8.com</a></li>
<li class="nav-item"><a class="nav-link" href="https://goqr.me/">goQR.me</a></li>
<li class="nav-item"><a class="nav-link" href="https://onesignal.com/web-push">OneSignal</a></li>
</ul>
</details>
<details class="my-2">
<summary><h3>See also</h3></summary>
Similar projects that you might find useful:
<ul class="nav nav-pills nav-justified">
<li class="nav-item"><a class="nav-link" href="https://formspree.io/">Formspree</a></li>
<li class="nav-item"><a class="nav-link" href="https://fabform.io/">FabForm</a></li>
<li class="nav-item"><a class="nav-link" href="https://formspark.io/">Formspark</a></li>
<li class="nav-item"><a class="nav-link" href="https://www.emailjs.com/">EmailJS</a></li>
</ul>
</details>
</section>
</div>
<div class="spa-page" id="forms">
<h3>Forms</h3>
<details class="my-4" open>
<summary><strong>Embeddable snippet</strong></summary>
<p>Edit the below snippet as follows and embed in your website.</p>
<ol class="list-group list-group-numbered">
<li class="list-group-item list-group-item-success">
Replace <code>XXXXX</code> with <mark id="formActionURL">the URL generated upon sign-up</mark>
<button class="clipboard-js-btn" data-clipboard-target="#formActionURL">Copy URL</button>
</li>
<li class="list-group-item list-group-item-danger">
To setup custom redirects upon form submission, simply add the following query string to the <code>XXXXX</code> URL:
<code>?ok=:successURL&err=:errorURL</code>, where <code>:successURL</code> denotes the URL-encoded redirect URL for form submission success
and <code>:errorURL</code> is the same for submission error. If no redirect is required, a hidden iframe target may be used as shown in the snippet below.
</li>
<li class="list-group-item list-group-item-info">Replace <code>YYYYY</code> with a unique descriptive string</li>
</ol>
<pre><code class="language-html" id="formSnippet"><form action="XXXXX" method="POST" target="hidden_iframe">
<input type="hidden" name="FormID" value="YYYYY">
<input type="email" name="Email" placeholder="Your Email">
<input type="text" name="Name" placeholder="Your Name">
<input type="text" name="Message" placeholder="Your Message">
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<!-- when the form is submitted, the server response will appear in the following iframe, hidden from view -->
<iframe name="hidden_iframe" src="about:blank" hidden></iframe>
</code></pre>
<button class="clipboard-js-btn" data-clipboard-target="#formSnippet">Copy above snippet</button>
</details>
<details class="my-4" open>
<summary><strong>Shareable links</strong></summary>
<p>Following lists your custom webpages made with ❤️ and hosted by Formonit. Numbers on the right represent page views since your last sign-in.</p>
<!-- ToDo: https://getbootstrap.com/docs/5.0/components/modal/ and https://stackoverflow.com/a/68077623 -->
<ol class="list-group list-group-numbered">
<li name="anon" class="shareable-link list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">Anonymous</div>
<a class="link" id="anonLink">https://formonit.github.io/anon/<span></span></a>
<a class="clipboard-js-btn" data-clipboard-target="#anonLink"><img width="24" height="24" src="https://img.icons8.com/material-outlined/24/copy.png" alt="copy"/></a>
<a class="qr"><img width="30" height="30" src="https://img.icons8.com/ios-glyphs/30/qr-code--v1.png" alt="qr-code--v1"/></a>
</div>
<span id="anonViews" class="badge bg-primary rounded-pill">14</span>
</li>
<li name="vehicle" class="shareable-link list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">Vehicle sticker</div>
<a class="link" href="#" id="vehicleLink">https://formonit.github.io/car/<span></span></a>
<a class="clipboard-js-btn" data-clipboard-target="#vehicleLink"><img width="24" height="24" src="https://img.icons8.com/material-outlined/24/copy.png" alt="copy"/></a>
<a class="qr"><img width="30" height="30" src="https://img.icons8.com/ios-glyphs/30/qr-code--v1.png" alt="qr-code--v1"/></a>
</div>
<span id="vehicleViews" class="badge bg-primary rounded-pill">14</span>
</li>
</ol>
</details>
</div>
<div class="spa-page" id="settings">
<section>
<h3>Settings</h3>
<button type="button" class="my-4" onclick="signout();">Sign out</button>
<details class="my-4" open>
<summary><strong>Telegram</strong></summary>
<form onsubmit="event.preventDefault();TGconfig(this);">
<p>Setup Telegram notificiation in just 2 easy steps.</p>
<label for="TGbotKey" class="form-label">1. Enter your Telegram Bot API Token (available from <a href="https://t.me/botfather">@BotFather</a>):</label>
<input type="text" id="TGbotKey" name="TGbotKey" class="form-control" autocomplete="on" required>
<input type="checkbox" id="togglePasswordVisibilityCBox1" onchange="togglePasswordVisibility('TGbotKey');" checked>
<label for="togglePasswordVisibilityCBox1">Show</label>
<br><br>
<p>2. Send any text to the Bot. Then, click Fetch Chat ID.</p>
<button type="button" onclick="fetchChatID(this.parentElement.elements['TGbotKey'].value);">Fetch Chat ID</button>
<input type="text" id="chatID" name="TGchatID" hidden required>
<input type="text" id="chatIDShow" disabled>
<input type="checkbox" name="TGnotify" id="TGnotify" hidden checked>
<label for="TGnotify" hidden>Enable Telegram notification</label>
<br><br>
<button type="submit" class="btn btn-success">Save</button>
<button type="reset" class="btn btn-danger">Reset</button>
</form>
</details>
<details class="my-4" open>
<summary><strong>Sync</strong></summary>
<button onClick="sync();">Sync Now</button>
<input type="button" id="toggleServer" value="Stop syncing" disabled onclick="toggleWorker();">
<input type="checkbox" id="autoSync" checked onchange="autoSyncToggle();">
<label for="autoSync">Enable automatic syncing</label>
</details>
</section>
</div>
<div class="spa-page" id="admin">
<section>
<h3>Admin</h3>
<details class="my-4" open>
<summary><strong>Test</strong></summary>
<p>Test if everything is working properly with the following sample form. The HTML underlying this form is based on the embeddable snippet shown in <a href="#forms" onClick="spaGoTo('forms');">Forms</a>.</p>
<p>4 things should happen when you submit the form.</p>
<ol type="1">
<li>The below Log will report an entry.</li>
<li><a href="#inbox" onClick="spaGoTo('inbox');">Inbox</a> will be populated.</li>
<li>You will get a push notification.</li>
<li>You will get the form data as a Telegram message.</li>
</ol>
<form method="POST" target="testForm_iframe" onsubmit="document.getElementsByName('testForm_iframe')[0].style='background:url(https://img.icons8.com/material-outlined/30/spinner-frame-2.png) no-repeat';">
<input type="hidden" name="FormID" value="tester">
<input type="hidden" name="ChatID" id="testFormChatID">
<div class="row mt-2">
<div class="col">
<input type="email" class="form-control" name="Email" placeholder="Your Email" autocomplete="on" required>
</div>
<div class="col">
<input type="text" class="form-control" name="Name" placeholder="Your Name" autocomplete="on" required>
</div>
<div class="mt-2"><input type="text" class="form-control" name="Message" placeholder="Your Message" required></div>
</div>
<button type="submit" id="testFormBtn" class="btn btn-success mt-2" disabled>Submit</button>
<button type="button" class="btn btn-info mt-2" onclick="this.parentElement.elements['Message'].value='';document.getElementsByName('testForm_iframe')[0].src='about:blank';">New Message</button>
<button type="reset" class="btn btn-warning mt-2" onClick="document.getElementsByName('testForm_iframe')[0].src='about:blank';">Reset</button>
<iframe name="testForm_iframe" height="30" width="30" scrolling="no" src="about:blank" onload="this.removeAttribute('style')"></iframe>
</form>
<div>
<p class="my-4"><strong>Reply:</strong></p>
<p class="my-2"></p>
<button type="button" class="my-4" onclick="loadReply(this);">Check for new reply</button>
</div>
</details>
<details class="my-4" open>
<summary><strong>Log</strong></summary>
<button type="button" onclick="spaBottom();">Bottom</button>
<br><br>
<p class="alert alert-success">Only high level logs are shown here. For the more technical low level logs, check console. Reload this window to clear the logs.</p>
<div id="logs" class="text-bg-info"></div>
<br><br>
<button type="button" onclick="spaTop();">Top</button>
</details>
</section>
</div>
<div class="spa-page" id="inbox">
<section>
<h3>Inbox</h3>
<div class="alert alert-success">
<div class="d-inline-flex w-100 justify-content-between">
<p>This is your storageless Inbox. All data is lost once you close or even reload this window.</p>
<button onClick="sync();">Sync Now</button>
</div>
</div>
<p class="alert alert-info"><strong>Protip:</strong> To export to <a href="https://www.microsoft.com/en/microsoft-365/excel">MS Excel</a>, copy the table below, followed by <code>Paste Special > Text</code> in Excel. To export to <a href="https://docs.google.com/spreadsheets/create">Google Sheets</a>, use <code>Paste Special > Values only (Ctrl+Shift+V)</code> instead.</p>
</section>
</div>
<div class="spa-page" id="contact">
<section>
<h3>Contact us</h3>
<p>We are dogfooding by using Formonit to host Formonit's own contact page!</p>
<p>Feel free to get in touch by submitting the following form.</p>
<p>Alternatively, if you have a GitHub account, you can also reach us <a href="https://github.com/formonit/formonit.github.io/discussions">here</a>.</p>
<div class="my-2 ratio ratio-16x9">
<iframe src="https://formonit.github.io/anon/Na8kpb_9he%40alz2h" style="border:2px solid black;" title="Formonit's own contact page">
</iframe>
</div>
</section>
</div>
<p class="d-flex justify-content-start"><button type="button" class="btn btn-danger mt-4" id="login">Let's get started</button></p>
<div class="badge bg-warning position-fixed bottom-0 end-0">
Sync: <span id="serverStatus">Off</span>
</div>
</div>
</main>
</div>
</div>
<footer class="mt-auto container-fluid p-2 text-bg-dark text-center">
Copyright © 2024 Formonit
</footer>
<dialog id="signIn">
<div class="d-flex mb-2">
<h5>Sign in</h5>
<button type="button" class="btn-close ms-auto" onClick="document.getElementById('signIn').close();"></button>
</div>
<form method="dialog" onsubmit="event.preventDefault(); signIn(this)">
<p>Generate or enter your Formonit Access Key</p>
<button type="button" class="btn btn-success me-2" onclick="genUUID();">Generate New Key</button>
<input type="password" id="uuid" name="appKey" placeholder="Enter Your Key" required>
<input type="checkbox" id="togglePasswordVisibilityCBox0" onchange="togglePasswordVisibility('uuid');">
<label for="togglePasswordVisibilityCBox0">Show</label>
<div id="keyAlert" class="alert alert-warning my-4">Store this key safely for all future logins. It cannot be recovered, if lost.</div>
<input type="checkbox" id="keepSignedIn" name="keepSignedIn" checked>
<label for="keepSignedIn" data-bs-toggle="tooltip" title="Uses localStorage for storing session">Keep me signed in</label>
<br>
<input type="checkbox" id="notifyMe" name="notifyMe" checked>
<label for="notifyMe" data-bs-toggle="tooltip" title="Sends some user data to onesignal.com">Notify me when new messages arrive</label>
<br>
<input type="checkbox" id="termsUnderstood" name="termsUnderstood" required>
<label for="termsUnderstood">I have read and understood the <a href="ToS.txt">Terms</a> and <a href="PRIVACY.txt">Privacy</a> declarations</label>
<br>
<button type="submit" class="btn btn-primary mt-2">Sign in</button>
<button type="reset" class="btn btn-secondary mt-2">Reset</button>
</form>
</dialog>
<dialog id="reply">
<div class="d-flex mb-2">
<h5>Reply</h5>
<button type="button" class="btn-close ms-auto" onClick="document.getElementById('reply').close();"></button>
</div>
<form method="POST" target="replyForm_iframe" onsubmit="this.getElementsByTagName('iframe')[0].style='background:url(https://img.icons8.com/material-outlined/30/spinner-frame-2.png) no-repeat';">
<textarea name="Message" class="form-control"></textarea>
<button type="submit" class="btn btn-primary mt-2">Post</button>
<button type="reset" class="btn btn-secondary mt-2" onClick="document.getElementsByName('replyForm_iframe')[0].src='about:blank';">Reset</button>
<iframe name="replyForm_iframe" height="30" width="30" scrolling="no" src="about:blank" onload="this.removeAttribute('style')"></iframe>
</form>
</dialog>
</body>
</html>