Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/guide/capp.js

3.7 KiB, 1 run

created by r2519314175:1151, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1/* capp.js — the one control in this guide that asks Daimond to do something.
2 *
3 * The guide is framed inside Daimond's Web panel WITHOUT `allow-same-origin`
4 * (see the head of frame.js, which says why). An opaque origin cannot reach the
5 * app's document, so this cannot call a function in the app: it posts a request
6 * and waits to be told what happened, exactly as the palette does.
7 *
8 * WHAT IS SENT IS A REQUEST AND NOT AN INSTRUCTION. The app decides: it reads
9 * the message only from its own guide frame, it puts a dialog in its own chrome
10 * where a click is provably a person's, and what it will do at most is make one
11 * Diamond -- a second ask opens the first one's. Nothing here can be turned into
12 * a way of reading anything, because nothing comes back but a status word.
13 *
14 * Opened outside Daimond -- the guide is a real site and stands on its own --
15 * there is nobody to ask, and the button says so rather than doing nothing.
16 */
17(function () {
18 'use strict';
19
20 /// How long a request may go unanswered before the control is offered again.
21 /// Long, because the app's answer waits on a person reading a dialog, and a
22 /// button that re-arms while the question is still on screen invites a second
23 /// dialog behind the first.
24 var PATIENCE = 90000;
25
26 /// What each answer says. The app sends the word; the sentence is here,
27 /// because it is the reader's language that decides the wording and this page
28 /// is the thing they are looking at.
29 var SAID = {
30 made: 'Made. Log Life is open in the Diamonds rail.',
31 opened: 'You already had one. It is open in the Diamonds rail.',
32 cancelled: 'Nothing was made.',
33 missing: 'This build of Daimond does not carry the Log Life template, so nothing was made.',
34 failed: 'Daimond could not finish making it. It said so on screen.',
35 };
36
37 function ready() {
38 var btn = document.getElementById('make-lifelog');
39 var say = document.getElementById('lifelog-say');
40 if (!btn || !say) return;
41
42 var timer = 0;
43
44 function tell(text) { say.textContent = text || ''; }
45
46 function rearm() {
47 if (timer) { clearTimeout(timer); timer = 0; }
48 btn.disabled = false;
49 }
50
51 btn.addEventListener('click', function () {
52 // Standing on its own, with no Daimond around it. Said plainly: a
53 // control that silently does nothing teaches a reader to distrust every
54 // control on the page.
55 if (!window.parent || window.parent === window) {
56 tell('Open this page inside Daimond, with the ? in its top bar, and this '
57 + 'button will make the Diamond for you.');
58 return;
59 }
60 btn.disabled = true;
61 tell('Asked Daimond. Answer the question it puts up.');
62 timer = setTimeout(function () {
63 timer = 0;
64 btn.disabled = false;
65 tell('No answer from Daimond. Try again, or ask your daimon for a page.');
66 }, PATIENCE);
67 try {
68 window.parent.postMessage({ daimondGuide: 'make', what: 'lifelog' }, '*');
69 } catch (e) {
70 rearm();
71 tell('This page could not reach Daimond.');
72 }
73 });
74
75 window.addEventListener('message', function (e) {
76 // Only from the framer we asked, mirroring the check the app makes on
77 // its own side. Nothing here does more than write a sentence, so this
78 // is not what keeps the channel safe -- it is that the question is
79 // asked in app chrome. It is here because a listener that answers to
80 // anybody is a habit, and the next one may do more.
81 if (!e || e.source !== window.parent) return;
82 var d = e.data;
83 if (!d || d.daimondGuide !== 'made' || d.what !== 'lifelog') return;
84 rearm();
85 tell(SAID[d.status] || 'Daimond answered, and this page does not know that word.');
86 });
87 }
88
89 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready);
90 else ready();
91})();