Unify TODO comments
parent
97cd1aeea1
commit
31edf30064
|
@ -49,31 +49,31 @@ context('visual tests', () => {
|
|||
});
|
||||
|
||||
it('flip and rotate window', () => {
|
||||
// TODO: make menus more testable, with IDs
|
||||
// @TODO: make menus more testable, with IDs
|
||||
cy.get('.menus > .menu-container:nth-child(4) > .menu-button > .menu-hotkey').click();
|
||||
cy.get('.menus > .menu-container:nth-child(4) > .menu-popup > table > tr:nth-child(1)').click();
|
||||
cy.get('.window:visible').matchImageSnapshot();
|
||||
});
|
||||
|
||||
it('stretch and skew window', () => {
|
||||
// TODO: make menus more testable, with IDs
|
||||
// @TODO: make menus more testable, with IDs
|
||||
cy.get('.menus > .menu-container:nth-child(4) > .menu-button > .menu-hotkey').click();
|
||||
cy.get('.menus > .menu-container:nth-child(4) > .menu-popup > table > tr:nth-child(2)').click();
|
||||
// TODO: wait for images to load and include images?
|
||||
// @TODO: wait for images to load and include images?
|
||||
cy.get('.window:visible').matchImageSnapshot({ blackout: ["img"] });
|
||||
});
|
||||
|
||||
it('help window', () => {
|
||||
// TODO: make menus more testable, with IDs
|
||||
// @TODO: make menus more testable, with IDs
|
||||
cy.get('.menus > .menu-container:nth-child(6) > .menu-button > .menu-hotkey').click();
|
||||
cy.get('.menus > .menu-container:nth-child(6) > .menu-popup > table > tr:nth-child(1)').click();
|
||||
cy.get('.window:visible .folder', {timeout: 10000}); // wait for sidebar contents to load
|
||||
// TODO: wait for iframe to load
|
||||
// @TODO: wait for iframe to load
|
||||
cy.get('.window:visible').matchImageSnapshot({ blackout: ["iframe"] });
|
||||
});
|
||||
|
||||
it('about window', () => {
|
||||
// TODO: make menus more testable, with IDs
|
||||
// @TODO: make menus more testable, with IDs
|
||||
cy.get('.menus > .menu-container:nth-child(6) > .menu-button > .menu-hotkey').click();
|
||||
cy.get('.menus > .menu-container:nth-child(6) > .menu-popup > table > tr:nth-child(3)').click();
|
||||
cy.get('.window:visible').matchImageSnapshot({ blackout: ["img"] });
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
<li>Click <b>Black and white</b>.</li>
|
||||
</ol>
|
||||
<!--
|
||||
As of writing, there's no conversion to monochrome.
|
||||
The black and white mode just works with color in the document.
|
||||
TODO: add *optional* conversion to monochrome, and update this help accordingly
|
||||
There's now a conversion to monochrome
|
||||
(altho the black and white mode allows color in the document.)
|
||||
@TODO: Update this help accordingly
|
||||
-->
|
||||
<!--
|
||||
<p><b>Note</b></p>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</ol>
|
||||
<p><b>Notes</b></p>
|
||||
<ul>
|
||||
<!-- TODO: maybe emulate the color picker dialog and make this note unecessary: -->
|
||||
<!-- @TODO: maybe emulate the color picker dialog and make this note unecessary: -->
|
||||
<li>
|
||||
These instructions are very operating system specific and you may get a completely different color picker.
|
||||
On some systems the color picker may be utterly useless, and give you less color options than the default palette.
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<p><b>Notes</b></p>
|
||||
<ul>
|
||||
<li>JS Paint can't directly set your wallpaper, but it can generate a tiled image the size of your desktop.</li>
|
||||
<!-- TODO: make this note unnecessary? -->
|
||||
<!-- @TODO: make this note unnecessary? -->
|
||||
<li>The <b>Set As Wallpaper (Centered)</b> option doesn't currently use your desktop size, it just saves a copy of the image.
|
||||
You need to make sure in your system settings to choose to center the image.</li>
|
||||
</ul>
|
||||
|
|
|
@ -5,7 +5,7 @@ function E(t){
|
|||
return document.createElement(t);
|
||||
}
|
||||
|
||||
// TODO: make menus not take focus so we can support copy/pasting text in the text tool textarea from the menus
|
||||
// @TODO: make menus not take focus so we can support copy/pasting text in the text tool textarea from the menus
|
||||
|
||||
const MENU_DIVIDER = "MENU_DIVIDER";
|
||||
|
||||
|
@ -38,7 +38,7 @@ function $MenuBar(menus){
|
|||
}
|
||||
};
|
||||
|
||||
// TODO: API for context menus (i.e. floating menu popups)
|
||||
// @TODO: API for context menus (i.e. floating menu popups)
|
||||
function $MenuPopup(menu_items){
|
||||
const $menu_popup = $(E("div")).addClass("menu-popup");
|
||||
const $menu_popup_table = $(E("table")).addClass("menu-popup-table").appendTo($menu_popup);
|
||||
|
|
|
@ -85,9 +85,9 @@
|
|||
"lint-cat": "concat-glob-cli --files \"src/**/!(electron*).js\" --output concatenated-source.js && eslint --rule \"no-undef: error\" --rule \"no-unused-vars: error\" concatenated-source.js",
|
||||
"lint-cat:NOTE": "Disable the eslint comment that disables ThisExpression to use this.",
|
||||
"dev": "live-server --ignorePattern=\"(node_modules|cypress|out)[/\\\\\\\\]|package\\.json|cypress\\.json\"",
|
||||
"dev:NOTE": "XXX: the octuple backlash ends up meaning a single backslash on Linux, two backslashes on Windows. In this case it's fine because it's in a regexp character class so the extra is redundant and doesn't cause an error.",
|
||||
"dev:NOTE": "@XXX: the octuple backlash ends up meaning a single backslash on Linux, two backslashes on Windows. In this case it's fine because it's in a regexp character class so the extra is redundant and doesn't cause an error.",
|
||||
"test:start-server": "live-server --port=11822 --no-browser --ignorePattern=\"(node_modules|cypress|out)[/\\\\\\\\]|package\\.json|cypress\\.json\"",
|
||||
"test:start-server:NOTE": "XXX: the octuple backlash ends up meaning a single backslash on Linux, two backslashes on Windows. In this case it's fine because it's in a regexp character class so the extra is redundant and doesn't cause an error.",
|
||||
"test:start-server:NOTE": "@XXX: the octuple backlash ends up meaning a single backslash on Linux, two backslashes on Windows. In this case it's fine because it's in a regexp character class so the extra is redundant and doesn't cause an error.",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
"cy:accept": "cypress run --env updateSnapshots=true",
|
||||
|
|
|
@ -101,7 +101,7 @@ function $ColorBox(vertical){
|
|||
$i.val(color_to_hex(color));
|
||||
|
||||
$b.on("pointerdown", e => {
|
||||
// TODO: how should the ternary color, and selection cropping, work on macOS?
|
||||
// @TODO: how should the ternary color, and selection cropping, work on macOS?
|
||||
ctrl = e.ctrlKey;
|
||||
button = e.button;
|
||||
if(button === 0){
|
||||
|
@ -160,7 +160,7 @@ function $ColorBox(vertical){
|
|||
// Edit the last color cell that's been selected as the foreground color.
|
||||
create_and_trigger_input({type: "color"}, input => {
|
||||
// window.console && console.log(input, input.value);
|
||||
// FIXME
|
||||
// @FIXME
|
||||
$last_fg_color_button.trigger({type: "pointerdown", ctrlKey: false, button: 0});
|
||||
$last_fg_color_button.find("input").val(input.value).triggerHandler("change");
|
||||
})
|
||||
|
|
|
@ -89,7 +89,7 @@ function $Component(name, orientation, $el){
|
|||
|
||||
let iid;
|
||||
if($("body").hasClass("eye-gaze-mode")){
|
||||
// TODO: don't use an interval for this!
|
||||
// @TODO: don't use an interval for this!
|
||||
iid = setInterval(()=> {
|
||||
const scale = 3;
|
||||
$c.css({
|
||||
|
|
|
@ -59,7 +59,7 @@ function $Handles($container, getRect, options){
|
|||
|
||||
const rect = getRect();
|
||||
const m = to_canvas_coords(event);
|
||||
// TODO: decide between Math.floor/Math.ceil/Math.round for these values
|
||||
// @TODO: decide between Math.floor/Math.ceil/Math.round for these values
|
||||
if(x_axis === "right"){
|
||||
delta_x = 0;
|
||||
width = ~~(m.x - rect.left);
|
||||
|
|
|
@ -21,9 +21,9 @@ function $ToolWindow($component){
|
|||
e.preventDefault();
|
||||
});
|
||||
|
||||
// TODO: prevent selection *outside* of the window *via* the window
|
||||
// @TODO: prevent selection *outside* of the window *via* the window
|
||||
|
||||
// TODO: keep track of last focused control in the window, and focus it when clicking on / focusing the window
|
||||
// @TODO: keep track of last focused control in the window, and focus it when clicking on / focusing the window
|
||||
|
||||
$w.css({
|
||||
position: "absolute",
|
||||
|
@ -81,9 +81,9 @@ function $ToolWindow($component){
|
|||
break;
|
||||
case 9: { // Tab
|
||||
|
||||
// TODO: handle shift+tab as well (note: early return at top of function)
|
||||
// @TODO: handle shift+tab as well (note: early return at top of function)
|
||||
// wrap around when tabbing through controls in a window
|
||||
// TODO: other element types? also [tabIndex]
|
||||
// @TODO: other element types? also [tabIndex]
|
||||
const $controls = $w.$content.find("input, textarea, select, button, a");
|
||||
const focused_control_index = $controls.index($focused);
|
||||
if(focused_control_index === $controls.length - 1){
|
||||
|
|
|
@ -23,7 +23,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
}
|
||||
position() {
|
||||
super.position(true);
|
||||
update_helper_layer(); // TODO: under-grid specific helper layer?
|
||||
update_helper_layer(); // @TODO: under-grid specific helper layer?
|
||||
}
|
||||
instantiate(img) {
|
||||
this.$el.css({
|
||||
|
@ -39,7 +39,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
// (width vs naturalWidth?)
|
||||
// and at least apply_image_transformation needs it to be a canvas now (and the property name says canvas anyways)
|
||||
this.source_canvas = make_canvas(img);
|
||||
// TODO: is this width/height code needed? probably not! wouldn't it clear the canvas anyways?
|
||||
// @TODO: is this width/height code needed? probably not! wouldn't it clear the canvas anyways?
|
||||
// but maybe we should assert in some way that the widths are the same, or resize the selection?
|
||||
if (this.source_canvas.width !== this.width) {
|
||||
this.source_canvas.width = this.width;
|
||||
|
@ -114,7 +114,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
this.draw();
|
||||
});
|
||||
}
|
||||
// TODO: how should this work for macOS? where ctrl+click = secondary click?
|
||||
// @TODO: how should this work for macOS? where ctrl+click = secondary click?
|
||||
else if (e.ctrlKey) {
|
||||
// Stamp selection
|
||||
undoable({
|
||||
|
@ -143,7 +143,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
// and should end up as the cut out image data for the selection
|
||||
// canvasImageData is initially the portion of image data on the canvas,
|
||||
// and should end up as... the portion of image data on the canvas that it should end up as.
|
||||
// TODO: could simplify by making the later (shared) condition just if(colored_cutout){}
|
||||
// @TODO: could simplify by making the later (shared) condition just if(colored_cutout){}
|
||||
// but might change how it works anyways so whatever
|
||||
// if(!transparency){ // now if !transparency or if tool_transparent_mode
|
||||
// this is mainly in order to support patterns as the background color
|
||||
|
@ -180,7 +180,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
// (and it would be complicated to make it update the canvas when switching tool options (as opposed to just the selection))
|
||||
// I'm having it use the tool_transparent_mode option here, so you could at least choose beforehand
|
||||
// (and this might actually give you more options, although it could be confusingly inconsistent)
|
||||
// FIXME: yeah, this is confusing; if you have both transparency modes on and you try to clear an area to transparency, it doesn't work
|
||||
// @FIXME: yeah, this is confusing; if you have both transparency modes on and you try to clear an area to transparency, it doesn't work
|
||||
// and there's no indication that you should try the other selection transparency mode,
|
||||
// and even if you do, if you do it after creating a selection, it still won't work,
|
||||
// because you will have already *not cut out* the selection from the canvas
|
||||
|
@ -202,7 +202,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
for (let i = 0; i < cutoutImageData.data.length; i += 4) {
|
||||
let in_cutout = sourceImageData.data[i + 3] > 0;
|
||||
if (tool_transparent_mode) {
|
||||
// FIXME: work with transparent selected background color
|
||||
// @FIXME: work with transparent selected background color
|
||||
// (support treating partially transparent background colors as transparency)
|
||||
if (sourceImageData.data[i + 0] === background_color_rgba[0] &&
|
||||
sourceImageData.data[i + 1] === background_color_rgba[1] &&
|
||||
|
@ -228,7 +228,7 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
|
||||
update_helper_layer();
|
||||
}
|
||||
// TODO: should Image > Invert apply to this.source_canvas or to this.canvas (replacing this.source_canvas with the result)?
|
||||
// @TODO: should Image > Invert apply to this.source_canvas or to this.canvas (replacing this.source_canvas with the result)?
|
||||
replace_source_canvas(new_source_canvas) {
|
||||
this.source_canvas = new_source_canvas;
|
||||
const new_canvas = make_canvas(new_source_canvas);
|
||||
|
@ -274,6 +274,6 @@ class OnCanvasSelection extends OnCanvasObject {
|
|||
destroy() {
|
||||
super.destroy();
|
||||
$G.off("option-changed", this._on_option_changed);
|
||||
update_helper_layer(); // TODO: under-grid specific helper layer?
|
||||
update_helper_layer(); // @TODO: under-grid specific helper layer?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ class OnCanvasTextBox extends OnCanvasObject {
|
|||
this.canvas.width = this.width;
|
||||
this.canvas.height = this.height;
|
||||
this.canvas.ctx.drawImage(img, 0, 0);
|
||||
update_helper_layer(); // TODO: under-grid specific helper layer?
|
||||
update_helper_layer(); // @TODO: under-grid specific helper layer?
|
||||
};
|
||||
img.onerror = (e)=> {
|
||||
window.console && console.log("Failed to load image", e);
|
||||
|
@ -125,7 +125,7 @@ class OnCanvasTextBox extends OnCanvasObject {
|
|||
this.y = Math.max(Math.min(m.y - moy, canvas.height), -this.height);
|
||||
this.position();
|
||||
if (e.shiftKey) {
|
||||
// TODO: maybe re-enable but handle undoables well
|
||||
// @TODO: maybe re-enable but handle undoables well
|
||||
// this.draw();
|
||||
}
|
||||
};
|
||||
|
@ -185,7 +185,7 @@ class OnCanvasTextBox extends OnCanvasObject {
|
|||
}
|
||||
position() {
|
||||
super.position(true);
|
||||
update_helper_layer(); // TODO: under-grid specific helper layer?
|
||||
update_helper_layer(); // @TODO: under-grid specific helper layer?
|
||||
}
|
||||
destroy() {
|
||||
super.destroy();
|
||||
|
@ -197,6 +197,6 @@ class OnCanvasTextBox extends OnCanvasObject {
|
|||
this.$editor.off("input", this._on_input);
|
||||
this.$editor.off("scroll", this._on_scroll);
|
||||
$(window).off("resize", this._on_window_resize);
|
||||
update_helper_layer(); // TODO: under-grid specific helper layer?
|
||||
update_helper_layer(); // @TODO: under-grid specific helper layer?
|
||||
}
|
||||
}
|
||||
|
|
10
src/app.js
10
src/app.js
|
@ -180,7 +180,7 @@ $news_indicator.on("click auxclick", (event)=> {
|
|||
event.preventDefault();
|
||||
show_news();
|
||||
});
|
||||
// TODO: use localstorage to show until clicked, if available
|
||||
// @TODO: use localstorage to show until clicked, if available
|
||||
// and show for a longer period of time after the update, if available
|
||||
if (Date.now() < Date.parse("Jan 5 2020 23:42:42 GMT-0500")) {
|
||||
$status_area.append($news_indicator);
|
||||
|
@ -298,7 +298,7 @@ $G.on("keydown", e => {
|
|||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
// TODO: return if menus/menubar focused or focus in dialog window
|
||||
// @TODO: return if menus/menubar focused or focus in dialog window
|
||||
// or maybe there's a better way to do this that works more generally
|
||||
// maybe it should only handle the event if document.activeElement is the body or html element?
|
||||
// (or $app could have a tabIndex and no focus style and be focused under various conditions,
|
||||
|
@ -310,7 +310,7 @@ $G.on("keydown", e => {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: preventDefault in all cases where the event is handled
|
||||
// @TODO: preventDefault in all cases where the event is handled
|
||||
// also, ideally check that modifiers *aren't* pressed
|
||||
// probably best to use a library at this point!
|
||||
|
||||
|
@ -522,7 +522,7 @@ reset_colors();
|
|||
reset_canvas_and_history(); // (with newly reset colors)
|
||||
set_magnification(default_magnification);
|
||||
|
||||
// this is synchronous for now, but TODO: handle possibility of loading a document before callback
|
||||
// this is synchronous for now, but @TODO: handle possibility of loading a document before callback
|
||||
// when switching to asynchronous storage, e.g. with localforage
|
||||
storage.get({
|
||||
width: default_canvas_width,
|
||||
|
@ -1247,7 +1247,7 @@ $canvas_area.on("pointerdown", (event)=> {
|
|||
// prevent multitouch panning in case of dragging across iframe boundary with a mouse/pen
|
||||
// Note: there can be multiple active primary pointers, one per pointer type
|
||||
!(pointer.isPrimary && (pointer.pointerType === "mouse" || pointer.pointerType === "pen"))
|
||||
// TODO: handle case of dragging across iframe boundary with touch
|
||||
// @TODO: handle case of dragging across iframe boundary with touch
|
||||
)) {
|
||||
pointers.push({
|
||||
pointerId: event.pointerId,
|
||||
|
|
|
@ -10,7 +10,7 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const argv = require("electron").remote.process.argv;
|
||||
|
||||
// TODO: let user apply this setting somewhere in the UI
|
||||
// @TODO: let user apply this setting somewhere in the UI
|
||||
// (and ideally revert it)
|
||||
// (Note: it would be better to use REG.EXE to apply the change, rather than a .reg file)
|
||||
// This registry modification changes the right click > Edit option for images in Windows Explorer
|
||||
|
@ -106,18 +106,18 @@ function blob_to_buffer(blob, callback) {
|
|||
return file_reader;
|
||||
}
|
||||
|
||||
// TODO: window.platform.saveCanvasAs etc. or platformIntegration or system or something
|
||||
// @TODO: window.platform.saveCanvasAs etc. or platformIntegration or system or something
|
||||
window.systemSaveCanvasAs = (canvas, suggestedFileName, savedCallback) => {
|
||||
const getExtension = filePathOrName => {
|
||||
const splitByDots = filePathOrName.split(/\./g);
|
||||
return splitByDots[splitByDots.length - 1].toLowerCase();
|
||||
};
|
||||
// TODO: default to existing extension, except it would be awkward to rearrange the list...
|
||||
// @TODO: default to existing extension, except it would be awkward to rearrange the list...
|
||||
// const suggestedExtension = getExtension(suggestedFileName);
|
||||
const filters = [
|
||||
// top one is considered default by electron
|
||||
{name: "PNG", extensions: ["png"]},
|
||||
// TODO: enable more formats
|
||||
// @TODO: enable more formats
|
||||
// {name: "Monochrome Bitmap", extensions: ["bmp", "dib"]},
|
||||
// {name: "16 Color Bitmap", extensions: ["bmp", "dib"]},
|
||||
// {name: "256 Color Bitmap", extensions: ["bmp", "dib"]},
|
||||
|
@ -128,8 +128,8 @@ window.systemSaveCanvasAs = (canvas, suggestedFileName, savedCallback) => {
|
|||
// {name: "PNG", extensions: ["png"]},
|
||||
{name: "WebP", extensions: ["webp"]},
|
||||
];
|
||||
// TODO: pass BrowserWindow to make dialog modal?
|
||||
// TODO: should suggestedFileName be sanitized in some way?
|
||||
// @TODO: pass BrowserWindow to make dialog modal?
|
||||
// @TODO: should suggestedFileName be sanitized in some way?
|
||||
dialog.showSaveDialog({
|
||||
defaultPath: suggestedFileName,
|
||||
filters,
|
||||
|
@ -139,7 +139,7 @@ window.systemSaveCanvasAs = (canvas, suggestedFileName, savedCallback) => {
|
|||
}
|
||||
const extension = getExtension(filePath);
|
||||
if(!extension){
|
||||
// TODO: Linux/Unix?? you're not supposed to need file extensions
|
||||
// @TODO: Linux/Unix?? you're not supposed to need file extensions
|
||||
return show_error_message("Missing file extension - try adding .png to the file name");
|
||||
}
|
||||
const formatNameMatched = ((filters.find(({extensions}) => extensions.includes(extension))) || {}).name;
|
||||
|
@ -158,7 +158,7 @@ window.systemSetAsWallpaperCentered = c => {
|
|||
const fs = require("fs");
|
||||
const wallpaper = require("wallpaper");
|
||||
|
||||
// TODO: implement centered option for Windows and Linux in https://www.npmjs.com/package/wallpaper
|
||||
// @TODO: implement centered option for Windows and Linux in https://www.npmjs.com/package/wallpaper
|
||||
// currently it's only supported on macOS
|
||||
let wallpaperCanvas;
|
||||
if(process.platform === "darwin"){
|
||||
|
|
|
@ -36,7 +36,7 @@ const createWindow = () => {
|
|||
},
|
||||
});
|
||||
|
||||
// TODO: maybe use the native menu for the "Modern" theme
|
||||
// @TODO: maybe use the native menu for the "Modern" theme
|
||||
mainWindow.setMenu(null);
|
||||
|
||||
// and load the index.html of the app.
|
||||
|
|
|
@ -8,8 +8,8 @@ extra_tools = [{
|
|||
rendered_size: 0,
|
||||
rendered_shape: "",
|
||||
paint(ctx, x, y) {
|
||||
// XXX: copy pasted all this brush caching/rendering code!
|
||||
// TODO: DRY!
|
||||
// @XXX: copy pasted all this brush caching/rendering code!
|
||||
// @TODO: DRY!
|
||||
const csz = get_brush_canvas_size(brush_size, brush_shape);
|
||||
if(
|
||||
this.rendered_shape !== brush_shape ||
|
||||
|
@ -64,8 +64,8 @@ extra_tools = [{
|
|||
this.velocity.y = 0;
|
||||
},
|
||||
paint(ctx, x, y) {
|
||||
// XXX: copy pasted all this brush caching/rendering code!
|
||||
// TODO: DRY!
|
||||
// @XXX: copy pasted all this brush caching/rendering code!
|
||||
// @TODO: DRY!
|
||||
const csz = get_brush_canvas_size(brush_size, brush_shape);
|
||||
if(
|
||||
this.rendered_shape !== brush_shape ||
|
||||
|
|
|
@ -179,7 +179,7 @@ function show_custom_zoom_window() {
|
|||
const $w = new $FormToolWindow("Custom Zoom");
|
||||
$custom_zoom_window = $w;
|
||||
|
||||
// TODO: show Current zoom: blah% ?
|
||||
// @TODO: show Current zoom: blah% ?
|
||||
const $fieldset = $(E("fieldset")).appendTo($w.$main);
|
||||
$fieldset.append("<legend>Zoom to</legend>");
|
||||
$fieldset.append("<label><input type='radio' name='custom-zoom-radio' value='1'/>100%</label>");
|
||||
|
@ -352,9 +352,9 @@ function create_and_trigger_input(attrs, callback){
|
|||
return $input;
|
||||
}
|
||||
|
||||
// TODO: rename these functions to lowercase (and maybe say "files" in this case)
|
||||
// @TODO: rename these functions to lowercase (and maybe say "files" in this case)
|
||||
function get_FileList_from_file_select_dialog(callback){
|
||||
// TODO: specify mime types?
|
||||
// @TODO: specify mime types?
|
||||
create_and_trigger_input({type: "file"}, input => {
|
||||
callback(input.files);
|
||||
});
|
||||
|
@ -362,7 +362,7 @@ function get_FileList_from_file_select_dialog(callback){
|
|||
|
||||
function open_from_Image(img, callback, canceled){
|
||||
are_you_sure(() => {
|
||||
// TODO: shouldn't open_from_* start a new session?
|
||||
// @TODO: shouldn't open_from_* start a new session?
|
||||
|
||||
deselect();
|
||||
cancel();
|
||||
|
@ -379,7 +379,7 @@ function open_from_Image(img, callback, canceled){
|
|||
|
||||
current_history_node.name = "Load Document";
|
||||
current_history_node.image_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
current_history_node.icon = null; // TODO
|
||||
current_history_node.icon = null; // @TODO
|
||||
|
||||
$G.triggerHandler("session-update"); // autosave
|
||||
$G.triggerHandler("history-update"); // update history view
|
||||
|
@ -407,7 +407,7 @@ function get_URIs(text) {
|
|||
return uris;
|
||||
}
|
||||
function load_image_from_URI(uri, callback){
|
||||
// TODO: if URI is not blob: or data:, show dialog with progress bar and this string from mspaint.exe: "Downloading picture"
|
||||
// @TODO: if URI is not blob: or data:, show dialog with progress bar and this string from mspaint.exe: "Downloading picture"
|
||||
fetch(uri)
|
||||
.then(response => response.blob()).then(blob => {
|
||||
const img = new Image();
|
||||
|
@ -503,7 +503,7 @@ function file_new(){
|
|||
});
|
||||
}
|
||||
|
||||
// TODO: factor out open_select/choose_file_dialog or get_file_from_file_select_dialog or whatever
|
||||
// @TODO: factor out open_select/choose_file_dialog or get_file_from_file_select_dialog or whatever
|
||||
// all these open_from_* things are done backwards, basically
|
||||
// there's this little thing called Inversion of Control...
|
||||
// also paste_from_file_select_dialog
|
||||
|
@ -521,13 +521,13 @@ function file_load_from_url(){
|
|||
const $w = new $FormToolWindow().addClass("dialogue-window");
|
||||
$file_load_from_url_window = $w;
|
||||
$w.title("Load from URL");
|
||||
// TODO: URL validation (input has to be in a form (and we don't want the form to submit))
|
||||
// @TODO: URL validation (input has to be in a form (and we don't want the form to submit))
|
||||
$w.$main.html("<label>URL: <input type='url' required value='' class='url-input'/></label>");
|
||||
const $input = $w.$main.find(".url-input");
|
||||
$w.$Button("Load", () => {
|
||||
const uris = get_URIs($input.val());
|
||||
if (uris.length > 0) {
|
||||
// TODO: retry loading if same URL entered
|
||||
// @TODO: retry loading if same URL entered
|
||||
// actually, make it change the hash only after loading successfully
|
||||
// (but still load from the hash when necessary)
|
||||
// make sure it doesn't overwrite the old session before switching
|
||||
|
@ -547,12 +547,12 @@ function file_load_from_url(){
|
|||
function file_save(){
|
||||
deselect();
|
||||
if(file_name.match(/\.svg$/)){
|
||||
//TODO: only affect suggested name in save dialog, don't change file_name
|
||||
// @TODO: only affect suggested name in save dialog, don't change file_name
|
||||
file_name = `${file_name.replace(/\.svg$/, "")}.png`;
|
||||
return file_save_as();
|
||||
}
|
||||
if(document_file_path){
|
||||
// TODO: save as JPEG by default if the previously opened/saved file was a JPEG?
|
||||
// @TODO: save as JPEG by default if the previously opened/saved file was a JPEG?
|
||||
return save_to_file_path(document_file_path, "PNG", (saved_file_path, saved_file_name) => {
|
||||
saved = true;
|
||||
document_file_path = saved_file_path;
|
||||
|
@ -630,7 +630,7 @@ function show_error_message(message, error){
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: close are_you_sure windows and these Error windows when switching sessions
|
||||
// @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(){
|
||||
// NOTE: apparently distinguishing cross-origin errors is disallowed
|
||||
|
@ -682,7 +682,7 @@ function show_about_paint(){
|
|||
$("#outdated").attr("hidden", "hidden");
|
||||
|
||||
$about_paint_window.center();
|
||||
$about_paint_window.center(); // XXX - but it helps tho
|
||||
$about_paint_window.center(); // @XXX - but it helps tho
|
||||
|
||||
$("#refresh-to-update").on("click", (event)=> {
|
||||
event.preventDefault();
|
||||
|
@ -720,7 +720,7 @@ function show_about_paint(){
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: visibly mark entries that overlap
|
||||
// @TODO: visibly mark entries that overlap
|
||||
entries_newer_than_this_version =
|
||||
$latest_entries.get().filter((el_from_latest)=>
|
||||
!entries_contains_update($this_version_entries, el_from_latest.id)
|
||||
|
@ -785,11 +785,11 @@ function show_news(){
|
|||
$news_window.$content.append($latest_news.removeAttr("hidden"));
|
||||
|
||||
$news_window.center();
|
||||
$news_window.center(); // XXX - but it helps tho
|
||||
$news_window.center(); // @XXX - but it helps tho
|
||||
}
|
||||
|
||||
|
||||
// TODO: DRY between these functions and open_from_* functions further?
|
||||
// @TODO: DRY between these functions and open_from_* functions further?
|
||||
|
||||
// function paste_image_from_URI(uri, callback){
|
||||
// load_image_from_URI(uri, (err, img)=> {
|
||||
|
@ -802,7 +802,7 @@ function paste_image_from_file(file){
|
|||
const blob_url = URL.createObjectURL(file);
|
||||
// paste_image_from_URI(blob_url);
|
||||
load_image_from_URI(blob_url, (err, img) => {
|
||||
// TODO: this shouldn't really have the CORS error message, if it's from a blob URI
|
||||
// @TODO: this shouldn't really have the CORS error message, if it's from a blob URI
|
||||
if(err){ return show_resource_load_error_message(); }
|
||||
paste(img);
|
||||
URL.revokeObjectURL(blob_url);
|
||||
|
@ -981,7 +981,7 @@ function go_to_history_node(target_history_node, canceling) {
|
|||
if (selection) {
|
||||
selection.destroy();
|
||||
}
|
||||
// TODO maybe: could store whether a selection is from Free-Form Select
|
||||
// @TODO maybe: could store whether a selection is from Free-Form Select
|
||||
// so it selects Free-Form Select when you jump to e.g. Move Selection
|
||||
// (or could traverse history to figure it out)
|
||||
if (target_history_node.name === "Free-Form Select") {
|
||||
|
@ -1279,7 +1279,7 @@ function meld_selection_into_canvas(going_to_history_node) {
|
|||
undoable({
|
||||
name: "Deselect",
|
||||
icon: get_icon_for_tool(get_tool_by_name("Select")),
|
||||
use_loose_canvas_changes: true, // HACK; TODO: make OnCanvasSelection not change the canvas outside undoable, same rules as tools
|
||||
use_loose_canvas_changes: true, // HACK; @TODO: make OnCanvasSelection not change the canvas outside undoable, same rules as tools
|
||||
}, ()=> { });
|
||||
}
|
||||
}
|
||||
|
@ -1320,7 +1320,7 @@ function delete_selection(meta={}){
|
|||
undoable({
|
||||
name: meta.name || "Delete",
|
||||
icon: meta.icon || get_help_folder_icon("p_delete.png"),
|
||||
// soft: TODO: conditionally soft?,
|
||||
// soft: @TODO: conditionally soft?,
|
||||
}, ()=> {
|
||||
selection.destroy();
|
||||
selection = null;
|
||||
|
@ -2037,14 +2037,14 @@ function image_stretch_and_skew(){
|
|||
$w.center();
|
||||
}
|
||||
|
||||
// TODO: establish a better pattern for this (platform-specific functions, with browser-generic fallbacks)
|
||||
// @TODO: establish a better pattern for this (platform-specific functions, with browser-generic fallbacks)
|
||||
// Note: we can't just poke in a different save_canvas_as function in electron-injected.js because electron-injected.js is loaded first
|
||||
function save_canvas_as(canvas, fileName, savedCallbackUnreliable){
|
||||
if(window.systemSaveCanvasAs){
|
||||
return systemSaveCanvasAs(canvas, fileName, savedCallbackUnreliable);
|
||||
}
|
||||
|
||||
// TODO: file name + type dialog
|
||||
// @TODO: file name + type dialog
|
||||
canvas.toBlob(blob => {
|
||||
sanity_check_blob(blob, () => {
|
||||
const file_saver = saveAs(blob, `${file_name.replace(/\.(bmp|dib|a?png|gif|jpe?g|jpe|jfif|tiff?|webp|raw)$/, "")}.png`);
|
||||
|
|
10
src/help.js
10
src/help.js
|
@ -100,7 +100,7 @@ function open_help_viewer(options){
|
|||
forward_length -= 1;
|
||||
back_length += 1;
|
||||
}, ()=> forward_length > 0);
|
||||
add_toolbar_button("Options", 3, ()=> {}, ()=> false); // TODO: hotkey and underline on O
|
||||
add_toolbar_button("Options", 3, ()=> {}, ()=> false); // @TODO: hotkey and underline on O
|
||||
add_toolbar_button("Web Help", 4, ()=> {
|
||||
iframe.src = "help/online_support.htm";
|
||||
});
|
||||
|
@ -111,7 +111,7 @@ function open_help_viewer(options){
|
|||
const $resizer = $(E("div")).addClass("resizer");
|
||||
const $contents = $(E("ul")).addClass("contents inset-deep");
|
||||
|
||||
// TODO: fix race conditions
|
||||
// @TODO: fix race conditions
|
||||
$iframe.on("load", ()=> {
|
||||
if (!ignore_one_load) {
|
||||
back_length += 1;
|
||||
|
@ -369,7 +369,7 @@ function $Iframe(options){
|
|||
iframe.contentWindow.close = function(){
|
||||
iframe.$window && iframe.$window.close();
|
||||
};
|
||||
// TODO: hook into saveAs (a la FileSaver.js) and another function for opening files
|
||||
// @TODO: hook into saveAs (a la FileSaver.js) and another function for opening files
|
||||
// iframe.contentWindow.saveAs = function(){
|
||||
// saveAsDialog();
|
||||
// };
|
||||
|
@ -395,7 +395,7 @@ function $Iframe(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?
|
||||
// // @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;
|
||||
|
@ -428,7 +428,7 @@ function $Iframe(options){
|
|||
// flexDirection: "column",
|
||||
// });
|
||||
|
||||
// // TODO: cascade windows
|
||||
// // @TODO: cascade windows
|
||||
// $win.center();
|
||||
// $win.hide();
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ function make_canvas(width, height){
|
|||
new_ctx.imageSmoothingEnabled = true;
|
||||
};
|
||||
|
||||
// TODO: simplify the abstraction by defining setters for width/height
|
||||
// @TODO: simplify the abstraction by defining setters for width/height
|
||||
// that reset the image smoothing to disabled
|
||||
// and make image smoothing a parameter to make_canvas
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
function get_brush_canvas_size(brush_size, brush_shape){
|
||||
// brush_shape optional, only matters if it's circle
|
||||
// TODO: does it actually still matter? the ellipse drawing code has changed
|
||||
// @TODO: does it actually still matter? the ellipse drawing code has changed
|
||||
|
||||
// round to nearest even number in order for the canvas to be drawn centered at a point reasonably
|
||||
return Math.ceil(brush_size * (brush_shape === "circle" ? 2.1 : 1) / 2) * 2;
|
||||
|
@ -20,7 +20,7 @@ function render_brush(ctx, shape, size){
|
|||
const bottom = Math.round(mid_y + size/2);
|
||||
|
||||
if(shape === "circle"){
|
||||
// TODO: ideally _without_pattern_support
|
||||
// @TODO: ideally _without_pattern_support
|
||||
draw_ellipse(ctx, left, top, size, size, false, true);
|
||||
// was useful for testing:
|
||||
// ctx.fillStyle = "red";
|
||||
|
@ -117,7 +117,7 @@ function draw_rounded_rectangle(ctx, x, y, width, height, radius_x, radius_y, st
|
|||
}
|
||||
|
||||
// USAGE NOTE: must be called outside of any other usage of op_canvas (because of render_brush)
|
||||
// TODO: protect against browser clearing canvases, invalidate cache
|
||||
// @TODO: protect against browser clearing canvases, invalidate cache
|
||||
const get_brush_canvas = memoize_synchronous_function((brush_shape, brush_size)=> {
|
||||
const canvas_size = get_brush_canvas_size(brush_size, brush_shape);
|
||||
|
||||
|
@ -258,10 +258,10 @@ function brosandham_line(x1, y1, x2, y2, callback){
|
|||
|
||||
function draw_fill_without_pattern_support(ctx, start_x, start_y, fill_r, fill_g, fill_b, fill_a){
|
||||
|
||||
// TODO: split up processing in case it takes too long?
|
||||
// @TODO: split up processing in case it takes too long?
|
||||
// progress bar and abort button (outside of image-manipulation.js)
|
||||
// or at least just free up the main thread every once in a while
|
||||
// TODO: speed up with typed arrays? https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/
|
||||
// @TODO: speed up with typed arrays? https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/
|
||||
// could avoid endianness issues if only copying colors
|
||||
// the jsperf only shows ~15% improvement
|
||||
// maybe do something fancier like special-casing large chunks of single-color image
|
||||
|
@ -797,7 +797,7 @@ function draw_bezier_curve_without_pattern_support(ctx, start_x, start_y, contro
|
|||
let point_a = {x: start_x, y: start_y};
|
||||
for(let t=0; t<1; t+=1/steps){
|
||||
const point_b = compute_bezier(t, start_x, start_y, control_1_x, control_1_y, control_2_x, control_2_y, end_x, end_y);
|
||||
// TODO: carry "error" from Bresenham line algorithm between iterations? and/or get a proper Bezier drawing algorithm
|
||||
// @TODO: carry "error" from Bresenham line algorithm between iterations? and/or get a proper Bezier drawing algorithm
|
||||
draw_line_without_pattern_support(ctx, point_a.x, point_a.y, point_b.x, point_b.y, stroke_size);
|
||||
point_a = point_b;
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ function draw_line(ctx, x1, y1, x2, y2, stroke_size){
|
|||
|
||||
let grid_pattern;
|
||||
function draw_grid(ctx, scale) {
|
||||
const pattern_size = Math.floor(scale); // TODO: try ceil too
|
||||
const pattern_size = Math.floor(scale); // @TODO: try ceil too
|
||||
if (!grid_pattern || grid_pattern.width !== pattern_size || grid_pattern.height !== pattern_size) {
|
||||
const grid_pattern_canvas = make_canvas(pattern_size, pattern_size);
|
||||
const dark_gray = "#808080";
|
||||
|
@ -852,7 +852,7 @@ function draw_grid(ctx, scale) {
|
|||
if (scale !== pattern_size) {
|
||||
ctx.translate(-0.5, -0.75); // hand picked to look "good" at 110% in chrome
|
||||
// might be better to just hide the grid in some more cases tho
|
||||
// ...TODO: if I can get helper layer to be pixel aligned, I can probably remove this
|
||||
// ...@TODO: if I can get helper layer to be pixel aligned, I can probably remove this
|
||||
}
|
||||
ctx.scale(scale / pattern_size, scale / pattern_size);
|
||||
ctx.enable_image_smoothing();
|
||||
|
@ -1161,7 +1161,7 @@ function draw_grid(ctx, scale) {
|
|||
for (let i = 0; i < numPoints; i++) {
|
||||
coords[i*2+0] = (points[i].x - x_min) / op_canvas_webgl.width * 2 - 1;
|
||||
coords[i*2+1] = 1 - (points[i].y - y_min) / op_canvas_webgl.height * 2;
|
||||
// TODO: investigate: does this cause resolution/information loss? can we change the coordinate system?
|
||||
// @TODO: investigate: does this cause resolution/information loss? can we change the coordinate system?
|
||||
}
|
||||
|
||||
if(fill){
|
||||
|
@ -1225,7 +1225,7 @@ function draw_grid(ctx, scale) {
|
|||
const width = x_max - x_min;
|
||||
const height = y_max - y_min;
|
||||
|
||||
// TODO: maybe have the cutout only the width/height of the bounds
|
||||
// @TODO: maybe have the cutout only the width/height of the bounds
|
||||
// const cutout = make_canvas(width, height);
|
||||
const cutout = make_canvas(canvas);
|
||||
|
||||
|
@ -1240,7 +1240,7 @@ function draw_grid(ctx, scale) {
|
|||
return cutout_crop;
|
||||
}
|
||||
|
||||
// TODO: maybe shouldn't be external...
|
||||
// @TODO: maybe shouldn't be external...
|
||||
window.draw_with_swatch = (ctx, x_min, y_min, x_max, y_max, swatch, callback) => {
|
||||
const stroke_margin = ~~(stroke_size * 1.1);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ function show_imgur_uploader(blob){
|
|||
const $imgur_url_area = $(E("div")).appendTo($imgur_window.$main);
|
||||
const $imgur_status = $(E("div")).appendTo($imgur_window.$main);
|
||||
|
||||
// TODO: maybe make this preview small but zoomable to full size?
|
||||
// @TODO: maybe make this preview small but zoomable to full size?
|
||||
// (starting small (max-width: 100%) and toggling to either scrollable or fullscreen)
|
||||
// it should be clear that it's not going to upload a downsized version of your image
|
||||
const $preview_image = $(E("img")).appendTo($preview_image_area);
|
||||
|
@ -31,7 +31,7 @@ function show_imgur_uploader(blob){
|
|||
|
||||
$preview_image_area.remove();
|
||||
$upload_button.remove();
|
||||
$cancel_button.remove(); // TODO: allow canceling upload request
|
||||
$cancel_button.remove(); // @TODO: allow canceling upload request
|
||||
|
||||
$imgur_window.width(300);
|
||||
$imgur_window.center();
|
||||
|
@ -54,7 +54,7 @@ function show_imgur_uploader(blob){
|
|||
// show_error_message("Received an invalid JSON response from Imgur: ", responseJSON, but also error);
|
||||
// $imgur_window.close();
|
||||
|
||||
// TODO: DRY, including with show_error_message
|
||||
// @TODO: DRY, including with show_error_message
|
||||
$(E("pre"))
|
||||
.appendTo($imgur_status)
|
||||
.text(responseJSON)
|
||||
|
@ -117,7 +117,7 @@ function show_imgur_uploader(blob){
|
|||
$imgur_url_area.append(
|
||||
"<label>URL: </label>"
|
||||
).append($imgur_url);
|
||||
// TODO: a button to copy the URL to the clipboard
|
||||
// @TODO: a button to copy the URL to the clipboard
|
||||
// (also maybe put the URL in a readonly input)
|
||||
|
||||
const $delete_button = $imgur_window.$Button("Delete", () => {
|
||||
|
|
|
@ -94,8 +94,8 @@ function manage_storage(){
|
|||
}
|
||||
|
||||
if (!localStorageAvailable) {
|
||||
// TODO: DRY with similar message
|
||||
// TODO: instructions for your browser; it's called Cookies in chrome/chromium at least, and "storage" gives NO results
|
||||
// @TODO: DRY with similar message
|
||||
// @TODO: instructions for your browser; it's called Cookies in chrome/chromium at least, and "storage" gives NO results
|
||||
$message.html("<p>Please enable local storage in your browser's settings for local backup. It may be called Cookies, Storage, or Site Data.</p>");
|
||||
} else if($table.find("tr").length == 0) {
|
||||
$message.html("<p>All clear!</p>");
|
||||
|
|
12
src/menus.js
12
src/menus.js
|
@ -137,7 +137,7 @@ window.menus = {
|
|||
item: "Cu&t",
|
||||
shortcut: "Ctrl+X",
|
||||
enabled: () =>
|
||||
// TODO: support cutting text with this menu item as well (e.g. for the text tool)
|
||||
// @TODO: support cutting text with this menu item as well (e.g. for the text tool)
|
||||
!!selection,
|
||||
action: ()=> {
|
||||
edit_cut(true);
|
||||
|
@ -148,7 +148,7 @@ window.menus = {
|
|||
item: "&Copy",
|
||||
shortcut: "Ctrl+C",
|
||||
enabled: () =>
|
||||
// TODO: support copying text with this menu item as well (e.g. for the text tool)
|
||||
// @TODO: support copying text with this menu item as well (e.g. for the text tool)
|
||||
!!selection,
|
||||
action: ()=> {
|
||||
edit_copy(true);
|
||||
|
@ -159,7 +159,7 @@ window.menus = {
|
|||
item: "&Paste",
|
||||
shortcut: "Ctrl+V",
|
||||
enabled: () =>
|
||||
// TODO: disable if nothing in clipboard or wrong type (if we can access that)
|
||||
// @TODO: disable if nothing in clipboard or wrong type (if we can access that)
|
||||
true,
|
||||
action: ()=> {
|
||||
edit_paste(true);
|
||||
|
@ -467,7 +467,7 @@ window.menus = {
|
|||
{
|
||||
item: "New &Blank Session",
|
||||
action: ()=> {
|
||||
// TODO: load new empty session in the same browser tab
|
||||
// @TODO: load new empty session in the same browser tab
|
||||
let name = prompt("Enter the session name that will be used in the URL for sharing.");
|
||||
if(typeof name == "string"){
|
||||
name = name.trim();
|
||||
|
@ -518,7 +518,7 @@ window.menus = {
|
|||
checkbox: {
|
||||
toggle: ()=> {
|
||||
if (location.hash.match(/eye-gaze-mode/i)) {
|
||||
// TODO: confirmation dialog that you could cancel with dwell clicking!
|
||||
// @TODO: confirmation dialog that you could cancel with dwell clicking!
|
||||
// if (confirm("This will disable eye gaze mode.")) {
|
||||
location.hash = location.hash.replace(/eye-gaze-mode,|,eye-gaze-mode/i, "");
|
||||
// }
|
||||
|
@ -537,7 +537,7 @@ window.menus = {
|
|||
checkbox: {
|
||||
toggle: ()=> {
|
||||
if (location.hash.match(/eye-gaze-mode/i)) {
|
||||
// TODO: confirmation dialog that you could cancel with dwell clicking!
|
||||
// @TODO: confirmation dialog that you could cancel with dwell clicking!
|
||||
// if (confirm("This will disable eye gaze mode.")) {
|
||||
// location.hash = location.hash.replace(/eye-gaze-mode,|,eye-gaze-mode/i, "");
|
||||
// location.hash = location.hash.replace(/vertical-color-box-mode,|,vertical-color-box-mode/i, "");
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
else {
|
||||
// e.g. localStorage is disabled
|
||||
// (or there's some other error?)
|
||||
// TODO: show warning with "Don't tell me again" type option
|
||||
// @TODO: show warning with "Don't tell me again" type option
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -123,7 +123,7 @@
|
|||
show_error_message("Failed to retrieve image from local storage:", err);
|
||||
}
|
||||
else {
|
||||
// TODO: DRY with storage manager message
|
||||
// @TODO: DRY with storage manager message
|
||||
show_error_message("Please enable local storage in your browser's settings for local backup. It may be called Cookies, Storage, or Site Data.");
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,7 @@
|
|||
user.color = `hsla(${user.hue}, ${user.saturation}%, ${user.lightness}%, 1)`;
|
||||
// Unused
|
||||
user.color_transparent = `hsla(${user.hue}, ${user.saturation}%, ${user.lightness}%, 0.5)`;
|
||||
// (@TODO) The color used in the toolbar indicating to other users it is selected by this user
|
||||
// (@TODO) The color (that may be) used in the toolbar indicating to other users it is selected by this user
|
||||
user.color_desaturated = `hsla(${user.hue}, ${~~(user.saturation*0.4)}%, ${user.lightness}%, 0.8)`;
|
||||
|
||||
|
||||
|
@ -226,7 +226,7 @@
|
|||
}
|
||||
}
|
||||
start() {
|
||||
// TODO: how do you actually detect if it's failing???
|
||||
// @TODO: how do you actually detect if it's failing???
|
||||
const $w = $FormToolWindow().title("Warning").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>"
|
||||
|
@ -537,14 +537,14 @@
|
|||
}
|
||||
end_current_session();
|
||||
|
||||
// TODO: fix loading duplicately, from popstate and hashchange
|
||||
// @TODO: fix loading duplicately, from popstate and hashchange
|
||||
open_from_URI(url, err => {
|
||||
if(err){
|
||||
show_resource_load_error_message();
|
||||
}
|
||||
// TODO: saved = false;?
|
||||
// @TODO: saved = false;?
|
||||
// NOTE: the following is intended to run regardless of error (as opposed to returning if there's an error)
|
||||
// FIXME: race condition (make the timeout long and try to fix it with a flag or something )
|
||||
// @FIXME: race condition (make the timeout long and try to fix it with a flag or something )
|
||||
setTimeout(() => {
|
||||
// NOTE: this "change" event doesn't *guarantee* there was a change :/
|
||||
// let alone that there was a user interaction with the currently loaded document
|
||||
|
|
|
@ -197,7 +197,7 @@ window.simulateRandomGesturesPeriodically = () => {
|
|||
});
|
||||
};
|
||||
let waitThenGo = () => {
|
||||
// TODO: a button to stop it as well (maybe make "stop drawing randomly" a link button?)
|
||||
// @TODO: a button to stop it as well (maybe make "stop drawing randomly" a link button?)
|
||||
$status_text.text("Press Esc to stop drawing randomly.");
|
||||
if (isAnyMenuOpen()) {
|
||||
periodicGesturesTimeoutID = setTimeout(waitThenGo, 50);
|
||||
|
@ -218,7 +218,7 @@ window.simulateRandomGesturesPeriodically = () => {
|
|||
$(choose($(".tool-options *"))).trigger("click");
|
||||
}
|
||||
if (seededRandom() < pickColorChance) {
|
||||
// TODO: maybe these should respond to a normal click?
|
||||
// @TODO: maybe these should respond to a normal click?
|
||||
let secondary = seededRandom() < 0.5;
|
||||
const colorButton = choose($(".swatch, .color-button"));
|
||||
$(colorButton)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// TODO: remove remaining cruft from being compiled from CoffeeScript
|
||||
// @TODO: remove remaining cruft from being compiled from CoffeeScript
|
||||
// or maybe replace this module with localforage actually
|
||||
// (but need to address asynchronous concerns if doing that)
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ const $Choose = (things, display, choose, is_chosen) => {
|
|||
return option_canvas;
|
||||
};
|
||||
const update = () => {
|
||||
const selected_color = get_theme() === "modern.css" ? "#0178d7" : "#000080"; // TODO: get from a CSS variable
|
||||
const selected_color = get_theme() === "modern.css" ? "#0178d7" : "#000080"; // @TODO: get from a CSS variable
|
||||
$option_container.css({
|
||||
backgroundColor: is_chosen(thing) ? selected_color : ""
|
||||
});
|
||||
|
|
10
src/tools.js
10
src/tools.js
|
@ -718,7 +718,7 @@ window.tools = [{
|
|||
ctx.fillRect(x, y, w, h);
|
||||
ctx.restore();
|
||||
}else{
|
||||
// TODO: shouldn't that be ~~(stroke_size / 2)?
|
||||
// @TODO: shouldn't that be ~~(stroke_size / 2)?
|
||||
ctx.strokeRect(x + stroke_size / 2, y + stroke_size / 2, w - stroke_size, h - stroke_size);
|
||||
}
|
||||
}
|
||||
|
@ -758,7 +758,7 @@ window.tools = [{
|
|||
}
|
||||
}
|
||||
}else{
|
||||
if(d < stroke_size * 5.1010101){ // arbitrary 101 (TODO: find correct value (or formula))
|
||||
if(d < stroke_size * 5.1010101){ // arbitrary number (@TODO: find correct value (or formula))
|
||||
this.complete(ctx);
|
||||
}
|
||||
}
|
||||
|
@ -784,7 +784,7 @@ window.tools = [{
|
|||
const dy = y - ly;
|
||||
const dt = +(new Date) - lt;
|
||||
const d = Math.sqrt(dx*dx + dy*dy);
|
||||
if(d < 4.1010101 && dt < 250){ // arbitrary 101 (TODO: find correct value (or formula))
|
||||
if(d < 4.1010101 && dt < 250){ // arbitrary 101 (@TODO: find correct value (or formula))
|
||||
this.complete(ctx);
|
||||
}else{
|
||||
// Add the point
|
||||
|
@ -1102,7 +1102,7 @@ tools.forEach((tool)=> {
|
|||
gradient.addColorStop(6/n, "gold");
|
||||
color = gradient;
|
||||
}
|
||||
// TODO: perf: keep this canvas around too
|
||||
// @TODO: perf: keep this canvas around too
|
||||
const mask_fill_canvas = make_canvas(tool.mask_canvas);
|
||||
replace_colors_with_swatch(mask_fill_canvas.ctx, color, 0, 0);
|
||||
ctx.drawImage(mask_fill_canvas, 0, 0);
|
||||
|
@ -1199,7 +1199,7 @@ tools.forEach((tool)=> {
|
|||
gradient.addColorStop(6/n, "gold");
|
||||
color = gradient;
|
||||
}
|
||||
// TODO: perf: keep this canvas around too
|
||||
// @TODO: perf: keep this canvas around too
|
||||
const mask_fill_canvas = make_canvas(tool.mask_canvas);
|
||||
if (previewing && tool.dynamic_preview_cursor) {
|
||||
const brush = tool.get_brush();
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
filter:
|
||||
`hue-rotate(${
|
||||
Math.sin(Date.now() / 4000)
|
||||
// TODO: slow down and stop when you pause
|
||||
// @TODO: slow down and stop when you pause
|
||||
}turn)`,
|
||||
});
|
||||
|
||||
|
@ -102,7 +102,7 @@
|
|||
}turn)`,
|
||||
transformOrigin: "50% 50%",
|
||||
transformStyle: "preserve-3d",
|
||||
// FIXME: interactivity problems (with order elements are considered to have), I think related to preserve-3d
|
||||
// @FIXME: interactivity problems (with order elements are considered to have), I think related to preserve-3d
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -112,7 +112,7 @@
|
|||
document.querySelector(".canvas-area").appendChild(player_placeholder);
|
||||
$(player_placeholder).css({
|
||||
position: "absolute",
|
||||
top: "3px", // TODO: dynamic
|
||||
top: "3px", // @TODO: dynamic
|
||||
left: "3px",
|
||||
mixBlendMode: "multiply",
|
||||
pointerEvents: "none",
|
||||
|
@ -137,7 +137,7 @@
|
|||
onStateChange: onPlayerStateChange,
|
||||
},
|
||||
});
|
||||
// TODO: attribution for this video!
|
||||
// @TODO: attribution for this video!
|
||||
// I mean, you can see the title if you hit spacebar, but
|
||||
// I could make it wave across the screen behind Paint on the desktop
|
||||
// I could add a "Song Name?" button that responds "Darude Sandstorm"
|
||||
|
@ -154,7 +154,7 @@
|
|||
// The API calls this function when the player's state changes.
|
||||
function onPlayerStateChange(event) {
|
||||
if (event.data == YT.PlayerState.PLAYING) {
|
||||
// TODO: pause and resume this timer with the video
|
||||
// @TODO: pause and resume this timer with the video
|
||||
setTimeout(() => {
|
||||
$(rotologo).css({opacity: 1});
|
||||
}, 14150);
|
||||
|
@ -162,7 +162,7 @@
|
|||
if (event.data == YT.PlayerState.ENDED) {
|
||||
player.destroy();
|
||||
player = null;
|
||||
// TODO: fade to white instead of black, to work with the multiply effect
|
||||
// @TODO: fade to white instead of black, to work with the multiply effect
|
||||
// or fade out opacity alternatively
|
||||
// setTimeout/setInterval and check player.getCurrentTime() for when near the end?
|
||||
// or we might switch to using soundcloud for the audio and so trigger it with that, with a separate video of just clouds
|
||||
|
@ -177,7 +177,7 @@
|
|||
space_phase_key_handler = e => {
|
||||
// press space to phase in and out of space phase スペース相 - windows 98 マイクロソフト 『WINTRAP』 X 将来のオペレーティングシステムサウンド 1998 VAPORWAVE
|
||||
if (e.which === 32) {
|
||||
// TODO: record player SFX
|
||||
// @TODO: record player SFX
|
||||
if (is_theoretically_playing) {
|
||||
player.pauseVideo();
|
||||
is_theoretically_playing = false;
|
||||
|
|
|
@ -90,7 +90,7 @@ html, body, .jspaint {
|
|||
}
|
||||
.selection,
|
||||
.textbox {
|
||||
display: block !important; /* TODO: reduce overzealous display: flex; */
|
||||
display: block !important; /* @TODO: reduce overzealous display: flex; */
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ html, body, .jspaint {
|
|||
.textbox > canvas,
|
||||
.selection > img,
|
||||
.selection > canvas {
|
||||
/* TODO: maybe don't include the canvas in the DOM (is it helpful to inspect it tho? it's not critical...) */
|
||||
/* @TODO: maybe don't include the canvas in the DOM (is it helpful to inspect it tho? it's not critical...) */
|
||||
opacity: 0;
|
||||
}
|
||||
.selection > img,
|
||||
|
@ -390,7 +390,7 @@ html, body, .jspaint {
|
|||
.handle::after {
|
||||
/* make handles way easier to grab */
|
||||
content: "";
|
||||
pointer-events: all; /* TODO: maybe don't have a blanket pointer-events: none; on pseudo elements */
|
||||
pointer-events: all; /* @TODO: maybe don't have a blanket pointer-events: none; on pseudo elements */
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
|
@ -409,7 +409,7 @@ html, body, .jspaint {
|
|||
which makes it more reasonable to have the border be a draggable thing.
|
||||
I'm making the draggable area outside the border for now. */
|
||||
content: "";
|
||||
pointer-events: all; /* TODO: maybe don't have a blanket pointer-events: none; on pseudo elements */
|
||||
pointer-events: all; /* @TODO: maybe don't have a blanket pointer-events: none; on pseudo elements */
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -10px;
|
||||
|
@ -460,12 +460,12 @@ html, body, .jspaint {
|
|||
width: 200px;
|
||||
}
|
||||
|
||||
/* TODO: part of os-gui */
|
||||
/* @TODO: part of os-gui */
|
||||
.os-window {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* TODO: part of os-gui */
|
||||
/* @TODO: part of os-gui */
|
||||
.os-window .window-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
@ -631,7 +631,7 @@ html, body, .jspaint {
|
|||
margin-right: 8px;
|
||||
}
|
||||
#jspaint-version {
|
||||
/* TODO: separate into a shared.css? (not really layout!) */
|
||||
/* @TODO: separate into a shared.css? (not really layout!) */
|
||||
font-size: 0.6em;
|
||||
color: #7b7b7b;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ body,
|
|||
background: var(--Hilight);
|
||||
}
|
||||
.useless-handle {
|
||||
background: var(--HilightText); /* TODO: --ButtonHilight? --Window? */
|
||||
background: var(--HilightText); /* @TODO: --ButtonHilight? --Window? */
|
||||
box-shadow: 1px 1px 0 var(--Hilight) inset;
|
||||
}
|
||||
.resize-ghost {
|
||||
|
@ -164,14 +164,14 @@ body,
|
|||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
/* TODO: ButtonAlternateFace? */
|
||||
/* @TODO: ButtonAlternateFace? */
|
||||
border: 1px solid var(--ButtonFace);
|
||||
}
|
||||
.current-colors,
|
||||
.color-button {
|
||||
border-top: 1px solid var(--ButtonShadow);
|
||||
border-left: 1px solid var(--ButtonShadow);
|
||||
/* TODO: var(--ButtonAlternateFace)? */
|
||||
/* @TODO: var(--ButtonAlternateFace)? */
|
||||
border-right: 1px solid var(--ButtonFace);
|
||||
border-bottom: 1px solid var(--ButtonFace);
|
||||
/*box-shadow: 1px 1px 0px var(--ButtonDkShadow) inset;*/
|
||||
|
@ -332,7 +332,7 @@ body,
|
|||
|
||||
input[type=text],
|
||||
input[type=url] {
|
||||
/* TODO: fancy 3d inset border (might need a surrounding element because pseudo elements won't work with input) */
|
||||
/* @TODO: fancy 3d inset border (might need a surrounding element because pseudo elements won't work with input) */
|
||||
/* I guess an image border could work; maybe I should be using (svg) image borders for stuff */
|
||||
/* (css inset border style doesn't look good and isn't consistent between browsers) */
|
||||
border: 1px solid gray;
|
||||
|
|
|
@ -205,7 +205,7 @@ body {
|
|||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5) inset;
|
||||
}
|
||||
|
||||
/* TODO: padding/margin on the top at least when in the sidebar */
|
||||
/* @TODO: padding/margin on the top at least when in the sidebar */
|
||||
.tools {
|
||||
width: 50px;
|
||||
}
|
||||
|
@ -241,8 +241,8 @@ body,
|
|||
}
|
||||
|
||||
/* NOTE: copy/pasted from classic.css */
|
||||
/* TODO: actually show a preview of the component itself when dragging for the modern theme */
|
||||
/* TODO: cursor maybe? just when already dragging? */
|
||||
/* @TODO: actually show a preview of the component itself when dragging for the modern theme */
|
||||
/* @TODO: cursor maybe? just when already dragging? */
|
||||
.component-ghost.dock {
|
||||
outline: 1px solid black;
|
||||
/*outline: 1px solid invert;*/
|
||||
|
@ -270,7 +270,7 @@ body,
|
|||
}
|
||||
.help-window iframe {
|
||||
border: 0;
|
||||
/* TODO ideally, apply a padding: 16px on the body in the iframe, except for on the landing page (which has a background) */
|
||||
/* @TODO ideally, apply a padding: 16px on the body in the iframe, except for on the landing page (which has a background) */
|
||||
}
|
||||
.help-window ul.contents {
|
||||
padding: 16px;
|
||||
|
@ -287,7 +287,7 @@ body,
|
|||
cursor: pointer;
|
||||
}
|
||||
.help-window li:before {
|
||||
/* TODO: more modern icons */
|
||||
/* @TODO: more modern icons */
|
||||
background-image: url("../../images/help-icons.png");
|
||||
}
|
||||
.help-window .item.selected {
|
||||
|
|
|
@ -18,6 +18,6 @@
|
|||
}
|
||||
|
||||
.menu-hotkey {
|
||||
/* TODO: when actually supporting menu navigation hotkeys, enable if accessing the menus with the keyboard */
|
||||
/* @TODO: when actually supporting menu navigation hotkeys, enable if accessing the menus with the keyboard */
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue