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 | })(); |