parent
d044c9be57
commit
4624f13c50
7
TODO.md
7
TODO.md
|
@ -3,6 +3,7 @@
|
|||
|
||||
* Visual
|
||||
* Warning sign for "Save changes to X?" dialogue
|
||||
* Error symbol for error message dialogues
|
||||
* The window close button uses text; font rendering is not consistent
|
||||
* The menus use text; the arrow character is converted to an icon on some mobile devices
|
||||
* The progress bar (Rendering GIF) is left native
|
||||
|
@ -16,7 +17,6 @@
|
|||
|
||||
|
||||
* Issues
|
||||
* Address `localStorage` quota limits
|
||||
* If you open an image it resets the zoom but if you're on the magnification tool it doesn't update the options
|
||||
* If you zoom in with the magnifier without previously changing the magnification on the toolbar,
|
||||
then switch back to the magnifier, the toolbar doesn't show any magnification highlighted
|
||||
|
@ -243,6 +243,11 @@ Prankily wait for next user input before fullscreening and bluescreening
|
|||
* Basic things that people would complain about
|
||||
|
||||
|
||||
* Save/manage application state
|
||||
* On restart, reopen images from storage
|
||||
* On close / Exit, ask to save, remove image from storage
|
||||
|
||||
|
||||
### Also
|
||||
|
||||
* Anything marked `@TODO` or `@FIXME` in the source code
|
||||
|
|
|
@ -446,4 +446,4 @@
|
|||
}
|
||||
.help-window .page:before {
|
||||
background-position: -32px 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,19 +17,13 @@
|
|||
<body>
|
||||
<script src="lib/jquery.min.js"></script>
|
||||
<script src="lib/pep.js"></script>
|
||||
<script>
|
||||
$.event.props.push("button", "buttons", "clientX", "clientY", "offsetX", "offsetY", "pageX", "pageY", "screenX", "screenY", "toElement");
|
||||
$.event.props.push("pointerType", "pointerId", "width", "height", "pressure", "tiltX", "tiltY", "hwTimestamp", "isPrimary");
|
||||
</script>
|
||||
<script src="lib/canvas.toBlob.js"></script>
|
||||
<script src="lib/gif.js/gif.js"></script>
|
||||
<script src="lib/palette.js"></script>
|
||||
<script src="lib/FileSaver.js"></script>
|
||||
<script src="lib/font-detective.js"></script>
|
||||
<script>
|
||||
FontDetective.swf = "./lib/FontList.swf"
|
||||
</script>
|
||||
<script src="src/helpers.js"></script>
|
||||
<script src="src/storage.js"></script>
|
||||
<script src="src/$Component.js"></script>
|
||||
<script src="src/$Window.js"></script>
|
||||
<script src="src/$ToolBox.js"></script>
|
||||
|
@ -46,6 +40,7 @@
|
|||
<script src="src/menus.js"></script>
|
||||
<script src="src/app.js"></script>
|
||||
<script src="src/$menus.js"></script>
|
||||
<script src="src/manage-storage.js"></script>
|
||||
<script src="src/canvas-change.js"></script>
|
||||
<script src="src/multiplayer.js"></script> <!-- @TODO: rename -->
|
||||
</body>
|
||||
|
|
|
@ -235,8 +235,9 @@ html, body, .jspaint {
|
|||
flex-flow: column;
|
||||
}
|
||||
.jspaint-window-content .jspaint-button-group > button {
|
||||
width: 80px;
|
||||
min-width: 80px;
|
||||
padding: 3px 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.jspaint-window-content > form {
|
||||
display: flex;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
}
|
||||
},
|
||||
"permissions": [
|
||||
"storage",
|
||||
"fullscreen",
|
||||
"clipboardRead",
|
||||
"clipboardWrite",
|
||||
|
|
38
src/app.js
38
src/app.js
|
@ -9,14 +9,6 @@ var default_canvas_height = 384;
|
|||
var my_canvas_width = default_canvas_width;
|
||||
var my_canvas_height = default_canvas_height;
|
||||
|
||||
try{
|
||||
// @TODO support for chrome app where only chrome.storage is available
|
||||
if(localStorage){
|
||||
my_canvas_width = Number(localStorage.width) || default_canvas_width;
|
||||
my_canvas_height = Number(localStorage.height) || default_canvas_height;
|
||||
}
|
||||
}catch(e){}
|
||||
|
||||
var palette = [
|
||||
"#000000","#787878","#790300","#757A01","#007902","#007778","#0A0078","#7B0077","#767A38","#003637","#286FFE","#083178","#4C00FE","#783B00",
|
||||
"#FFFFFF","#BBBBBB","#FF0E00","#FAFF08","#00FF0B","#00FEFF","#3400FE","#FF00FE","#FBFF7A","#00FF7B","#76FEFF","#8270FE","#FF0677","#FF7D36",
|
||||
|
@ -101,9 +93,7 @@ $canvas.on("user-resized", function(e, _x, _y, width, height){
|
|||
undoable(0, function(){
|
||||
canvas.width = Math.max(1, width);
|
||||
canvas.height = Math.max(1, height);
|
||||
if(transparency){
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}else{
|
||||
if(!transparency){
|
||||
ctx.fillStyle = colors.background;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
@ -115,15 +105,31 @@ $canvas.on("user-resized", function(e, _x, _y, width, height){
|
|||
|
||||
$canvas.trigger("update"); // update handles
|
||||
|
||||
try{
|
||||
localStorage.width = canvas.width;
|
||||
localStorage.height = canvas.height;
|
||||
}catch(e){
|
||||
storage.set({
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
}, function(err){
|
||||
// oh well
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
storage.get({
|
||||
width: default_canvas_width,
|
||||
height: default_canvas_height,
|
||||
}, function(err, values){
|
||||
if(err){return;}
|
||||
my_canvas_width = values.width;
|
||||
my_canvas_height = values.height;
|
||||
canvas.width = Math.max(1, my_canvas_width);
|
||||
canvas.height = Math.max(1, my_canvas_height);
|
||||
if(!transparency){
|
||||
ctx.fillStyle = colors.background;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
$canvas.trigger("update"); // update handles
|
||||
});
|
||||
|
||||
$("body").on("dragover dragenter", function(e){
|
||||
if(
|
||||
e.target instanceof HTMLInputElement ||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
|
||||
// make jQuery play well with PEP
|
||||
$.event.props.push("button", "buttons", "clientX", "clientY", "offsetX", "offsetY", "pageX", "pageY", "screenX", "screenY", "toElement");
|
||||
$.event.props.push("pointerType", "pointerId", "width", "height", "pressure", "tiltX", "tiltY", "hwTimestamp", "isPrimary");
|
||||
|
||||
// configure Font Detective
|
||||
FontDetective.swf = "./lib/FontList.swf";
|
||||
|
||||
|
||||
var TAU = //////|//////
|
||||
///// | /////
|
||||
/// tau ///
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
var $storage_manager;
|
||||
var $quota_exceeded_window;
|
||||
var ignoring_quota_exceeded = false;
|
||||
|
||||
function storage_quota_exceeded(){
|
||||
if($quota_exceeded_window){
|
||||
$quota_exceeded_window.close();
|
||||
$quota_exceeded_window = null;
|
||||
}
|
||||
if(ignoring_quota_exceeded){
|
||||
return;
|
||||
}
|
||||
var $w = $FormWindow().title("Storage Error").addClass("jspaint-dialogue-window");
|
||||
$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("View and manage storage", function(){
|
||||
$w.close();
|
||||
ignoring_quota_exceeded = false;
|
||||
manage_storage();
|
||||
});
|
||||
$w.$Button("Ignore", function(){
|
||||
$w.close();
|
||||
ignoring_quota_exceeded = true;
|
||||
});
|
||||
$w.width(500);
|
||||
$w.center();
|
||||
$quota_exceeded_window = $w;
|
||||
}
|
||||
|
||||
function manage_storage(){
|
||||
if($storage_manager){
|
||||
$storage_manager.close();
|
||||
}
|
||||
$storage_manager = $Window().title("Manage Storage");
|
||||
$storage_manager.$content.html(
|
||||
"<p>Any images you've saved to your computer with <b>File > Save</b> will not be affected.</p>"
|
||||
);
|
||||
$table = $(E("table")).appendTo($storage_manager.$content);
|
||||
|
||||
var addRowFor = function(k, imgSrc){
|
||||
$tr = $(E("tr")).appendTo($table);
|
||||
|
||||
$img = $(E("img")).attr({src: imgSrc});
|
||||
$img.css({maxWidth: "50px", maxHeight: "50px"});
|
||||
$remove = $(E("button")).addClass("jspaint-button jspaint-dialogue-button").text("Remove");
|
||||
|
||||
$(E("td")).text(k).appendTo($tr);
|
||||
$(E("td")).append($img).appendTo($tr);
|
||||
$(E("td")).append($remove).appendTo($tr);
|
||||
|
||||
$remove.click(function(){
|
||||
localStorage.removeItem(k);
|
||||
$tr.remove();
|
||||
});
|
||||
};
|
||||
|
||||
for(k in localStorage){
|
||||
if(k.match(/^image#/)){
|
||||
var v = localStorage[k];
|
||||
addRowFor(k, v[0] === '"' ? JSON.parse(v) : v);
|
||||
}
|
||||
}
|
||||
$storage_manager.width(500);
|
||||
$storage_manager.center();
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
(function(){
|
||||
|
||||
var debug = function(text){
|
||||
if(console){
|
||||
if(typeof console !== "undefined"){
|
||||
console.log(text);
|
||||
}
|
||||
};
|
||||
|
@ -14,23 +14,37 @@
|
|||
|
||||
var LocalSession = function(session_id){
|
||||
var lsid = "image#" + session_id;
|
||||
debug("localStorage id: "+lsid);
|
||||
debug("local storage id: " + lsid);
|
||||
|
||||
var uri = localStorage[lsid];
|
||||
if(uri){
|
||||
open_from_URI(uri, function(){
|
||||
saved = false; // it's safe, sure, but you haven't "Saved" it
|
||||
});
|
||||
}
|
||||
storage.get(lsid, function(err, uri){
|
||||
if(err){
|
||||
$w = $FormWindow().title("Error").addClass("jspaint-dialogue-window");
|
||||
$w.$main.text("Error retrieving image from localStorage:");
|
||||
$(E("pre")).text(err.toString()).appendTo($w.$main);
|
||||
$w.$Button("OK", function(){
|
||||
$w.close();
|
||||
});
|
||||
}else if(uri){
|
||||
open_from_URI(uri, function(){
|
||||
saved = false; // it's safe, sure, but you haven't "Saved" it
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$canvas.on("change.session-hook", function(){
|
||||
localStorage[lsid] = canvas.toDataURL("image/png");
|
||||
storage.set(lsid, canvas.toDataURL("image/png"), function(err){
|
||||
if(err){
|
||||
if(err.quotaExceeded){
|
||||
storage_quota_exceeded();
|
||||
}else{
|
||||
// e.g. localStorage is disabled
|
||||
// (or there's some other error?)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LocalSession.prototype.end = function(){
|
||||
//?
|
||||
// @TODO: clean up localStorage sessions (when?)
|
||||
|
||||
// Remove session-related hooks
|
||||
$app.find("*").off(".session-hook");
|
||||
$G.off(".session-hook");
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
// Generated by CoffeeScript 1.9.2
|
||||
(function() {
|
||||
var chromeStore, isArray, localStore, ref, ref1;
|
||||
|
||||
isArray = (ref = Array.isArray) != null ? ref : function(a) {
|
||||
return ("" + a) !== a && {}.toString.call(a) === "[object Array]";
|
||||
};
|
||||
|
||||
localStore = {
|
||||
get: function(key, callback) {
|
||||
var defaultValue, err, i, item, len, obj, ref1, ref2;
|
||||
try {
|
||||
if (typeof key === "string") {
|
||||
item = localStorage.getItem(key);
|
||||
if (item) {
|
||||
obj = JSON.parse(item);
|
||||
}
|
||||
} else {
|
||||
obj = {};
|
||||
if (isArray(arguments[0])) {
|
||||
ref1 = arguments[0];
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
key = ref1[i];
|
||||
item = localStorage.getItem(key);
|
||||
if (item) {
|
||||
obj[key] = JSON.parse(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ref2 = arguments[0];
|
||||
for (key in ref2) {
|
||||
defaultValue = ref2[key];
|
||||
item = localStorage.getItem(key);
|
||||
if (item) {
|
||||
obj[key] = JSON.parse(item);
|
||||
} else {
|
||||
obj[key] = defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
callback(null, obj);
|
||||
},
|
||||
set: function(key, value, callback) {
|
||||
var err, obj1, to_set;
|
||||
to_set = {};
|
||||
if (typeof arguments[0] === "string") {
|
||||
to_set = (
|
||||
obj1 = {},
|
||||
obj1["" + key] = value,
|
||||
obj1
|
||||
);
|
||||
} else if (isArray(arguments[0])) {
|
||||
throw new TypeError("Cannot set an array of keys (to what?)");
|
||||
} else {
|
||||
to_set = arguments[0], callback = arguments[1];
|
||||
}
|
||||
for (key in to_set) {
|
||||
value = to_set[key];
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
err.quotaExceeded = err.code === 22 || err.name === "NS_ERROR_DOM_QUOTA_REACHED" || err.number === -2147024882;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return callback(null);
|
||||
}
|
||||
};
|
||||
|
||||
chromeStore = {
|
||||
get: function(key, callback) {
|
||||
console.log('GET', key);
|
||||
return chrome.storage.local.get(key, function(values) {
|
||||
if (chrome.runtime.lastError) {
|
||||
callback(chrome.runtime.lastError);
|
||||
return;
|
||||
}
|
||||
console.log('GOT', key, values);
|
||||
if (typeof key === "string") {
|
||||
return callback(null, values[key]);
|
||||
} else {
|
||||
return callback(null, values);
|
||||
}
|
||||
});
|
||||
},
|
||||
set: function(key, value, callback) {
|
||||
var obj1, to_set;
|
||||
if (arguments.length < 3) {
|
||||
to_set = arguments[0], callback = arguments[1];
|
||||
} else {
|
||||
to_set = (
|
||||
obj1 = {},
|
||||
obj1["" + key] = value,
|
||||
obj1
|
||||
);
|
||||
}
|
||||
console.log('SET', to_set);
|
||||
return chrome.storage.local.set(to_set, function() {
|
||||
return callback(chrome.runtime.lastError);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.storage = ((ref1 = window.chrome) != null ? ref1.storage : void 0) != null ? chromeStore : localStore;
|
||||
|
||||
}).call(this);
|
Loading…
Reference in New Issue