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 URLsmain
parent
6d9d744a10
commit
d2895ad1c7
|
@ -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>
|
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 |
27
index.html
27
index.html
|
@ -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>
|
||||
|
|
411
src/help.js
411
src/help.js
|
@ -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", "");
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue