348 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html>
 | 
						|
<head>
 | 
						|
	<meta charset="UTF-8">
 | 
						|
	<style type="text/css">
 | 
						|
		* {
 | 
						|
			box-sizing: border-box;
 | 
						|
		}
 | 
						|
		html {
 | 
						|
			width: 100%; height: 100%;
 | 
						|
		}
 | 
						|
		body {
 | 
						|
			position: absolute; top: 0; left: 0; right: 0; bottom: 0;
 | 
						|
			margin: 0; padding: 0;
 | 
						|
			overflow: hidden;
 | 
						|
			color: #eee;
 | 
						|
			font-family: sans-serif;
 | 
						|
			background: rgba(62, 62, 62, .1);
 | 
						|
		}
 | 
						|
		.box {
 | 
						|
			display: none;
 | 
						|
 | 
						|
			position: fixed;
 | 
						|
			top: 50%; right: 50%;
 | 
						|
			max-height: 100%; max-width: 100%;
 | 
						|
			transform: translate(50%, -50%);
 | 
						|
 | 
						|
			overflow: auto;
 | 
						|
 | 
						|
			padding: 15px;
 | 
						|
 | 
						|
			background: #333;
 | 
						|
			border-radius: 15px;
 | 
						|
			border: 2px solid #999;
 | 
						|
 | 
						|
			cursor: default;
 | 
						|
			-webkit-user-select: none;
 | 
						|
		}
 | 
						|
		body.lit .box {
 | 
						|
			border-color: #AAA;
 | 
						|
		}
 | 
						|
		.box h2 {
 | 
						|
			margin: 0 0 10px;
 | 
						|
		}
 | 
						|
		.box button {
 | 
						|
			margin: 7px 0px 0px;
 | 
						|
			float: right;
 | 
						|
			font-size: 125%;
 | 
						|
		}
 | 
						|
		.box button[data-affirm="0"] {
 | 
						|
			float: left;
 | 
						|
		}
 | 
						|
		.box p {
 | 
						|
			margin: 4px 0px;
 | 
						|
			white-space: pre-wrap;
 | 
						|
		}
 | 
						|
		.box input {
 | 
						|
			font-size: 110%;
 | 
						|
			width: 100%;
 | 
						|
			margin: 7px 0px;
 | 
						|
			padding: 3px;
 | 
						|
		}
 | 
						|
 | 
						|
		.menu {
 | 
						|
			display: none;
 | 
						|
			border: 1px solid black;
 | 
						|
			color: black;
 | 
						|
			background: gray;
 | 
						|
			position: absolute;
 | 
						|
			-webkit-user-select: none;
 | 
						|
			overflow: auto;
 | 
						|
			max-width: 100%;
 | 
						|
			max-height: 100%;
 | 
						|
		}
 | 
						|
		ul.menuList {
 | 
						|
			margin: 0; padding: 0;
 | 
						|
		}
 | 
						|
		ul.menuList > li {
 | 
						|
			margin: 1px;
 | 
						|
			padding: 6px;
 | 
						|
			list-style: none;
 | 
						|
			cursor: default;
 | 
						|
		}
 | 
						|
		ul.menuList > li.sep {
 | 
						|
			padding: 0;
 | 
						|
			height: 5px;
 | 
						|
		}
 | 
						|
		ul.menuList > li.disabled {
 | 
						|
			color: #444;
 | 
						|
		}
 | 
						|
		ul.menuList > li:not(.unpickable) {
 | 
						|
			cursor: pointer;
 | 
						|
		}
 | 
						|
		ul.menuList > li:not(.unpickable):hover {
 | 
						|
			background: darkturquoise;
 | 
						|
		}
 | 
						|
 | 
						|
	</style>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
	<div class="box" id="alert">
 | 
						|
		<h2>JavaScript Alert</h2>
 | 
						|
		<p></p>
 | 
						|
		<button>OK</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="box" id="confirm">
 | 
						|
		<h2>JavaScript Confirmation</h2>
 | 
						|
		<p></p>
 | 
						|
		<button>OK</button>
 | 
						|
		<button data-affirm="0">Cancel</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="box" id="confirmNav">
 | 
						|
		<h2>Confirm Page Navigation</h2>
 | 
						|
		<div>The page says:</div>
 | 
						|
		<p></p>
 | 
						|
		<div>Do you want to leave this page?</div>
 | 
						|
		<button>Leave</button>
 | 
						|
		<button data-affirm="0">Stay</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="box" id="confirmReload">
 | 
						|
		<h2>Confirm Page Reload</h2>
 | 
						|
		<div>The page says:</div>
 | 
						|
		<p></p>
 | 
						|
		<div>Do you want to reload this page?</div>
 | 
						|
		<button>Reload</button>
 | 
						|
		<button data-affirm="0">Stay</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="box" id="prompt">
 | 
						|
		<h2>JavaScript Prompt</h2>
 | 
						|
		<p></p>
 | 
						|
		<input type="text"><br/>
 | 
						|
		<button>OK</button>
 | 
						|
		<button data-affirm="0">Cancel</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="box" id="auth">
 | 
						|
		<h2>Authentication Prompt</h2>
 | 
						|
		<p></p>
 | 
						|
		<label for="authUser">User name:</label><input id="authUser" type="text"><br/>
 | 
						|
		<label for="authPass">Password:</label><input id="authPass" type="password"><br/>
 | 
						|
		<button>Log In</button>
 | 
						|
		<button data-affirm="0">Cancel</button>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<div class="menu" id="contextMenu">
 | 
						|
		<ul class="menuList"></ul>
 | 
						|
	</div>
 | 
						|
 | 
						|
	<script type="application/javascript">
 | 
						|
	"use strict";
 | 
						|
	function $(sel) { return document.querySelector(sel); }
 | 
						|
	function $$(sel) { return Array.from(document.querySelectorAll(sel)); }
 | 
						|
 | 
						|
	//the embedder will replace these functions:
 | 
						|
	function reportDialogResult(affirm, text1, text2) {
 | 
						|
		console.log("Would report " + JSON.stringify(arguments));
 | 
						|
	}
 | 
						|
	function reportContextMenuResult(commandId) { "Would report " + JSON.stringify(arguments); }
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Makes the border of the dialog flash subtly.
 | 
						|
	 * This isn't here so much to look nice as to work around a bug
 | 
						|
	 * that sometimes prevents popups from rendering fully.
 | 
						|
	 */
 | 
						|
	function flashBorder() {
 | 
						|
		var speed = 100;
 | 
						|
		document.body.className = "";
 | 
						|
		setTimeout(() => document.body.className = "lit", 1 * speed);
 | 
						|
		setTimeout(() => document.body.className = "", 2 * speed);
 | 
						|
		setTimeout(() => document.body.className = "lit", 3 * speed);
 | 
						|
		setTimeout(() => document.body.className = "", 4 * speed);
 | 
						|
	}
 | 
						|
 | 
						|
	reset();
 | 
						|
 | 
						|
	var buttons = document.querySelectorAll(".box button");
 | 
						|
	for (var i = 0; i < buttons.length; i++) {
 | 
						|
		buttons[i].addEventListener("click", function() {
 | 
						|
			reportResult(this.getAttribute("data-affirm") !== "0");
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	var bodyClick = null;
 | 
						|
	var bodyResize = null;
 | 
						|
	var inputFrom = null;
 | 
						|
 | 
						|
	document.body.addEventListener("click", ev => {
 | 
						|
		if (bodyClick) bodyClick(ev);
 | 
						|
	});
 | 
						|
	window.addEventListener("resize", ev => {
 | 
						|
		if (bodyResize) bodyResize(ev);
 | 
						|
	});
 | 
						|
 | 
						|
 | 
						|
	/* global reportDialogResult, reportContextMenuResult */
 | 
						|
	function reportResult(affirm) {
 | 
						|
		if (inputFrom === "prompt") reportDialogResult(affirm, $("#prompt input").value);
 | 
						|
		else if (inputFrom === "auth") reportDialogResult(affirm, $("#authUser").value, $("#authPass").value);
 | 
						|
		else reportDialogResult(affirm);
 | 
						|
		reset();
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	//Things the embedder can call:
 | 
						|
	function reset() {
 | 
						|
		$$("body > div").forEach(el => el.style.display = "none");
 | 
						|
		$("#prompt input").value = "";
 | 
						|
		bodyClick = null;
 | 
						|
		bodyResize = null;
 | 
						|
		inputFrom = null;
 | 
						|
	}
 | 
						|
 | 
						|
	function showAlert(text) {
 | 
						|
		flashBorder();
 | 
						|
		$("#alert").style.display = "block";
 | 
						|
		$("#alert p").textContent = text;
 | 
						|
		inputFrom = null;
 | 
						|
	}
 | 
						|
 | 
						|
	function showConfirm(text) {
 | 
						|
		flashBorder();
 | 
						|
		$("#confirm").style.display = "block";
 | 
						|
		$("#confirm p").textContent = text;
 | 
						|
		inputFrom = null;
 | 
						|
	}
 | 
						|
 | 
						|
	function showConfirmNav(text) {
 | 
						|
		flashBorder();
 | 
						|
		$("#confirmNav").style.display = "block";
 | 
						|
		$("#confirmNav p").textContent = text;
 | 
						|
		inputFrom = null;
 | 
						|
	}
 | 
						|
 | 
						|
	function showConfirmReload(text) {
 | 
						|
		flashBorder();
 | 
						|
		$("#confirmReload").style.display = "block";
 | 
						|
		$("#confirmReload p").textContent = text;
 | 
						|
		inputFrom = null;
 | 
						|
	}
 | 
						|
 | 
						|
	function showPrompt(text, defaultText) {
 | 
						|
		flashBorder();
 | 
						|
		$("#prompt").style.display = "block";
 | 
						|
		$("#prompt p").textContent = text;
 | 
						|
		$("#prompt input").value = defaultText || "";
 | 
						|
		$("#prompt input").focus();
 | 
						|
		inputFrom = "prompt";
 | 
						|
	}
 | 
						|
 | 
						|
	function showAuthPrompt(text) {
 | 
						|
		flashBorder();
 | 
						|
		$("#auth").style.display = "block";
 | 
						|
		$("#auth p").textContent = text;
 | 
						|
		$("#auth #authUser").value = "";
 | 
						|
		$("#auth #authPass").value = "";
 | 
						|
		$("#auth #authUser").focus();
 | 
						|
		inputFrom = "auth";
 | 
						|
	}
 | 
						|
 | 
						|
	function buildMenu(menuDef, ul) {
 | 
						|
		//Note: I have not yet found any cases where we have checkboxes,
 | 
						|
		//radio buttons, or child menus, so they are currently unimplemented.
 | 
						|
 | 
						|
		ul.innerHTML = "";
 | 
						|
 | 
						|
		menuDef.forEach(item => {
 | 
						|
			if (!item.visible) return;
 | 
						|
 | 
						|
			var itemEl = document.createElement("li");
 | 
						|
			ul.appendChild(itemEl);
 | 
						|
 | 
						|
			if (item.type == "separator") {
 | 
						|
				itemEl.className = "unpickable sep";
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
 | 
						|
			//todolater: Underline chars, kb nav
 | 
						|
			itemEl.textContent = item.label.replace(/&/g, "");
 | 
						|
 | 
						|
			if (item.checked) itemEl.classList.add("checked");
 | 
						|
 | 
						|
			if (item.commandId && item.enabled) {
 | 
						|
				itemEl.addEventListener("click", () => {
 | 
						|
					reportContextMenuResult(item.commandId);
 | 
						|
					reset();
 | 
						|
				});
 | 
						|
			} else {
 | 
						|
				itemEl.classList.add("unpickable");
 | 
						|
			}
 | 
						|
 | 
						|
			if (!item.enabled) {
 | 
						|
				itemEl.classList.add("unpickable");
 | 
						|
				itemEl.classList.add("disabled");
 | 
						|
			}
 | 
						|
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	function showContextMenu(defJSON, x, y) {
 | 
						|
		var def = JSON.parse(defJSON);
 | 
						|
		bodyClick = ev => {
 | 
						|
			if (ev.target != document.body) return;
 | 
						|
			reportContextMenuResult(-1);
 | 
						|
			reset();
 | 
						|
		};
 | 
						|
 | 
						|
		var menuEl = $('#contextMenu');
 | 
						|
 | 
						|
		var ul = $('#contextMenu ul');
 | 
						|
		buildMenu(def, ul);
 | 
						|
 | 
						|
 | 
						|
		bodyResize = () => {
 | 
						|
			//Position the menu.
 | 
						|
			menuEl.style.display = "block";
 | 
						|
			menuEl.style.left = "";
 | 
						|
			menuEl.style.right = "";
 | 
						|
			menuEl.style.top = "";
 | 
						|
			menuEl.style.bottom = "";
 | 
						|
 | 
						|
			//Make sure it's all on screen
 | 
						|
			if (x < 0) x = 0;
 | 
						|
			if (y < 0) y = 0;
 | 
						|
 | 
						|
			var mw = menuEl.offsetWidth;
 | 
						|
			var mh = menuEl.offsetHeight;
 | 
						|
			var sw = document.body.offsetWidth;
 | 
						|
			var sh = document.body.offsetHeight;
 | 
						|
 | 
						|
			if (x + mw >= sw) menuEl.style.right = "0";
 | 
						|
			else menuEl.style.left = x + "px";
 | 
						|
 | 
						|
			if (y + mh >= sh) menuEl.style.bottom = "0";
 | 
						|
			else menuEl.style.top = y + "px";
 | 
						|
		};
 | 
						|
 | 
						|
		bodyResize();
 | 
						|
	}
 | 
						|
 | 
						|
	</script>
 | 
						|
</body>
 | 
						|
</html>
 |