Dialogs.html 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <style type="text/css">
  6. * {
  7. box-sizing: border-box;
  8. }
  9. html {
  10. width: 100%; height: 100%;
  11. }
  12. body {
  13. position: absolute; top: 0; left: 0; right: 0; bottom: 0;
  14. margin: 0; padding: 0;
  15. overflow: hidden;
  16. color: #eee;
  17. font-family: sans-serif;
  18. background: rgba(62, 62, 62, .1);
  19. }
  20. .box {
  21. display: none;
  22. position: fixed;
  23. top: 50%; right: 50%;
  24. max-height: 100%; max-width: 100%;
  25. transform: translate(50%, -50%);
  26. overflow: auto;
  27. padding: 15px;
  28. background: #333;
  29. border-radius: 15px;
  30. border: 2px solid #999;
  31. cursor: default;
  32. -webkit-user-select: none;
  33. }
  34. body.lit .box {
  35. border-color: #AAA;
  36. }
  37. .box h2 {
  38. margin: 0 0 10px;
  39. }
  40. .box button {
  41. margin: 7px 0px 0px;
  42. float: right;
  43. font-size: 125%;
  44. }
  45. .box button[data-affirm="0"] {
  46. float: left;
  47. }
  48. .box p {
  49. margin: 4px 0px;
  50. white-space: pre-wrap;
  51. }
  52. .box input {
  53. font-size: 110%;
  54. width: 100%;
  55. margin: 7px 0px;
  56. padding: 3px;
  57. }
  58. .menu {
  59. display: none;
  60. border: 1px solid black;
  61. color: black;
  62. background: gray;
  63. position: absolute;
  64. -webkit-user-select: none;
  65. overflow: auto;
  66. max-width: 100%;
  67. max-height: 100%;
  68. }
  69. ul.menuList {
  70. margin: 0; padding: 0;
  71. }
  72. ul.menuList > li {
  73. margin: 1px;
  74. padding: 6px;
  75. list-style: none;
  76. cursor: default;
  77. }
  78. ul.menuList > li.sep {
  79. padding: 0;
  80. height: 5px;
  81. }
  82. ul.menuList > li.disabled {
  83. color: #444;
  84. }
  85. ul.menuList > li:not(.unpickable) {
  86. cursor: pointer;
  87. }
  88. ul.menuList > li:not(.unpickable):hover {
  89. background: darkturquoise;
  90. }
  91. </style>
  92. </head>
  93. <body>
  94. <div class="box" id="alert">
  95. <h2>JavaScript Alert</h2>
  96. <p></p>
  97. <button>OK</button>
  98. </div>
  99. <div class="box" id="confirm">
  100. <h2>JavaScript Confirmation</h2>
  101. <p></p>
  102. <button>OK</button>
  103. <button data-affirm="0">Cancel</button>
  104. </div>
  105. <div class="box" id="confirmNav">
  106. <h2>Confirm Page Navigation</h2>
  107. <div>The page says:</div>
  108. <p></p>
  109. <div>Do you want to leave this page?</div>
  110. <button>Leave</button>
  111. <button data-affirm="0">Stay</button>
  112. </div>
  113. <div class="box" id="confirmReload">
  114. <h2>Confirm Page Reload</h2>
  115. <div>The page says:</div>
  116. <p></p>
  117. <div>Do you want to reload this page?</div>
  118. <button>Reload</button>
  119. <button data-affirm="0">Stay</button>
  120. </div>
  121. <div class="box" id="prompt">
  122. <h2>JavaScript Prompt</h2>
  123. <p></p>
  124. <input type="text"><br/>
  125. <button>OK</button>
  126. <button data-affirm="0">Cancel</button>
  127. </div>
  128. <div class="box" id="auth">
  129. <h2>Authentication Prompt</h2>
  130. <p></p>
  131. <label for="authUser">User name:</label><input id="authUser" type="text"><br/>
  132. <label for="authPass">Password:</label><input id="authPass" type="password"><br/>
  133. <button>Log In</button>
  134. <button data-affirm="0">Cancel</button>
  135. </div>
  136. <div class="menu" id="contextMenu">
  137. <ul class="menuList"></ul>
  138. </div>
  139. <script type="application/javascript">
  140. "use strict";
  141. function $(sel) { return document.querySelector(sel); }
  142. function $$(sel) { return Array.from(document.querySelectorAll(sel)); }
  143. //the embedder will replace these functions:
  144. function reportDialogResult(affirm, text1, text2) {
  145. console.log("Would report " + JSON.stringify(arguments));
  146. }
  147. function reportContextMenuResult(commandId) { "Would report " + JSON.stringify(arguments); }
  148. /**
  149. * Makes the border of the dialog flash subtly.
  150. * This isn't here so much to look nice as to work around a bug
  151. * that sometimes prevents popups from rendering fully.
  152. */
  153. function flashBorder() {
  154. var speed = 100;
  155. document.body.className = "";
  156. setTimeout(() => document.body.className = "lit", 1 * speed);
  157. setTimeout(() => document.body.className = "", 2 * speed);
  158. setTimeout(() => document.body.className = "lit", 3 * speed);
  159. setTimeout(() => document.body.className = "", 4 * speed);
  160. }
  161. reset();
  162. var buttons = document.querySelectorAll(".box button");
  163. for (var i = 0; i < buttons.length; i++) {
  164. buttons[i].addEventListener("click", function() {
  165. reportResult(this.getAttribute("data-affirm") !== "0");
  166. });
  167. }
  168. var bodyClick = null;
  169. var bodyResize = null;
  170. var inputFrom = null;
  171. document.body.addEventListener("click", ev => {
  172. if (bodyClick) bodyClick(ev);
  173. });
  174. window.addEventListener("resize", ev => {
  175. if (bodyResize) bodyResize(ev);
  176. });
  177. /* global reportDialogResult, reportContextMenuResult */
  178. function reportResult(affirm) {
  179. if (inputFrom === "prompt") reportDialogResult(affirm, $("#prompt input").value);
  180. else if (inputFrom === "auth") reportDialogResult(affirm, $("#authUser").value, $("#authPass").value);
  181. else reportDialogResult(affirm);
  182. reset();
  183. }
  184. //Things the embedder can call:
  185. function reset() {
  186. $$("body > div").forEach(el => el.style.display = "none");
  187. $("#prompt input").value = "";
  188. bodyClick = null;
  189. bodyResize = null;
  190. inputFrom = null;
  191. }
  192. function showAlert(text) {
  193. flashBorder();
  194. $("#alert").style.display = "block";
  195. $("#alert p").textContent = text;
  196. inputFrom = null;
  197. }
  198. function showConfirm(text) {
  199. flashBorder();
  200. $("#confirm").style.display = "block";
  201. $("#confirm p").textContent = text;
  202. inputFrom = null;
  203. }
  204. function showConfirmNav(text) {
  205. flashBorder();
  206. $("#confirmNav").style.display = "block";
  207. $("#confirmNav p").textContent = text;
  208. inputFrom = null;
  209. }
  210. function showConfirmReload(text) {
  211. flashBorder();
  212. $("#confirmReload").style.display = "block";
  213. $("#confirmReload p").textContent = text;
  214. inputFrom = null;
  215. }
  216. function showPrompt(text, defaultText) {
  217. flashBorder();
  218. $("#prompt").style.display = "block";
  219. $("#prompt p").textContent = text;
  220. $("#prompt input").value = defaultText || "";
  221. $("#prompt input").focus();
  222. inputFrom = "prompt";
  223. }
  224. function showAuthPrompt(text) {
  225. flashBorder();
  226. $("#auth").style.display = "block";
  227. $("#auth p").textContent = text;
  228. $("#auth #authUser").value = "";
  229. $("#auth #authPass").value = "";
  230. $("#auth #authUser").focus();
  231. inputFrom = "auth";
  232. }
  233. function buildMenu(menuDef, ul) {
  234. //Note: I have not yet found any cases where we have checkboxes,
  235. //radio buttons, or child menus, so they are currently unimplemented.
  236. ul.innerHTML = "";
  237. menuDef.forEach(item => {
  238. if (!item.visible) return;
  239. var itemEl = document.createElement("li");
  240. ul.appendChild(itemEl);
  241. if (item.type == "separator") {
  242. itemEl.className = "unpickable sep";
  243. return;
  244. }
  245. //todolater: Underline chars, kb nav
  246. itemEl.textContent = item.label.replace(/&/g, "");
  247. if (item.checked) itemEl.classList.add("checked");
  248. if (item.commandId && item.enabled) {
  249. itemEl.addEventListener("click", () => {
  250. reportContextMenuResult(item.commandId);
  251. reset();
  252. });
  253. } else {
  254. itemEl.classList.add("unpickable");
  255. }
  256. if (!item.enabled) {
  257. itemEl.classList.add("unpickable");
  258. itemEl.classList.add("disabled");
  259. }
  260. });
  261. }
  262. function showContextMenu(defJSON, x, y) {
  263. var def = JSON.parse(defJSON);
  264. bodyClick = ev => {
  265. if (ev.target != document.body) return;
  266. reportContextMenuResult(-1);
  267. reset();
  268. };
  269. var menuEl = $('#contextMenu');
  270. var ul = $('#contextMenu ul');
  271. buildMenu(def, ul);
  272. bodyResize = () => {
  273. //Position the menu.
  274. menuEl.style.display = "block";
  275. menuEl.style.left = "";
  276. menuEl.style.right = "";
  277. menuEl.style.top = "";
  278. menuEl.style.bottom = "";
  279. //Make sure it's all on screen
  280. if (x < 0) x = 0;
  281. if (y < 0) y = 0;
  282. var mw = menuEl.offsetWidth;
  283. var mh = menuEl.offsetHeight;
  284. var sw = document.body.offsetWidth;
  285. var sh = document.body.offsetHeight;
  286. if (x + mw >= sw) menuEl.style.right = "0";
  287. else menuEl.style.left = x + "px";
  288. if (y + mh >= sh) menuEl.style.bottom = "0";
  289. else menuEl.style.top = y + "px";
  290. };
  291. bodyResize();
  292. }
  293. </script>
  294. </body>
  295. </html>