Improve message boxes
- Add icons - Add audio - Add padding - Add "default button" styling logic, where the default or focused button gets a bolder border. - Tweak some dialogs, favoring localized and accurate renditions, rather than more modern, more specific button labels.main
2
TODO.md
|
@ -188,7 +188,7 @@ Functionality:
|
|||
|
||||
* JS
|
||||
* Organize things into files better; "functions.js" is like ONE step above saying "code.js"
|
||||
* `$ToolWindow` has a `$Button` facility; `$DialogWindow` overrides it with essentially a better one
|
||||
* `$ToolWindow` has a `$Button` facility; `$DialogWindow` overrides it with essentially a better one; now there's `showMessageBox` too! and `$ToolWindow` is a wrapper for OS-GUI's `$Window`, and should be removed at some point; btw, should `show_error_message` functionality be folded into `showMessageBox`?
|
||||
* Make code clearer / improve code quality
|
||||
* https://codeclimate.com/github/1j01/jspaint
|
||||
|
||||
|
|
After Width: | Height: | Size: 418 B |
After Width: | Height: | Size: 477 B |
After Width: | Height: | Size: 497 B |
After Width: | Height: | Size: 431 B |
After Width: | Height: | Size: 469 B |
After Width: | Height: | Size: 516 B |
After Width: | Height: | Size: 603 B |
After Width: | Height: | Size: 488 B |
After Width: | Height: | Size: 529 B |
After Width: | Height: | Size: 417 B |
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 504 B |
After Width: | Height: | Size: 428 B |
After Width: | Height: | Size: 504 B |
|
@ -396,13 +396,14 @@
|
|||
<script src="lib/imagetracer_v1.2.5.js"></script>
|
||||
|
||||
<script src="src/app-localization.js"></script> <!-- must not be async/deferred, as it uses document.write(); and must come before other app code which uses localization functions -->
|
||||
<script src="src/msgbox.js"></script>
|
||||
<script src="src/functions.js"></script>
|
||||
<script src="src/helpers.js"></script>
|
||||
<script src="src/storage.js"></script>
|
||||
<script src="src/$Component.js"></script>
|
||||
<script src="src/$ToolWindow.js"></script>
|
||||
<script>
|
||||
// after show_error_message, $DialogWindow, and localize are defined,
|
||||
// after show_error_message, showMessageBox, and localize are defined,
|
||||
// set up better global error handling
|
||||
const old_onerror = window.onerror;
|
||||
window.onerror = (message, source, lineno, colno, error)=> {
|
||||
|
|
|
@ -1088,24 +1088,27 @@ function loaded_localizations(language, mapping) {
|
|||
current_language = language;
|
||||
}
|
||||
function set_language(language) {
|
||||
const $w = $DialogWindow().title("Reload Required").addClass("dialogue-window");
|
||||
$w.$main.text("The application needs to reload to change the language.");
|
||||
$w.$main.css("max-width", "600px");
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
are_you_sure(() => {
|
||||
try {
|
||||
localStorage[language_storage_key] = language;
|
||||
location.reload();
|
||||
} catch(error) {
|
||||
show_error_message("Failed to store language preference. Make sure cookies / local storage is enabled in your browser settings.", error);
|
||||
}
|
||||
});
|
||||
}).focus();
|
||||
$w.$Button(localize("Cancel"), () => {
|
||||
$w.close();
|
||||
showMessageBox({
|
||||
title: "Reload Required",
|
||||
message: "The application needs to reload to change the language.",
|
||||
buttons: [
|
||||
{ label: localize("OK"), value: "reload", default: true },
|
||||
{ label: localize("Cancel"), value: "cancel" },
|
||||
],
|
||||
windowOptions: {
|
||||
innerWidth: 450,
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result === "reload") {
|
||||
are_you_sure(() => {
|
||||
try {
|
||||
localStorage[language_storage_key] = language;
|
||||
location.reload();
|
||||
} catch (error) {
|
||||
show_error_message("Failed to store language preference. Make sure cookies / local storage is enabled in your browser settings.", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
$w.center();
|
||||
// load_language(language);
|
||||
}
|
||||
load_language(current_language);
|
|
@ -152,12 +152,12 @@ window.systemHookDefaults = {
|
|||
newFileName = newHandle.name;
|
||||
const newFileExtension = get_file_extension(newFileName);
|
||||
if (!newFileExtension) {
|
||||
show_error_message(`Missing file extension - try adding .${new_format.extensions[0]} to the name.`);
|
||||
show_error_message(`Missing file extension.\n\nTry adding .${new_format.extensions[0]} to the name.`);
|
||||
return;
|
||||
}
|
||||
if (!new_format.extensions.includes(newFileExtension)) {
|
||||
// Closest translation: "Paint cannot save to the same filename with a different file type."
|
||||
show_error_message(`Wrong file extension for selected file type - try adding .${new_format.extensions[0]} to the name.`);
|
||||
show_error_message(`Wrong file extension for selected file type.\n\nTry adding .${new_format.extensions[0]} to the name.`);
|
||||
return;
|
||||
}
|
||||
// const new_format =
|
||||
|
|
262
src/functions.js
|
@ -829,35 +829,32 @@ try {
|
|||
// This will be known as the "Y2T bug"
|
||||
confirmed_overwrite = Date.now() >= 2000000000000;
|
||||
}
|
||||
function confirm_overwrite() {
|
||||
return new Promise((resolve) => {
|
||||
if (confirmed_overwrite) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const $w = new $DialogWindow().addClass("dialogue-window");
|
||||
$w.title(localize("Paint"));
|
||||
$w.$main.html(`
|
||||
async function confirm_overwrite() {
|
||||
if (confirmed_overwrite) {
|
||||
return;
|
||||
}
|
||||
const { $window, promise } = showMessageBox({
|
||||
messageHTML: `
|
||||
<p>JS Paint can now save over existing files.</p>
|
||||
<p>Do you want to overwrite the file?</p>
|
||||
<p>
|
||||
<label><input type='checkbox'/> Don't ask me again</label>
|
||||
</p>
|
||||
`);
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
confirmed_overwrite = $w.$main.find("input[type='checkbox']").prop("checked");
|
||||
try {
|
||||
localStorage[confirmed_overwrite_key] = confirmed_overwrite;
|
||||
} catch (error) {
|
||||
// no localStorage
|
||||
}
|
||||
}).focus();
|
||||
$w.$Button(localize("Cancel"), () => {
|
||||
$w.close();
|
||||
});
|
||||
$w.center();
|
||||
`,
|
||||
buttons: [
|
||||
{ label: localize("Yes"), value: "overwrite", default: true },
|
||||
{ label: localize("Cancel"), value: "cancel" },
|
||||
],
|
||||
});
|
||||
const result = await promise;
|
||||
if (result === "overwrite") {
|
||||
confirmed_overwrite = $window.$content.find("input[type='checkbox']").prop("checked");
|
||||
try {
|
||||
localStorage[confirmed_overwrite_key] = confirmed_overwrite;
|
||||
} catch (error) {
|
||||
// no localStorage... @TODO: don't show the checkbox in this case
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -908,49 +905,62 @@ function file_save_as(maybe_saved_callback=()=>{}, update_from_saved=true){
|
|||
}
|
||||
|
||||
|
||||
function are_you_sure(action, canceled){
|
||||
if(saved){
|
||||
function are_you_sure(action, canceled) {
|
||||
if (saved) {
|
||||
action();
|
||||
}else{
|
||||
const $w = new $DialogWindow().addClass("dialogue-window");
|
||||
$w.title(localize("Paint"));
|
||||
$w.$main.text(localize("Save changes to %1?", file_name));
|
||||
$w.$Button(localize("Save"), () => {
|
||||
$w.close();
|
||||
file_save(()=> {
|
||||
} else {
|
||||
showMessageBox({
|
||||
message: localize("Save changes to %1?", file_name),
|
||||
buttons: [
|
||||
{
|
||||
// label: localize("Save"),
|
||||
label: localize("Yes"),
|
||||
value: "save",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
// label: "Discard",
|
||||
label: localize("No"),
|
||||
value: "discard",
|
||||
},
|
||||
{
|
||||
label: localize("Cancel"),
|
||||
value: "cancel",
|
||||
},
|
||||
],
|
||||
}).then((result) => {
|
||||
if (result === "save") {
|
||||
file_save(() => {
|
||||
action();
|
||||
}, false);
|
||||
} else if (result === "discard") {
|
||||
action();
|
||||
}, false);
|
||||
})[0].focus();
|
||||
$w.$Button("Discard", () => {
|
||||
$w.close();
|
||||
action();
|
||||
} else {
|
||||
canceled?.();
|
||||
}
|
||||
});
|
||||
$w.$Button(localize("Cancel"), () => {
|
||||
$w.close();
|
||||
canceled && canceled();
|
||||
});
|
||||
$w.$x.on("click", () => {
|
||||
canceled && canceled();
|
||||
});
|
||||
$w.center();
|
||||
}
|
||||
}
|
||||
|
||||
function please_enter_a_number() {
|
||||
const $w = new $DialogWindow("Invalid Value").addClass("dialogue-window");
|
||||
$w.$main.text(localize("Please enter a number."));
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
showMessageBox({
|
||||
// title: "Invalid Value",
|
||||
message: localize("Please enter a number."),
|
||||
});
|
||||
}
|
||||
|
||||
function show_error_message(message, error){
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window squish");
|
||||
$w.$main.text(message);
|
||||
$w.$main.css("max-width", "600px");
|
||||
function show_error_message(message, error) {
|
||||
const { $message } = showMessageBox({
|
||||
iconID: "error",
|
||||
message,
|
||||
// windowOptions: {
|
||||
// innerWidth: 600,
|
||||
// },
|
||||
});
|
||||
// $message.css("max-width", "600px");
|
||||
if(error){
|
||||
const $details = $("<details><summary><span>Details</span></summary></details>")
|
||||
.appendTo($w.$main);
|
||||
.appendTo($message);
|
||||
|
||||
// Chrome includes the error message in the error.stack string, whereas Firefox doesn't.
|
||||
// Also note that there can be Exception objects that don't have a message (empty string) but a name,
|
||||
|
@ -979,25 +989,21 @@ function show_error_message(message, error){
|
|||
overflow: "auto",
|
||||
});
|
||||
}
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
$w.center();
|
||||
if (error) {
|
||||
window.console && console.error(message, error);
|
||||
window.console?.error?.(message, error);
|
||||
} else {
|
||||
window.console && console.error(message);
|
||||
window.console?.error?.(message);
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: close are_you_sure windows and these Error windows when switching sessions
|
||||
// because it can get pretty confusing
|
||||
function show_resource_load_error_message(error){
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window");
|
||||
const { $window, $message } = showMessageBox({});
|
||||
const firefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||
// @TODO: copy & paste vs download & open, more specific guidance
|
||||
if (error.code === "cross-origin-blob-uri") {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Can't load image from address starting with "blob:".</p>
|
||||
${
|
||||
firefox ?
|
||||
|
@ -1006,47 +1012,43 @@ function show_resource_load_error_message(error){
|
|||
}
|
||||
`);
|
||||
} else if (error.code === "html-not-image") {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Address points to a web page, not an image file.</p>
|
||||
<p>Try copying and pasting an image instead of a URL.</p>
|
||||
`);
|
||||
} else if (error.code === "decoding-failure") {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Address doesn't point to an image file of a supported format.</p>
|
||||
<p>Try copying and pasting an image instead of a URL.</p>
|
||||
`);
|
||||
} else if (error.code === "access-failure") {
|
||||
if (navigator.onLine) {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Failed to download image.</p>
|
||||
<p>Try copying and pasting an image instead of a URL.</p>
|
||||
`);
|
||||
if (error.fails) {
|
||||
$("<ul>").append(error.fails.map(({status, statusText, url})=>
|
||||
$("<li>").text(url).prepend($("<b>").text(`${status || ""} ${statusText || "Failed"} `))
|
||||
)).appendTo($w.$main);
|
||||
)).appendTo($message);
|
||||
}
|
||||
} else {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Failed to download image.</p>
|
||||
<p>You're offline. Connect to the internet and try again.</p>
|
||||
<p>Or copy and paste an image instead of a URL, if possible.</p>
|
||||
`);
|
||||
}
|
||||
} else {
|
||||
$w.$main.html(`
|
||||
$message.html(`
|
||||
<p>Failed to load image from URL.</p>
|
||||
<p>Check your browser's devtools for details.</p>
|
||||
`);
|
||||
}
|
||||
$w.$main.css({maxWidth: "500px"});
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
$w.center();
|
||||
$message.css({ maxWidth: "500px" });
|
||||
$window.center(); // after adding content
|
||||
}
|
||||
function show_file_format_errors({ as_image_error, as_palette_error }) {
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window");
|
||||
let html = `
|
||||
<p>${localize("Paint cannot open this file.")}</p>
|
||||
`;
|
||||
|
@ -1096,10 +1098,9 @@ function show_file_format_errors({ as_image_error, as_palette_error }) {
|
|||
</details>
|
||||
`;
|
||||
}
|
||||
$w.$main.html(html);
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
showMessageBox({
|
||||
messageHTML: html,
|
||||
});
|
||||
}
|
||||
function show_read_image_file_error(error) {
|
||||
// @TODO: similar friendly messages to show_resource_load_error_message,
|
||||
|
@ -1300,34 +1301,50 @@ async function choose_file_to_paste() {
|
|||
function paste(img_or_canvas){
|
||||
|
||||
if(img_or_canvas.width > main_canvas.width || img_or_canvas.height > main_canvas.height){
|
||||
const $w = new $DialogWindow().addClass("dialogue-window");
|
||||
$w.title(localize("Paint"));
|
||||
$w.$main.html(`
|
||||
${localize("The image in the clipboard is larger than the bitmap.")}<br>
|
||||
${localize("Would you like the bitmap enlarged?")}<br>
|
||||
`);
|
||||
$w.$Button("Enlarge", () => {
|
||||
$w.close();
|
||||
// The resize gets its own undoable, as in mspaint
|
||||
resize_canvas_and_save_dimensions(
|
||||
Math.max(main_canvas.width, img_or_canvas.width),
|
||||
Math.max(main_canvas.height, img_or_canvas.height),
|
||||
showMessageBox({
|
||||
message: localize("The image in the clipboard is larger than the bitmap.") + "\n" +
|
||||
localize("Would you like the bitmap enlarged?"),
|
||||
iconID: "question",
|
||||
windowOptions: {
|
||||
icons: {
|
||||
16: "images/windows-16x16.png",
|
||||
32: "images/windows-32x32.png",
|
||||
},
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
name: "Enlarge Canvas For Paste",
|
||||
icon: get_help_folder_icon("p_stretch_both.png"),
|
||||
}
|
||||
);
|
||||
do_the_paste();
|
||||
$canvas_area.trigger("resize");
|
||||
})[0].focus();
|
||||
$w.$Button("Crop", () => {
|
||||
$w.close();
|
||||
do_the_paste();
|
||||
// label: "Enlarge",
|
||||
label: localize("Yes"),
|
||||
value: "enlarge",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
// label: "Crop",
|
||||
label: localize("No"),
|
||||
value: "crop",
|
||||
},
|
||||
{
|
||||
label: localize("Cancel"),
|
||||
value: "cancel",
|
||||
},
|
||||
],
|
||||
}).then((result) => {
|
||||
if (result === "enlarge") {
|
||||
// The resize gets its own undoable, as in mspaint
|
||||
resize_canvas_and_save_dimensions(
|
||||
Math.max(main_canvas.width, img_or_canvas.width),
|
||||
Math.max(main_canvas.height, img_or_canvas.height),
|
||||
{
|
||||
name: "Enlarge Canvas For Paste",
|
||||
icon: get_help_folder_icon("p_stretch_both.png"),
|
||||
}
|
||||
);
|
||||
do_the_paste();
|
||||
$canvas_area.trigger("resize");
|
||||
} else if (result === "crop") {
|
||||
do_the_paste();
|
||||
}
|
||||
});
|
||||
$w.$Button(localize("Cancel"), () => {
|
||||
$w.close();
|
||||
});
|
||||
$w.center();
|
||||
}else{
|
||||
do_the_paste();
|
||||
}
|
||||
|
@ -1647,6 +1664,7 @@ function undo(){
|
|||
return true;
|
||||
}
|
||||
|
||||
// @TODO: use Clippy.js instead for potentially annoying tips
|
||||
let $document_history_prompt_window;
|
||||
function redo(){
|
||||
if(redos.length<1){
|
||||
|
@ -1654,13 +1672,11 @@ function redo(){
|
|||
$document_history_prompt_window.close();
|
||||
}
|
||||
if (!$document_history_window || $document_history_window.closed) {
|
||||
const $w = $document_history_prompt_window = new $DialogWindow();
|
||||
$w.title("Redo");
|
||||
$w.$main.html("To view all branches of the history tree, click <b>Edit > History</b>.").css({padding: 10});
|
||||
// $w.$Button("Show History", show_document_history).css({margin: 10}).focus();
|
||||
// $w.$Button(localize("Cancel"), ()=> { $w.close(); }).css({margin: 10});
|
||||
$w.$Button(localize("OK"), ()=> { $w.close(); }).css({margin: 10}).focus();
|
||||
$w.center();
|
||||
$document_history_prompt_window = showMessageBox({
|
||||
title: "Redo",
|
||||
messageHTML: `To view all branches of the history tree, click <b>Edit > History</b>.`,
|
||||
iconID: "info",
|
||||
}).$window;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3196,18 +3212,16 @@ function sanity_check_blob(blob, okay_callback, magic_number_bytes, magic_wanted
|
|||
if (magic_found === magic_wanted) {
|
||||
okay_callback();
|
||||
} else {
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window");
|
||||
// hackily combining messages that are already localized
|
||||
// you have to do some deduction to understand this message
|
||||
$w.$main.html(`
|
||||
<p>${localize("Unexpected file format.")}</p>
|
||||
<p>${localize("An unsupported operation was attempted.")}</p>
|
||||
`);
|
||||
$w.$main.css({maxWidth: "500px"});
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
$w.center();
|
||||
showMessageBox({
|
||||
// hackily combining messages that are already localized, in ways they were not meant to be used.
|
||||
// you may have to do some deduction to understand this message.
|
||||
// messageHTML: `
|
||||
// <p>${localize("Unexpected file format.")}</p>
|
||||
// <p>${localize("An unsupported operation was attempted.")}</p>
|
||||
// `,
|
||||
message: "Your browser does not support writing images in this file format.",
|
||||
iconID: "error",
|
||||
});
|
||||
}
|
||||
}, (error)=> {
|
||||
show_error_message(localize("An unknown error has occurred."), error);
|
||||
|
|
24
src/help.js
|
@ -255,17 +255,19 @@ function open_help_viewer(options){
|
|||
}, (/* error */)=> {
|
||||
// access to error message is not allowed either, basically
|
||||
if (location.protocol === "file:") {
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window");
|
||||
$w.$main.html(`
|
||||
<p>${localize("Failed to launch help.")}</p>
|
||||
<p>This feature is not available when running from the <code>file:</code> protocol.</p>
|
||||
<p>To use this feature, start a web server. If you have Python, you can use <code>python -m SimpleHTTPServer</code></p>
|
||||
`);
|
||||
$w.$main.css({maxWidth: "500px"});
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
$w.center();
|
||||
showMessageBox({
|
||||
// <p>${localize("Failed to launch help.")}</p>
|
||||
// but it's already launched at this point
|
||||
|
||||
// what's a good tutorial for starting a web server?
|
||||
// https://gist.github.com/willurd/5720255 - impressive list, but not a tutorial
|
||||
// https://attacomsian.com/blog/local-web-server - OK, good enough
|
||||
messageHTML: `
|
||||
<p>Help is not available when running from the <code>file:</code> protocol.</p>
|
||||
<p>To use this feature, <a href="https://attacomsian.com/blog/local-web-server">start a web server</a>.</p>
|
||||
`,
|
||||
iconID: "error",
|
||||
});
|
||||
} else {
|
||||
show_error_message(`${localize("Failed to launch help.")} ${localize("Access to %1 was denied.", options.contentsFile)}`);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ let $storage_manager;
|
|||
let $quota_exceeded_window;
|
||||
let ignoring_quota_exceeded = false;
|
||||
|
||||
function storage_quota_exceeded(){
|
||||
async function storage_quota_exceeded(){
|
||||
if($quota_exceeded_window){
|
||||
$quota_exceeded_window.close();
|
||||
$quota_exceeded_window = null;
|
||||
|
@ -11,27 +11,27 @@ function storage_quota_exceeded(){
|
|||
if(ignoring_quota_exceeded){
|
||||
return;
|
||||
}
|
||||
const $w = $DialogWindow().title("Storage Error").addClass("dialogue-window squish");
|
||||
$w.$main.html(
|
||||
"<p>JS Paint stores images as you work on them so that if you " +
|
||||
"close your browser or tab or reload the page " +
|
||||
"your images are usually safe.</p>" +
|
||||
"<p>However, it has run out of space to do so.</p>" +
|
||||
"<p>You can still save the current image with <b>File > Save</b>. " +
|
||||
"You should save frequently, or free up enough space to keep the image safe.</p>"
|
||||
);
|
||||
$w.$Button("Manage Storage", () => {
|
||||
$w.close();
|
||||
const { promise, $window } = showMessageBox({
|
||||
title: "Storage Error",
|
||||
messageHTML: `
|
||||
<p>JS Paint stores images as you work on them so that if you close your browser or tab or reload the page your images are usually safe.</p>
|
||||
<p>However, it has run out of space to do so.</p>
|
||||
<p>You can still save the current image with <b>File > Save</b>. You should save frequently, or free up enough space to keep the image safe.</p>
|
||||
`,
|
||||
buttons: [
|
||||
{ label: "Manage Storage", value: "manage", default: true },
|
||||
{ label: "Ignore", value: "ignore" },
|
||||
],
|
||||
iconID: "warning",
|
||||
});
|
||||
$quota_exceeded_window = $window;
|
||||
const result = await promise;
|
||||
if (result === "ignore") {
|
||||
ignoring_quota_exceeded = true;
|
||||
} else if (result === "manage") {
|
||||
ignoring_quota_exceeded = false;
|
||||
manage_storage();
|
||||
}).focus();
|
||||
$w.$Button("Ignore", () => {
|
||||
$w.close();
|
||||
ignoring_quota_exceeded = true;
|
||||
});
|
||||
$w.$content.width(500);
|
||||
$w.center();
|
||||
$quota_exceeded_window = $w;
|
||||
}
|
||||
}
|
||||
|
||||
function manage_storage(){
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
// Prefer a function injected from outside an iframe,
|
||||
// which will make dialogs that can go outside the iframe,
|
||||
// for 98.js.org integration.
|
||||
// Note that this API must be kept in sync with the version in 98.js.org.
|
||||
|
||||
// Note `defaultMessageBoxTitle` handling in make_iframe_window
|
||||
// Any other default parameters need to be handled there (as it works now)
|
||||
|
||||
window.defaultMessageBoxTitle = localize("Paint");
|
||||
|
||||
var chord_audio = new Audio("audio/chord.wav");
|
||||
|
||||
window.showMessageBox = window.showMessageBox || (({
|
||||
title = window.defaultMessageBoxTitle ?? "Alert",
|
||||
message,
|
||||
messageHTML,
|
||||
buttons = [{ label: "OK", value: "ok", default: true }],
|
||||
iconID = "warning", // "error", "warning", "info", or "nuke" for deleting files/folders
|
||||
windowOptions = {}, // for controlling width, etc.
|
||||
}) => {
|
||||
let $window, $message;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
$window = make_window_supporting_scale(Object.assign({
|
||||
title,
|
||||
resizable: false,
|
||||
innerWidth: 400,
|
||||
maximizeButton: false,
|
||||
minimizeButton: false,
|
||||
}, windowOptions));
|
||||
// $window.addClass("dialogue-window");
|
||||
$message =
|
||||
$("<div>").css({
|
||||
textAlign: "left",
|
||||
fontFamily: "MS Sans Serif, Arial, sans-serif",
|
||||
fontSize: "14px",
|
||||
marginTop: "22px",
|
||||
flex: 1,
|
||||
minWidth: 0, // Fixes hidden overflow, see https://css-tricks.com/flexbox-truncated-text/
|
||||
whiteSpace: "normal", // overriding .window:not(.squish)
|
||||
});
|
||||
if (messageHTML) {
|
||||
$message.html(messageHTML);
|
||||
} else if (message) { // both are optional because you may populate later with dynamic content
|
||||
$message.text(message).css({
|
||||
whiteSpace: "pre-wrap",
|
||||
wordWrap: "break-word",
|
||||
});
|
||||
}
|
||||
$("<div>").append(
|
||||
$("<img width='32' height='32'>").attr("src", `images/${iconID}-32x32-8bpp.png`).css({
|
||||
margin: "16px",
|
||||
display: "block",
|
||||
}),
|
||||
$message
|
||||
).css({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
}).appendTo($window.$content);
|
||||
|
||||
$window.$content.css({
|
||||
textAlign: "center",
|
||||
});
|
||||
for (const button of buttons) {
|
||||
const $button = $window.$Button(button.label, () => {
|
||||
button.action?.(); // API may be required for using user gesture requiring APIs
|
||||
resolve(button.value);
|
||||
$window.close(); // actually happens automatically
|
||||
});
|
||||
if (button.default) {
|
||||
$button.addClass("default");
|
||||
$button.focus();
|
||||
setTimeout(() => $button.focus(), 0); // @TODO: why is this needed? does it have to do with the iframe window handling?
|
||||
}
|
||||
$button.css({
|
||||
minWidth: 75,
|
||||
height: 23,
|
||||
margin: "16px 2px",
|
||||
});
|
||||
}
|
||||
$window.on("focusin", "button", (event) => {
|
||||
$(event.currentTarget).addClass("default");
|
||||
});
|
||||
$window.on("focusout", "button", (event) => {
|
||||
$(event.currentTarget).removeClass("default");
|
||||
});
|
||||
$window.on("closed", () => {
|
||||
resolve("closed"); // or "cancel"? do you need to distinguish?
|
||||
});
|
||||
$window.center();
|
||||
});
|
||||
promise.$window = $window;
|
||||
promise.$message = $message;
|
||||
promise.promise = promise; // for easy destructuring
|
||||
try {
|
||||
chord_audio.play();
|
||||
} catch (error) {
|
||||
console.log(`Failed to play ${chord_audio.src}: `, error);
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
|
||||
window.alert = (message) => {
|
||||
showMessageBox({ message });
|
||||
};
|
|
@ -233,20 +233,17 @@
|
|||
}
|
||||
start() {
|
||||
// @TODO: how do you actually detect if it's failing???
|
||||
const $w = $DialogWindow().title(localize("Paint")).addClass("dialogue-window");
|
||||
$w.$main.html("<p>The document may not load. Changes may not save.</p>" +
|
||||
"<p>Multiuser sessions are public. There is no security.</p>"
|
||||
// "<p>The document may not load. Changes may not save. If it does save, it's public. There is no security.</p>"// +
|
||||
// "<p>I haven't found a way to detect Firebase quota limits being exceeded, " +
|
||||
// "so for now I'm showing this message regardless of whether it's working.</p>" +
|
||||
// "<p>If you're interested in using multiuser mode, please thumbs-up " +
|
||||
// "<a href='https://github.com/1j01/jspaint/issues/68'>this issue</a> to show interest, and/or subscribe for updates.</p>"
|
||||
);
|
||||
$w.$main.css({ maxWidth: "500px" });
|
||||
$w.$Button(localize("OK"), () => {
|
||||
$w.close();
|
||||
}).focus();
|
||||
$w.center();
|
||||
showMessageBox({
|
||||
messageHTML: `
|
||||
<p>The document may not load. Changes may not save.</p>
|
||||
<p>Multiuser sessions are public. There is no security.</p>
|
||||
`
|
||||
});
|
||||
// "<p>The document may not load. Changes may not save. If it does save, it's public. There is no security.</p>"// +
|
||||
// "<p>I haven't found a way to detect Firebase quota limits being exceeded, " +
|
||||
// "so for now I'm showing this message regardless of whether it's working.</p>" +
|
||||
// "<p>If you're interested in using multiuser mode, please thumbs-up " +
|
||||
// "<a href='https://github.com/1j01/jspaint/issues/68'>this issue</a> to show interest, and/or subscribe for updates.</p>"
|
||||
|
||||
// Wrap the Firebase API because they don't
|
||||
// provide a great way to clean up event listeners
|
||||
|
|