Update help viewer from 98.js.org

- The contents sidebar is now resizable and togglable
- There's now a toolbar with working forwards/backwards buttons
- There's a new Web Help page
- The window now gets focus when clicking in the page content
- Some tweaks were needed to get this to work, like changing "drag" to "dragging", and updating relative URLs
main
Isaiah Odhner 2020-04-22 22:46:26 -04:00
parent 6d9d744a10
commit d2895ad1c7
7 changed files with 483 additions and 23 deletions

36
help/online_support.htm Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Technical Support Online</title>
<link href="nobgcolor.css" rel="stylesheet" type="text/css">
</head>
<body style="margin-left: 35px; margin-top: 80px;">
<div id="background-animation"></div>
<hr id="os-logo-colorbar">
<div id="foreground-contents">
<a href="https://98.js.org/" target="_blank" id="os-logo-link">
<img src="../images/98.js.org.svg" alt="98.js.org" id="os-logo" height="100">
</a>
<br>
<p><a id="windows_update" name="windows_update"></a><b><i>Additional help via the Internet</i></b></p>
If you have a technical question on a 98.js.org product and can't find your answer in the product Help file or manual, take advantage of one of these online resources:
<p></p>
<ul>
<li>
Your primary source for support is the computer manufacturer who provided your software.
Your computer manufacturer may provide a web site to help you find answers to technical questions.
Check the documentation that came with your computer to determine the availability of an online support site.
</li>
<li>
For the latest technical information on 98.js.org products, you can also find answers at GitHub.
From README articles in the repositories, to Troubleshooting <small>that there arent any</small> Wizards,
the repos on GitHub have the specific resources most likely to help you find the answer to your question.
To begin your search, go to <a href="https://github.com/1j01/98" target="_blank">Support Online</a>.
</li>
</ul>
<p></p>
</div>
<script src="vaporwave.js"></script>
</body>
</html>

BIN
images/chm-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -305,5 +305,32 @@
<p>You can also check out <a href="https://github.com/1j01/jspaint">the source code and project info</a>.</p>
</noscript>
<svg style="position: absolute; pointer-events: none; bottom: 100%;">
<defs>
<filter id="disabled-inset-filter" x="0" y="0" width="1px" height="1px">
<feColorMatrix
in="SourceGraphic"
type="matrix"
values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
-1000 -1000 -1000 1 0
"
result="black-parts-isolated"
/>
<feFlood result="shadow-color" flood-color="var(--ButtonShadow)"/>
<feFlood result="hilight-color" flood-color="var(--ButtonHilight)"/>
<feOffset in="black-parts-isolated" dx="1" dy="1" result="offset"/>
<feComposite in="hilight-color" in2="offset" operator="in" result="hilight-colored-offset"/>
<feComposite in="shadow-color" in2="black-parts-isolated" operator="in" result="shadow-colored"/>
<feMerge>
<feMergeNode in="hilight-colored-offset"/>
<feMergeNode in="shadow-colored"/>
</feMerge>
</filter>
</defs>
</svg>
</body>
</html>

View File

@ -1,22 +1,185 @@
let $help_window;
function show_help(){
if($help_window){
$help_window.close();
function show_help() {
if ($help_window) {
$help_window.focus();
return;
}
$help_window = $Window().title("Paint Help"); // "Help Topics" // "Windows Help"
$help_window = open_help_viewer({
title: "Paint Help",
root: "help",
contentsFile: "help/mspaint.hhc",
}).$help_window;
$help_window.on("close", ()=> {
$help_window = null;
});
}
// awkward shim to interface with my own stupid code
window.TITLEBAR_ICON_SIZE = 16;
window.$Icon = ()=> {
return $("<img src='images/chm-16x16.png'>");
};
// shared code with 98.js.org
// (copy-pasted for now)
function open_help_viewer(options){
const $help_window = $Window({
title: options.title || "Help Topics",
icon: "chm",
})
$help_window.addClass("help-window");
// $toolbar = $(E("div")).addClass("toolbar");
// $help_window.$content.append($toolbar);
let ignore_one_load = true;
let back_length = 0;
let forward_length = 0;
const $main = $(E("div")).addClass("main");
const $toolbar = $(E("div")).addClass("toolbar");
const add_toolbar_button = (name, sprite_n, action_fn, enabled_fn)=> {
const $button = $("<button class='lightweight'>")
.text(name)
.appendTo($toolbar)
.on("click", ()=> {
action_fn();
});
$("<div class='icon'/>")
.appendTo($button)
.css({
backgroundPosition: `${-sprite_n * 55}px 0px`,
});
const update_enabled = ()=> {
$button[0].disabled = enabled_fn && !enabled_fn();
};
update_enabled();
$help_window.on("click", "*", update_enabled);
$help_window.on("update-buttons", update_enabled);
return $button;
};
const measure_sidebar_width = ()=>
$contents.outerWidth() +
parseFloat(getComputedStyle($contents[0]).getPropertyValue("margin-left")) +
parseFloat(getComputedStyle($contents[0]).getPropertyValue("margin-right")) +
$resizer.outerWidth();
const $hide_button = add_toolbar_button("Hide", 0, ()=> {
const toggling_width = measure_sidebar_width();
$contents.hide();
$resizer.hide();
$hide_button.hide();
$show_button.show();
$help_window.width($help_window.width() - toggling_width);
$help_window.css("left", $help_window.offset().left + toggling_width);
});
const $show_button = add_toolbar_button("Show", 5, ()=> {
$contents.show();
$resizer.show();
$show_button.hide();
$hide_button.show();
const toggling_width = measure_sidebar_width();
$help_window.width($help_window.width() + toggling_width);
$help_window.css("left", $help_window.offset().left - toggling_width);
// $help_window.applyBounds() would push the window to fit (before trimming it only if needed)
// Trim the window to fit (especially for if maximized)
if ($help_window.offset().left < 0) {
$help_window.width($help_window.width() + $help_window.offset().left);
$help_window.css("left", 0);
}
}).hide();
add_toolbar_button("Back", 1, ()=> {
$iframe[0].contentWindow.history.back();
ignore_one_load = true;
back_length -= 1;
forward_length += 1;
}, ()=> back_length > 0);
add_toolbar_button("Forward", 2, ()=> {
$iframe[0].contentWindow.history.forward();
ignore_one_load = true;
forward_length -= 1;
back_length += 1;
}, ()=> forward_length > 0);
add_toolbar_button("Options", 3, ()=> {}, ()=> false); // TODO: hotkey and underline on O
add_toolbar_button("Web Help", 4, ()=> {
iframe.src = "help/online_support.htm";
});
$iframe = $(E("iframe"));
$contents = $(E("ul")).addClass("contents");
$help_window.$content.append($contents, $iframe);
const $iframe = $Iframe({src: "help/default.html"}).addClass("inset-deep");
const iframe = $iframe[0];
iframe.$window = $help_window; // for focus handling integration
const $resizer = $(E("div")).addClass("resizer");
const $contents = $(E("ul")).addClass("contents inset-deep");
// TODO: fix race conditions
$iframe.on("load", ()=> {
if (!ignore_one_load) {
back_length += 1;
forward_length = 0;
}
iframe.contentWindow.location.href
ignore_one_load = false;
$help_window.triggerHandler("update-buttons");
});
$main.append($contents, $resizer, $iframe);
$help_window.$content.append($toolbar, $main);
$help_window.css({width: 800, height: 600});
$iframe.attr({name: "help-frame", src: "help/default.html"});
$iframe.css({backgroundColor: "white"});
$iframe.attr({name: "help-frame"});
$iframe.css({
backgroundColor: "white",
border: "",
margin: "1px",
});
$contents.css({
margin: "1px",
});
$help_window.center();
$main.css({
position: "relative", // for resizer
});
const resizer_width = 4;
$resizer.css({
cursor: "ew-resize",
width: resizer_width,
boxSizing: "border-box",
background: "var(--ButtonFace)",
borderLeft: "1px solid var(--ButtonShadow)",
boxShadow: "inset 1px 0 0 var(--ButtonHilight)",
top: 0,
bottom: 0,
zIndex: 1,
});
$resizer.on("pointerdown", (e)=> {
let pointermove, pointerup;
const getPos = (e)=>
Math.min($help_window.width() - 100, Math.max(20,
e.clientX - $help_window.$content.offset().left
));
$G.on("pointermove", pointermove = (e)=> {
$resizer.css({
position: "absolute",
left: getPos(e)
});
$contents.css({
marginRight: resizer_width,
});
});
$G.on("pointerup", pointerup = (e)=> {
$G.off("pointermove", pointermove);
$G.off("pointerup", pointerup);
$resizer.css({
position: "",
left: ""
});
$contents.css({
flexBasis: getPos(e) - resizer_width,
marginRight: "",
});
});
});
const parse_object_params = $object => {
// parse an $(<object>) to a plain object of key value pairs
@ -54,10 +217,9 @@ function show_help(){
}));
$contents.append($default_item_li);
$.get("help/mspaint.hhc", hhc => {
$($.parseHTML(hhc)).filter("ul").children().get().forEach((li) => {
const object = parse_object_params($(li).children("object"));
function renderItem(source_li, $folder_items_ul) {
const object = parse_object_params($(source_li).children("object"));
if ($(source_li).find("li").length > 0){
const $folder_li = $(E("li")).addClass("folder");
$folder_li.append($Item(object.Name));
@ -66,14 +228,25 @@ function show_help(){
const $folder_items_ul = $(E("ul"));
$folder_li.append($folder_items_ul);
$(li).children("ul").children().get().forEach((li) => {
const object = parse_object_params($(li).children("object"));
const $item_li = $(E("li")).addClass("page");
$item_li.append($Item(object.Name).on("click", ()=> {
$iframe.attr({src: `help/${object.Local}`});
}));
$folder_items_ul.append($item_li);
$(source_li).children("ul").children().get().forEach((li)=> {
renderItem(li, $folder_items_ul);
});
} else {
const $item_li = $(E("li")).addClass("page");
$item_li.append($Item(object.Name).on("click", ()=> {
$iframe.attr({src: `${options.root}/${object.Local}`});
}));
if ($folder_items_ul) {
$folder_items_ul.append($item_li);
} else {
$contents.append($item_li);
}
}
}
$.get(options.contentsFile, hhc => {
$($.parseHTML(hhc)).filter("ul").children().get().forEach((li)=> {
renderItem(li, null);
});
});
@ -85,4 +258,196 @@ function show_help(){
// break;
// }
// });
// var task = new Task($help_window);
var task = {};
task.$help_window = $help_window;
return task;
}
var programs_being_loaded = 0;
function $Iframe(options){
var $iframe = $("<iframe allowfullscreen sandbox='allow-same-origin allow-scripts allow-forms allow-pointer-lock allow-modals allow-popups allow-downloads'>");
var iframe = $iframe[0];
var disable_delegate_pointerup = false;
$iframe.focus_contents = function(){
if (!iframe.contentWindow) {
return;
}
if (iframe.contentDocument.hasFocus()) {
return;
}
disable_delegate_pointerup = true;
iframe.contentWindow.focus();
setTimeout(function(){
iframe.contentWindow.focus();
disable_delegate_pointerup = false;
});
};
// Let the iframe to handle mouseup events outside itself
var delegate_pointerup = function(){
if (disable_delegate_pointerup) {
return;
}
if(iframe.contentWindow && iframe.contentWindow.jQuery){
iframe.contentWindow.jQuery("body").trigger("pointerup");
}
if(iframe.contentWindow){
const event = new iframe.contentWindow.MouseEvent("mouseup", {button: 0});
iframe.contentWindow.dispatchEvent(event);
const event2 = new iframe.contentWindow.MouseEvent("mouseup", {button: 2});
iframe.contentWindow.dispatchEvent(event2);
}
};
$G.on("mouseup blur", delegate_pointerup);
$iframe.destroy = ()=> {
$G.off("mouseup blur", delegate_pointerup);
};
// @TODO: delegate pointermove events too?
$("body").addClass("loading-program");
programs_being_loaded += 1;
$iframe.on("load", function(){
if(--programs_being_loaded <= 0){
$("body").removeClass("loading-program");
}
if (window.themeCSSProperties) {
applyTheme(themeCSSProperties, iframe.contentDocument.documentElement);
}
// on Wayback Machine, and iframe's url not saved yet
if (iframe.contentDocument.querySelector("#error #livewebInfo.available")) {
var message = document.createElement("div");
message.style.position = "absolute";
message.style.left = "0";
message.style.right = "0";
message.style.top = "0";
message.style.bottom = "0";
message.style.background = "#c0c0c0";
message.style.color = "#000";
message.style.padding = "50px";
iframe.contentDocument.body.appendChild(message);
message.innerHTML = `<a target="_blank">Save this url in the Wayback Machine</a>`;
message.querySelector("a").href =
"https://web.archive.org/save/https://98.js.org/" +
iframe.src.replace(/.*https:\/\/98.js.org\/?/, "");
message.querySelector("a").style.color = "blue";
}
var $contentWindow = $(iframe.contentWindow);
$contentWindow.on("pointerdown click", function(e){
iframe.$window && iframe.$window.focus();
// from close_menus in $MenuBar
$(".menu-button").trigger("release");
// Close any rogue floating submenus
$(".menu-popup").hide();
});
// We want to disable pointer events for other iframes, but not this one
$contentWindow.on("pointerdown", function(e){
$iframe.css("pointer-events", "all");
$("body").addClass("dragging");
});
$contentWindow.on("pointerup", function(e){
$("body").removeClass("dragging");
$iframe.css("pointer-events", "");
});
// $("iframe").css("pointer-events", ""); is called elsewhere.
// Otherwise iframes would get stuck in this interaction mode
iframe.contentWindow.close = function(){
iframe.$window && iframe.$window.close();
};
// TODO: hook into saveAs (a la FileSaver.js) and another function for opening files
// iframe.contentWindow.saveAs = function(){
// saveAsDialog();
// };
});
if (options.src) {
$iframe.attr({src: options.src});
}
$iframe.css({
minWidth: 0,
minHeight: 0, // overrides user agent styling apparently, fixes Sound Recorder
flex: 1,
border: 0, // overrides user agent styling
});
return $iframe;
}
// function $IframeWindow(options){
// var $win = new $Window(options);
// var $iframe = $win.$iframe = $Iframe({src: options.src});
// $win.$content.append($iframe);
// var iframe = $win.iframe = $iframe[0];
// // TODO: should I instead of having iframe.$window, have a get$Window type of dealio?
// // where all is $window needed?
// // I know it's used from within the iframe contents as frameElement.$window
// iframe.$window = $win;
// $win.on("close", function(){
// $iframe.destroy();
// });
// $win.onFocus($iframe.focus_contents);
// $iframe.on("load", function(){
// $win.show();
// $win.focus();
// // $iframe.focus_contents();
// });
// $win.setInnerDimensions = ({width, height})=> {
// const width_from_frame = $win.width() - $win.$content.width();
// const height_from_frame = $win.height() - $win.$content.height();
// $win.css({
// width: width + width_from_frame,
// height: height + height_from_frame + 21,
// });
// };
// $win.setInnerDimensions({
// width: (options.innerWidth || 640),
// height: (options.innerHeight || 380),
// });
// $win.$content.css({
// display: "flex",
// flexDirection: "column",
// });
// // TODO: cascade windows
// $win.center();
// $win.hide();
// return $win;
// }
// Fix dragging things (i.e. windows) over iframes (i.e. other windows)
// (when combined with a bit of css, .dragging iframe { pointer-events: none; })
// (and a similar thing in $IframeWindow)
$(window).on("pointerdown", function(e){
//console.log(e.type);
$("body").addClass("dragging");
});
$(window).on("pointerup dragend blur", function(e){
//console.log(e.type);
if(e.type === "blur"){
if(document.activeElement.tagName.match(/iframe/i)){
return;
}
}
$("body").removeClass("dragging");
$("iframe").css("pointer-events", "");
});

View File

@ -465,9 +465,41 @@ html, body, .jspaint {
}
.help-window .window-content {
display: flex;
flex-flow: column;
}
.help-window .main {
flex: 1;
display: flex;
flex-flow: row;
}
.help-window .toolbar button {
width: 55px;
height: 40px;
padding: 0;
font-size: 12px;
line-height: 60px;
}
.help-window .toolbar button {
position: relative;
}
.help-window .toolbar button .icon {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.help-window .toolbar button .icon {
background-image: url("../images/help-viewer-toolbar-icons-grayscale.png");
}
.help-window .toolbar button:not([disabled]):hover .icon {
background-image: url("../images/help-viewer-toolbar-icons.png");
}
.help-window .toolbar button[disabled] .icon {
filter: saturate(0%) opacity(50%); /* fallback */
filter: url("#disabled-inset-filter");
}
.help-window .contents {
background: white;
background: var(--Window);