Compare commits
179 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
21b8bdd0f3 | ||
![]() |
881d2f1100 | ||
![]() |
117d094df9 | ||
![]() |
ae8b2ade39 | ||
![]() |
6943dc3ebe | ||
![]() |
cb41a72b5b | ||
![]() |
17b1712b82 | ||
![]() |
24b28504a1 | ||
![]() |
3e926ff093 | ||
![]() |
b9d4c5c2a6 | ||
![]() |
c13acba6b1 | ||
![]() |
cf92045489 | ||
![]() |
025c34d07c | ||
![]() |
eb14e489a7 | ||
![]() |
ef945ef8a6 | ||
![]() |
c5c8184ae3 | ||
![]() |
66452de1e5 | ||
![]() |
fa62ae511e | ||
![]() |
f106909150 | ||
![]() |
b54efbde21 | ||
![]() |
5ebf83badf | ||
![]() |
3804afbeb5 | ||
![]() |
0a64d62b84 | ||
![]() |
af9095b934 | ||
![]() |
65f4fd0f27 | ||
![]() |
5e364e984c | ||
![]() |
5a0b6b90e4 | ||
![]() |
717e8dd52a | ||
![]() |
bd0478fb35 | ||
![]() |
2bbbfd1219 | ||
![]() |
da4156912e | ||
![]() |
99c060f4a7 | ||
![]() |
52e49a1ed6 | ||
![]() |
6acfa2256c | ||
![]() |
89629d2939 | ||
![]() |
e5d89104a1 | ||
![]() |
6e84f0a2b5 | ||
![]() |
ba0e86dc7e | ||
![]() |
27497313ff | ||
![]() |
8662ab65ad | ||
![]() |
594d748146 | ||
![]() |
379c61a11d | ||
![]() |
230a38bf07 | ||
![]() |
0e5d74e1d8 | ||
![]() |
f462cd3b70 | ||
![]() |
45d2245346 | ||
![]() |
e5e85f67c2 | ||
![]() |
4836251bd3 | ||
![]() |
e3182504dd | ||
![]() |
34a88b79a0 | ||
![]() |
7b8978da7a | ||
![]() |
1c3b359b5c | ||
![]() |
5c4cbbbba1 | ||
![]() |
b9cc2eb4f8 | ||
![]() |
ac13cf13c1 | ||
![]() |
32600f9e66 | ||
![]() |
39eb0d3143 | ||
![]() |
6e53bce2cf | ||
![]() |
090acee11a | ||
![]() |
a1bb5f3b97 | ||
![]() |
f69e502639 | ||
![]() |
027cd2e2a6 | ||
![]() |
2726af2d81 | ||
![]() |
22cec2e8c7 | ||
![]() |
57ee8e4365 | ||
![]() |
a9cdf98281 | ||
![]() |
0a9e7dcffb | ||
![]() |
fe3f6996f1 | ||
![]() |
9d32a8c3aa | ||
![]() |
64cd724139 | ||
![]() |
dae08107e2 | ||
![]() |
779761628e | ||
![]() |
3e450c3d77 | ||
![]() |
5c7070b01b | ||
![]() |
7c215ebcbe | ||
![]() |
bcee24609f | ||
![]() |
f420c6f1fd | ||
![]() |
76429dfd86 | ||
![]() |
4ebf43fda4 | ||
![]() |
2f2b4cd9ba | ||
![]() |
dfb049bbf0 | ||
![]() |
8c54108a9b | ||
![]() |
3dd72f9781 | ||
![]() |
80001eab0e | ||
![]() |
5ecf351e0f | ||
![]() |
407b432227 | ||
![]() |
69cc27557e | ||
![]() |
cfd3773a2b | ||
![]() |
0eface45f1 | ||
![]() |
87893bb4ac | ||
![]() |
77d26bffa9 | ||
![]() |
652027bd3f | ||
![]() |
bf4cc3302a | ||
![]() |
95c8df1224 | ||
![]() |
7445357368 | ||
![]() |
a2369cac0c | ||
![]() |
51538dff48 | ||
![]() |
da739e78da | ||
![]() |
dd8217e21b | ||
![]() |
d502d3416b | ||
![]() |
d1156954ca | ||
![]() |
dc5209628c | ||
![]() |
8568663949 | ||
![]() |
fd3d828067 | ||
![]() |
e1797b2008 | ||
![]() |
0a43f6bbec | ||
![]() |
b9423bc831 | ||
![]() |
5e6280301d | ||
![]() |
5671eb4782 | ||
![]() |
35788b54ba | ||
![]() |
629ecf83b4 | ||
![]() |
c037b07693 | ||
![]() |
c31b7a351c | ||
![]() |
7de03f1e73 | ||
![]() |
eab21e0839 | ||
![]() |
2b3bd02479 | ||
![]() |
4e86fa1570 | ||
![]() |
170a7e4731 | ||
![]() |
6b7f04b63e | ||
![]() |
da2e9f99e4 | ||
![]() |
530a949e54 | ||
![]() |
4377c9e601 | ||
![]() |
e0bbb88d47 | ||
![]() |
9ff2ecbb45 | ||
![]() |
8beba2088b | ||
![]() |
ee45cdcc45 | ||
![]() |
30ea7fa079 | ||
![]() |
e9b39a5c61 | ||
![]() |
d0a32b18c5 | ||
![]() |
372ad1f513 | ||
![]() |
c6e106fe2d | ||
![]() |
f9570ea3c5 | ||
![]() |
f9cb631acb | ||
![]() |
ed749a747f | ||
![]() |
30ecd41452 | ||
![]() |
af65344c23 | ||
![]() |
183133496e | ||
![]() |
8a2c0191f9 | ||
![]() |
a096dcabfd | ||
![]() |
96d326ef12 | ||
![]() |
7c37372b13 | ||
![]() |
b21ea30fa8 | ||
![]() |
c2dbddcf9f | ||
![]() |
09ce6ff88f | ||
![]() |
2c4a8efb44 | ||
![]() |
02a25d3f84 | ||
![]() |
d159de2e65 | ||
![]() |
726a8f74c1 | ||
![]() |
89a65ab032 | ||
![]() |
d8ec58b42c | ||
![]() |
1168870ee0 | ||
![]() |
d3a37c74e9 | ||
![]() |
5d2ca7e70c | ||
![]() |
2976fd09ea | ||
![]() |
317fda83c3 | ||
![]() |
94160d8fc4 | ||
![]() |
b977a146e9 | ||
![]() |
aea4e4d6a6 | ||
![]() |
5456ea973a | ||
![]() |
a299d9aed0 | ||
![]() |
cc2fc48107 | ||
![]() |
799c9fbf5a | ||
![]() |
a9e22535d6 | ||
![]() |
4b4cbe47c8 | ||
![]() |
e7d07c5353 | ||
![]() |
cf3383722a | ||
![]() |
6566ca07a5 | ||
![]() |
9fafa8b7a7 | ||
![]() |
4a9f7cc74b | ||
![]() |
319060beb6 | ||
![]() |
729c9f4732 | ||
![]() |
cd560012e1 | ||
![]() |
e819503cc5 | ||
![]() |
6512c3dcc0 | ||
![]() |
3535dfb25e | ||
![]() |
a69554f6c9 | ||
![]() |
1040cb4d8c | ||
![]() |
d18b85df16 | ||
![]() |
76563bfc41 |
2
.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Exclude libs.
|
||||
**/lib/**/*.js
|
6
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
# mac artefacts
|
||||
# mac artifacts
|
||||
*.DS_Store
|
||||
|
||||
# nodejs local installs
|
||||
|
@ -15,10 +15,12 @@ cache
|
|||
# netbeans project folder
|
||||
nbproject
|
||||
|
||||
# vscode workspace folder
|
||||
.vscode
|
||||
|
||||
# git stackdumps
|
||||
*.stackdump
|
||||
|
||||
|
||||
# diffs
|
||||
diff.txt
|
||||
|
||||
|
|
75
.jscsrc
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"requireCurlyBraces": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
"requireOperatorBeforeLineBreak": true,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
||||
"maximumLineLength": {
|
||||
"value": 80,
|
||||
"allExcept": ["comments", "regex"]
|
||||
},
|
||||
"validateIndentation": 2,
|
||||
"validateQuoteMarks": "'",
|
||||
|
||||
"disallowMultipleLineStrings": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"disallowSpaceAfterPrefixUnaryOperators": true,
|
||||
"disallowMultipleVarDecl": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
|
||||
"requireSpaceAfterKeywords": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"switch",
|
||||
"return",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
"requireSpaceBeforeBinaryOperators": [
|
||||
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
|
||||
"&=", "|=", "^=", "+=",
|
||||
|
||||
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
|
||||
"|", "^", "&&", "||", "===", "==", ">=",
|
||||
"<=", "<", ">", "!=", "!=="
|
||||
],
|
||||
"requireSpaceAfterBinaryOperators": true,
|
||||
"requireSpacesInConditionalExpression": true,
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireSpacesInForStatement": true,
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInAnonymousFunctionExpression": {
|
||||
"beforeOpeningRoundBrace": false
|
||||
},
|
||||
"disallowSpacesInsideObjectBrackets": "all",
|
||||
"disallowSpacesInsideArrayBrackets": "all",
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowNewlineBeforeBlockStatements": true,
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowSpacesInFunctionExpression": null,
|
||||
"disallowSpacesInFunctionDeclaration": null,
|
||||
"disallowSpacesInCallExpression": true,
|
||||
"disallowSpaceAfterObjectKeys": false,
|
||||
"requireSpaceBeforeObjectValues": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireDotNotation": true,
|
||||
"requireSemicolons": true,
|
||||
"validateParameterSeparator": ", ",
|
||||
|
||||
"jsDoc": null
|
||||
}
|
|
@ -1,14 +1,9 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "4.1"
|
||||
- "7.4.0"
|
||||
before_install:
|
||||
- npm update -g npm
|
||||
- npm install -g grunt-cli
|
||||
- git clone git://github.com/n1k0/casperjs.git ~/casperjs
|
||||
- cd ~/casperjs
|
||||
- git checkout tags/1.1.3
|
||||
- export PATH=$PATH:`pwd`/bin
|
||||
- cd -
|
||||
before_script:
|
||||
- phantomjs --version
|
||||
- casperjs --version
|
||||
|
|
82
Gruntfile.js
|
@ -47,16 +47,13 @@ module.exports = function(grunt) {
|
|||
var integrationTestPaths = require('./test/casperjs/integration/IntegrationSuite.js').tests;
|
||||
var integrationTests = prefixPaths(integrationTestPaths, "test/casperjs/integration/");
|
||||
|
||||
var getConnectConfig = function (base, port, host) {
|
||||
if (typeof base === 'string') {
|
||||
base = [base];
|
||||
}
|
||||
|
||||
var getConnectConfig = function (base, port, host, open) {
|
||||
return {
|
||||
options: {
|
||||
port: port,
|
||||
hostname : host,
|
||||
base: base
|
||||
base: base,
|
||||
open: open
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -83,35 +80,17 @@ module.exports = function(grunt) {
|
|||
css : ['src/css/**/*.css']
|
||||
},
|
||||
|
||||
jscs : {
|
||||
options : {
|
||||
"config": ".jscsrc",
|
||||
"maximumLineLength": 120,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
|
||||
"validateQuoteMarks": { "mark": "'", "escape": true },
|
||||
"disallowMultipleVarDecl": "exceptUndefined",
|
||||
"disallowSpacesInAnonymousFunctionExpression": null
|
||||
},
|
||||
js : [ 'src/js/**/*.js' , '!src/js/**/lib/**/*.js' ]
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
undef : true,
|
||||
latedef : true,
|
||||
browser : true,
|
||||
trailing : true,
|
||||
curly : true,
|
||||
globals : {'$':true, 'jQuery' : true, 'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true, 'Q':true, 'Promise': true}
|
||||
},
|
||||
eslint: {
|
||||
files: [
|
||||
// Includes
|
||||
'Gruntfile.js',
|
||||
'package.json',
|
||||
'src/js/**/*.js',
|
||||
// Excludes
|
||||
// Exludes
|
||||
// TODO: remove this (for now we still get warnings from the lib folder)
|
||||
'!src/js/**/lib/**/*.js'
|
||||
]
|
||||
],
|
||||
options: {
|
||||
fix: grunt.option('fix') // this will get params from the flags
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -119,18 +98,9 @@ module.exports = function(grunt) {
|
|||
*/
|
||||
|
||||
connect: {
|
||||
prod: getConnectConfig('dest/prod', PORT.PROD, hostname),
|
||||
test: getConnectConfig(['dest/dev', 'test'], PORT.TEST, hostname),
|
||||
dev: getConnectConfig(['dest/dev', 'test'], PORT.DEV, hostname)
|
||||
},
|
||||
|
||||
open : {
|
||||
prod : {
|
||||
path : 'http://' + hostname + ':' + PORT.PROD + '/'
|
||||
},
|
||||
dev : {
|
||||
path : 'http://' + hostname + ':' + PORT.DEV + '/?debug'
|
||||
}
|
||||
prod: getConnectConfig('dest/prod', PORT.PROD, hostname, true),
|
||||
test: getConnectConfig(['dest/dev', 'test'], PORT.TEST, hostname, false),
|
||||
dev: getConnectConfig(['dest/dev', 'test'], PORT.DEV, hostname, 'http://' + hostname + ':' + PORT.DEV + '/?debug')
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -238,20 +208,6 @@ module.exports = function(grunt) {
|
|||
src: ['dest/tmp/css/piskel-style-packaged' + version + '.css'],
|
||||
dest: 'dest/prod/css/piskel-style-packaged' + version + '.css'
|
||||
}]
|
||||
},
|
||||
// remove the fake header from the desktop build
|
||||
desktop: {
|
||||
options: {
|
||||
patterns: [{
|
||||
match: /<!--standalone-start-->(?:.|[\r\n])*<!--standalone-end-->/,
|
||||
replacement: "",
|
||||
description : "Remove everything between standalone-start & standalone-end"
|
||||
}
|
||||
]
|
||||
},
|
||||
files: [
|
||||
{src: ['dest/prod/index.html'], dest: 'dest/prod/index.html'}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -351,11 +307,13 @@ module.exports = function(grunt) {
|
|||
|
||||
// TEST TASKS
|
||||
// Run linting
|
||||
grunt.registerTask('lint', ['jscs:js', 'leadingIndent:css', 'jshint']);
|
||||
grunt.registerTask('lint', ['eslint', 'leadingIndent:css']);
|
||||
// Run unit-tests
|
||||
grunt.registerTask('unit-test', ['karma']);
|
||||
// Run integration tests
|
||||
grunt.registerTask('integration-test', ['build-dev', 'connect:test', 'casperjs:integration']);
|
||||
// Run drawing tests
|
||||
grunt.registerTask('drawing-test', ['build-dev', 'connect:test', 'casperjs:drawing']);
|
||||
// Run linting, unit tests, drawing tests and integration tests
|
||||
grunt.registerTask('test', ['lint', 'unit-test', 'build-dev', 'connect:test', 'casperjs:drawing', 'casperjs:integration']);
|
||||
|
||||
|
@ -370,15 +328,15 @@ module.exports = function(grunt) {
|
|||
grunt.registerTask('merge-statics', ['concat:js', 'concat:css', 'uglify']);
|
||||
grunt.registerTask('build', ['clean:prod', 'sprite', 'merge-statics', 'build-index.html', 'replace:mainPartial', 'replace:css', 'copy:prod']);
|
||||
grunt.registerTask('build-dev', ['clean:dev', 'sprite', 'build-index.html', 'copy:dev']);
|
||||
grunt.registerTask('desktop', ['clean:desktop', 'default', 'replace:desktop', 'nwjs:windows']);
|
||||
grunt.registerTask('desktop-mac', ['clean:desktop', 'default', 'replace:desktop', 'nwjs:macos']);
|
||||
grunt.registerTask('desktop', ['clean:desktop', 'default', 'nwjs:windows']);
|
||||
grunt.registerTask('desktop-mac', ['clean:desktop', 'default', 'nwjs:macos']);
|
||||
grunt.registerTask('desktop-mac-old', ['clean:desktop', 'default', 'replace:desktop', 'nwjs:macos_old']);
|
||||
|
||||
// SERVER TASKS
|
||||
// Start webserver and watch for changes
|
||||
grunt.registerTask('serve', ['build', 'connect:prod', 'open:prod', 'watch:prod']);
|
||||
grunt.registerTask('serve', ['build', 'connect:prod', 'watch:prod']);
|
||||
// Start webserver on src folder, in debug mode
|
||||
grunt.registerTask('play', ['build-dev', 'connect:dev', 'open:dev', 'watch:dev']);
|
||||
grunt.registerTask('play', ['build-dev', 'connect:dev', 'watch:dev']);
|
||||
|
||||
// ALIASES, kept for backward compatibility
|
||||
grunt.registerTask('serve-debug', ['play']);
|
||||
|
|
82
README.md
|
@ -1,75 +1,61 @@
|
|||
Piskel
|
||||
======
|
||||
|
||||
[![Travis Status](https://api.travis-ci.org/juliandescottes/piskel.png?branch=master)](https://travis-ci.org/juliandescottes/piskel) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)
|
||||
[![Travis Status](https://api.travis-ci.org/piskelapp/piskel.png?branch=master)](https://travis-ci.org/piskelapp/piskel) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](https://gruntjs.com/)
|
||||
|
||||
A simple web-based tool for Spriting and Pixel art.
|
||||
Piskel is an easy-to-use sprite editor. It can be used to create game sprites, animations, pixel-art...
|
||||
It is the editor used in **[piskelapp.com](https://www.piskelapp.com)**.
|
||||
|
||||
![Piskel editor screenshot](https://screenletstore.appspot.com/img/8f03e768-ac59-11e3-b2a1-7f5a1b97c420.jpeg "Piskel editor screenshot")
|
||||
<img
|
||||
src="https://screenletstore.appspot.com/img/95aaa0f0-37a4-11e7-a652-7b8128ce3e3b.png"
|
||||
title="Piskel editor screenshot"
|
||||
width="500">
|
||||
|
||||
You can try the standalone editor at **http://juliandescottes.github.io/piskel** or see it integrated in **http://piskelapp.com**.
|
||||
## About Piskel
|
||||
|
||||
Piskel is mainly developped by :
|
||||
### Built with
|
||||
|
||||
* **[@juliandescottes](https://github.com/juliandescottes)**
|
||||
* **[@grosbouddha](https://github.com/grosbouddha)**
|
||||
|
||||
## What's the point ?
|
||||
|
||||
You can use Piskel to do two things :
|
||||
* **spriting** : create retro-style sprites for games
|
||||
|
||||
![Megaman spritesheet](http://piskel-imgstore-a.appspot.com/img/c8081287-ac58-11e3-bd8c-b3c4036c0eee.png "Megaman spritesheet")
|
||||
|
||||
* **pixelart** : create crazy/pretty pixelart animations for fun !
|
||||
|
||||
![Rabbit jumping](http://piskel-imgstore-a.appspot.com/img/947f2dab-ac58-11e3-949a-b3c4036c0eee.gif "Rabit jumping")
|
||||
|
||||
Integrated in **[piskelapp.com](http://piskelapp.com)**, you can share everything you work on with others as easily as you share a link.
|
||||
|
||||
## Requirements
|
||||
|
||||
Piskel supports the following browsers :
|
||||
* **Chrome** (latest)
|
||||
* **Firefox** (latest)
|
||||
* **Internet Explorer** 11+
|
||||
|
||||
... and a fairly recent computer.
|
||||
|
||||
We don't plan/want/could be forced into supporting older IEs. For Opera and Safari, we've never tested them but the gap shouldn't be huge.
|
||||
|
||||
## Offline version
|
||||
|
||||
Offline builds are available. More details in the [dedicated wiki page](https://github.com/juliandescottes/piskel/wiki/Desktop-applications).
|
||||
|
||||
## Built with
|
||||
|
||||
The Piskel editor is purely built in **JavaScript, HTML and CSS**. It uses Canvas extensively for displaying all them pretty sprites.
|
||||
The Piskel editor is purely built in **JavaScript, HTML and CSS**.
|
||||
|
||||
We also use the following **libraries** :
|
||||
* [spectrum](https://github.com/bgrins/spectrum) : awesome standalone colorpicker
|
||||
* [gifjs](http://jnordberg.github.io/gif.js/) : generate animated GIFs in javascript, using webworkers
|
||||
* [gifjs](https://jnordberg.github.io/gif.js/) : generate animated GIFs in javascript, using webworkers
|
||||
* [supergif](https://github.com/buzzfeed/libgif-js) : modified version of SuperGif to parse and import GIFs
|
||||
* [jszip](https://github.com/Stuk/jszip) : create, read and edit .zip files with Javascript
|
||||
* [canvas-toBlob](https://github.com/eligrey/canvas-toBlob.js/) : shim for canvas toBlob
|
||||
* [jquery](http://jquery.com/) : used sporadically in the application
|
||||
* [bootstrap-tooltip](http://getbootstrap.com/javascript/#tooltips) : nice tooltips
|
||||
* [jquery](https://jquery.com/) : used sporadically in the application
|
||||
* [bootstrap-tooltip](https://getbootstrap.com/javascript/#tooltips) : nice tooltips
|
||||
|
||||
As well as some **icons** from the [Noun Project](http://thenounproject.com/) :
|
||||
As well as some **icons** from the [Noun Project](https://thenounproject.com/) :
|
||||
* Folder by Simple Icons from The Noun Project
|
||||
* (and probably one or two others)
|
||||
|
||||
### Browser Support
|
||||
|
||||
Piskel supports the following browsers:
|
||||
* **Chrome** (latest)
|
||||
* **Firefox** (latest)
|
||||
* **Edge** (latest)
|
||||
* **Internet Explorer** 11
|
||||
|
||||
### Mobile/Tablets
|
||||
|
||||
There is no support for mobile.
|
||||
|
||||
### Offline builds
|
||||
|
||||
Offline builds are available. More details in the [dedicated wiki page](https://github.com/piskelapp/piskel/wiki/Desktop-applications).
|
||||
|
||||
## Contributing ?
|
||||
|
||||
Help is always welcome !
|
||||
|
||||
* **Issues** : Found a problem when using the application, want to request a feature, [open an issue](https://github.com/juliandescottes/piskel/issues).
|
||||
* **Participate** : Have a look at the [wiki](https://github.com/juliandescottes/piskel/wiki) to set up the development environment
|
||||
* **Issues** : Found a problem when using the application, want to request a feature, [open an issue](https://github.com/piskelapp/piskel/issues).
|
||||
* **Development** : Have a look at the [wiki](https://github.com/piskelapp/piskel/wiki) to set up the development environment
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2016 Julian Descottes
|
||||
Copyright 2017 Julian Descottes
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -83,7 +69,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Mobile/Tablets
|
||||
|
||||
There is no support for mobile for now.
|
||||
|
||||
|
|
32
RELEASE.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Release instructions
|
||||
|
||||
- retrieve source code and dependencies
|
||||
- clone piskel `git clone https://github.com/piskelapp/piskel`
|
||||
- clone piskel website `git clone https://github.com/piskelapp/piskel-website`
|
||||
- download the appengine SDK for python at [this url](https://cloud.google.com/appengine/docs/standard/python/download). It seems recent versions don't work for me on Windows. [GoogleAppEngine-1.9.67.msi](https://storage.cloud.google.com/appengine-sdks/featured/GoogleAppEngine-1.9.67.msi?_ga=2.6838962.-1257793585.1543157659) looks ok...
|
||||
- prepare the release
|
||||
- in `piskel-website` clone, checkout the `master` branch, not the `release`. The release branch is similar to the master branch, except it doesn't ignore the statics files coming from the `piskel` project. The idea is that we can easily checkout the release branch to do a new release, without having to rebuild piskel, in case nothing changed in piskel itself. We will switch to the release branch when we perform the final release.
|
||||
- in `piskel` clone, create a new branch for the release `git checkout -b vX.Y`
|
||||
- build and copy piskel to piskel-website (assumes the clones are in the same folder) `grunt && node bin/copy-to-piskel-website.js`
|
||||
- local test
|
||||
- start piskel-website in the Google app engine launcher, test manually the update
|
||||
- if any issue is detected create a commit to fix it (on the `vX.Y` branch) and push it.
|
||||
|
||||
- beta test
|
||||
- check the version in app.yaml is set to 'beta'. For historical reasons, alpha is the "production" version, while beta is the ... beta version. Don't ask.
|
||||
- in the google app engine launcher, deploy this version.
|
||||
- go to https://beta-dot-piskel-app.appspot.com and test (while this is not production frontend, it shares the same database, so be careful with what you create and delete)
|
||||
- if any issue is detected create a commit to fix it (on `master`) and push it.
|
||||
- check caches...
|
||||
- If any icon was modified or any script of piskel-website was modified, there's no nice way to clean the cache on each release... so we just put a time stamp in the requests. eg `background-image: url(../img/icons@2x.png?20181125);` or `<script type="text/javascript" src="/static/js/piskel-animated-preview.js?20181124"></script>`. Erk.
|
||||
- finalize the release
|
||||
- in `piskel` update version in package.json. Remove the "-SNAPSHOT" at the end and commit the change, with a commit message such as `release: bump version to vX.Y`.
|
||||
- in `piskel-website` checkout the `release` branch. Rebase it on master (that's pretty bad, we should probably cherry pick all the changes between the two branches...)
|
||||
- once again build and copy piskel to piskel-website
|
||||
- in `piskel-website` run `git status` to check that there are a bunch of changes to commit. (test locally again if you are paranoid)
|
||||
- in `piskel-website` commit the current changes with a commit message such as `update statics for release vX.Y`
|
||||
- Check that the version in app.yaml is now set to alpha. Deploy. Pray.
|
||||
- post release
|
||||
- in piskel-website push the release branch to github
|
||||
- in piskel add a last commit to update the version in package.json to update the version to `vX+1.0-SNAPSHOT`
|
||||
- create a tag on github
|
|
@ -17,7 +17,16 @@ function onCopy(err) {
|
|||
|
||||
console.log('Copied static files to piskel-website...');
|
||||
let previousPartialPath = path.resolve(PISKELAPP_PATH, 'templates/editor/main-partial.html');
|
||||
fs.unlink(previousPartialPath, onDeletePreviousPartial);
|
||||
fs.access(previousPartialPath, fs.constants.F_OK, function (err) {
|
||||
if (err) {
|
||||
// File does not exit, call next step directly.
|
||||
console.error('Previous main partial doesn\'t exist yet.');
|
||||
onDeletePreviousPartial();
|
||||
} else {
|
||||
// File exists, try to delete it before moving on.
|
||||
fs.unlink(previousPartialPath, onDeletePreviousPartial);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onDeletePreviousPartial(err) {
|
||||
|
|
64
cli/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Piskel CLI
|
||||
|
||||
Wraps the Piskel pixel editing application to enable similar export options via the command line.
|
||||
|
||||
## Installation
|
||||
|
||||
Option 1: Globally install Piskel
|
||||
```
|
||||
npm install -g https://github.com/piskelapp/piskel/tarball/master
|
||||
```
|
||||
|
||||
Option 2: Clone and install Piskel normally and then run npm link inside the installation root
|
||||
|
||||
## Usage
|
||||
|
||||
**Export provided .piskel file as a png sprite sheet using app defaults**
|
||||
```
|
||||
piskel-cli snow-monster.piskel
|
||||
```
|
||||
|
||||
**Export scaled sprite sheet**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --scale 5
|
||||
```
|
||||
|
||||
**Export scaled to specific (single frame) width value**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --scaledWidth 435
|
||||
```
|
||||
|
||||
**Export scaled to specific (single frame) height value**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --scaledHeight 435
|
||||
```
|
||||
|
||||
**Export sprite sheet as a single column**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --columns 1
|
||||
```
|
||||
|
||||
**Export sprite sheet as a single row**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --rows 1
|
||||
```
|
||||
|
||||
**Export a single frame (0 is first frame)**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --frame 3
|
||||
```
|
||||
|
||||
**Export a second file containing the data-uri for the exported png**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --dataUri
|
||||
```
|
||||
|
||||
**Export cropped**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --crop
|
||||
```
|
||||
|
||||
**Custom output path and/or filename**
|
||||
```
|
||||
piskel-cli snow-monster.piskel --dest ./output-folder/snah-monstah.png
|
||||
```
|
130
cli/export-png.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
const fs = require('fs');
|
||||
|
||||
function onPageEvaluate(window, options, piskel) {
|
||||
console.log("\nPiskel name: " + piskel.descriptor.name);
|
||||
|
||||
// Setup piskelController
|
||||
var piskelController = new pskl.controller.piskel.PiskelController(piskel);
|
||||
|
||||
pskl.app.piskelController = piskelController;
|
||||
|
||||
piskelController.init();
|
||||
|
||||
// Apply crop if enabled
|
||||
if (options.crop) {
|
||||
// Mock selection manager to avoid errors during crop
|
||||
pskl.app.selectionManager = {};
|
||||
|
||||
// Setup crop tool
|
||||
var crop = new pskl.tools.transform.Crop();
|
||||
|
||||
// Perform crop
|
||||
crop.applyTransformation();
|
||||
|
||||
// Get cropped piskel
|
||||
piskel = piskelController.getPiskel();
|
||||
}
|
||||
|
||||
// Mock exportController to provide zoom value based on cli args
|
||||
// and to avoid errors and/or unnecessary bootstrapping
|
||||
var exportController = {
|
||||
getExportZoom: function () {
|
||||
var zoom = options.zoom;
|
||||
|
||||
if (options.scaledWidth) {
|
||||
zoom = options.scaledWidth / piskel.getWidth();
|
||||
} else if (options.scaledHeight) {
|
||||
zoom = options.scaledHeight / piskel.getHeight();
|
||||
}
|
||||
|
||||
return zoom;
|
||||
}
|
||||
};
|
||||
|
||||
// Setup pngExportController
|
||||
var pngExportController = new pskl.controller.settings.exportimage.PngExportController(piskelController, exportController);
|
||||
|
||||
// Mock getColumns and getRows to use values from cli arguments
|
||||
pngExportController.getColumns_ = function () {
|
||||
if (options.columns) return options.columns;
|
||||
|
||||
if (options.rows) {
|
||||
return Math.ceil(piskelController.getFrameCount() / pngExportController.getRows_());
|
||||
} else {
|
||||
return pngExportController.getBestFit_();
|
||||
}
|
||||
};
|
||||
|
||||
pngExportController.getRows_ = function () {
|
||||
if (options.columns && !options.rows) {
|
||||
return Math.ceil(piskelController.getFrameCount() / pngExportController.getColumns_());
|
||||
}
|
||||
|
||||
return options.rows;
|
||||
};
|
||||
|
||||
// Render to output canvas
|
||||
var canvas;
|
||||
|
||||
if (options.frame > -1) {
|
||||
// Render a single frame
|
||||
canvas = piskelController.renderFrameAt(options.frame, true);
|
||||
|
||||
var zoom = exportController.getExportZoom();
|
||||
|
||||
if (zoom != 1) {
|
||||
// Scale rendered frame
|
||||
canvas = pskl.utils.ImageResizer.resize(canvas, canvas.width * zoom, canvas.height * zoom, false);
|
||||
}
|
||||
} else {
|
||||
// Render the sprite sheet
|
||||
canvas = pngExportController.createPngSpritesheet_();
|
||||
}
|
||||
|
||||
// Add output canvas to DOM
|
||||
window.document.body.appendChild(canvas);
|
||||
|
||||
// Prepare return data
|
||||
const returnData = {
|
||||
width: canvas.width,
|
||||
height: canvas.height
|
||||
};
|
||||
|
||||
// Wait a tick for things to wrap up
|
||||
setTimeout(function () {
|
||||
// Exit and pass data to parent process
|
||||
window.callPhantom(returnData);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function onPageExit(page, options, data) {
|
||||
// Set clip for output image
|
||||
if (data.width && data.height) {
|
||||
page.clipRect = { top: 0, left: 0, width: data.width, height: data.height };
|
||||
}
|
||||
|
||||
console.log("\n" + 'Generated file(s):');
|
||||
|
||||
const dest = options.dest.replace('.png', '') + '.png';
|
||||
|
||||
// Render page to the output image
|
||||
page.render(dest);
|
||||
|
||||
console.log(" " + dest);
|
||||
|
||||
if (options.dataUri) {
|
||||
const dataUriPath = options.dest + '.datauri';
|
||||
|
||||
const dataUri = 'data:image/png;base64,' + page.renderBase64('PNG');
|
||||
|
||||
// Write data-uri to file
|
||||
fs.write(dataUriPath, dataUri, 'w');
|
||||
|
||||
console.log(" " + dataUriPath);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
onPageEvaluate: onPageEvaluate,
|
||||
onPageExit: onPageExit
|
||||
};
|
93
cli/index.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const minimist = require('minimist');
|
||||
const childProcess = require('child_process');
|
||||
const phantomjs = require('phantomjs');
|
||||
const binPath = phantomjs.path;
|
||||
|
||||
// Parse command args
|
||||
let args = minimist(process.argv.slice(2), {
|
||||
default: {
|
||||
crop: false,
|
||||
dataUri: false,
|
||||
debug: false,
|
||||
scale: 1
|
||||
},
|
||||
});
|
||||
|
||||
if (args.debug) console.log(args);
|
||||
|
||||
// Ensure a path for the src file was passed
|
||||
if (!args._ || (args._ && !args._.length)) {
|
||||
console.error('Path to a .piskel file is required');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const src = args._[0];
|
||||
|
||||
// Ensure the src file exists
|
||||
if (!fs.existsSync(src)) {
|
||||
console.error('No such file: ' + src);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Read src piskel file
|
||||
const piskelFile = fs.readFileSync(src, 'utf-8');
|
||||
|
||||
const dest = args.dest || path.basename(src, '.piskel');
|
||||
|
||||
console.log('Piskel CLI is exporting...');
|
||||
|
||||
// Get path to Piskel's app js bundle
|
||||
let piskelAppJsDir = path.resolve(__dirname +'/../dest/prod/js/');
|
||||
let minJsFiles = fs.readdirSync(piskelAppJsDir).filter(filename => filename.indexOf('min') > -1);
|
||||
let piskelAppJsFileName = minJsFiles[0];
|
||||
let piskelAppJsPath = (piskelAppJsFileName) ? path.join(piskelAppJsDir, piskelAppJsFileName) : '';
|
||||
|
||||
if (!fs.existsSync(piskelAppJsPath)) {
|
||||
console.error(`Piskel's application JS file not found in: ${piskelAppJsDir}. Run prod build and try again.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare args to pass to phantom script
|
||||
const options = {
|
||||
dest: dest,
|
||||
zoom: args.scale,
|
||||
crop: !!args.crop,
|
||||
rows: args.rows,
|
||||
columns: args.columns,
|
||||
frame: args.frame,
|
||||
dataUri: !!args.dataUri,
|
||||
debug: args.debug,
|
||||
piskelAppJsPath: piskelAppJsPath,
|
||||
scaledWidth: args.scaledWidth,
|
||||
scaledHeight: args.scaledHeight
|
||||
};
|
||||
|
||||
const childArgs = [
|
||||
path.join(__dirname, 'piskel-export.js'),
|
||||
piskelFile,
|
||||
JSON.stringify(options)
|
||||
];
|
||||
|
||||
if (args.debug) {
|
||||
childArgs.unshift(
|
||||
'--remote-debugger-port=9035',
|
||||
'--remote-debugger-autorun=yes'
|
||||
);
|
||||
}
|
||||
|
||||
// Run phantom script
|
||||
childProcess.execFile(binPath, childArgs, function (err, stdout, stderr) {
|
||||
// Print any output the from child process
|
||||
if (err) console.log(err);
|
||||
if (stderr) console.log(stderr);
|
||||
if (stdout) console.log(stdout);
|
||||
|
||||
console.log('Export complete');
|
||||
});
|
45
cli/piskel-export.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
// PhantomJS system
|
||||
const system = require('system');
|
||||
|
||||
// Exporter
|
||||
const exporter = require('./export-png');
|
||||
|
||||
// Get passed args
|
||||
const args = system.args;
|
||||
|
||||
// Parse input piskel file and options
|
||||
const piskelFile = JSON.parse(args[1]);
|
||||
const options = JSON.parse(args[2]);
|
||||
|
||||
// Create page w/ canvas
|
||||
const page = require('webpage').create();
|
||||
|
||||
page.content = '<html><body></body></html>';
|
||||
|
||||
// Inject Piskel JS
|
||||
page.injectJs(options.piskelAppJsPath);
|
||||
|
||||
// Listen for page console logs
|
||||
page.onConsoleMessage = function (msg) {
|
||||
console.log(msg);
|
||||
};
|
||||
|
||||
// Run page logic
|
||||
page.evaluate(function (piskelFile, options, onPageEvaluate) {
|
||||
// Zero out default body margin
|
||||
document.body.style.margin = 0;
|
||||
|
||||
// Deserialize piskel file and run exporter's page evaluate task
|
||||
pskl.utils.serialization.Deserializer.deserialize(piskelFile, function (piskel) {
|
||||
onPageEvaluate(window, options, piskel);
|
||||
});
|
||||
}, piskelFile, options, exporter.onPageEvaluate);
|
||||
|
||||
// Wait for page to trigger exit
|
||||
page.onCallback = function (data) {
|
||||
// Run exporter page exit task
|
||||
exporter.onPageExit(page, options, data);
|
||||
|
||||
// Exit
|
||||
phantom.exit(0);
|
||||
};
|
80
misc/icons/source/common-backup.svg
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="90"
|
||||
height="90"
|
||||
viewBox="0 0 89.999997 90"
|
||||
enable-background="new 0 0 89.231 100"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="common-backup.svg"
|
||||
inkscape:export-filename="C:\Development\git\piskel\misc\icons\source\tool-rotate.png"
|
||||
inkscape:export-xdpi="45"
|
||||
inkscape:export-ydpi="45"><metadata
|
||||
id="metadata15"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.75"
|
||||
inkscape:cx="13.031976"
|
||||
inkscape:cy="43.272537"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" /><g
|
||||
id="g3760"
|
||||
transform="matrix(0,-0.97677741,0.97203982,0,-2.1261998,91.355253)"
|
||||
style="fill:#ff00ff;fill-opacity:1"><path
|
||||
style="fill:#ff00ff;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3"
|
||||
d="m 29.229405,55.37008 c -0.387431,-1.333059 -0.642506,-2.72161 -0.738881,-4.152895 h -8.675106 c 0.115651,2.460738 0.552554,4.838559 1.260594,7.099021 z" /><path
|
||||
style="fill:#ff00ff;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5"
|
||||
d="m 29.023802,70.783821 5.579515,-6.601516 c -1.862622,-1.780814 -3.387929,-3.907969 -4.44999,-6.287065 l -8.152106,2.946124 c 1.604978,3.800815 4.017584,7.185766 7.022581,9.942457 z" /><path
|
||||
style="fill:#ff00ff;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path7"
|
||||
d="m 47.110967,69.703978 c -3.887799,-0.260871 -7.469766,-1.6322 -10.437498,-3.790608 l -5.577588,6.598963 c 4.487901,3.403448 10.011517,5.524225 16.015086,5.803594 z" /><path
|
||||
style="fill:#ff00ff;fill-opacity:1;stroke:none"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path9"
|
||||
d="M 48.464084,21.400659 V 14.532532 L 28.981398,25.698341 48.464084,36.86415 v -6.867489 c 11.042093,0 20.024317,8.91683 20.024317,19.877897 0,10.509484 -8.258763,19.134189 -18.671845,19.828145 v 8.611948 c 15.190751,-0.703524 27.330245,-13.189635 27.330245,-28.440093 0,-15.700763 -12.86681,-28.473899 -28.682717,-28.473899 z" /></g><g
|
||||
id="g4513"
|
||||
transform="translate(0,-2)"><rect
|
||||
y="32.516129"
|
||||
x="42"
|
||||
height="15.612903"
|
||||
width="7.9999986"
|
||||
id="rect4494"
|
||||
style="fill:#ff00f7;fill-opacity:1;stroke:#ffffed;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6.04534006;stroke-opacity:1" /><rect
|
||||
transform="rotate(120)"
|
||||
y="-76.050484"
|
||||
x="12.680965"
|
||||
height="15.612903"
|
||||
width="7.9999986"
|
||||
id="rect4494-7"
|
||||
style="fill:#ff00f7;fill-opacity:1;stroke:#ffffed;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6.04534006;stroke-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 3.7 KiB |
62
package.json
|
@ -1,60 +1,67 @@
|
|||
{
|
||||
"name": "piskel",
|
||||
"version": "0.11.0",
|
||||
"version": "0.15.2-SNAPSHOT",
|
||||
"description": "Pixel art editor",
|
||||
"author": "Julian Descottes <julian.descottes@gmail.com>",
|
||||
"contributors": [
|
||||
"Vincent Renaudin"
|
||||
],
|
||||
"homepage": "http://github.com/juliandescottes/piskel",
|
||||
"homepage": "http://github.com/piskelapp/piskel",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/juliandescottes/piskel.git"
|
||||
"url": "http://github.com/piskelapp/piskel.git"
|
||||
},
|
||||
"files": [
|
||||
"dest/prod",
|
||||
"misc/scripts/piskel-root"
|
||||
],
|
||||
"bin": {
|
||||
"piskel-root": "./misc/scripts/piskel-root"
|
||||
"piskel-root": "./misc/scripts/piskel-root",
|
||||
"piskel-cli": "./cli/index.js"
|
||||
},
|
||||
"main": "./dest/prod/index.html",
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"start": "nodewebkit",
|
||||
"dev": "grunt play",
|
||||
"start": "grunt build && nw",
|
||||
"preversion": "grunt test build",
|
||||
"postversion": "git push && git push --tags && npm publish",
|
||||
"release": "grunt && node ./bin/copy-to-piskel-website"
|
||||
"release": "grunt && node ./bin/copy-to-piskel-website",
|
||||
"build": "grunt desktop",
|
||||
"build:mac": "grunt desktop-mac",
|
||||
"format": "grunt eslint --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dateformat": "2.0.0",
|
||||
"fs-extra": "3.0.1",
|
||||
"grunt": "0.4.5",
|
||||
"dateformat": "4.5.1",
|
||||
"fs-extra": "10.0.0",
|
||||
"grunt": "1.4.1",
|
||||
"grunt-casperjs": "^2.2.1",
|
||||
"grunt-contrib-clean": "1.1.0",
|
||||
"grunt-cli": "^1.4.3",
|
||||
"grunt-contrib-clean": "2.0.0",
|
||||
"grunt-contrib-concat": "1.0.1",
|
||||
"grunt-contrib-connect": "1.0.2",
|
||||
"grunt-contrib-connect": "3.0.0",
|
||||
"grunt-contrib-copy": "1.0.0",
|
||||
"grunt-contrib-jshint": "1.1.0",
|
||||
"grunt-contrib-uglify": "2.3.0",
|
||||
"grunt-contrib-watch": "1.0.0",
|
||||
"grunt-include-replace": "4.0.1",
|
||||
"grunt-jscs": "2.8.0",
|
||||
"grunt-karma": "1.0.0",
|
||||
"grunt-contrib-jshint": "3.0.0",
|
||||
"grunt-contrib-uglify": "5.0.1",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "23.0.0",
|
||||
"grunt-include-replace": "5.0.0",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-karma": "4.0.2",
|
||||
"grunt-leading-indent": "0.2.0",
|
||||
"grunt-nw-builder": "3.1.0",
|
||||
"grunt-open": "0.2.3",
|
||||
"grunt-replace": "1.0.1",
|
||||
"grunt-spritesmith": "6.4.0",
|
||||
"jasmine-core": "2.6.1",
|
||||
"karma": "1.7.0",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"grunt-replace": "2.0.2",
|
||||
"grunt-spritesmith": "^6.9.0",
|
||||
"jasmine-core": "3.8.0",
|
||||
"karma": "6.3.4",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-phantomjs-launcher": "1.0.4",
|
||||
"load-grunt-tasks": "3.5.0",
|
||||
"load-grunt-tasks": "5.1.0",
|
||||
"nw": "0.54.0",
|
||||
"phantomjs": "2.1.7",
|
||||
"phantomjs-polyfill-object-assign": "0.0.2",
|
||||
"promise-polyfill": "6.0.2",
|
||||
"promise-polyfill": "8.2.0",
|
||||
"rmdir": "1.2.0"
|
||||
},
|
||||
"window": {
|
||||
|
@ -62,6 +69,9 @@
|
|||
"icon": "dest/prod/logo.png",
|
||||
"toolbar": false,
|
||||
"width": 1000,
|
||||
"height": 500
|
||||
"height": 700
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
}
|
||||
|
|
152
src/css/dialogs-browse-backups.css
Normal file
|
@ -0,0 +1,152 @@
|
|||
#dialog-container.browse-backups {
|
||||
width: 700px;
|
||||
height: 500px;
|
||||
top : 50%;
|
||||
left : 50%;
|
||||
position : absolute;
|
||||
margin-left: -350px;
|
||||
}
|
||||
|
||||
.backups-step-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.backups-step-content {
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
flex-grow: 1;
|
||||
background: #444;
|
||||
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.backups-step-actions {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.show #dialog-container.browse-backups {
|
||||
margin-top: -250px;
|
||||
}
|
||||
|
||||
.browse-backups .browse-backups-disclaimer {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.browse-backups .browse-backups-disclaimer-content {
|
||||
padding: 0 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.browse-backups .browse-backups-disclaimer .backups-icon {
|
||||
border: 1px solid gold;
|
||||
flex-shrink: 0;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.browse-backups .centered-message {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-top: 100px;
|
||||
margin-left: -130px;
|
||||
padding: 30px;
|
||||
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.browse-backups .session-list-empty,
|
||||
.browse-backups .snapshot-list-empty {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.browse-backups .session-list-error,
|
||||
.browse-backups .snapshot-list-error {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.browse-backups .session-item {
|
||||
/* Transition duration should be kept in sync with SelectSession.DELETE_TRANSITION_DURATION */
|
||||
transition: all 500ms;
|
||||
}
|
||||
|
||||
/* Hide and slide up next sessions when deleting an item */
|
||||
.browse-backups .session-item.deleting {
|
||||
opacity: 0;
|
||||
margin-bottom: -60px;
|
||||
}
|
||||
|
||||
.browse-backups .session-item,
|
||||
.browse-backups .snapshot-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
|
||||
margin-bottom: 10px;
|
||||
padding: 0 20px;
|
||||
|
||||
border: 1px solid #666;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.browse-backups .snapshot-preview {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
/* Keep synced with SessionDetails.PREVIEW_SIZE */
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.browse-backups .session-details,
|
||||
.browse-backups .snapshot-details {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.browse-backups .session-details-title,
|
||||
.browse-backups .snapshot-details-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.browse-backups .session-details-info,
|
||||
.browse-backups .snapshot-details-info {
|
||||
font-size: 11px;
|
||||
color: #bbb;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.browse-backups .session-actions,
|
||||
.browse-backups .snapshot-actions {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.browse-backups .session-actions button,
|
||||
.browse-backups .snapshot-actions button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.browse-backups .session-item:last-child,
|
||||
.browse-backups .snapshot-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
18
src/css/dialogs-import.css
vendored
|
@ -183,22 +183,14 @@
|
|||
.import-meta-value,
|
||||
.import-meta-label {
|
||||
padding: 2px 4px;
|
||||
border: 1px solid gold;
|
||||
}
|
||||
|
||||
.import-meta-label {
|
||||
border-radius: 2px 0 0 2px;
|
||||
color: var(--highlight-color);
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
.import-meta-title .import-meta-label {
|
||||
border-right-width: 1px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.import-meta-value {
|
||||
border-radius: 0 2px 2px 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
@ -242,7 +234,7 @@
|
|||
.insert-mode-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.import-resize-option :checked + span,
|
||||
|
@ -250,11 +242,15 @@
|
|||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.import-resize-option input,
|
||||
.insert-mode-option input {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/**
|
||||
* ADJUST SIZE
|
||||
*/
|
||||
.import-resize-anchor-info,
|
||||
.import-resize-option-label {
|
||||
.import-resize-anchor-info {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,11 @@
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.preview-tile .tile-overlay.tile-count.toggled {
|
||||
background-color: gold;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.preview-tile .tile-overlay.delete-frame-action {
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
.left-column {
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
margin-right: 7px;
|
||||
padding-right: 7px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,8 @@
|
|||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
margin-left: 10px;
|
||||
/* Keep in sync with Constants.RIGHT_COLUMN_PADDING_LEFT */
|
||||
padding-left: 10px;
|
||||
/* Add some padding for the absolutely positioned .cursor-coordinates */
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
@ -80,10 +81,21 @@
|
|||
}
|
||||
|
||||
.cursor-coordinates {
|
||||
color:#888;
|
||||
font-size:12px;
|
||||
font-weight:bold;
|
||||
font-family:monospace;
|
||||
color: var(--highlight-color);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.cursor-coordinates .drawing-zoom {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.cursor-coordinates .frame-info {
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,4 +169,4 @@
|
|||
.canvas.drawing-canvas {z-index: 8;}
|
||||
.canvas.canvas-overlay {z-index: 9;}
|
||||
.canvas.onion-skin-canvas {z-index: 10;}
|
||||
.canvas.layers-above-canvas {z-index: 11;}
|
||||
.canvas.layers-above-canvas {z-index: 11;}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/*******************************/
|
||||
/* Application Setting panel */
|
||||
/*******************************/
|
||||
.application-settings-form {
|
||||
height: 100%;
|
||||
}
|
||||
/********************************/
|
||||
/* Preferences Settings panel */
|
||||
/********************************/
|
||||
|
||||
.settings-section-application {
|
||||
.settings-section-preferences {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: 0 20px;
|
||||
|
@ -45,14 +43,11 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.layer-opacity-input {
|
||||
.layer-opacity-input,
|
||||
.tile-mask-opacity-input {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.seamless-opacity-input {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.settings-opacity-text {
|
||||
height: 31px;
|
||||
display: inline-block;
|
||||
|
@ -64,17 +59,88 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.settings-item-grid-size,
|
||||
.settings-item-grid-spacing,
|
||||
.settings-item-grid-color {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-item-grid-size > label,
|
||||
.settings-item-grid-spacing > label,
|
||||
.settings-item-grid-color > label {
|
||||
width: 65px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.grid-spacing-container .size-picker-option[data-size='1'] {
|
||||
padding: 7px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='2'] {
|
||||
padding: 6px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='4'] {
|
||||
padding: 5px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='8'] {
|
||||
padding: 4px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='16'] {
|
||||
padding: 3px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='32'] {
|
||||
padding: 2px;
|
||||
}
|
||||
.grid-spacing-container .size-picker-option[data-size='64'] {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.settings-item-grid-size .size-picker-option,
|
||||
.settings-item-grid-spacing .size-picker-option {
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
.settings-item-grid-size .size-picker-option:not(.selected):hover,
|
||||
.settings-item-grid-spacing .size-picker-option:not(.selected):hover {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.grid-width-select,
|
||||
.color-format-select {
|
||||
margin: 5px 5px 0 5px;
|
||||
}
|
||||
|
||||
.settings-section-application > .settings-title {
|
||||
|
||||
.grid-colors-list {
|
||||
overflow: hidden;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.grid-colors-item {
|
||||
float: left;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
border: 2px solid #888;
|
||||
margin-right: 2px;
|
||||
margin-bottom: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.grid-colors-item:hover {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.grid-colors-item.selected {
|
||||
border-color: gold;
|
||||
}
|
||||
|
||||
.settings-section-preferences > .settings-title {
|
||||
/* Override the default 10px margin bottom for this panel */
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.settings-section-application .button-primary {
|
||||
.settings-section-preferences .button-primary {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
@ -91,4 +157,4 @@
|
|||
.settings-version:hover {
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,29 @@
|
|||
float : left;
|
||||
}
|
||||
|
||||
.gif-export-warning {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gif-export-warning.visible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid red;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.gif-export-warning-icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.gif-export-warning-message {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.preview-upload-ongoing:before{
|
||||
content: "Upload ongoing ...";
|
||||
content: "Upload in progress...";
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 100%;
|
||||
|
@ -100,45 +121,6 @@
|
|||
line-height: 23px;
|
||||
}
|
||||
|
||||
.export-tabs {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.export-tabs:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
z-index: 0;
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.export-tab {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px 2px 0 0;
|
||||
/* Make sure the tab and its border are positioned above the :after element; */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.export-tab.selected,
|
||||
.export-tab:hover {
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.export-tab.selected {
|
||||
border-color: var(--highlight-color);
|
||||
border-bottom-color: #444;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.export-panel-header {
|
||||
padding: 10px 5px 0px;
|
||||
}
|
||||
|
@ -162,7 +144,8 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.export-panel-gif .button {
|
||||
.export-panel-gif .button,
|
||||
.export-panel-png .button {
|
||||
margin-right: 5px;
|
||||
width: 75px;
|
||||
flex-shrink: 0;
|
||||
|
@ -172,11 +155,6 @@
|
|||
width: 50px;
|
||||
}
|
||||
|
||||
.png-export-dimension-info,
|
||||
.png-export-datauri-info {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#png-export-columns,
|
||||
#png-export-rows {
|
||||
/* Override default textfield padding-right to keep the number spinners
|
||||
|
|
|
@ -52,7 +52,16 @@ body {
|
|||
}
|
||||
|
||||
.checkbox-fix {
|
||||
margin-left: 0;
|
||||
margin: 3px 3px 3px 0;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,6 +124,14 @@
|
|||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the icon represents an enabled state, the border should always be gold.
|
||||
*/
|
||||
.preview-contextual-action-enabled {
|
||||
color: var(--highlight-color);
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-down in preview size selection
|
||||
*/
|
||||
|
@ -195,6 +203,7 @@
|
|||
|
||||
@media (-webkit-min-device-pixel-ratio: 2),
|
||||
(min-resolution: 192dpi) {
|
||||
background-image: url(../img/icons/minimap/minimap-popup-preview-arrow-gold@2x.png);
|
||||
|
||||
.icon-minimap-popup-preview-arrow-white:hover {
|
||||
background-image: url(../img/icons/minimap/minimap-popup-preview-arrow-gold@2x.png);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,12 +66,17 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layer-item .layer-name {
|
||||
.layer-item .layer-name,
|
||||
.layer-item .layer-name-input {
|
||||
padding: 0 0 0 10px;
|
||||
flex: 1 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.layer-item .layer-name-input {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.layer-item .layer-name.overflowing-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -82,7 +87,8 @@
|
|||
}
|
||||
|
||||
.layer-item-opacity {
|
||||
padding-right: 8px;
|
||||
padding: 0 8px 0 8px;
|
||||
flex: 0 auto;
|
||||
}
|
||||
|
||||
.current-layer-item,
|
||||
|
|
|
@ -76,6 +76,11 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.palettes-list-colors.tiny > .palettes-list-color {
|
||||
width: calc((100% - 35px) / 10);
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.palettes-list-color div {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -89,7 +94,7 @@
|
|||
line-height: 35px;
|
||||
width: 100%;
|
||||
color: gray;
|
||||
font-size: 0.7em;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
text-align: center
|
||||
}
|
||||
|
@ -121,7 +126,8 @@
|
|||
* Color index for the 9 first colors
|
||||
*/
|
||||
|
||||
.palettes-list-color:nth-child(-n+10):after {
|
||||
:not(.tiny) > .palettes-list-color:nth-child(-n+10):after {
|
||||
content: attr(data-color-index);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
@ -136,39 +142,3 @@
|
|||
padding: 2px 3px 2px 3px;
|
||||
border-radius: 0 0 0 2px;
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(1):after {
|
||||
content: "1";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(2):after {
|
||||
content: "2";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(3):after {
|
||||
content: "3";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(4):after {
|
||||
content: "4";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(5):after {
|
||||
content: "5";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(6):after {
|
||||
content: "6";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(7):after {
|
||||
content: "7";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(8):after {
|
||||
content: "8";
|
||||
}
|
||||
|
||||
.palettes-list-color:nth-child(9):after {
|
||||
content: "9";
|
||||
}
|
||||
|
|
|
@ -3,5 +3,38 @@
|
|||
}
|
||||
|
||||
.transformations-container .tool-icon {
|
||||
float:left;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.transformations-container .tools-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
height: 46px;
|
||||
|
||||
/* Override the float:left set on tools-wrapper in layout.css; */
|
||||
float: initial;
|
||||
}
|
||||
|
||||
.transformations-container.show-more .tools-wrapper {
|
||||
height: auto;
|
||||
/* Compensate the 5px bottom-margin coming from the tool-icon */
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.transformations-show-more-link {
|
||||
position: absolute;
|
||||
color: #999;
|
||||
right: 10px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
transition: 0.2s linear;
|
||||
}
|
||||
|
||||
.transformations-show-more-link:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.show-more .transformations-show-more-link {
|
||||
color: gold;
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
.pen-size-container {
|
||||
/***********************/
|
||||
/* SIZE PICKER WIDGET */
|
||||
/***********************/
|
||||
|
||||
.size-picker-container {
|
||||
overflow: hidden;
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
.pen-size-option {
|
||||
.size-picker-option {
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
width: 20px;
|
||||
|
@ -15,20 +19,20 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pen-size-option[data-size='1'] {
|
||||
.size-picker-option[data-size='1'] {
|
||||
padding: 5px;
|
||||
}
|
||||
.pen-size-option[data-size='2'] {
|
||||
.size-picker-option[data-size='2'] {
|
||||
padding: 4px;
|
||||
}
|
||||
.pen-size-option[data-size='3'] {
|
||||
.size-picker-option[data-size='3'] {
|
||||
padding: 3px;
|
||||
}
|
||||
.pen-size-option[data-size='4'] {
|
||||
.size-picker-option[data-size='4'] {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.pen-size-option:before {
|
||||
.size-picker-option:before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -39,19 +43,19 @@
|
|||
font-size: 90%;
|
||||
}
|
||||
|
||||
.pen-size-option:hover {
|
||||
.size-picker-option:hover {
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
.pen-size-option.selected:before {
|
||||
.size-picker-option.selected:before {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.pen-size-option.selected {
|
||||
.size-picker-option.selected {
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.pen-size-option.labeled:before {
|
||||
content: attr(real-pen-size);
|
||||
.size-picker-option.labeled:before {
|
||||
content: attr(real-size);
|
||||
color: black;
|
||||
}
|
42
src/css/widgets-tabs.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*****************/
|
||||
/* TABS WIDGET */
|
||||
/*****************/
|
||||
|
||||
.tab-list {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-list:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
z-index: 0;
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px 2px 0 0;
|
||||
/* Make sure the tab and its border are positioned above the :after element; */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tab-item.selected,
|
||||
.tab-item:hover {
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.tab-item.selected {
|
||||
border-color: var(--highlight-color);
|
||||
border-bottom-color: #444;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
BIN
src/img/icons/common/common-backup-white.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/img/icons/common/common-backup-white@2x.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
src/img/icons/minimap/minimap-grid-gold.png
Normal file
After Width: | Height: | Size: 135 B |
BIN
src/img/icons/minimap/minimap-grid-gold@2x.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/img/icons/minimap/minimap-grid-white.png
Normal file
After Width: | Height: | Size: 135 B |
BIN
src/img/icons/minimap/minimap-grid-white@2x.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
src/img/icons/transform/tool-crop.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
src/img/icons/transform/tool-crop@2x.png
Normal file
After Width: | Height: | Size: 693 B |
|
@ -22,9 +22,7 @@
|
|||
color:white;">
|
||||
<span style="top:45%">Loading Piskel ...</span>
|
||||
</div>
|
||||
<!--standalone-start-->
|
||||
@@include('templates/debug-header.html', {})
|
||||
<!--standalone-end-->
|
||||
|
||||
<!-- the comment below indicates the beginning of markup reused by the editor integrated in piskelapp.com -->
|
||||
<!-- do not delete, do not move :) -->
|
||||
|
@ -69,6 +67,7 @@
|
|||
</div>
|
||||
|
||||
@@include('templates/misc-templates.html', {})
|
||||
@@include('templates/data-uri-export.html', {})
|
||||
@@include('templates/popup-preview.html', {})
|
||||
|
||||
<span class="cheatsheet-link icon-common-keyboard-gold"
|
||||
|
@ -77,15 +76,19 @@
|
|||
rel="tooltip" data-placement="left" title="Performance problem detected, learn more."> </div>
|
||||
|
||||
<!-- dialogs partials -->
|
||||
@@include('templates/dialogs/create-palette.html', {})
|
||||
@@include('templates/dialogs/browse-backups.html', {})
|
||||
@@include('templates/dialogs/browse-local.html', {})
|
||||
@@include('templates/dialogs/cheatsheet.html', {})
|
||||
@@include('templates/dialogs/create-palette.html', {})
|
||||
@@include('templates/dialogs/import.html', {})
|
||||
@@include('templates/dialogs/performance-info.html', {})
|
||||
@@include('templates/dialogs/unsupported-browser.html', {})
|
||||
|
||||
<!-- settings-panel partials -->
|
||||
@@include('templates/settings/application.html', {})
|
||||
@@include('templates/settings/preferences.html', {})
|
||||
@@include('templates/settings/preferences/grid.html', {})
|
||||
@@include('templates/settings/preferences/misc.html', {})
|
||||
@@include('templates/settings/preferences/tile.html', {})
|
||||
@@include('templates/settings/resize.html', {})
|
||||
@@include('templates/settings/save.html', {})
|
||||
@@include('templates/settings/import.html', {})
|
||||
|
|
99
src/js/.eslintrc
Normal file
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true
|
||||
},
|
||||
"globals": {
|
||||
"$": true,
|
||||
"jQuery": true,
|
||||
"pskl": true,
|
||||
"Events": true,
|
||||
"Constants": true,
|
||||
"console": true,
|
||||
"module": true,
|
||||
"require": true,
|
||||
"Q": true,
|
||||
"Promise": true
|
||||
},
|
||||
"rules": {
|
||||
"no-undef": 2,
|
||||
"no-use-before-define": [
|
||||
2,
|
||||
{
|
||||
"functions": false
|
||||
}
|
||||
],
|
||||
"curly": [
|
||||
2,
|
||||
"all"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
2,
|
||||
"after"
|
||||
],
|
||||
"camelcase": [
|
||||
2,
|
||||
{
|
||||
"properties": "never"
|
||||
}
|
||||
],
|
||||
"max-len": [
|
||||
2,
|
||||
120
|
||||
],
|
||||
"indent": [
|
||||
2,
|
||||
2,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"quotes": [
|
||||
2,
|
||||
"single"
|
||||
],
|
||||
"no-multi-str": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"space-unary-ops": [
|
||||
2,
|
||||
{
|
||||
"nonwords": false,
|
||||
"overrides": {}
|
||||
}
|
||||
],
|
||||
"one-var": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"brace-style": [
|
||||
2,
|
||||
"1tbs",
|
||||
{
|
||||
"allowSingleLine": true
|
||||
}
|
||||
],
|
||||
"keyword-spacing": [
|
||||
2,
|
||||
{}
|
||||
],
|
||||
"space-infix-ops": 2,
|
||||
"space-before-blocks": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"eol-last": 2,
|
||||
"space-in-parens": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"no-multiple-empty-lines": 2,
|
||||
"no-with": 2,
|
||||
"no-spaced-func": 2,
|
||||
"dot-notation": 2,
|
||||
"semi": [
|
||||
2,
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -12,13 +12,15 @@ var Constants = {
|
|||
MAX_HEIGHT : 1024,
|
||||
MAX_WIDTH : 1024,
|
||||
|
||||
MAX_PALETTE_COLORS : 100,
|
||||
MAX_PALETTE_COLORS : 256,
|
||||
// allow current colors service to get up to 256 colors.
|
||||
// GIF generation is different if the color count goes over 256.
|
||||
MAX_WORKER_COLORS : 256,
|
||||
|
||||
PREVIEW_FILM_SIZE : 96,
|
||||
ANIMATED_PREVIEW_WIDTH : 200,
|
||||
// Keep in sync with padding-left: 10px in layout.css
|
||||
RIGHT_COLUMN_PADDING_LEFT : 10,
|
||||
|
||||
DEFAULT_PEN_COLOR : '#000000',
|
||||
TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)',
|
||||
|
@ -58,8 +60,11 @@ var Constants = {
|
|||
// The datastore limit is 1 MiB, which we roughly approximate to 1 million characters.
|
||||
APPENGINE_SAVE_LIMIT : 1 * 1024 * 1024,
|
||||
|
||||
// Message displayed when an action will lead to erase the current animation.
|
||||
CONFIRM_OVERWRITE: 'This will replace your current animation, are you sure you want to continue?',
|
||||
|
||||
// SERVICE URLS
|
||||
APPENGINE_SAVE_URL : 'save',
|
||||
IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-b.appspot.com/__/upload',
|
||||
IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-b.appspot.com/img/'
|
||||
IMAGE_SERVICE_UPLOAD_URL : '{{protocol}}://piskel-imgstore-b.appspot.com/__/upload',
|
||||
IMAGE_SERVICE_GET_URL : '{{protocol}}://piskel-imgstore-b.appspot.com/img/'
|
||||
};
|
||||
|
|
|
@ -64,9 +64,10 @@ var Events = {
|
|||
SELECTION_CREATED: 'SELECTION_CREATED',
|
||||
SELECTION_MOVE_REQUEST: 'SELECTION_MOVE_REQUEST',
|
||||
SELECTION_DISMISSED: 'SELECTION_DISMISSED',
|
||||
SELECTION_COPY: 'SELECTION_COPY',
|
||||
SELECTION_CUT: 'SELECTION_CUT',
|
||||
SELECTION_PASTE: 'SELECTION_PASTE',
|
||||
|
||||
CLIPBOARD_COPY: 'CLIPBOARD_COPY',
|
||||
CLIPBOARD_CUT: 'CLIPBOARD_CUT',
|
||||
CLIPBOARD_PASTE: 'CLIPBOARD_PASTE',
|
||||
|
||||
SHOW_NOTIFICATION: 'SHOW_NOTIFICATION',
|
||||
HIDE_NOTIFICATION: 'HIDE_NOTIFICATION',
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
this.isAppEngineVersion = !!pskl.appEngineToken_;
|
||||
|
||||
// This id is used to keep track of sessions in the BackupService.
|
||||
this.sessionId = pskl.utils.Uuid.generate();
|
||||
|
||||
this.shortcutService = new pskl.service.keyboard.ShortcutService();
|
||||
this.shortcutService.init();
|
||||
|
||||
|
@ -61,24 +64,24 @@
|
|||
|
||||
this.drawingController = new pskl.controller.DrawingController(
|
||||
this.piskelController,
|
||||
$('#drawing-canvas-container'));
|
||||
document.querySelector('#drawing-canvas-container'));
|
||||
this.drawingController.init();
|
||||
|
||||
this.previewController = new pskl.controller.preview.PreviewController(
|
||||
this.piskelController,
|
||||
$('#animated-preview-canvas-container'));
|
||||
document.querySelector('#animated-preview-canvas-container'));
|
||||
this.previewController.init();
|
||||
|
||||
this.minimapController = new pskl.controller.MinimapController(
|
||||
this.piskelController,
|
||||
this.previewController,
|
||||
this.drawingController,
|
||||
$('.minimap-container'));
|
||||
document.querySelector('.minimap-container'));
|
||||
this.minimapController.init();
|
||||
|
||||
this.framesListController = new pskl.controller.FramesListController(
|
||||
this.piskelController,
|
||||
$('#preview-list-wrapper').get(0));
|
||||
document.querySelector('#preview-list-wrapper'));
|
||||
this.framesListController.init();
|
||||
|
||||
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
|
||||
|
@ -111,6 +114,9 @@
|
|||
this.canvasBackgroundController = new pskl.controller.CanvasBackgroundController();
|
||||
this.canvasBackgroundController.init();
|
||||
|
||||
this.indexedDbStorageService = new pskl.service.storage.IndexedDbStorageService(this.piskelController);
|
||||
this.indexedDbStorageService.init();
|
||||
|
||||
this.localStorageService = new pskl.service.storage.LocalStorageService(this.piskelController);
|
||||
this.localStorageService.init();
|
||||
|
||||
|
@ -165,6 +171,9 @@
|
|||
this.currentColorsService);
|
||||
this.performanceReportService.init();
|
||||
|
||||
this.clipboardService = new pskl.service.ClipboardService(this.piskelController);
|
||||
this.clipboardService.init();
|
||||
|
||||
this.drawingLoop = new pskl.rendering.DrawingLoop();
|
||||
this.drawingLoop.addCallback(this.render, this);
|
||||
this.drawingLoop.start();
|
||||
|
@ -192,6 +201,11 @@
|
|||
dialogId : 'unsupported-browser'
|
||||
});
|
||||
}
|
||||
|
||||
if (pskl.utils.Environment.isDebug()) {
|
||||
pskl.app.shortcutService.registerShortcut(pskl.service.keyboard.Shortcuts.DEBUG.RELOAD_STYLES,
|
||||
window.reloadStyles);
|
||||
}
|
||||
},
|
||||
|
||||
loadPiskel_ : function (piskelData) {
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
$.subscribe(Events.DRAG_START, this.onDragStart_.bind(this));
|
||||
$.subscribe(Events.DRAG_END, this.onDragEnd_.bind(this));
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.redraw.bind(this));
|
||||
$.subscribe(Events.ZOOM_CHANGED, this.redraw.bind(this));
|
||||
$.subscribe(Events.PISKEL_RESET, this.redraw.bind(this));
|
||||
|
||||
this.redraw();
|
||||
};
|
||||
|
@ -39,7 +41,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
this.coordinatesContainer.innerHTML = this.getFrameSizeHTML_() + html;
|
||||
if (pskl.app.drawingController) {
|
||||
var zoom = pskl.app.drawingController.compositeRenderer.getZoom().toFixed(2);
|
||||
html += '<div class="drawing-zoom">x' + zoom + '</div>';
|
||||
}
|
||||
|
||||
this.coordinatesContainer.innerHTML = this.getFrameSizeHTML_() + html + this.getCurrentFrameIndexHTML_();
|
||||
};
|
||||
|
||||
ns.CursorCoordinatesController.prototype.getCurrentFrameIndexHTML_ = function () {
|
||||
var currentFrameIndex = this.piskelController.getCurrentFrameIndex() + 1;
|
||||
var frameCount = this.piskelController.getFrameCount();
|
||||
return '<div class="frame-info">' + currentFrameIndex + '/' + frameCount + '</div>';
|
||||
};
|
||||
|
||||
ns.CursorCoordinatesController.prototype.getFrameSizeHTML_ = function () {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
var cfg = {
|
||||
'zoom': this.calculateZoom_(),
|
||||
'supportGridRendering' : true,
|
||||
'supportGridRendering' : false,
|
||||
'height' : this.getContainerHeight_(),
|
||||
'width' : this.getContainerWidth_(),
|
||||
'xOffset' : 0,
|
||||
|
@ -30,9 +30,10 @@
|
|||
};
|
||||
|
||||
this.overlayRenderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, cfg, ['canvas-overlay']);
|
||||
this.renderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, cfg, ['drawing-canvas']);
|
||||
this.onionSkinRenderer = pskl.rendering.OnionSkinRenderer.createInContainer(this.container, cfg, piskelController);
|
||||
this.layersRenderer = new pskl.rendering.layer.LayersRenderer(this.container, cfg, piskelController);
|
||||
cfg.supportGridRendering = true;
|
||||
this.renderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, cfg, ['drawing-canvas']);
|
||||
|
||||
this.compositeRenderer = new pskl.rendering.CompositeRenderer();
|
||||
this.compositeRenderer
|
||||
|
@ -50,12 +51,12 @@
|
|||
ns.DrawingController.prototype.init = function () {
|
||||
this.initMouseBehavior();
|
||||
|
||||
$.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
|
||||
$.subscribe(Events.TOOL_SELECTED, (function(evt, toolBehavior) {
|
||||
this.currentToolBehavior = toolBehavior;
|
||||
this.overlayFrame.clear();
|
||||
}, this));
|
||||
}).bind(this));
|
||||
|
||||
$(window).resize($.proxy(this.startResizeTimer_, this));
|
||||
window.addEventListener('resize', this.startResizeTimer_.bind(this));
|
||||
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this));
|
||||
|
@ -64,6 +65,10 @@
|
|||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.RESET_ZOOM, this.resetZoom_.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.INCREASE_ZOOM, this.updateZoom_.bind(this, 1));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.DECREASE_ZOOM, this.updateZoom_.bind(this, -1));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.OFFSET_UP, this.updateOffset_.bind(this, 'up'));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.OFFSET_RIGHT, this.updateOffset_.bind(this, 'right'));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.OFFSET_DOWN, this.updateOffset_.bind(this, 'down'));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.OFFSET_LEFT, this.updateOffset_.bind(this, 'left'));
|
||||
|
||||
window.setTimeout(function () {
|
||||
this.afterWindowResize_();
|
||||
|
@ -72,13 +77,12 @@
|
|||
};
|
||||
|
||||
ns.DrawingController.prototype.initMouseBehavior = function() {
|
||||
var body = $('body');
|
||||
this.container.mousedown($.proxy(this.onMousedown_, this));
|
||||
this.container.addEventListener('mousedown', this.onMousedown_.bind(this));
|
||||
|
||||
if (pskl.utils.UserAgent.isChrome || pskl.utils.UserAgent.isIE11) {
|
||||
this.container.on('mousewheel', $.proxy(this.onMousewheel_, this));
|
||||
this.container.addEventListener('mousewheel', this.onMousewheel_.bind(this));
|
||||
} else {
|
||||
this.container.on('wheel', $.proxy(this.onMousewheel_, this));
|
||||
this.container.addEventListener('wheel', this.onMousewheel_.bind(this));
|
||||
}
|
||||
|
||||
window.addEventListener('mouseup', this.onMouseup_.bind(this));
|
||||
|
@ -89,22 +93,20 @@
|
|||
window.addEventListener('touchend', this.onTouchend_.bind(this));
|
||||
|
||||
// Deactivate right click:
|
||||
body.contextmenu(this.onCanvasContextMenu_);
|
||||
|
||||
document.body.addEventListener('contextmenu', this.onCanvasContextMenu_.bind(this));
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.startResizeTimer_ = function () {
|
||||
if (this.resizeTimer) {
|
||||
window.clearInterval(this.resizeTimer);
|
||||
}
|
||||
this.resizeTimer = window.setTimeout($.proxy(this.afterWindowResize_, this), 200);
|
||||
this.resizeTimer = window.setTimeout(this.afterWindowResize_.bind(this), 200);
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.afterWindowResize_ = function () {
|
||||
var initialWidth = this.compositeRenderer.getDisplaySize().width;
|
||||
|
||||
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
|
||||
this.centerColumnWrapperHorizontally_();
|
||||
var ratio = this.compositeRenderer.getDisplaySize().width / initialWidth;
|
||||
var newZoom = ratio * this.compositeRenderer.getZoom();
|
||||
this.compositeRenderer.setZoom(newZoom);
|
||||
|
@ -129,7 +131,6 @@
|
|||
|
||||
ns.DrawingController.prototype.onFrameSizeChange_ = function () {
|
||||
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
|
||||
this.centerColumnWrapperHorizontally_();
|
||||
this.compositeRenderer.setZoom(this.calculateZoom_());
|
||||
this.compositeRenderer.setOffset(0, 0);
|
||||
$.publish(Events.ZOOM_CHANGED);
|
||||
|
@ -237,8 +238,7 @@
|
|||
$.publish(Events.CURSOR_MOVED, [coords.x, coords.y]);
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.onMousewheel_ = function (jQueryEvent) {
|
||||
var evt = jQueryEvent.originalEvent;
|
||||
ns.DrawingController.prototype.onMousewheel_ = function (evt) {
|
||||
// Ratio between wheelDeltaY (mousewheel event) and deltaY (wheel event) is -40
|
||||
var delta;
|
||||
if (pskl.utils.UserAgent.isIE11) {
|
||||
|
@ -262,6 +262,29 @@
|
|||
this.updateZoom_(modifier, coords);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the current viewport offset of 1 pixel in the provided direction.
|
||||
* Direction can be one of 'up', 'right', 'down', 'left'.
|
||||
* Callback for the OFFSET_${DIR} shortcuts.
|
||||
*/
|
||||
ns.DrawingController.prototype.updateOffset_ = function (direction) {
|
||||
var off = this.getOffset();
|
||||
if (direction === 'up') {
|
||||
off.y -= 1;
|
||||
} else if (direction === 'right') {
|
||||
off.x += 1;
|
||||
} else if (direction === 'down') {
|
||||
off.y += 1;
|
||||
} else if (direction === 'left') {
|
||||
off.x -= 1;
|
||||
}
|
||||
|
||||
this.setOffset(
|
||||
off.x,
|
||||
off.y
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the current zoom level by a given multiplier.
|
||||
*
|
||||
|
@ -323,6 +346,8 @@
|
|||
// Picking color after ALT+click or middle mouse button click.
|
||||
this.pickColorAt_(coords);
|
||||
this.isPickingColor = false;
|
||||
// Flash the cursor to briefly show the colorpicker cursor.
|
||||
this.flashColorPicker_();
|
||||
} else if (isMiddleDrag) {
|
||||
// Stop the drag handler after a middle button drag action.
|
||||
this.dragHandler.stopDrag();
|
||||
|
@ -358,6 +383,16 @@
|
|||
$.publish(evt, [color]);
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.flashColorPicker_ = function () {
|
||||
document.body.classList.add('tool-colorpicker');
|
||||
document.body.classList.remove(this.currentToolBehavior.toolId);
|
||||
window.clearTimeout(this.flashColorPickerTimer);
|
||||
this.flashColorPickerTimer = window.setTimeout(function () {
|
||||
document.body.classList.remove('tool-colorpicker');
|
||||
document.body.classList.add(this.currentToolBehavior.toolId);
|
||||
}.bind(this), 200);
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate absolute x,y screen coordinates into sprite coordinates
|
||||
* @param {Number} screenX
|
||||
|
@ -376,7 +411,8 @@
|
|||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
|
||||
if ($(event.target).closest('#drawing-canvas-container').length) {
|
||||
// closest() not really available everywhere yet, just skip if missing.
|
||||
if (event.target.closest && event.target.closest('#drawing-canvas-container')) {
|
||||
// Deactivate right click on drawing canvas only.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
@ -414,17 +450,21 @@
|
|||
};
|
||||
|
||||
ns.DrawingController.prototype.getAvailableHeight_ = function () {
|
||||
return $('#main-wrapper').height();
|
||||
return document.querySelector('#main-wrapper').getBoundingClientRect().height;
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.getSelectorWidth_ = function (selector) {
|
||||
return document.querySelector(selector).getBoundingClientRect().width;
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.getAvailableWidth_ = function () {
|
||||
var leftSectionWidth = $('.left-column').outerWidth(true);
|
||||
var rightSectionWidth = $('.right-column').outerWidth(true);
|
||||
var toolsContainerWidth = $('#tool-section').outerWidth(true);
|
||||
var settingsContainerWidth = $('#application-action-section').outerWidth(true);
|
||||
var leftSectionWidth = this.getSelectorWidth_('.left-column');
|
||||
var rightSectionWidth = this.getSelectorWidth_('.right-column');
|
||||
var toolsContainerWidth = this.getSelectorWidth_('#tool-section');
|
||||
var settingsContainerWidth = this.getSelectorWidth_('#application-action-section');
|
||||
|
||||
var usedWidth = leftSectionWidth + rightSectionWidth + toolsContainerWidth + settingsContainerWidth;
|
||||
var availableWidth = $('#main-wrapper').width() - usedWidth;
|
||||
var availableWidth = this.getSelectorWidth_('#main-wrapper') - usedWidth;
|
||||
|
||||
var comfortMargin = 10;
|
||||
return availableWidth - comfortMargin;
|
||||
|
@ -438,17 +478,6 @@
|
|||
return this.getAvailableWidth_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.centerColumnWrapperHorizontally_ = function() {
|
||||
var containerHeight = this.getContainerHeight_();
|
||||
var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - containerHeight) / 2);
|
||||
$('#column-wrapper').css({
|
||||
'top': verticalGapInPixel + 'px'
|
||||
});
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.getRenderer = function () {
|
||||
return this.compositeRenderer;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
SELECT : 'select',
|
||||
CLONE : 'clone',
|
||||
DELETE : 'delete',
|
||||
NEW_FRAME : 'newframe'
|
||||
NEW_FRAME : 'newframe',
|
||||
TOGGLE: 'toggle'
|
||||
};
|
||||
|
||||
ns.FramesListController = function (piskelController, container) {
|
||||
|
@ -114,6 +115,8 @@
|
|||
this.tiles.push(newtile);
|
||||
this.previewList.insertBefore(newtile, this.addFrameTile);
|
||||
this.updateScrollerOverflows();
|
||||
} else if (action == ACTION.TOGGLE) {
|
||||
this.piskelController.toggleFrameVisibilityAt(index);
|
||||
}
|
||||
|
||||
this.flagForRedraw_();
|
||||
|
@ -127,10 +130,18 @@
|
|||
// Remove selected class
|
||||
this.tiles[i].classList.remove('selected');
|
||||
|
||||
// Remove toggle class
|
||||
this.tiles[i].querySelector('.tile-count').classList.remove('toggled');
|
||||
|
||||
// Update tile numbers
|
||||
this.tiles[i].setAttribute('data-tile-number', i);
|
||||
this.tiles[i].querySelector('.tile-count').innerHTML = (i + 1);
|
||||
|
||||
// Update visibility
|
||||
if (this.piskelController.hasVisibleFrameAt(i)) {
|
||||
this.tiles[i].querySelector('.tile-count').classList.add('toggled');
|
||||
}
|
||||
|
||||
// Check if any tile is updated
|
||||
var hash = this.piskelController.getCurrentLayer().getFrameAt(i).getHash();
|
||||
if (this.tiles[i].getAttribute('data-tile-hash') !== hash) {
|
||||
|
@ -162,7 +173,10 @@
|
|||
this.previewList.innerHTML = '';
|
||||
|
||||
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
|
||||
$('.tooltip').remove();
|
||||
var tooltips = document.querySelectorAll('.tooltip');
|
||||
Array.prototype.forEach.call(tooltips, function (tooltip) {
|
||||
tooltip.parentNode.removeChild(tooltip);
|
||||
});
|
||||
|
||||
var frameCount = this.piskelController.getFrameCount();
|
||||
|
||||
|
@ -190,8 +204,8 @@
|
|||
ns.FramesListController.prototype.initDragndropBehavior_ = function () {
|
||||
$(this.previewList).sortable({
|
||||
placeholder: 'preview-tile preview-tile-drop-proxy',
|
||||
update: $.proxy(this.onUpdate_, this),
|
||||
stop: $.proxy(this.onSortableStop_, this),
|
||||
update: this.onUpdate_.bind(this),
|
||||
stop: this.onSortableStop_.bind(this),
|
||||
items: '.preview-tile',
|
||||
axis: 'y',
|
||||
tolerance: 'pointer'
|
||||
|
@ -203,8 +217,10 @@
|
|||
* @private
|
||||
*/
|
||||
ns.FramesListController.prototype.onUpdate_ = function (event, ui) {
|
||||
var originFrameId = parseInt(ui.item.data('tile-number'), 10);
|
||||
var targetInsertionId = $('.preview-tile').index(ui.item);
|
||||
var movedItem = ui.item.get(0);
|
||||
var originFrameId = parseInt(movedItem.dataset.tileNumber, 10);
|
||||
var tiles = document.querySelectorAll('.preview-tile');
|
||||
var targetInsertionId = Array.prototype.indexOf.call(tiles, movedItem);
|
||||
|
||||
this.piskelController.moveFrame(originFrameId, targetInsertionId);
|
||||
this.piskelController.setCurrentFrameIndex(targetInsertionId);
|
||||
|
@ -220,14 +236,13 @@
|
|||
ns.FramesListController.prototype.onSortableStop_ = function (event, ui) {
|
||||
this.justDropped = true;
|
||||
|
||||
this.resizeTimer = window.setTimeout($.proxy(function() {
|
||||
this.resizeTimer = window.setTimeout((function() {
|
||||
this.justDropped = false;
|
||||
}, this), 200);
|
||||
}).bind(this), 200);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO(vincz): clean this giant rendering function & remove listeners.
|
||||
*/
|
||||
ns.FramesListController.prototype.createPreviewTile_ = function(tileNumber) {
|
||||
var currentFrame = this.piskelController.getCurrentLayer().getFrameAt(tileNumber);
|
||||
|
@ -286,8 +301,12 @@
|
|||
previewTileRoot.appendChild(dndHandle);
|
||||
|
||||
// Add tile count
|
||||
var tileCount = document.createElement('div');
|
||||
tileCount.className = 'tile-overlay tile-count';
|
||||
var tileCount = document.createElement('button');
|
||||
tileCount.setAttribute('rel', 'tooltip');
|
||||
tileCount.setAttribute('title', 'Toggle for preview');
|
||||
tileCount.setAttribute('data-tile-number', tileNumber);
|
||||
tileCount.setAttribute('data-tile-action', ACTION.TOGGLE);
|
||||
tileCount.className = 'tile-overlay tile-count toggle-frame-action';
|
||||
tileCount.innerHTML = tileNumber + 1;
|
||||
previewTileRoot.appendChild(tileCount);
|
||||
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller');
|
||||
|
||||
var TOGGLE_LAYER_SHORTCUT = 'alt+L';
|
||||
|
||||
ns.LayersListController = function (piskelController) {
|
||||
this.piskelController = piskelController;
|
||||
this.layerPreviewShortcut = pskl.service.keyboard.Shortcuts.MISC.LAYER_PREVIEW ;
|
||||
this.layerPreviewShortcut = pskl.service.keyboard.Shortcuts.MISC.LAYER_PREVIEW;
|
||||
this.startRenamingCurrentLayer_ = this.startRenamingCurrentLayer_.bind(this);
|
||||
this.onRenameInput_ = this.onRenameInput_.bind(this);
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.init = function () {
|
||||
this.isRenaming = false;
|
||||
|
||||
this.layerItemTemplate_ = pskl.utils.Template.get('layer-item-template');
|
||||
this.layerNameInputTemplate_ = pskl.utils.Template.get('layer-name-input-template');
|
||||
this.rootEl = document.querySelector('.layers-list-container');
|
||||
this.layersListEl = document.querySelector('.layers-list');
|
||||
this.toggleLayerPreviewEl = document.querySelector('.layers-toggle-preview');
|
||||
|
@ -17,14 +20,14 @@
|
|||
this.rootEl.addEventListener('click', this.onClick_.bind(this));
|
||||
this.toggleLayerPreviewEl.addEventListener('click', this.toggleLayerPreview_.bind(this));
|
||||
|
||||
this.initCreateLayerButton_();
|
||||
this.createButtonTooltips_();
|
||||
this.initToggleLayerPreview_();
|
||||
|
||||
this.renderLayerList_();
|
||||
this.updateToggleLayerPreview_();
|
||||
|
||||
$.subscribe(Events.PISKEL_RESET, this.renderLayerList_.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.renderLayerList_ = function () {
|
||||
|
@ -46,12 +49,24 @@
|
|||
}
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.initCreateLayerButton_ = function () {
|
||||
var tooltip = pskl.utils.TooltipFormatter.format('Create a layer', null, [
|
||||
ns.LayersListController.prototype.createButtonTooltips_ = function () {
|
||||
var addTooltip = pskl.utils.TooltipFormatter.format('Create a layer', null, [
|
||||
{key : 'shift', description : 'Clone current layer'}
|
||||
]);
|
||||
var addButton = this.rootEl.querySelector('[data-action="add"]');
|
||||
addButton.setAttribute('title', tooltip);
|
||||
addButton.setAttribute('title', addTooltip);
|
||||
|
||||
var moveDownTooltip = pskl.utils.TooltipFormatter.format('Move layer down', null, [
|
||||
{key : 'shift', description : 'Move to the bottom'}
|
||||
]);
|
||||
var moveDownButton = this.rootEl.querySelector('[data-action="down"]');
|
||||
moveDownButton.setAttribute('title', moveDownTooltip);
|
||||
|
||||
var moveUpTooltip = pskl.utils.TooltipFormatter.format('Move layer up', null, [
|
||||
{key : 'shift', description : 'Move to the top'}
|
||||
]);
|
||||
var moveUpButton = this.rootEl.querySelector('[data-action="up"]');
|
||||
moveUpButton.setAttribute('title', moveUpTooltip);
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.initToggleLayerPreview_ = function () {
|
||||
|
@ -65,7 +80,6 @@
|
|||
|
||||
ns.LayersListController.prototype.updateButtonStatus_ = function () {
|
||||
var layers = this.piskelController.getLayers();
|
||||
var currentLayer = this.piskelController.getCurrentLayer();
|
||||
var index = this.piskelController.getCurrentLayerIndex();
|
||||
|
||||
var isLast = index === 0;
|
||||
|
@ -106,19 +120,46 @@
|
|||
|
||||
ns.LayersListController.prototype.addLayerItem = function (layer, index) {
|
||||
var isSelected = this.piskelController.getCurrentLayer() === layer;
|
||||
var isRenaming = isSelected && this.isRenaming;
|
||||
var layerItemHtml = pskl.utils.Template.replace(this.layerItemTemplate_, {
|
||||
'layername' : layer.getName(),
|
||||
'layerindex' : index,
|
||||
'isselected:current-layer-item' : isSelected,
|
||||
'opacity': layer.getOpacity()
|
||||
'opacity' : layer.getOpacity()
|
||||
});
|
||||
var layerItem = pskl.utils.Template.createFromHTML(layerItemHtml);
|
||||
this.layersListEl.insertBefore(layerItem, this.layersListEl.firstChild);
|
||||
if (layerItem.offsetWidth < layerItem.scrollWidth) {
|
||||
$(layerItem).find('.layer-name')
|
||||
.addClass('overflowing-name')
|
||||
.attr('title', layer.getName())
|
||||
.tooltip();
|
||||
var layerNameEl = layerItem.querySelector('.layer-name');
|
||||
layerNameEl.classList.add('overflowing-name');
|
||||
layerNameEl.setAttribute('title', layer.getName());
|
||||
layerNameEl.setAttribute('rel', 'tooltip');
|
||||
}
|
||||
if (isSelected) {
|
||||
layerItem.removeEventListener('dblclick', this.startRenamingCurrentLayer_);
|
||||
layerItem.addEventListener('dblclick', this.startRenamingCurrentLayer_);
|
||||
}
|
||||
if (isRenaming) {
|
||||
var layerNameInputHtml = pskl.utils.Template.replace(this.layerNameInputTemplate_, {
|
||||
'layername' : layer.getName()
|
||||
});
|
||||
var layerNameInput = pskl.utils.Template.createFromHTML(layerNameInputHtml);
|
||||
var layerNameEl = layerItem.querySelector('.layer-name');
|
||||
layerItem.replaceChild(layerNameInput, layerNameEl);
|
||||
layerNameInput.removeEventListener('blur', this.onRenameInput_);
|
||||
layerNameInput.removeEventListener('keydown', this.onRenameInput_);
|
||||
layerNameInput.addEventListener('blur', this.onRenameInput_);
|
||||
layerNameInput.addEventListener('keydown', this.onRenameInput_);
|
||||
layerNameInput.focus();
|
||||
layerNameInput.select();
|
||||
}
|
||||
var opacity = layer.getOpacity();
|
||||
if (opacity == 1) {
|
||||
layerItem.querySelector('.layer-item-opacity').style.color = '#ffd700';
|
||||
} else if (opacity == 0) {
|
||||
layerItem.querySelector('.layer-item-opacity').style.color = '#969696';
|
||||
} else {
|
||||
layerItem.querySelector('.layer-item-opacity').style.color = '#ffffff';
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -128,8 +169,13 @@
|
|||
if (el.classList.contains('button')) {
|
||||
this.onButtonClick_(el, evt);
|
||||
} else if (el.classList.contains('layer-name')) {
|
||||
var currentIndex = this.piskelController.getCurrentLayerIndex();
|
||||
index = pskl.utils.Dom.getData(el, 'layerIndex');
|
||||
this.piskelController.setCurrentLayerIndex(parseInt(index, 10));
|
||||
if (index != currentIndex) {
|
||||
var currentItem = el.parentElement.parentElement.querySelector('.current-layer-item');
|
||||
currentItem.removeEventListener('dblclick', this.startRenamingCurrentLayer_);
|
||||
this.piskelController.setCurrentLayerIndex(parseInt(index, 10));
|
||||
}
|
||||
} else if (el.classList.contains('layer-item-opacity')) {
|
||||
index = pskl.utils.Dom.getData(el, 'layerIndex');
|
||||
var layer = this.piskelController.getLayerAt(parseInt(index, 10));
|
||||
|
@ -138,16 +184,31 @@
|
|||
}
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.renameCurrentLayer_ = function () {
|
||||
var layer = this.piskelController.getCurrentLayer();
|
||||
var name = window.prompt('Please enter the layer name', layer.getName());
|
||||
if (name) {
|
||||
var index = this.piskelController.getCurrentLayerIndex();
|
||||
this.piskelController.renameLayerAt(index, name);
|
||||
this.renderLayerList_();
|
||||
ns.LayersListController.prototype.startRenamingCurrentLayer_ = function () {
|
||||
this.isRenaming = true;
|
||||
this.renderLayerList_();
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.onRenameInput_ = function (evt) {
|
||||
var el = evt.target || evt.srcElement;
|
||||
if (evt.key === 'Enter') {
|
||||
this.finishRenamingCurrentLayer_(el, el.value);
|
||||
} else if (!evt.key || evt.key === 'Escape') {
|
||||
this.finishRenamingCurrentLayer_(el);
|
||||
}
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.finishRenamingCurrentLayer_ = function (input, newName) {
|
||||
if (newName) {
|
||||
var index = this.piskelController.getCurrentLayerIndex();
|
||||
this.piskelController.renameLayerAt(index, newName);
|
||||
}
|
||||
input.removeEventListener('blur', this.onRenameInput_);
|
||||
input.removeEventListener('keydown', this.onRenameInput_);
|
||||
this.isRenaming = false;
|
||||
this.renderLayerList_();
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.mergeDownCurrentLayer_ = function () {
|
||||
var index = this.piskelController.getCurrentLayerIndex();
|
||||
this.piskelController.mergeDownLayerAt(index);
|
||||
|
@ -157,9 +218,9 @@
|
|||
ns.LayersListController.prototype.onButtonClick_ = function (button, evt) {
|
||||
var action = button.getAttribute('data-action');
|
||||
if (action == 'up') {
|
||||
this.piskelController.moveLayerUp();
|
||||
this.piskelController.moveLayerUp(evt.shiftKey);
|
||||
} else if (action == 'down') {
|
||||
this.piskelController.moveLayerDown();
|
||||
this.piskelController.moveLayerDown(evt.shiftKey);
|
||||
} else if (action == 'add') {
|
||||
if (evt.shiftKey) {
|
||||
this.piskelController.duplicateCurrentLayer();
|
||||
|
@ -171,7 +232,7 @@
|
|||
} else if (action == 'merge') {
|
||||
this.mergeDownCurrentLayer_();
|
||||
} else if (action == 'edit') {
|
||||
this.renameCurrentLayer_();
|
||||
this.startRenamingCurrentLayer_();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
this.minimapEl = document.createElement('DIV');
|
||||
this.minimapEl.className = 'minimap-crop-frame';
|
||||
this.minimapEl.style.display = 'none';
|
||||
$(this.container).append(this.minimapEl);
|
||||
this.container.appendChild(this.minimapEl);
|
||||
|
||||
// Init mouse events
|
||||
$(this.container).mousedown(this.onMinimapMousedown_.bind(this));
|
||||
$('body').mousemove(this.onMinimapMousemove_.bind(this));
|
||||
$('body').mouseup(this.onMinimapMouseup_.bind(this));
|
||||
this.container.addEventListener('mousedown', this.onMinimapMousedown_.bind(this));
|
||||
document.body.addEventListener('mousemove', this.onMinimapMousemove_.bind(this));
|
||||
document.body.addEventListener('mouseup', this.onMinimapMouseup_.bind(this));
|
||||
|
||||
$.subscribe(Events.ZOOM_CHANGED, $.proxy(this.renderMinimap_, this));
|
||||
$.subscribe(Events.ZOOM_CHANGED, this.renderMinimap_.bind(this));
|
||||
};
|
||||
|
||||
ns.MinimapController.prototype.renderMinimap_ = function () {
|
||||
|
@ -40,8 +40,9 @@
|
|||
var minimapSize = this.getMinimapSize_();
|
||||
var previewSize = this.getPreviewSize_();
|
||||
|
||||
var containerHeight = this.container.height();
|
||||
var containerWidth = this.container.width();
|
||||
var containerRect = this.container.getBoundingClientRect();
|
||||
var containerHeight = containerRect.height;
|
||||
var containerWidth = containerRect.width;
|
||||
|
||||
// offset(x, y) in frame pixels
|
||||
var offset = this.drawingController.getRenderer().getOffset();
|
||||
|
@ -60,7 +61,7 @@
|
|||
this.minimapEl.style.display = 'block';
|
||||
this.minimapEl.style.width = Math.min(minimapSize.width, containerWidth) + 'px';
|
||||
this.minimapEl.style.height = Math.min(minimapSize.height, containerHeight) + 'px';
|
||||
this.minimapEl.style.left = Math.max(0, left) + 'px';
|
||||
this.minimapEl.style.left = (Math.max(0, left) + Constants.RIGHT_COLUMN_PADDING_LEFT) + 'px';
|
||||
this.minimapEl.style.top = Math.max(0, top) + 'px';
|
||||
|
||||
this.isVisible = true;
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* @public
|
||||
*/
|
||||
ns.NotificationController.prototype.init = function() {
|
||||
$.subscribe(Events.SHOW_NOTIFICATION, $.proxy(this.displayMessage_, this));
|
||||
$.subscribe(Events.HIDE_NOTIFICATION, $.proxy(this.removeMessage_, this));
|
||||
$.subscribe(Events.SHOW_NOTIFICATION, this.displayMessage_.bind(this));
|
||||
$.subscribe(Events.HIDE_NOTIFICATION, this.removeMessage_.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -35,9 +35,9 @@
|
|||
* @private
|
||||
*/
|
||||
ns.NotificationController.prototype.removeMessage_ = function (evt) {
|
||||
var message = $('#user-message');
|
||||
if (message.length) {
|
||||
message.remove();
|
||||
var message = document.querySelector('#user-message');
|
||||
if (message) {
|
||||
message.parentNode.removeChild(message);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -35,24 +35,24 @@
|
|||
// Initialize colorpickers:
|
||||
var colorPicker = $('#color-picker');
|
||||
colorPicker.spectrum($.extend({color: Constants.DEFAULT_PEN_COLOR}, spectrumCfg));
|
||||
colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this));
|
||||
this.setTitleOnPicker_(Constants.DEFAULT_PEN_COLOR, colorPicker);
|
||||
colorPicker.change({isPrimary : true}, this.onPickerChange_.bind(this));
|
||||
this.setTitleOnPicker_(Constants.DEFAULT_PEN_COLOR, colorPicker.get(0));
|
||||
|
||||
var secondaryColorPicker = $('#secondary-color-picker');
|
||||
secondaryColorPicker.spectrum($.extend({color: Constants.TRANSPARENT_COLOR}, spectrumCfg));
|
||||
secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this));
|
||||
this.setTitleOnPicker_(Constants.TRANSPARENT_COLOR, secondaryColorPicker);
|
||||
secondaryColorPicker.change({isPrimary : false}, this.onPickerChange_.bind(this));
|
||||
this.setTitleOnPicker_(Constants.TRANSPARENT_COLOR, secondaryColorPicker.get(0));
|
||||
|
||||
var swapColorsIcon = $('.swap-colors-button');
|
||||
swapColorsIcon.click(this.swapColors.bind(this));
|
||||
var swapColorsIcon = document.querySelector('.swap-colors-button');
|
||||
swapColorsIcon.addEventListener('click', this.swapColors.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
|
||||
var inputPicker = $(evt.target);
|
||||
var color = inputPicker.val();
|
||||
ns.PaletteController.prototype.onPickerChange_ = function(evt) {
|
||||
var inputPicker = evt.target;
|
||||
var color = inputPicker.value;
|
||||
|
||||
if (color != Constants.TRANSPARENT_COLOR) {
|
||||
// Unless the color is TRANSPARENT_COLOR, format it to hexstring, as
|
||||
|
@ -71,7 +71,6 @@
|
|||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.onColorSelected_ = function(args, evt, color) {
|
||||
var inputPicker = $(evt.target);
|
||||
if (args.isPrimary) {
|
||||
this.setPrimaryColor_(color);
|
||||
} else {
|
||||
|
@ -80,12 +79,12 @@
|
|||
};
|
||||
|
||||
ns.PaletteController.prototype.setPrimaryColor_ = function (color) {
|
||||
this.updateColorPicker_(color, $('#color-picker'));
|
||||
this.updateColorPicker_(color, document.querySelector('#color-picker'));
|
||||
$.publish(Events.PRIMARY_COLOR_SELECTED, [color]);
|
||||
};
|
||||
|
||||
ns.PaletteController.prototype.setSecondaryColor_ = function (color) {
|
||||
this.updateColorPicker_(color, $('#secondary-color-picker'));
|
||||
this.updateColorPicker_(color, document.querySelector('#secondary-color-picker'));
|
||||
$.publish(Events.SECONDARY_COLOR_SELECTED, [color]);
|
||||
};
|
||||
|
||||
|
@ -104,6 +103,7 @@
|
|||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.updateColorPicker_ = function (color, colorPicker) {
|
||||
var jqueryColorPicker = $(colorPicker);
|
||||
if (color == Constants.TRANSPARENT_COLOR) {
|
||||
// We can set the current palette color to transparent.
|
||||
// You can then combine this transparent color with an advanced
|
||||
|
@ -114,17 +114,17 @@
|
|||
// The colorpicker can't be set to a transparent state.
|
||||
// We set its background to white and insert the
|
||||
// string "TRANSPARENT" to mimic this state:
|
||||
colorPicker.spectrum('set', Constants.TRANSPARENT_COLOR);
|
||||
colorPicker.val(Constants.TRANSPARENT_COLOR);
|
||||
jqueryColorPicker.spectrum('set', Constants.TRANSPARENT_COLOR);
|
||||
colorPicker.value = Constants.TRANSPARENT_COLOR;
|
||||
} else {
|
||||
colorPicker.spectrum('set', color);
|
||||
jqueryColorPicker.spectrum('set', color);
|
||||
}
|
||||
this.setTitleOnPicker_(color, colorPicker);
|
||||
};
|
||||
|
||||
ns.PaletteController.prototype.setTitleOnPicker_ = function (title, colorPicker) {
|
||||
var parent = colorPicker.parent();
|
||||
title = parent.data('initial-title') + '<br/>' + title;
|
||||
parent.attr('data-original-title', title);
|
||||
var parent = colorPicker.parentNode;
|
||||
title = parent.dataset.initialTitle + '<br/>' + title;
|
||||
parent.dataset.originalTitle = title;
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
$.subscribe(Events.CURRENT_COLORS_UPDATED, this.fillColorListContainer.bind(this));
|
||||
$.subscribe(Events.PRIMARY_COLOR_SELECTED, this.highlightSelectedColors.bind(this));
|
||||
$.subscribe(Events.SECONDARY_COLOR_SELECTED, this.highlightSelectedColors.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
|
||||
var shortcuts = pskl.service.keyboard.Shortcuts;
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.COLOR.PREVIOUS_COLOR, this.selectPreviousColor_.bind(this));
|
||||
|
@ -51,12 +51,17 @@
|
|||
};
|
||||
|
||||
ns.PalettesListController.prototype.fillColorListContainer = function () {
|
||||
|
||||
var colors = this.getSelectedPaletteColors_();
|
||||
|
||||
if (colors.length > 0) {
|
||||
var html = colors.map(function (color, index) {
|
||||
return pskl.utils.Template.replace(this.paletteColorTemplate_, {color : color, index : index});
|
||||
var html = colors.filter(function (color) {
|
||||
return !!color;
|
||||
}).map(function (color, index) {
|
||||
return pskl.utils.Template.replace(this.paletteColorTemplate_, {
|
||||
color : color,
|
||||
index : index + 1,
|
||||
title : color.toUpperCase()
|
||||
});
|
||||
}.bind(this)).join('');
|
||||
this.colorListContainer_.innerHTML = html;
|
||||
|
||||
|
@ -64,6 +69,10 @@
|
|||
} else {
|
||||
this.colorListContainer_.innerHTML = pskl.utils.Template.get('palettes-list-no-colors-partial');
|
||||
}
|
||||
|
||||
// If we have more than 10 colors, use tiny mode, where 10 colors will fit on the same
|
||||
// line.
|
||||
this.colorListContainer_.classList.toggle('tiny', colors.length > 10);
|
||||
};
|
||||
|
||||
ns.PalettesListController.prototype.selectPalette = function (paletteId) {
|
||||
|
@ -101,7 +110,7 @@
|
|||
var currentIndex = 0;
|
||||
var selectedColor = document.querySelector('.' + PRIMARY_COLOR_CLASSNAME);
|
||||
if (selectedColor) {
|
||||
currentIndex = parseInt(selectedColor.dataset.colorIndex, 10);
|
||||
currentIndex = parseInt(selectedColor.dataset.colorIndex, 10) - 1;
|
||||
}
|
||||
return currentIndex;
|
||||
};
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller');
|
||||
|
||||
ns.PenSizeController = function () {};
|
||||
ns.PenSizeController = function () {
|
||||
this.sizePicker = new pskl.widgets.SizePicker(this.onSizePickerChanged_.bind(this));
|
||||
};
|
||||
|
||||
ns.PenSizeController.prototype.init = function () {
|
||||
this.container = document.querySelector('.pen-size-container');
|
||||
pskl.utils.Event.addEventListener(this.container, 'click', this.onPenSizeOptionClick_, this);
|
||||
this.sizePicker.init(document.querySelector('.pen-size-container'));
|
||||
|
||||
$.subscribe(Events.PEN_SIZE_CHANGED, this.onPenSizeChanged_.bind(this));
|
||||
|
||||
this.updateSelectedOption_();
|
||||
};
|
||||
|
||||
ns.PenSizeController.prototype.onPenSizeOptionClick_ = function (e) {
|
||||
var size = e.target.dataset.size;
|
||||
if (!isNaN(size)) {
|
||||
size = parseInt(size, 10);
|
||||
pskl.app.penSizeService.setPenSize(size);
|
||||
}
|
||||
ns.PenSizeController.prototype.onSizePickerChanged_ = function (size) {
|
||||
pskl.app.penSizeService.setPenSize(size);
|
||||
};
|
||||
|
||||
ns.PenSizeController.prototype.onPenSizeChanged_ = function (e) {
|
||||
|
@ -25,19 +21,7 @@
|
|||
};
|
||||
|
||||
ns.PenSizeController.prototype.updateSelectedOption_ = function () {
|
||||
pskl.utils.Dom.removeClass('labeled', this.container);
|
||||
pskl.utils.Dom.removeClass('selected', this.container);
|
||||
var size = pskl.app.penSizeService.getPenSize();
|
||||
var selectedOption;
|
||||
if (size <= 4) {
|
||||
selectedOption = this.container.querySelector('[data-size="' + size + '"]');
|
||||
} else {
|
||||
selectedOption = this.container.querySelector('[data-size="4"]');
|
||||
selectedOption.classList.add('labeled');
|
||||
selectedOption.setAttribute('real-pen-size', size);
|
||||
}
|
||||
if (selectedOption) {
|
||||
selectedOption.classList.add('selected');
|
||||
}
|
||||
this.sizePicker.setSize(size);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
};
|
||||
|
||||
ns.ProgressBarController.prototype.init = function () {
|
||||
$.subscribe(Events.SHOW_PROGRESS, $.proxy(this.showProgress_, this));
|
||||
$.subscribe(Events.UPDATE_PROGRESS, $.proxy(this.updateProgress_, this));
|
||||
$.subscribe(Events.HIDE_PROGRESS, $.proxy(this.hideProgress_, this));
|
||||
$.subscribe(Events.SHOW_PROGRESS, this.showProgress_.bind(this));
|
||||
$.subscribe(Events.UPDATE_PROGRESS, this.updateProgress_.bind(this));
|
||||
$.subscribe(Events.HIDE_PROGRESS, this.hideProgress_.bind(this));
|
||||
};
|
||||
|
||||
ns.ProgressBarController.prototype.showProgress_ = function (event, progressInfo) {
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
// Set SimplePen as default selected tool:
|
||||
this.selectTool_(this.tools[0]);
|
||||
// Activate listener on tool panel:
|
||||
$('#tool-section').mousedown($.proxy(this.onToolIconClicked_, this));
|
||||
var toolSection = document.querySelector('#tool-section');
|
||||
toolSection.addEventListener('mousedown', this.onToolIconClicked_.bind(this));
|
||||
|
||||
$.subscribe(Events.SELECT_TOOL, this.onSelectToolEvent_.bind(this));
|
||||
$.subscribe(Events.SHORTCUTS_CHANGED, this.createToolsDom_.bind(this));
|
||||
|
@ -45,14 +46,14 @@
|
|||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
|
||||
var stage = $('body');
|
||||
var previousSelectedToolClass = stage.data('selected-tool-class');
|
||||
var stage = document.body;
|
||||
var previousSelectedToolClass = stage.dataset.selectedToolClass;
|
||||
if (previousSelectedToolClass) {
|
||||
stage.removeClass(previousSelectedToolClass);
|
||||
stage.removeClass(pskl.tools.drawing.Move.TOOL_ID);
|
||||
stage.classList.remove(previousSelectedToolClass);
|
||||
stage.classList.remove(pskl.tools.drawing.Move.TOOL_ID);
|
||||
}
|
||||
stage.addClass(tool.toolId);
|
||||
stage.data('selected-tool-class', tool.toolId);
|
||||
stage.classList.add(tool.toolId);
|
||||
stage.dataset.selectedToolClass = tool.toolId;
|
||||
};
|
||||
|
||||
ns.ToolController.prototype.onSelectToolEvent_ = function(event, toolId) {
|
||||
|
@ -69,11 +70,13 @@
|
|||
this.currentSelectedTool = tool;
|
||||
this.activateToolOnStage_(this.currentSelectedTool);
|
||||
|
||||
var selectedToolElement = $('#tool-section .tool-icon.selected');
|
||||
var toolElement = $('[data-tool-id=' + tool.toolId + ']');
|
||||
var selectedToolElement = document.querySelector('#tool-section .tool-icon.selected');
|
||||
if (selectedToolElement) {
|
||||
selectedToolElement.classList.remove('selected');
|
||||
}
|
||||
|
||||
selectedToolElement.removeClass('selected');
|
||||
toolElement.addClass('selected');
|
||||
var toolElement = document.querySelector('[data-tool-id=' + tool.toolId + ']');
|
||||
toolElement.classList.add('selected');
|
||||
|
||||
$.publish(Events.TOOL_SELECTED, [tool]);
|
||||
};
|
||||
|
@ -82,11 +85,11 @@
|
|||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
|
||||
var target = $(evt.target);
|
||||
var clickedTool = target.closest('.tool-icon');
|
||||
var target = evt.target;
|
||||
var clickedTool = pskl.utils.Dom.getParentWithData(target, 'toolId');
|
||||
|
||||
if (clickedTool.length) {
|
||||
var toolId = clickedTool.data().toolId;
|
||||
if (clickedTool) {
|
||||
var toolId = clickedTool.dataset.toolId;
|
||||
var tool = this.getToolById_(toolId);
|
||||
if (tool) {
|
||||
this.selectTool_(tool);
|
||||
|
@ -116,7 +119,7 @@
|
|||
var tool = this.tools[i];
|
||||
html += this.toolIconBuilder.createIcon(tool);
|
||||
}
|
||||
$('#tools-container').html(html);
|
||||
document.querySelector('#tools-container').innerHTML = html;
|
||||
};
|
||||
|
||||
ns.ToolController.prototype.addKeyboardShortcuts_ = function () {
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller');
|
||||
var SHOW_MORE_CLASS = 'show-more';
|
||||
|
||||
ns.TransformationsController = function () {
|
||||
this.tools = [
|
||||
new pskl.tools.transform.Flip(),
|
||||
new pskl.tools.transform.Rotate(),
|
||||
new pskl.tools.transform.Clone(),
|
||||
new pskl.tools.transform.Center()
|
||||
new pskl.tools.transform.Center(),
|
||||
new pskl.tools.transform.Crop(),
|
||||
];
|
||||
|
||||
this.toolIconBuilder = new pskl.tools.ToolIconBuilder();
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.init = function () {
|
||||
var container = document.querySelector('.transformations-container');
|
||||
this.toolsContainer = container.querySelector('.tools-wrapper');
|
||||
container.addEventListener('click', this.onTransformationClick_.bind(this));
|
||||
this.container = document.querySelector('.transformations-container');
|
||||
this.container.addEventListener('click', this.onTransformationClick_.bind(this));
|
||||
|
||||
this.showMoreLink = this.container.querySelector('.transformations-show-more-link');
|
||||
this.showMoreLink.addEventListener('click', this.toggleShowMoreTools_.bind(this));
|
||||
|
||||
this.createToolsDom_();
|
||||
this.updateShowMoreLink_();
|
||||
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.applyTool = function (toolId, evt) {
|
||||
|
@ -30,13 +38,35 @@
|
|||
|
||||
ns.TransformationsController.prototype.onTransformationClick_ = function (evt) {
|
||||
var toolId = evt.target.dataset.toolId;
|
||||
this.applyTool(toolId, evt);
|
||||
if (toolId) {
|
||||
this.applyTool(toolId, evt);
|
||||
}
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.toggleShowMoreTools_ = function (evt) {
|
||||
var showMore = pskl.UserSettings.get(pskl.UserSettings.TRANSFORM_SHOW_MORE);
|
||||
pskl.UserSettings.set(pskl.UserSettings.TRANSFORM_SHOW_MORE, !showMore);
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.onUserSettingsChange_ = function (evt, settingName) {
|
||||
if (settingName == pskl.UserSettings.TRANSFORM_SHOW_MORE) {
|
||||
this.updateShowMoreLink_();
|
||||
}
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.updateShowMoreLink_ = function () {
|
||||
var showMoreEnabled = pskl.UserSettings.get(pskl.UserSettings.TRANSFORM_SHOW_MORE);
|
||||
this.container.classList.toggle(SHOW_MORE_CLASS, showMoreEnabled);
|
||||
|
||||
// Hide the link in case there are 4 or less tools available.
|
||||
this.showMoreLink.classList.toggle('hidden', this.tools.length < 5);
|
||||
};
|
||||
|
||||
ns.TransformationsController.prototype.createToolsDom_ = function() {
|
||||
var html = this.tools.reduce(function (p, tool) {
|
||||
return p + this.toolIconBuilder.createIcon(tool, 'left');
|
||||
}.bind(this), '');
|
||||
this.toolsContainer.innerHTML = html;
|
||||
var toolsContainer = this.container.querySelector('.tools-wrapper');
|
||||
toolsContainer.innerHTML = html;
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -10,13 +10,12 @@
|
|||
|
||||
this.localStorageItemTemplate_ = pskl.utils.Template.get('local-storage-item-template');
|
||||
|
||||
this.service_ = pskl.app.localStorageService;
|
||||
this.piskelList = $('.local-piskel-list');
|
||||
this.prevSessionContainer = $('.previous-session');
|
||||
this.service_ = pskl.app.indexedDbStorageService;
|
||||
this.piskelList = document.querySelector('.local-piskel-list');
|
||||
|
||||
this.fillLocalPiskelsList_();
|
||||
|
||||
this.piskelList.click(this.onPiskelsListClick_.bind(this));
|
||||
this.piskelList.addEventListener('click', this.onPiskelsListClick_.bind(this));
|
||||
};
|
||||
|
||||
ns.BrowseLocalController.prototype.onPiskelsListClick_ = function (evt) {
|
||||
|
@ -36,24 +35,24 @@
|
|||
};
|
||||
|
||||
ns.BrowseLocalController.prototype.fillLocalPiskelsList_ = function () {
|
||||
var html = '';
|
||||
var keys = this.service_.getKeys();
|
||||
|
||||
keys.sort(function (k1, k2) {
|
||||
if (k1.date < k2.date) {return 1;}
|
||||
if (k1.date > k2.date) {return -1;}
|
||||
return 0;
|
||||
});
|
||||
|
||||
keys.forEach((function (key) {
|
||||
var date = pskl.utils.DateUtils.format(key.date, '{{Y}}/{{M}}/{{D}} {{H}}:{{m}}');
|
||||
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {
|
||||
name : key.name,
|
||||
date : date
|
||||
this.service_.getKeys().then(function (keys) {
|
||||
var html = '';
|
||||
keys.sort(function (k1, k2) {
|
||||
if (k1.date < k2.date) {return 1;}
|
||||
if (k1.date > k2.date) {return -1;}
|
||||
return 0;
|
||||
});
|
||||
}).bind(this));
|
||||
|
||||
var tableBody_ = this.piskelList.get(0).tBodies[0];
|
||||
tableBody_.innerHTML = html;
|
||||
keys.forEach((function (key) {
|
||||
var date = pskl.utils.DateUtils.format(key.date, '{{Y}}/{{M}}/{{D}} {{H}}:{{m}}');
|
||||
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {
|
||||
name : key.name,
|
||||
date : date
|
||||
});
|
||||
}).bind(this));
|
||||
|
||||
var tableBody_ = this.piskelList.tBodies[0];
|
||||
tableBody_.innerHTML = html;
|
||||
}.bind(this));
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
'unsupported-browser' : {
|
||||
template : 'templates/dialogs/unsupported-browser.html',
|
||||
controller : ns.UnsupportedBrowserController
|
||||
},
|
||||
'browse-backups' : {
|
||||
template : 'templates/dialogs/browse-backups.html',
|
||||
controller : ns.backups.BrowseBackups
|
||||
}
|
||||
};
|
||||
|
||||
|
|
81
src/js/controller/dialogs/backups/BrowseBackups.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.dialogs.backups');
|
||||
|
||||
var stepDefinitions = {
|
||||
'SELECT_SESSION' : {
|
||||
controller : ns.steps.SelectSession,
|
||||
template : 'backups-select-session'
|
||||
},
|
||||
'SESSION_DETAILS' : {
|
||||
controller : ns.steps.SessionDetails,
|
||||
template : 'backups-session-details'
|
||||
},
|
||||
};
|
||||
|
||||
ns.BrowseBackups = function (piskelController, args) {
|
||||
this.piskelController = piskelController;
|
||||
|
||||
// Backups data object used by steps to communicate and share their
|
||||
// results.
|
||||
this.backupsData = {
|
||||
sessions: [],
|
||||
selectedSession : null
|
||||
};
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.BrowseBackups, pskl.controller.dialogs.AbstractDialogController);
|
||||
|
||||
ns.BrowseBackups.prototype.init = function () {
|
||||
this.superclass.init.call(this);
|
||||
|
||||
// Prepare wizard steps.
|
||||
this.steps = this.createSteps_();
|
||||
|
||||
// Start wizard widget.
|
||||
var wizardContainer = document.querySelector('.backups-wizard-container');
|
||||
this.wizard = new pskl.widgets.Wizard(this.steps, wizardContainer);
|
||||
this.wizard.init();
|
||||
|
||||
this.wizard.goTo('SELECT_SESSION');
|
||||
};
|
||||
|
||||
ns.BrowseBackups.prototype.back = function () {
|
||||
this.wizard.back();
|
||||
this.wizard.getCurrentStep().instance.onShow();
|
||||
};
|
||||
|
||||
ns.BrowseBackups.prototype.next = function () {
|
||||
var step = this.wizard.getCurrentStep();
|
||||
if (step.name === 'SELECT_SESSION') {
|
||||
this.wizard.goTo('SESSION_DETAILS');
|
||||
}
|
||||
};
|
||||
|
||||
ns.BrowseBackups.prototype.destroy = function (file) {
|
||||
Object.keys(this.steps).forEach(function (stepName) {
|
||||
var step = this.steps[stepName];
|
||||
step.instance.destroy();
|
||||
step.instance = null;
|
||||
step.el = null;
|
||||
}.bind(this));
|
||||
|
||||
this.superclass.destroy.call(this);
|
||||
};
|
||||
|
||||
ns.BrowseBackups.prototype.createSteps_ = function () {
|
||||
var steps = {};
|
||||
Object.keys(stepDefinitions).forEach(function (stepName) {
|
||||
var definition = stepDefinitions[stepName];
|
||||
var el = pskl.utils.Template.getAsHTML(definition.template);
|
||||
var instance = new definition.controller(this.piskelController, this, el);
|
||||
instance.init();
|
||||
steps[stepName] = {
|
||||
name: stepName,
|
||||
el: el,
|
||||
instance: instance
|
||||
};
|
||||
}.bind(this));
|
||||
|
||||
return steps;
|
||||
};
|
||||
})();
|
101
src/js/controller/dialogs/backups/steps/SelectSession.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.dialogs.backups.steps');
|
||||
|
||||
// Should match the transition duration for.session-item defined in dialogs-browse-backups.css
|
||||
var DELETE_TRANSITION_DURATION = 500;
|
||||
/**
|
||||
* Helper that returns a promise that will resolve after waiting for a
|
||||
* given time (in ms).
|
||||
*
|
||||
* @param {Number} time
|
||||
* The time to wait.
|
||||
* @return {Promise} promise that resolves after time.
|
||||
*/
|
||||
var wait = function (time) {
|
||||
var deferred = Q.defer();
|
||||
setTimeout(function () {
|
||||
deferred.resolve();
|
||||
}, time);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ns.SelectSession = function (piskelController, backupsController, container) {
|
||||
this.piskelController = piskelController;
|
||||
this.backupsController = backupsController;
|
||||
this.container = container;
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.addEventListener = function (el, type, cb) {
|
||||
pskl.utils.Event.addEventListener(el, type, cb, this);
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.init = function () {
|
||||
this.addEventListener(this.container, 'click', this.onContainerClick_);
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.onShow = function () {
|
||||
this.update();
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.update = function () {
|
||||
pskl.app.backupService.list().then(function (sessions) {
|
||||
var html = this.getMarkupForSessions_(sessions);
|
||||
this.container.querySelector('.session-list').innerHTML = html;
|
||||
}.bind(this)).catch(function () {
|
||||
var html = pskl.utils.Template.get('session-list-error');
|
||||
this.container.querySelector('.session-list').innerHTML = html;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.getMarkupForSessions_ = function (sessions) {
|
||||
if (sessions.length === 0) {
|
||||
return pskl.utils.Template.get('session-list-empty');
|
||||
}
|
||||
|
||||
var sessionItemTemplate = pskl.utils.Template.get('session-list-item');
|
||||
return sessions.reduce(function (previous, session) {
|
||||
if (session.id === pskl.app.sessionId) {
|
||||
// Do not show backups for the current session.
|
||||
return previous;
|
||||
}
|
||||
var view = {
|
||||
id: session.id,
|
||||
name: session.name,
|
||||
description: session.description ? '- ' + session.description : '',
|
||||
date: pskl.utils.DateUtils.format(session.endDate, 'the {{Y}}/{{M}}/{{D}} at {{H}}:{{m}}'),
|
||||
count: session.count === 1 ? '1 snapshot' : session.count + ' snapshots'
|
||||
};
|
||||
return previous + pskl.utils.Template.replace(sessionItemTemplate, view);
|
||||
}, '');
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.destroy = function () {
|
||||
pskl.utils.Event.removeAllEventListeners(this);
|
||||
};
|
||||
|
||||
ns.SelectSession.prototype.onContainerClick_ = function (evt) {
|
||||
var sessionId = evt.target.dataset.sessionId;
|
||||
if (!sessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var action = evt.target.dataset.action;
|
||||
if (action == 'view') {
|
||||
this.backupsController.backupsData.selectedSession = sessionId;
|
||||
this.backupsController.next();
|
||||
} else if (action == 'delete') {
|
||||
if (window.confirm('Are you sure you want to delete this session?')) {
|
||||
evt.target.closest('.session-item').classList.add('deleting');
|
||||
Q.all([
|
||||
pskl.app.backupService.deleteSession(sessionId),
|
||||
// Wait for 500ms for the .hide opacity transition.
|
||||
wait(DELETE_TRANSITION_DURATION)
|
||||
]).then(function () {
|
||||
// Refresh the list of sessions
|
||||
this.update();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
102
src/js/controller/dialogs/backups/steps/SessionDetails.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.dialogs.backups.steps');
|
||||
|
||||
// Should match the preview dimensions defined in dialogs-browse-backups.css
|
||||
var PREVIEW_SIZE = 60;
|
||||
|
||||
ns.SessionDetails = function (piskelController, backupsController, container) {
|
||||
this.piskelController = piskelController;
|
||||
this.backupsController = backupsController;
|
||||
this.container = container;
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.init = function () {
|
||||
this.backButton = this.container.querySelector('.back-button');
|
||||
this.addEventListener(this.backButton, 'click', this.onBackClick_);
|
||||
this.addEventListener(this.container, 'click', this.onContainerClick_);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.destroy = function () {
|
||||
pskl.utils.Event.removeAllEventListeners(this);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.addEventListener = function (el, type, cb) {
|
||||
pskl.utils.Event.addEventListener(el, type, cb, this);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.onShow = function () {
|
||||
var sessionId = this.backupsController.backupsData.selectedSession;
|
||||
pskl.app.backupService.getSnapshotsBySessionId(sessionId).then(function (snapshots) {
|
||||
var html = this.getMarkupForSnapshots_(snapshots);
|
||||
this.container.querySelector('.snapshot-list').innerHTML = html;
|
||||
|
||||
// Load the image of the first frame for each sprite and update the list.
|
||||
snapshots.forEach(function (snapshot) {
|
||||
this.updateSnapshotPreview_(snapshot);
|
||||
}.bind(this));
|
||||
}.bind(this)).catch(function () {
|
||||
var html = pskl.utils.Template.get('snapshot-list-error');
|
||||
this.container.querySelector('.snapshot-list').innerHTML = html;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.getMarkupForSnapshots_ = function (snapshots) {
|
||||
if (snapshots.length === 0) {
|
||||
// This should normally never happen, all sessions have at least one snapshot and snapshots
|
||||
// can not be individually deleted.
|
||||
console.warn('Could not retrieve snapshots for a session');
|
||||
return pskl.utils.Template.get('snapshot-list-empty');
|
||||
}
|
||||
|
||||
var sessionItemTemplate = pskl.utils.Template.get('snapshot-list-item');
|
||||
return snapshots.reduce(function (previous, snapshot) {
|
||||
var view = {
|
||||
id: snapshot.id,
|
||||
name: snapshot.name,
|
||||
description: snapshot.description ? '- ' + snapshot.description : '',
|
||||
date: pskl.utils.DateUtils.format(snapshot.date, 'the {{Y}}/{{M}}/{{D}} at {{H}}:{{m}}'),
|
||||
frames: snapshot.frames === 1 ? '1 frame' : snapshot.frames + ' frames',
|
||||
resolution: pskl.utils.StringUtils.formatSize(snapshot.width, snapshot.height),
|
||||
fps: snapshot.fps
|
||||
};
|
||||
return previous + pskl.utils.Template.replace(sessionItemTemplate, view);
|
||||
}, '');
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.updateSnapshotPreview_ = function (snapshot) {
|
||||
pskl.utils.serialization.Deserializer.deserialize(
|
||||
JSON.parse(snapshot.serialized),
|
||||
function (piskel) {
|
||||
var selector = '.snapshot-item[data-snapshot-id="' + snapshot.id + '"] .snapshot-preview';
|
||||
var previewContainer = this.container.querySelector(selector);
|
||||
if (!previewContainer) {
|
||||
return;
|
||||
}
|
||||
var image = this.getFirstFrameAsImage_(piskel);
|
||||
previewContainer.appendChild(image);
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.getFirstFrameAsImage_ = function (piskel) {
|
||||
var frame = pskl.utils.LayerUtils.mergeFrameAt(piskel.getLayers(), 0);
|
||||
var wZoom = PREVIEW_SIZE / piskel.width;
|
||||
var hZoom = PREVIEW_SIZE / piskel.height;
|
||||
var zoom = Math.min(hZoom, wZoom);
|
||||
return pskl.utils.FrameUtils.toImage(frame, zoom);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.onBackClick_ = function () {
|
||||
this.backupsController.back(this);
|
||||
};
|
||||
|
||||
ns.SessionDetails.prototype.onContainerClick_ = function (evt) {
|
||||
var action = evt.target.dataset.action;
|
||||
if (action == 'load' && window.confirm(Constants.CONFIRM_OVERWRITE)) {
|
||||
var snapshotId = evt.target.dataset.snapshotId * 1;
|
||||
pskl.app.backupService.loadSnapshotById(snapshotId).then(function () {
|
||||
$.publish(Events.DIALOG_HIDE);
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -80,7 +80,13 @@
|
|||
var step = this.wizard.getCurrentStep();
|
||||
|
||||
if (step.name === 'IMAGE_IMPORT') {
|
||||
this.wizard.goTo('SELECT_MODE');
|
||||
if (this.piskelController.isEmpty()) {
|
||||
// If the current sprite is empty finalize immediately and replace the current sprite.
|
||||
this.mergeData.importMode = ns.steps.SelectMode.MODES.REPLACE;
|
||||
this.finalizeImport_();
|
||||
} else {
|
||||
this.wizard.goTo('SELECT_MODE');
|
||||
}
|
||||
} else if (step.name === 'SELECT_MODE') {
|
||||
if (this.mergeData.importMode === ns.steps.SelectMode.MODES.REPLACE) {
|
||||
this.finalizeImport_();
|
||||
|
@ -144,9 +150,7 @@
|
|||
|
||||
if (mode === ns.steps.SelectMode.MODES.REPLACE) {
|
||||
// Replace the current piskel and close the dialog.
|
||||
var message = 'This will replace your current animation,' +
|
||||
' are you sure you want to continue?';
|
||||
if (window.confirm(message)) {
|
||||
if (window.confirm(Constants.CONFIRM_OVERWRITE)) {
|
||||
this.piskelController.setPiskel(piskel);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
|
|
@ -69,16 +69,15 @@
|
|||
var anchorInfo = this.container.querySelector('.import-resize-anchor-info');
|
||||
if (isBigger && keep) {
|
||||
anchorInfo.innerHTML = [
|
||||
'<span class="import-resize-warning">',
|
||||
'Imported content will be cropped!',
|
||||
'</span>',
|
||||
' ',
|
||||
'Select crop origin'
|
||||
'<div class="import-resize-warning">',
|
||||
' Imported content will be cropped!',
|
||||
'</div>',
|
||||
'Select crop anchor:'
|
||||
].join('');
|
||||
} else if (isBigger) {
|
||||
anchorInfo.innerHTML = 'Select the anchor for resizing the canvas';
|
||||
anchorInfo.innerHTML = 'Select resize anchor:';
|
||||
} else {
|
||||
anchorInfo.innerHTML = 'Select where the import should be positioned';
|
||||
anchorInfo.innerHTML = 'Select position anchor:';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -42,12 +42,17 @@
|
|||
this.addEventListener(this.frameOffsetY, 'keyup', this.onFrameInputKeyUp_);
|
||||
|
||||
pskl.utils.FileUtils.readImageFile(this.file_, this.onImageLoaded_.bind(this));
|
||||
|
||||
if (this.piskelController.isEmpty()) {
|
||||
this.nextButton.textContent = 'import';
|
||||
}
|
||||
};
|
||||
|
||||
ns.ImageImport.prototype.onNextClick = function () {
|
||||
this.container.classList.add('import-image-loading');
|
||||
this.createPiskelFromImage().then(function (piskel) {
|
||||
this.mergeData.mergePiskel = piskel;
|
||||
this.container.classList.remove('import-image-loading');
|
||||
this.superclass.onNextClick.call(this);
|
||||
}.bind(this)).catch(function (e) {
|
||||
console.error(e);
|
||||
|
@ -69,9 +74,9 @@
|
|||
{
|
||||
importType: this.getImportType_(),
|
||||
frameSizeX: this.getImportType_() === 'single' ?
|
||||
this.resizeWidth.value : this.sanitizeInputValue_(this.frameSizeX, 1),
|
||||
this.resizeWidth.value : this.sanitizeInputValue_(this.frameSizeX, 1),
|
||||
frameSizeY: this.getImportType_() === 'single' ?
|
||||
this.resizeHeight.value : this.sanitizeInputValue_(this.frameSizeY, 1),
|
||||
this.resizeHeight.value : this.sanitizeInputValue_(this.frameSizeY, 1),
|
||||
frameOffsetX: this.sanitizeInputValue_(this.frameOffsetX, 0),
|
||||
frameOffsetY: this.sanitizeInputValue_(this.frameOffsetY, 0),
|
||||
smoothing: !!this.smoothResize.checked,
|
||||
|
@ -257,9 +262,7 @@
|
|||
context.lineTo(maxWidth * scaleX, y * scaleY);
|
||||
}
|
||||
|
||||
// Set the line style to dashed
|
||||
context.lineWidth = 1;
|
||||
// context.setLineDash([2, 1]);
|
||||
context.strokeStyle = 'gold';
|
||||
context.stroke();
|
||||
|
||||
|
|
|
@ -12,11 +12,15 @@
|
|||
/**
|
||||
* Set the current piskel. Will reset the selected frame and layer unless specified
|
||||
* @param {Object} piskel
|
||||
* @param {Boolean} preserveState if true, keep the selected frame and layer
|
||||
* @param {Object} options:
|
||||
* preserveState {Boolean} if true, keep the selected frame and layer
|
||||
* noSnapshot {Boolean} if true, do not save a snapshot in the piskel
|
||||
* history for this call to setPiskel
|
||||
*/
|
||||
ns.PiskelController.prototype.setPiskel = function (piskel, preserveState) {
|
||||
ns.PiskelController.prototype.setPiskel = function (piskel, options) {
|
||||
this.piskel = piskel;
|
||||
if (!preserveState) {
|
||||
options = options || {};
|
||||
if (!options.preserveState) {
|
||||
this.currentLayerIndex = 0;
|
||||
this.currentFrameIndex = 0;
|
||||
}
|
||||
|
@ -118,6 +122,17 @@
|
|||
l.addFrameAt(this.createEmptyFrame_(), index);
|
||||
}.bind(this));
|
||||
|
||||
this.onFrameAddedAt_(index);
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.onFrameAddedAt_ = function (index) {
|
||||
this.piskel.hiddenFrames = this.piskel.hiddenFrames.map(function (hiddenIndex) {
|
||||
if (hiddenIndex >= index) {
|
||||
return hiddenIndex + 1;
|
||||
}
|
||||
return hiddenIndex;
|
||||
});
|
||||
|
||||
this.setCurrentFrameIndex(index);
|
||||
};
|
||||
|
||||
|
@ -131,6 +146,15 @@
|
|||
this.getLayers().forEach(function (l) {
|
||||
l.removeFrameAt(index);
|
||||
});
|
||||
|
||||
// Update the array of hidden frames since some hidden indexes might have shifted.
|
||||
this.piskel.hiddenFrames = this.piskel.hiddenFrames.map(function (hiddenIndex) {
|
||||
if (hiddenIndex > index) {
|
||||
return hiddenIndex - 1;
|
||||
}
|
||||
return hiddenIndex;
|
||||
});
|
||||
|
||||
// Current frame index is impacted if the removed frame was before the current frame
|
||||
if (this.currentFrameIndex >= index && this.currentFrameIndex > 0) {
|
||||
this.setCurrentFrameIndex(this.currentFrameIndex - 1);
|
||||
|
@ -145,13 +169,64 @@
|
|||
this.getLayers().forEach(function (l) {
|
||||
l.duplicateFrameAt(index);
|
||||
});
|
||||
this.setCurrentFrameIndex(index + 1);
|
||||
this.onFrameAddedAt_(index + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle frame visibility for the frame at the provided index.
|
||||
* A visible frame will be included in the animated preview.
|
||||
*/
|
||||
ns.PiskelController.prototype.toggleFrameVisibilityAt = function (index) {
|
||||
var hiddenFrames = this.piskel.hiddenFrames;
|
||||
if (hiddenFrames.indexOf(index) === -1) {
|
||||
hiddenFrames.push(index);
|
||||
} else {
|
||||
hiddenFrames = hiddenFrames.filter(function (i) {
|
||||
return i !== index;
|
||||
});
|
||||
}
|
||||
|
||||
// Keep the hiddenFrames array sorted.
|
||||
this.piskel.hiddenFrames = hiddenFrames.sort();
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) {
|
||||
this.getLayers().forEach(function (l) {
|
||||
l.moveFrame(fromIndex, toIndex);
|
||||
});
|
||||
|
||||
// Update the array of hidden frames since some hidden indexes might have shifted.
|
||||
this.piskel.hiddenFrames = this.piskel.hiddenFrames.map(function (index) {
|
||||
if (index === fromIndex) {
|
||||
return toIndex;
|
||||
}
|
||||
|
||||
// All the frames between fromIndex and toIndex changed their index.
|
||||
var isImpacted = index >= Math.min(fromIndex, toIndex) &&
|
||||
index <= Math.max(fromIndex, toIndex);
|
||||
if (isImpacted) {
|
||||
if (fromIndex < toIndex) {
|
||||
// If the frame moved to a higher index, all impacted frames had their index
|
||||
// reduced by 1.
|
||||
return index - 1;
|
||||
} else {
|
||||
// Otherwise, they had their index increased by 1.
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.hasVisibleFrameAt = function (index) {
|
||||
return this.piskel.hiddenFrames.indexOf(index) === -1;
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.getVisibleFrameIndexes = function () {
|
||||
return this.getCurrentLayer().getFrames().map(function (frame, index) {
|
||||
return index;
|
||||
}).filter(function (index) {
|
||||
return this.piskel.hiddenFrames.indexOf(index) === -1;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.getFrameCount = function () {
|
||||
|
@ -233,8 +308,9 @@
|
|||
ns.PiskelController.prototype.duplicateCurrentLayer = function () {
|
||||
var layer = this.getCurrentLayer();
|
||||
var clone = pskl.utils.LayerUtils.clone(layer);
|
||||
this.piskel.addLayer(clone);
|
||||
this.setCurrentLayerIndex(this.piskel.getLayers().length - 1);
|
||||
var currentLayerIndex = this.getCurrentLayerIndex();
|
||||
this.piskel.addLayerAt(clone, currentLayerIndex + 1);
|
||||
this.setCurrentLayerIndex(currentLayerIndex + 1);
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.createLayer = function (name) {
|
||||
|
@ -246,9 +322,9 @@
|
|||
for (var i = 0 ; i < this.getFrameCount() ; i++) {
|
||||
layer.addFrame(this.createEmptyFrame_());
|
||||
}
|
||||
this.piskel.addLayer(layer);
|
||||
this.setCurrentLayerIndex(this.piskel.getLayers().length - 1);
|
||||
|
||||
var currentLayerIndex = this.getCurrentLayerIndex();
|
||||
this.piskel.addLayerAt(layer, currentLayerIndex + 1);
|
||||
this.setCurrentLayerIndex(currentLayerIndex + 1);
|
||||
} else {
|
||||
throw 'Layer name should be unique';
|
||||
}
|
||||
|
@ -258,15 +334,15 @@
|
|||
return this.piskel.getLayersByName(name).length > 0;
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.moveLayerUp = function () {
|
||||
ns.PiskelController.prototype.moveLayerUp = function (toTop) {
|
||||
var layer = this.getCurrentLayer();
|
||||
this.piskel.moveLayerUp(layer);
|
||||
this.piskel.moveLayerUp(layer, toTop);
|
||||
this.selectLayer(layer);
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.moveLayerDown = function () {
|
||||
ns.PiskelController.prototype.moveLayerDown = function (toBottom) {
|
||||
var layer = this.getCurrentLayer();
|
||||
this.piskel.moveLayerDown(layer);
|
||||
this.piskel.moveLayerDown(layer, toBottom);
|
||||
this.selectLayer(layer);
|
||||
};
|
||||
|
||||
|
@ -292,4 +368,12 @@
|
|||
ns.PiskelController.prototype.serialize = function () {
|
||||
return pskl.utils.serialization.Serializer.serialize(this.piskel);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current sprite is empty. Emptiness here means no pixel has been filled
|
||||
* on any layer or frame for the current sprite.
|
||||
*/
|
||||
ns.PiskelController.prototype.isEmpty = function () {
|
||||
return pskl.app.currentColorsService.getCurrentColors().length === 0;
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
this.saveWrap_('moveLayerDown', true);
|
||||
this.saveWrap_('removeCurrentLayer', true);
|
||||
this.saveWrap_('setLayerOpacityAt', true);
|
||||
this.saveWrap_('toggleFrameVisibilityAt', true);
|
||||
|
||||
var shortcuts = pskl.service.keyboard.Shortcuts;
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.PREVIOUS_FRAME, this.selectPreviousFrame.bind(this));
|
||||
|
@ -49,14 +50,25 @@
|
|||
return this.piskelController;
|
||||
};
|
||||
|
||||
ns.PublicPiskelController.prototype.setPiskel = function (piskel, preserveState) {
|
||||
this.piskelController.setPiskel(piskel, preserveState);
|
||||
/**
|
||||
* Set the current piskel. Will reset the selected frame and layer unless specified
|
||||
* @param {Object} piskel
|
||||
* @param {Object} options:
|
||||
* preserveState {Boolean} if true, keep the selected frame and layer
|
||||
* noSnapshot {Boolean} if true, do not save a snapshot in the piskel
|
||||
* history for this call to setPiskel
|
||||
*/
|
||||
ns.PublicPiskelController.prototype.setPiskel = function (piskel, options) {
|
||||
this.piskelController.setPiskel(piskel, options);
|
||||
|
||||
$.publish(Events.FRAME_SIZE_CHANGED);
|
||||
$.publish(Events.PISKEL_RESET);
|
||||
$.publish(Events.PISKEL_SAVE_STATE, {
|
||||
type : pskl.service.HistoryService.SNAPSHOT
|
||||
});
|
||||
|
||||
if (!options || !options.noSnapshot) {
|
||||
$.publish(Events.PISKEL_SAVE_STATE, {
|
||||
type : pskl.service.HistoryService.SNAPSHOT
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ns.PublicPiskelController.prototype.resetWrap_ = function (methodName) {
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
pskl.utils.Event.addEventListener(this.popup, 'resize', this.onWindowResize_, this);
|
||||
pskl.utils.Event.addEventListener(this.popup, 'unload', this.onPopupClosed_, this);
|
||||
var container = this.popup.document.querySelector('.preview-container');
|
||||
this.renderer = new pskl.rendering.frame.BackgroundImageFrameRenderer($(container));
|
||||
this.renderer = new pskl.rendering.frame.BackgroundImageFrameRenderer(container);
|
||||
this.updateZoom_();
|
||||
this.renderFlag = true;
|
||||
};
|
||||
|
|
232
src/js/controller/preview/PreviewActionsController.js
Normal file
|
@ -0,0 +1,232 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.preview');
|
||||
|
||||
ns.PreviewActionsController = function (previewController, container) {
|
||||
this.previewController = previewController;
|
||||
this.piskelController = previewController.piskelController;
|
||||
this.container = container;
|
||||
|
||||
this.onionSkinShortcut = pskl.service.keyboard.Shortcuts.MISC.ONION_SKIN;
|
||||
this.toggleGridShortcut = pskl.service.keyboard.Shortcuts.MISC.TOGGLE_GRID;
|
||||
|
||||
this.fpsRangeInput = document.querySelector('#preview-fps');
|
||||
this.fpsCounterDisplay = document.querySelector('#display-fps');
|
||||
this.openPopupPreview = document.querySelector('.open-popup-preview-button');
|
||||
this.toggleGridButton = document.querySelector('.toggle-grid-button');
|
||||
this.previewSizeDropdown = document.querySelector('.preview-drop-down');
|
||||
this.previewSizes = {
|
||||
original: {
|
||||
button: document.querySelector('.original-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.X1_PREVIEW,
|
||||
tooltip: 'Original size preview'
|
||||
},
|
||||
best: {
|
||||
button: document.querySelector('.best-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.BEST_PREVIEW,
|
||||
tooltip: 'Best size preview'
|
||||
},
|
||||
full: {
|
||||
button: document.querySelector('.full-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.FULL_PREVIEW,
|
||||
tooltip: 'Full size preview'
|
||||
}
|
||||
};
|
||||
this.toggleOnionSkinButton = document.querySelector('.preview-toggle-onion-skin');
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.init = function () {
|
||||
this.fpsRangeInput.addEventListener('change', this.onFpsRangeInputUpdate_.bind(this));
|
||||
this.fpsRangeInput.addEventListener('input', this.onFpsRangeInputUpdate_.bind(this));
|
||||
|
||||
var addEvent = pskl.utils.Event.addEventListener;
|
||||
addEvent(this.toggleOnionSkinButton, 'click', this.toggleOnionSkin_, this);
|
||||
addEvent(this.openPopupPreview, 'click', this.onOpenPopupPreviewClick_, this);
|
||||
addEvent(this.toggleGridButton, 'click', this.toggleGrid_, this);
|
||||
|
||||
var registerShortcut = pskl.app.shortcutService.registerShortcut.bind(pskl.app.shortcutService);
|
||||
registerShortcut(this.onionSkinShortcut, this.toggleOnionSkin_.bind(this));
|
||||
registerShortcut(this.toggleGridShortcut, this.toggleGrid_.bind(this));
|
||||
|
||||
var onionSkinTooltip = pskl.utils.TooltipFormatter.format('Toggle onion skin', this.onionSkinShortcut);
|
||||
this.toggleOnionSkinButton.setAttribute('title', onionSkinTooltip);
|
||||
|
||||
for (var size in this.previewSizes) {
|
||||
if (this.previewSizes.hasOwnProperty(size)) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
addEvent(previewSize.button, 'click', this.onChangePreviewSize_, this, size);
|
||||
registerShortcut(previewSize.shortcut, this.onChangePreviewSize_.bind(this, size));
|
||||
var tooltip = pskl.utils.TooltipFormatter.format(previewSize.tooltip, previewSize.shortcut);
|
||||
previewSize.button.setAttribute('title', tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.updatePreviewSizeButtons_.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
$.subscribe(Events.FPS_CHANGED, this.updateFPS_.bind(this));
|
||||
$.subscribe(Events.PISKEL_RESET, this.updateFPS_.bind(this));
|
||||
|
||||
this.updatePreviewSizeButtons_();
|
||||
this.updateOnionSkinPreview_();
|
||||
this.selectPreviewSizeButton_();
|
||||
this.updateFPS_();
|
||||
this.updateMaxFPS_();
|
||||
this.updateToggleGridButton_();
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.updateToggleGridButton_ = function () {
|
||||
var gridEnabled = pskl.UserSettings.get(pskl.UserSettings.GRID_ENABLED);
|
||||
this.toggleGridButton.classList.toggle('icon-minimap-grid-white', !gridEnabled);
|
||||
this.toggleGridButton.classList.toggle('icon-minimap-grid-gold', gridEnabled);
|
||||
this.toggleGridButton.classList.toggle('preview-contextual-action-enabled', gridEnabled);
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.toggleGrid_ = function () {
|
||||
var gridEnabled = pskl.UserSettings.get(pskl.UserSettings.GRID_ENABLED);
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_ENABLED, !gridEnabled);
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.updatePreviewSizeButtons_ = function () {
|
||||
var fullZoom = this.previewController.getZoom();
|
||||
var bestZoom = Math.floor(fullZoom);
|
||||
var seamlessModeEnabled = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
|
||||
var validSizes;
|
||||
if (fullZoom < 1) {
|
||||
this.disablePreviewSizeWidget_('No other option available');
|
||||
validSizes = ['full'];
|
||||
} else if (fullZoom === 1) {
|
||||
this.disablePreviewSizeWidget_('No other option available');
|
||||
validSizes = ['original'];
|
||||
} else if (seamlessModeEnabled) {
|
||||
this.disablePreviewSizeWidget_('Disabled in tile mode');
|
||||
validSizes = ['original'];
|
||||
} else {
|
||||
this.enablePreviewSizeWidget_();
|
||||
if (fullZoom === bestZoom) {
|
||||
// If the full zoom is the same as the best zoom, display the best option only as
|
||||
// it gives the exact factor information.
|
||||
validSizes = ['original', 'best'];
|
||||
} else if (bestZoom === 1) {
|
||||
// If best zoom is 1x, remove it as it is redundant with the original option.
|
||||
validSizes = ['full', 'original'];
|
||||
} else {
|
||||
validSizes = ['full', 'original', 'best'];
|
||||
}
|
||||
}
|
||||
|
||||
// Update buttons content and status.
|
||||
this.previewSizes.best.button.textContent = Math.floor(fullZoom) + 'x';
|
||||
for (var size in this.previewSizes) {
|
||||
if (this.previewSizes.hasOwnProperty(size)) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
var isSizeEnabled = validSizes.indexOf(size) != -1;
|
||||
|
||||
// classList.toggle is not available on IE11.
|
||||
if (isSizeEnabled) {
|
||||
previewSize.button.classList.remove('preview-contextual-action-hidden');
|
||||
} else {
|
||||
previewSize.button.classList.add('preview-contextual-action-hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the selected preview size if the currently selected size is not valid.
|
||||
var selectedSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
if (validSizes.indexOf(selectedSize) === -1) {
|
||||
this.onChangePreviewSize_(validSizes[0]);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.enablePreviewSizeWidget_ = function () {
|
||||
this.previewSizeDropdown.classList.remove('preview-drop-down-disabled');
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.disablePreviewSizeWidget_ = function (reason) {
|
||||
// The .preview-disable-overlay is displayed on top of the preview size widget
|
||||
document.querySelector('.preview-disable-overlay').setAttribute('data-original-title', reason);
|
||||
this.previewSizeDropdown.classList.add('preview-drop-down-disabled');
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.onOpenPopupPreviewClick_ = function () {
|
||||
this.previewController.openPopupPreview();
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.onChangePreviewSize_ = function (size) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
var isEnabled = !previewSize.button.classList.contains('preview-contextual-action-hidden');
|
||||
if (isEnabled) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.PREVIEW_SIZE, size);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.onUserSettingsChange_ = function (evt, name, value) {
|
||||
if (name == pskl.UserSettings.ONION_SKIN) {
|
||||
this.updateOnionSkinPreview_();
|
||||
} else if (name == pskl.UserSettings.MAX_FPS) {
|
||||
this.updateMaxFPS_();
|
||||
} else if (name === pskl.UserSettings.SEAMLESS_MODE) {
|
||||
this.updatePreviewSizeButtons_();
|
||||
} else if (name === pskl.UserSettings.GRID_ENABLED) {
|
||||
this.updateToggleGridButton_();
|
||||
} else {
|
||||
this.selectPreviewSizeButton_();
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.updateOnionSkinPreview_ = function () {
|
||||
var enabledClassname = 'preview-toggle-onion-skin-enabled';
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.ONION_SKIN);
|
||||
|
||||
// classList.toggle is not available on IE11.
|
||||
if (isEnabled) {
|
||||
this.toggleOnionSkinButton.classList.add(enabledClassname);
|
||||
} else {
|
||||
this.toggleOnionSkinButton.classList.remove(enabledClassname);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.selectPreviewSizeButton_ = function () {
|
||||
var currentlySelected = document.querySelector('.size-button-selected');
|
||||
if (currentlySelected) {
|
||||
currentlySelected.classList.remove('size-button-selected');
|
||||
}
|
||||
|
||||
var selectedSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
var previewSize = this.previewSizes[selectedSize];
|
||||
previewSize.button.classList.add('size-button-selected');
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.updateMaxFPS_ = function () {
|
||||
var maxFps = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
this.fpsRangeInput.setAttribute('max', maxFps);
|
||||
this.piskelController.setFPS(Math.min(maxFps, this.piskelController.getFPS()));
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler triggered on 'input' or 'change' events.
|
||||
*/
|
||||
ns.PreviewActionsController.prototype.onFpsRangeInputUpdate_ = function (evt) {
|
||||
var fps = parseInt(this.fpsRangeInput.value, 10);
|
||||
this.piskelController.setFPS(fps);
|
||||
// blur only on 'change' events, as blurring on 'input' breaks on Firefox
|
||||
if (evt.type === 'change') {
|
||||
this.fpsRangeInput.blur();
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.updateFPS_ = function () {
|
||||
var fps = this.piskelController.getFPS();
|
||||
if (fps !== this.fpsRangeInput.value) {
|
||||
// reset
|
||||
this.fpsRangeInput.value = 0;
|
||||
// set proper value
|
||||
this.fpsRangeInput.value = fps;
|
||||
this.fpsCounterDisplay.innerHTML = fps + ' FPS';
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewActionsController.prototype.toggleOnionSkin_ = function () {
|
||||
var currentValue = pskl.UserSettings.get(pskl.UserSettings.ONION_SKIN);
|
||||
pskl.UserSettings.set(pskl.UserSettings.ONION_SKIN, !currentValue);
|
||||
};
|
||||
})();
|
|
@ -11,201 +11,43 @@
|
|||
|
||||
this.elapsedTime = 0;
|
||||
this.currentIndex = 0;
|
||||
|
||||
this.onionSkinShortcut = pskl.service.keyboard.Shortcuts.MISC.ONION_SKIN;
|
||||
|
||||
this.lastRenderTime = 0;
|
||||
this.renderFlag = true;
|
||||
|
||||
this.fpsRangeInput = document.querySelector('#preview-fps');
|
||||
this.fpsCounterDisplay = document.querySelector('#display-fps');
|
||||
this.openPopupPreview = document.querySelector('.open-popup-preview-button');
|
||||
this.previewSizeDropdown = document.querySelector('.preview-drop-down');
|
||||
this.previewSizes = {
|
||||
original: {
|
||||
button: document.querySelector('.original-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.X1_PREVIEW,
|
||||
tooltip: 'Original size preview'
|
||||
},
|
||||
best: {
|
||||
button: document.querySelector('.best-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.BEST_PREVIEW,
|
||||
tooltip: 'Best size preview'
|
||||
},
|
||||
full: {
|
||||
button: document.querySelector('.full-size-button'),
|
||||
shortcut: pskl.service.keyboard.Shortcuts.MISC.FULL_PREVIEW,
|
||||
tooltip: 'Full size preview'
|
||||
}
|
||||
};
|
||||
this.toggleOnionSkinButton = document.querySelector('.preview-toggle-onion-skin');
|
||||
|
||||
this.renderer = new pskl.rendering.frame.BackgroundImageFrameRenderer(this.container);
|
||||
this.popupPreviewController = new ns.PopupPreviewController(piskelController);
|
||||
this.previewActionsController = new ns.PreviewActionsController(this, container);
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.init = function () {
|
||||
this.fpsRangeInput.addEventListener('change', this.onFpsRangeInputUpdate_.bind(this));
|
||||
this.fpsRangeInput.addEventListener('input', this.onFpsRangeInputUpdate_.bind(this));
|
||||
|
||||
document.querySelector('.right-column').style.width = Constants.ANIMATED_PREVIEW_WIDTH + 'px';
|
||||
|
||||
var addEvent = pskl.utils.Event.addEventListener;
|
||||
addEvent(this.toggleOnionSkinButton, 'click', this.toggleOnionSkin_, this);
|
||||
addEvent(this.openPopupPreview, 'click', this.onOpenPopupPreviewClick_, this);
|
||||
|
||||
var registerShortcut = pskl.app.shortcutService.registerShortcut.bind(pskl.app.shortcutService);
|
||||
registerShortcut(this.onionSkinShortcut, this.toggleOnionSkin_.bind(this));
|
||||
|
||||
var onionSkinTooltip = pskl.utils.TooltipFormatter.format('Toggle onion skin', this.onionSkinShortcut);
|
||||
this.toggleOnionSkinButton.setAttribute('title', onionSkinTooltip);
|
||||
|
||||
for (var size in this.previewSizes) {
|
||||
if (this.previewSizes.hasOwnProperty(size)) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
addEvent(previewSize.button, 'click', this.onChangePreviewSize_, this, size);
|
||||
registerShortcut(previewSize.shortcut, this.onChangePreviewSize_.bind(this, size));
|
||||
var tooltip = pskl.utils.TooltipFormatter.format(previewSize.tooltip, previewSize.shortcut);
|
||||
previewSize.button.setAttribute('title', tooltip);
|
||||
}
|
||||
}
|
||||
var width = Constants.ANIMATED_PREVIEW_WIDTH + Constants.RIGHT_COLUMN_PADDING_LEFT;
|
||||
document.querySelector('.right-column').style.width = width + 'px';
|
||||
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
$.subscribe(Events.PISKEL_SAVE_STATE, this.setRenderFlag_.bind(this, true));
|
||||
$.subscribe(Events.FPS_CHANGED, this.updateFPS_.bind(this));
|
||||
// On PISKEL_RESET, set the render flag and update the FPS input
|
||||
$.subscribe(Events.PISKEL_RESET, this.setRenderFlag_.bind(this, true));
|
||||
$.subscribe(Events.PISKEL_RESET, this.updateFPS_.bind(this));
|
||||
|
||||
this.updatePreviewSizeButtons_();
|
||||
this.popupPreviewController.init();
|
||||
this.previewActionsController.init();
|
||||
|
||||
this.updateZoom_();
|
||||
this.updateOnionSkinPreview_();
|
||||
this.selectPreviewSizeButton_();
|
||||
this.updateFPS_();
|
||||
this.updateMaxFPS_();
|
||||
this.updateContainerDimensions_();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updatePreviewSizeButtons_ = function () {
|
||||
var fullZoom = this.calculateZoom_();
|
||||
var bestZoom = Math.floor(fullZoom);
|
||||
var seamlessModeEnabled = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
|
||||
var validSizes;
|
||||
if (fullZoom < 1) {
|
||||
this.disablePreviewSizeWidget_('No other option available');
|
||||
validSizes = ['full'];
|
||||
} else if (fullZoom === 1) {
|
||||
this.disablePreviewSizeWidget_('No other option available');
|
||||
validSizes = ['original'];
|
||||
} else if (seamlessModeEnabled) {
|
||||
this.disablePreviewSizeWidget_('Disabled in seamless mode');
|
||||
validSizes = ['original'];
|
||||
} else {
|
||||
this.enablePreviewSizeWidget_();
|
||||
if (fullZoom === bestZoom) {
|
||||
// If the full zoom is the same as the best zoom, display the best option only as
|
||||
// it gives the exact factor information.
|
||||
validSizes = ['original', 'best'];
|
||||
} else if (bestZoom === 1) {
|
||||
// If best zoom is 1x, remove it as it is redundant with the original option.
|
||||
validSizes = ['full', 'original'];
|
||||
} else {
|
||||
validSizes = ['full', 'original', 'best'];
|
||||
}
|
||||
}
|
||||
|
||||
// Update buttons content and status.
|
||||
this.previewSizes.best.button.textContent = Math.floor(fullZoom) + 'x';
|
||||
for (var size in this.previewSizes) {
|
||||
if (this.previewSizes.hasOwnProperty(size)) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
var isSizeEnabled = validSizes.indexOf(size) != -1;
|
||||
|
||||
// classList.toggle is not available on IE11.
|
||||
if (isSizeEnabled) {
|
||||
previewSize.button.classList.remove('preview-contextual-action-hidden');
|
||||
} else {
|
||||
previewSize.button.classList.add('preview-contextual-action-hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the selected preview size if the currently selected size is not valid.
|
||||
var selectedSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
if (validSizes.indexOf(selectedSize) === -1) {
|
||||
this.onChangePreviewSize_(validSizes[0]);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.enablePreviewSizeWidget_ = function () {
|
||||
this.previewSizeDropdown.classList.remove('preview-drop-down-disabled');
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.disablePreviewSizeWidget_ = function (reason) {
|
||||
// The .preview-disable-overlay is displayed on top of the preview size widget
|
||||
document.querySelector('.preview-disable-overlay').setAttribute('data-original-title', reason);
|
||||
this.previewSizeDropdown.classList.add('preview-drop-down-disabled');
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.onOpenPopupPreviewClick_ = function () {
|
||||
ns.PreviewController.prototype.openPopupPreview = function () {
|
||||
this.popupPreviewController.open();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.onChangePreviewSize_ = function (size) {
|
||||
var previewSize = this.previewSizes[size];
|
||||
var isEnabled = !previewSize.button.classList.contains('preview-contextual-action-hidden');
|
||||
if (isEnabled) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.PREVIEW_SIZE, size);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.onUserSettingsChange_ = function (evt, name, value) {
|
||||
if (name == pskl.UserSettings.ONION_SKIN) {
|
||||
this.updateOnionSkinPreview_();
|
||||
} else if (name == pskl.UserSettings.MAX_FPS) {
|
||||
this.updateMaxFPS_();
|
||||
} else if (name === pskl.UserSettings.SEAMLESS_MODE) {
|
||||
if (name === pskl.UserSettings.SEAMLESS_MODE) {
|
||||
this.onFrameSizeChange_();
|
||||
} else {
|
||||
this.updateZoom_();
|
||||
this.selectPreviewSizeButton_();
|
||||
this.updateContainerDimensions_();
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateOnionSkinPreview_ = function () {
|
||||
var enabledClassname = 'preview-toggle-onion-skin-enabled';
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.ONION_SKIN);
|
||||
|
||||
// classList.toggle is not available on IE11.
|
||||
if (isEnabled) {
|
||||
this.toggleOnionSkinButton.classList.add(enabledClassname);
|
||||
} else {
|
||||
this.toggleOnionSkinButton.classList.remove(enabledClassname);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.selectPreviewSizeButton_ = function () {
|
||||
var currentlySelected = document.querySelector('.size-button-selected');
|
||||
if (currentlySelected) {
|
||||
currentlySelected.classList.remove('size-button-selected');
|
||||
}
|
||||
|
||||
var selectedSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
var previewSize = this.previewSizes[selectedSize];
|
||||
previewSize.button.classList.add('size-button-selected');
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateMaxFPS_ = function () {
|
||||
var maxFps = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
this.fpsRangeInput.setAttribute('max', maxFps);
|
||||
this.piskelController.setFPS(Math.min(maxFps, this.piskelController.getFPS()));
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateZoom_ = function () {
|
||||
var previewSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
|
||||
|
@ -227,9 +69,9 @@
|
|||
};
|
||||
|
||||
ns.PreviewController.prototype.getCoordinates = function(x, y) {
|
||||
var containerOffset = this.container.offset();
|
||||
x = x - containerOffset.left;
|
||||
y = y - containerOffset.top;
|
||||
var containerRect = this.container.getBoundingClientRect();
|
||||
x = x - containerRect.left;
|
||||
y = y - containerRect.top;
|
||||
var zoom = this.getZoom();
|
||||
return {
|
||||
x : Math.floor(x / zoom),
|
||||
|
@ -237,30 +79,6 @@
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler triggered on 'input' or 'change' events.
|
||||
*/
|
||||
ns.PreviewController.prototype.onFpsRangeInputUpdate_ = function (evt) {
|
||||
var fps = parseInt(this.fpsRangeInput.value, 10);
|
||||
this.piskelController.setFPS(fps);
|
||||
// blur only on 'change' events, as blurring on 'input' breaks on Firefox
|
||||
if (evt.type === 'change') {
|
||||
this.fpsRangeInput.blur();
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateFPS_ = function () {
|
||||
var fps = this.piskelController.getFPS();
|
||||
if (fps !== this.fps) {
|
||||
this.fps = fps;
|
||||
// reset
|
||||
this.fpsRangeInput.value = 0;
|
||||
// set proper value
|
||||
this.fpsRangeInput.value = this.fps;
|
||||
this.fpsCounterDisplay.innerHTML = this.fps + ' FPS';
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.render = function (delta) {
|
||||
this.elapsedTime += delta;
|
||||
var index = this.getNextIndex_(delta);
|
||||
|
@ -276,15 +94,18 @@
|
|||
};
|
||||
|
||||
ns.PreviewController.prototype.getNextIndex_ = function (delta) {
|
||||
if (this.fps === 0) {
|
||||
var fps = this.piskelController.getFPS();
|
||||
if (fps === 0) {
|
||||
return this.piskelController.getCurrentFrameIndex();
|
||||
} else {
|
||||
var index = Math.floor(this.elapsedTime / (1000 / this.fps));
|
||||
if (!this.piskelController.hasFrameAt(index)) {
|
||||
var index = Math.floor(this.elapsedTime / (1000 / fps));
|
||||
var frameIndexes = this.piskelController.getVisibleFrameIndexes();
|
||||
if (frameIndexes.length <= index) {
|
||||
this.elapsedTime = 0;
|
||||
index = 0;
|
||||
index = (frameIndexes.length) ? frameIndexes[0] : this.piskelController.getCurrentFrameIndex();
|
||||
return index;
|
||||
}
|
||||
return index;
|
||||
return frameIndexes[index];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -302,14 +123,14 @@
|
|||
ns.PreviewController.prototype.onFrameSizeChange_ = function () {
|
||||
this.updateZoom_();
|
||||
this.updateContainerDimensions_();
|
||||
this.updatePreviewSizeButtons_();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateContainerDimensions_ = function () {
|
||||
var isSeamless = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
this.renderer.setRepeated(isSeamless);
|
||||
|
||||
var height, width;
|
||||
var width;
|
||||
var height;
|
||||
|
||||
if (isSeamless) {
|
||||
height = PREVIEW_SIZE;
|
||||
|
@ -321,7 +142,7 @@
|
|||
width = frame.getWidth() * zoom;
|
||||
}
|
||||
|
||||
var containerEl = this.container.get(0);
|
||||
var containerEl = this.container;
|
||||
containerEl.style.height = height + 'px';
|
||||
containerEl.style.width = width + 'px';
|
||||
|
||||
|
@ -342,9 +163,4 @@
|
|||
return (this.renderFlag || this.popupPreviewController.renderFlag) &&
|
||||
(Date.now() - this.lastRenderTime > RENDER_MINIMUM_DELAY);
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.toggleOnionSkin_ = function () {
|
||||
var currentValue = pskl.UserSettings.get(pskl.UserSettings.ONION_SKIN);
|
||||
pskl.UserSettings.set(pskl.UserSettings.ONION_SKIN, !currentValue);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.settings');
|
||||
|
||||
ns.ApplicationSettingsController = function () {};
|
||||
|
||||
pskl.utils.inherit(ns.ApplicationSettingsController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.ApplicationSettingsController.prototype.init = function() {
|
||||
this.backgroundContainer = document.querySelector('.background-picker-wrapper');
|
||||
this.addEventListener(this.backgroundContainer, 'click', this.onBackgroundClick_);
|
||||
|
||||
// Highlight selected background :
|
||||
var background = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
|
||||
var selectedBackground = this.backgroundContainer.querySelector('[data-background=' + background + ']');
|
||||
if (selectedBackground) {
|
||||
selectedBackground.classList.add('selected');
|
||||
}
|
||||
|
||||
// Grid display and size
|
||||
var gridWidth = pskl.UserSettings.get(pskl.UserSettings.GRID_WIDTH);
|
||||
var gridSelect = document.querySelector('.grid-width-select');
|
||||
var selectedOption = gridSelect.querySelector('option[value="' + gridWidth + '"]');
|
||||
if (selectedOption) {
|
||||
selectedOption.setAttribute('selected', 'selected');
|
||||
}
|
||||
|
||||
this.addEventListener(gridSelect, 'change', this.onGridWidthChange_);
|
||||
|
||||
// Seamless mode
|
||||
var seamlessMode = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
var seamlessModeCheckbox = document.querySelector('.seamless-mode-checkbox');
|
||||
if (seamlessMode) {
|
||||
seamlessModeCheckbox.setAttribute('checked', seamlessMode);
|
||||
}
|
||||
this.addEventListener(seamlessModeCheckbox, 'change', this.onSeamlessModeChange_);
|
||||
|
||||
// Max FPS
|
||||
var maxFpsInput = document.querySelector('.max-fps-input');
|
||||
maxFpsInput.value = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
this.addEventListener(maxFpsInput, 'change', this.onMaxFpsChange_);
|
||||
|
||||
// Color format
|
||||
var colorFormat = pskl.UserSettings.get(pskl.UserSettings.COLOR_FORMAT);
|
||||
var colorFormatSelect = document.querySelector('.color-format-select');
|
||||
var selectedColorFormatOption = colorFormatSelect.querySelector('option[value="' + colorFormat + '"]');
|
||||
if (selectedColorFormatOption) {
|
||||
selectedColorFormatOption.setAttribute('selected', 'selected');
|
||||
}
|
||||
|
||||
this.addEventListener(colorFormatSelect, 'change', this.onColorFormatChange_);
|
||||
|
||||
// Layer preview opacity
|
||||
var layerOpacityInput = document.querySelector('.layer-opacity-input');
|
||||
layerOpacityInput.value = pskl.UserSettings.get(pskl.UserSettings.LAYER_OPACITY);
|
||||
this.addEventListener(layerOpacityInput, 'change', this.onLayerOpacityChange_);
|
||||
this.addEventListener(layerOpacityInput, 'input', this.onLayerOpacityChange_);
|
||||
this.updateLayerOpacityText_(layerOpacityInput.value);
|
||||
|
||||
// Seamless mask opacity
|
||||
var seamlessOpacityInput = document.querySelector('.seamless-opacity-input');
|
||||
seamlessOpacityInput.value = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_OPACITY);
|
||||
this.addEventListener(seamlessOpacityInput, 'change', this.onSeamlessOpacityChange_);
|
||||
this.addEventListener(seamlessOpacityInput, 'input', this.onSeamlessOpacityChange_);
|
||||
this.updateSeamlessOpacityText_(seamlessOpacityInput.value);
|
||||
|
||||
// Form
|
||||
this.applicationSettingsForm = document.querySelector('[name="application-settings-form"]');
|
||||
this.addEventListener(this.applicationSettingsForm, 'submit', this.onFormSubmit_);
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onGridWidthChange_ = function (evt) {
|
||||
var width = parseInt(evt.target.value, 10);
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_WIDTH, width);
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onColorFormatChange_ = function (evt) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.COLOR_FORMAT, evt.target.value);
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onSeamlessModeChange_ = function (evt) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.SEAMLESS_MODE, evt.currentTarget.checked);
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onBackgroundClick_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var background = target.dataset.background;
|
||||
if (background) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, background);
|
||||
var selected = this.backgroundContainer.querySelector('.selected');
|
||||
if (selected) {
|
||||
selected.classList.remove('selected');
|
||||
}
|
||||
target.classList.add('selected');
|
||||
}
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onMaxFpsChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var fps = parseInt(target.value, 10);
|
||||
if (fps && !isNaN(fps)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.MAX_FPS, fps);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
}
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onLayerOpacityChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var opacity = parseFloat(target.value);
|
||||
if (!isNaN(opacity)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.LAYER_OPACITY, opacity);
|
||||
pskl.UserSettings.set(pskl.UserSettings.LAYER_PREVIEW, opacity !== 0);
|
||||
this.updateLayerOpacityText_(opacity);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.LAYER_OPACITY);
|
||||
}
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onSeamlessOpacityChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var opacity = parseFloat(target.value);
|
||||
if (!isNaN(opacity)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.SEAMLESS_OPACITY, opacity);
|
||||
this.updateSeamlessOpacityText_(opacity);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_OPACITY);
|
||||
}
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.updateLayerOpacityText_ = function (opacity) {
|
||||
var layerOpacityText = document.querySelector('.layer-opacity-text');
|
||||
layerOpacityText.innerHTML = opacity;
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.updateSeamlessOpacityText_ = function (opacity) {
|
||||
var seamlessOpacityText = document.querySelector('.seamless-opacity-text');
|
||||
seamlessOpacityText.innerHTML = opacity;
|
||||
};
|
||||
|
||||
ns.ApplicationSettingsController.prototype.onFormSubmit_ = function (evt) {
|
||||
evt.preventDefault();
|
||||
$.publish(Events.CLOSE_SETTINGS_DRAWER);
|
||||
};
|
||||
})();
|
|
@ -14,6 +14,7 @@
|
|||
this.hiddenOpenPiskelInput = document.querySelector('[name="open-piskel-input"]');
|
||||
|
||||
this.addEventListener('.browse-local-button', 'click', this.onBrowseLocalClick_);
|
||||
this.addEventListener('.browse-backups-button', 'click', this.onBrowseBackupsClick_);
|
||||
this.addEventListener('.file-input-button', 'click', this.onFileInputClick_);
|
||||
|
||||
// different handlers, depending on the Environment
|
||||
|
@ -23,24 +24,6 @@
|
|||
this.addEventListener(this.hiddenOpenPiskelInput, 'change', this.onOpenPiskelChange_);
|
||||
this.addEventListener('.open-piskel-button', 'click', this.onOpenPiskelClick_);
|
||||
}
|
||||
|
||||
this.initRestoreSession_();
|
||||
};
|
||||
|
||||
ns.ImportController.prototype.initRestoreSession_ = function () {
|
||||
var previousSessionContainer = document.querySelector('.previous-session');
|
||||
var previousInfo = pskl.app.backupService.getPreviousPiskelInfo();
|
||||
if (previousInfo) {
|
||||
var previousSessionTemplate_ = pskl.utils.Template.get('previous-session-info-template');
|
||||
var date = pskl.utils.DateUtils.format(previousInfo.date, '{{H}}:{{m}} - {{Y}}/{{M}}/{{D}}');
|
||||
previousSessionContainer.innerHTML = pskl.utils.Template.replace(previousSessionTemplate_, {
|
||||
name : previousInfo.name,
|
||||
date : date
|
||||
});
|
||||
this.addEventListener('.restore-session-button', 'click', this.onRestorePreviousSessionClick_);
|
||||
} else {
|
||||
previousSessionContainer.innerHTML = 'No piskel backup was found on this browser.';
|
||||
}
|
||||
};
|
||||
|
||||
ns.ImportController.prototype.closeDrawer_ = function () {
|
||||
|
@ -77,6 +60,13 @@
|
|||
this.closeDrawer_();
|
||||
};
|
||||
|
||||
ns.ImportController.prototype.onBrowseBackupsClick_ = function (evt) {
|
||||
$.publish(Events.DIALOG_SHOW, {
|
||||
dialogId : 'browse-backups'
|
||||
});
|
||||
this.closeDrawer_();
|
||||
};
|
||||
|
||||
ns.ImportController.prototype.openPiskelFile_ = function (file) {
|
||||
if (this.isPiskel_(file)) {
|
||||
$.publish(Events.DIALOG_SHOW, {
|
||||
|
|
35
src/js/controller/settings/PreferencesController.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.settings');
|
||||
|
||||
var tabs = {
|
||||
'misc' : {
|
||||
template : 'templates/settings/preferences/misc.html',
|
||||
controller : ns.preferences.MiscPreferencesController
|
||||
},
|
||||
'grid' : {
|
||||
template : 'templates/settings/preferences/grid.html',
|
||||
controller : ns.preferences.GridPreferencesController
|
||||
},
|
||||
'tile' : {
|
||||
template : 'templates/settings/preferences/tile.html',
|
||||
controller : ns.preferences.TilePreferencesController
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreferencesController = function () {
|
||||
this.tabsWidget = new pskl.widgets.Tabs(tabs, this, pskl.UserSettings.PREFERENCES_TAB);
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.PreferencesController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.PreferencesController.prototype.init = function() {
|
||||
var container = document.querySelector('.settings-section-preferences');
|
||||
this.tabsWidget.init(container);
|
||||
};
|
||||
|
||||
ns.PreferencesController.prototype.destroy = function () {
|
||||
this.tabsWidget.destroy();
|
||||
this.superclass.destroy.call(this);
|
||||
};
|
||||
|
||||
})();
|
|
@ -34,7 +34,7 @@
|
|||
this.saveDesktopAsNewButton = document.querySelector('#save-desktop-as-new-button');
|
||||
this.saveFileDownloadButton = document.querySelector('#save-file-download-button');
|
||||
|
||||
this.safeAddEventListener_(this.saveLocalStorageButton, 'click', this.saveToLocalStorage_);
|
||||
this.safeAddEventListener_(this.saveLocalStorageButton, 'click', this.saveToIndexedDb_);
|
||||
this.safeAddEventListener_(this.saveGalleryButton, 'click', this.saveToGallery_);
|
||||
this.safeAddEventListener_(this.saveDesktopButton, 'click', this.saveToDesktop_);
|
||||
this.safeAddEventListener_(this.saveDesktopAsNewButton, 'click', this.saveToDesktopAsNew_);
|
||||
|
@ -99,7 +99,7 @@
|
|||
if (pskl.app.isLoggedIn()) {
|
||||
this.saveToGallery_();
|
||||
} else {
|
||||
this.saveToLocalStorage_();
|
||||
this.saveToIndexedDb_();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -111,8 +111,8 @@
|
|||
this.saveTo_('saveToGallery', false);
|
||||
};
|
||||
|
||||
ns.SaveController.prototype.saveToLocalStorage_ = function () {
|
||||
this.saveTo_('saveToLocalStorage', false);
|
||||
ns.SaveController.prototype.saveToIndexedDb_ = function () {
|
||||
this.saveTo_('saveToIndexedDb', false);
|
||||
};
|
||||
|
||||
ns.SaveController.prototype.saveToDesktop_ = function () {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
var settings = {
|
||||
'user' : {
|
||||
template : 'templates/settings/application.html',
|
||||
controller : ns.ApplicationSettingsController
|
||||
template : 'templates/settings/preferences.html',
|
||||
controller : ns.PreferencesController
|
||||
},
|
||||
'resize' : {
|
||||
template : 'templates/settings/resize.html',
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
ns.ExportController = function (piskelController) {
|
||||
this.piskelController = piskelController;
|
||||
this.tabsWidget = new pskl.widgets.Tabs(tabs, this, pskl.UserSettings.EXPORT_TAB);
|
||||
this.onSizeInputChange_ = this.onSizeInputChange_.bind(this);
|
||||
};
|
||||
|
||||
|
@ -47,47 +48,16 @@
|
|||
this.onSizeInputChange_();
|
||||
|
||||
// Initialize tabs and panel
|
||||
this.exportPanel = document.querySelector('.export-panel');
|
||||
this.exportTabs = document.querySelector('.export-tabs');
|
||||
this.addEventListener(this.exportTabs, 'click', this.onTabsClicked_);
|
||||
|
||||
var tab = pskl.UserSettings.get(pskl.UserSettings.EXPORT_TAB);
|
||||
this.selectTab(tab);
|
||||
var container = document.querySelector('.settings-section-export');
|
||||
this.tabsWidget.init(container);
|
||||
};
|
||||
|
||||
ns.ExportController.prototype.destroy = function () {
|
||||
this.sizeInputWidget.destroy();
|
||||
this.currentController.destroy();
|
||||
this.tabsWidget.destroy();
|
||||
this.superclass.destroy.call(this);
|
||||
};
|
||||
|
||||
ns.ExportController.prototype.selectTab = function (tabId) {
|
||||
if (!tabs[tabId] || this.currentTab == tabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentController) {
|
||||
this.currentController.destroy();
|
||||
}
|
||||
|
||||
this.exportPanel.innerHTML = pskl.utils.Template.get(tabs[tabId].template);
|
||||
this.currentController = new tabs[tabId].controller(this.piskelController, this);
|
||||
this.currentController.init();
|
||||
this.currentTab = tabId;
|
||||
pskl.UserSettings.set(pskl.UserSettings.EXPORT_TAB, tabId);
|
||||
|
||||
var selectedTab = this.exportTabs.querySelector('.selected');
|
||||
if (selectedTab) {
|
||||
selectedTab.classList.remove('selected');
|
||||
}
|
||||
this.exportTabs.querySelector('[data-tab-id="' + tabId + '"]').classList.add('selected');
|
||||
};
|
||||
|
||||
ns.ExportController.prototype.onTabsClicked_ = function (e) {
|
||||
var tabId = pskl.utils.Dom.getData(e.target, 'tabId');
|
||||
this.selectTab(tabId);
|
||||
};
|
||||
|
||||
ns.ExportController.prototype.onScaleChange_ = function () {
|
||||
var value = parseFloat(this.scaleInput.value);
|
||||
if (!isNaN(value)) {
|
||||
|
|
|
@ -14,14 +14,22 @@
|
|||
pskl.utils.inherit(ns.GifExportController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.GifExportController.prototype.init = function () {
|
||||
|
||||
this.uploadStatusContainerEl = document.querySelector('.gif-upload-status');
|
||||
this.previewContainerEl = document.querySelector('.gif-export-preview');
|
||||
this.uploadButton = document.querySelector('.gif-upload-button');
|
||||
this.downloadButton = document.querySelector('.gif-download-button');
|
||||
this.repeatCheckbox = document.querySelector('.gif-repeat-checkbox');
|
||||
|
||||
// Initialize repeatCheckbox state
|
||||
this.repeatCheckbox.checked = this.getRepeatSetting_();
|
||||
|
||||
this.addEventListener(this.uploadButton, 'click', this.onUploadButtonClick_);
|
||||
this.addEventListener(this.downloadButton, 'click', this.onDownloadButtonClick_);
|
||||
this.addEventListener(this.repeatCheckbox, 'change', this.onRepeatCheckboxChange_);
|
||||
|
||||
var currentColors = pskl.app.currentColorsService.getCurrentColors();
|
||||
var tooManyColors = currentColors.length >= MAX_GIF_COLORS;
|
||||
document.querySelector('.gif-export-warning').classList.toggle('visible', tooManyColors);
|
||||
};
|
||||
|
||||
ns.GifExportController.prototype.getZoom_ = function () {
|
||||
|
@ -85,7 +93,8 @@
|
|||
var isTransparent = layers.some(function (l) {return l.isTransparent();});
|
||||
var preserveColors = !isTransparent && currentColors.length < MAX_GIF_COLORS;
|
||||
|
||||
var transparentColor, transparent;
|
||||
var transparentColor;
|
||||
var transparent;
|
||||
// transparency only supported if preserveColors is true, see Issue #357
|
||||
if (preserveColors) {
|
||||
transparentColor = this.getTransparentColor(currentColors);
|
||||
|
@ -104,6 +113,7 @@
|
|||
width: width * zoom,
|
||||
height: height * zoom,
|
||||
preserveColors : preserveColors,
|
||||
repeat: this.getRepeatSetting_() ? 0 : -1,
|
||||
transparent : transparent
|
||||
});
|
||||
|
||||
|
@ -148,6 +158,15 @@
|
|||
return transparentColor;
|
||||
};
|
||||
|
||||
ns.GifExportController.prototype.onRepeatCheckboxChange_ = function () {
|
||||
var checked = this.repeatCheckbox.checked;
|
||||
pskl.UserSettings.set(pskl.UserSettings.EXPORT_GIF_REPEAT, checked);
|
||||
};
|
||||
|
||||
ns.GifExportController.prototype.getRepeatSetting_ = function () {
|
||||
return pskl.UserSettings.get(pskl.UserSettings.EXPORT_GIF_REPEAT);
|
||||
};
|
||||
|
||||
ns.GifExportController.prototype.updateStatus_ = function (imageUrl, error) {
|
||||
if (imageUrl) {
|
||||
var linkTpl = '<a class="highlight" href="{{link}}" target="_blank">{{shortLink}}</a>';
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
ns.MiscExportController.prototype.init = function () {
|
||||
var cDownloadButton = document.querySelector('.c-download-button');
|
||||
this.addEventListener(cDownloadButton, 'click', this.onDownloadCFileClick_);
|
||||
|
||||
var selectedFrameDownloadButton = document.querySelector('.selected-frame-download-button');
|
||||
this.addEventListener(selectedFrameDownloadButton, 'click', this.onDownloadSelectedFrameClick_);
|
||||
};
|
||||
|
||||
ns.MiscExportController.prototype.onDownloadCFileClick_ = function (evt) {
|
||||
|
@ -76,14 +73,4 @@
|
|||
hexStr += ('00' + r.toString(16)).substr(-2);
|
||||
return hexStr;
|
||||
};
|
||||
|
||||
ns.MiscExportController.prototype.onDownloadSelectedFrameClick_ = function (evt) {
|
||||
var frameIndex = this.piskelController.getCurrentFrameIndex();
|
||||
var fileName = this.getPiskelName_() + '-' + (frameIndex + 1) + '.png';
|
||||
var canvas = this.piskelController.renderFrameAt(frameIndex, true);
|
||||
|
||||
pskl.utils.BlobUtils.canvasToBlob(canvas, function(blob) {
|
||||
pskl.utils.FileUtils.downloadAsFile(blob, fileName);
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
var downloadButton = document.querySelector('.png-download-button');
|
||||
var downloadPixiButton = document.querySelector('.png-pixi-download-button');
|
||||
var dataUriButton = document.querySelector('.datauri-open-button');
|
||||
var selectedFrameDownloadButton = document.querySelector('.selected-frame-download-button');
|
||||
|
||||
this.pixiInlineImageCheckbox = document.querySelector('.png-pixi-inline-image-checkbox');
|
||||
|
||||
this.initLayoutSection_();
|
||||
this.updateDimensionLabel_();
|
||||
|
@ -39,6 +42,7 @@
|
|||
this.addEventListener(downloadButton, 'click', this.onDownloadClick_);
|
||||
this.addEventListener(downloadPixiButton, 'click', this.onPixiDownloadClick_);
|
||||
this.addEventListener(dataUriButton, 'click', this.onDataUriClick_);
|
||||
this.addEventListener(selectedFrameDownloadButton, 'click', this.onDownloadSelectedFrameClick_);
|
||||
$.subscribe(Events.EXPORT_SCALE_CHANGED, this.onScaleChanged_);
|
||||
};
|
||||
|
||||
|
@ -149,9 +153,9 @@
|
|||
};
|
||||
|
||||
// Used and overridden in casper integration tests.
|
||||
ns.PngExportController.prototype.downloadCanvas_ = function (canvas) {
|
||||
ns.PngExportController.prototype.downloadCanvas_ = function (canvas, name) {
|
||||
// Generate file name
|
||||
var name = this.piskelController.getPiskel().getDescriptor().name;
|
||||
name = name || this.piskelController.getPiskel().getDescriptor().name;
|
||||
var fileName = name + '.png';
|
||||
|
||||
// Transform to blob and start download.
|
||||
|
@ -167,7 +171,15 @@
|
|||
var canvas = this.createPngSpritesheet_();
|
||||
var name = this.piskelController.getPiskel().getDescriptor().name;
|
||||
|
||||
zip.file(name + '.png', pskl.utils.CanvasUtils.getBase64FromCanvas(canvas) + '\n', {base64: true});
|
||||
var image;
|
||||
|
||||
if (this.pixiInlineImageCheckbox.checked) {
|
||||
image = canvas.toDataURL('image/png');
|
||||
} else {
|
||||
image = name + '.png';
|
||||
|
||||
zip.file(image, pskl.utils.CanvasUtils.getBase64FromCanvas(canvas) + '\n', {base64: true});
|
||||
}
|
||||
|
||||
var width = canvas.width / this.getColumns_();
|
||||
var height = canvas.height / this.getRows_();
|
||||
|
@ -190,9 +202,9 @@
|
|||
var json = {
|
||||
'frames': frames,
|
||||
'meta': {
|
||||
'app': 'https://github.com/juliandescottes/piskel/',
|
||||
'app': 'https://github.com/piskelapp/piskel/',
|
||||
'version': '1.0',
|
||||
'image': name + '.png',
|
||||
'image': image,
|
||||
'format': 'RGBA8888',
|
||||
'size': {'w': canvas.width,'h': canvas.height}
|
||||
}
|
||||
|
@ -207,6 +219,27 @@
|
|||
};
|
||||
|
||||
ns.PngExportController.prototype.onDataUriClick_ = function (evt) {
|
||||
window.open(this.createPngSpritesheet_().toDataURL('image/png'));
|
||||
var popup = window.open('about:blank');
|
||||
var dataUri = this.createPngSpritesheet_().toDataURL('image/png');
|
||||
window.setTimeout(function () {
|
||||
var html = pskl.utils.Template.getAndReplace('data-uri-export-partial', {
|
||||
src: dataUri
|
||||
});
|
||||
popup.document.title = dataUri;
|
||||
popup.document.body.innerHTML = html;
|
||||
}.bind(this), 500);
|
||||
};
|
||||
|
||||
ns.PngExportController.prototype.onDownloadSelectedFrameClick_ = function (evt) {
|
||||
var frameIndex = this.piskelController.getCurrentFrameIndex();
|
||||
var name = this.piskelController.getPiskel().getDescriptor().name;
|
||||
var canvas = this.piskelController.renderFrameAt(frameIndex, true);
|
||||
var zoom = this.exportController.getExportZoom();
|
||||
if (zoom != 1) {
|
||||
canvas = pskl.utils.ImageResizer.resize(canvas, canvas.width * zoom, canvas.height * zoom, false);
|
||||
}
|
||||
|
||||
var fileName = name + '-' + (frameIndex + 1) + '.png';
|
||||
this.downloadCanvas_(canvas, fileName);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -12,12 +12,25 @@
|
|||
this.pngFilePrefixInput = document.querySelector('.zip-prefix-name');
|
||||
this.pngFilePrefixInput.value = 'sprite_';
|
||||
|
||||
this.splitByLayersCheckbox = document.querySelector('.zip-split-layers-checkbox');
|
||||
this.splitByLayersCheckbox = document.querySelector('.zip-split-layers-checkbox');
|
||||
this.addEventListener(this.splitByLayersCheckbox, 'change', this.onSplitLayersClick_);
|
||||
|
||||
this.useLayerNamesContainer = document.querySelector('.use-layer-names-container');
|
||||
this.useLayerNamesCheckbox = document.querySelector('.zip-use-layer-names-checkbox');
|
||||
this.toggleHideUseLayerNamesCheckbox();
|
||||
|
||||
var zipButton = document.querySelector('.zip-generate-button');
|
||||
this.addEventListener(zipButton, 'click', this.onZipButtonClick_);
|
||||
};
|
||||
|
||||
ns.ZipExportController.prototype.toggleHideUseLayerNamesCheckbox = function () {
|
||||
this.useLayerNamesContainer.style.display = (this.splitByLayersCheckbox.checked ? 'block' : 'none');
|
||||
};
|
||||
|
||||
ns.ZipExportController.prototype.onSplitLayersClick_ = function () {
|
||||
this.toggleHideUseLayerNamesCheckbox();
|
||||
};
|
||||
|
||||
ns.ZipExportController.prototype.onZipButtonClick_ = function () {
|
||||
var zip = new window.JSZip();
|
||||
|
||||
|
@ -63,6 +76,9 @@
|
|||
var basename = this.pngFilePrefixInput.value;
|
||||
var frameid = pskl.utils.StringUtils.leftPad(i + 1, framePaddingLength, '0');
|
||||
var filename = 'l' + layerid + '_' + basename + frameid + '.png';
|
||||
if (this.useLayerNamesCheckbox.checked) {
|
||||
filename = layer.getName() + '_' + basename + frameid + '.png';
|
||||
}
|
||||
zip.file(filename, pskl.utils.CanvasUtils.getBase64FromCanvas(canvas) + '\n', {base64: true});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.settings.preferences');
|
||||
|
||||
var colorsMap = {
|
||||
'transparent': Constants.TRANSPARENT_COLOR,
|
||||
'white': '#FFF1E8',
|
||||
'light-gray': '#C2C3C7',
|
||||
'dark-gray': '#5F574F',
|
||||
'black': '#000000',
|
||||
'blue': '#29ADFF',
|
||||
'dark-blue': '#1D2B53',
|
||||
'green': '#00E436',
|
||||
'dark-green': '#008751',
|
||||
'peach': '#FFCCAA',
|
||||
'pink': '#FF77A8',
|
||||
'yellow': '#FFEC27',
|
||||
'orange': '#FFA300',
|
||||
'red': '#FF004D',
|
||||
};
|
||||
|
||||
ns.GridPreferencesController = function (piskelController, preferencesController) {
|
||||
this.piskelController = piskelController;
|
||||
this.preferencesController = preferencesController;
|
||||
this.sizePicker = new pskl.widgets.SizePicker(this.onSizePickerChanged_.bind(this));
|
||||
this.spacingPicker = new pskl.widgets.SizePicker(this.onSpacingPickerChanged_.bind(this));
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.GridPreferencesController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.GridPreferencesController.prototype.init = function () {
|
||||
// Grid enabled
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.GRID_ENABLED);
|
||||
var enableGridCheckbox = document.querySelector('.enable-grid-checkbox');
|
||||
if (isEnabled) {
|
||||
enableGridCheckbox.setAttribute('checked', 'true');
|
||||
}
|
||||
this.addEventListener(enableGridCheckbox, 'change', this.onEnableGridChange_);
|
||||
|
||||
// Grid size
|
||||
var gridWidth = pskl.UserSettings.get(pskl.UserSettings.GRID_WIDTH);
|
||||
this.sizePicker.init(document.querySelector('.grid-size-container'));
|
||||
this.sizePicker.setSize(gridWidth);
|
||||
|
||||
//Grid Spacing
|
||||
var gridSpacing = pskl.UserSettings.get(pskl.UserSettings.GRID_SPACING);
|
||||
this.spacingPicker.init(document.querySelector('.grid-spacing-container'));
|
||||
this.spacingPicker.setSize(gridSpacing);
|
||||
|
||||
// Grid color
|
||||
var colorListItemTemplate = pskl.utils.Template.get('color-list-item-template');
|
||||
|
||||
var gridColor = pskl.UserSettings.get(pskl.UserSettings.GRID_COLOR);
|
||||
var gridColorSelect = document.querySelector('#grid-color');
|
||||
|
||||
var markup = '';
|
||||
Object.keys(colorsMap).forEach(function (key, index) {
|
||||
var background = colorsMap[key];
|
||||
if (key === 'transparent') {
|
||||
background = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZ' +
|
||||
'F8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)';
|
||||
}
|
||||
markup += pskl.utils.Template.replace(colorListItemTemplate, {
|
||||
color: colorsMap[key],
|
||||
title: key,
|
||||
background: background,
|
||||
':selected': gridColor === colorsMap[key]
|
||||
});
|
||||
});
|
||||
this.gridColorList = document.querySelector('.grid-colors-list');
|
||||
this.gridColorList.innerHTML = markup;
|
||||
|
||||
this.addEventListener(this.gridColorList, 'click', this.onGridColorClicked_.bind(this));
|
||||
};
|
||||
|
||||
ns.GridPreferencesController.prototype.destroy = function () {
|
||||
this.sizePicker.destroy();
|
||||
this.spacingPicker.destroy();
|
||||
this.superclass.destroy.call(this);
|
||||
};
|
||||
|
||||
ns.GridPreferencesController.prototype.onSizePickerChanged_ = function (size) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_WIDTH, size);
|
||||
};
|
||||
|
||||
ns.GridPreferencesController.prototype.onSpacingPickerChanged_ = function (size) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_SPACING, size);
|
||||
};
|
||||
|
||||
ns.GridPreferencesController.prototype.onEnableGridChange_ = function (evt) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_ENABLED, evt.currentTarget.checked);
|
||||
};
|
||||
|
||||
ns.GridPreferencesController.prototype.onGridColorClicked_ = function (evt) {
|
||||
var color = evt.target.dataset.color;
|
||||
if (color) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.GRID_COLOR, color);
|
||||
this.gridColorList.querySelector('.selected').classList.remove('selected');
|
||||
evt.target.classList.add('selected');
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,88 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.settings.preferences');
|
||||
|
||||
ns.MiscPreferencesController = function (piskelController, preferencesController) {
|
||||
this.piskelController = piskelController;
|
||||
this.preferencesController = preferencesController;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.MiscPreferencesController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.MiscPreferencesController.prototype.init = function () {
|
||||
|
||||
this.backgroundContainer = document.querySelector('.background-picker-wrapper');
|
||||
this.addEventListener(this.backgroundContainer, 'click', this.onBackgroundClick_);
|
||||
|
||||
// Highlight selected background :
|
||||
var background = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
|
||||
var selectedBackground = this.backgroundContainer.querySelector('[data-background=' + background + ']');
|
||||
if (selectedBackground) {
|
||||
selectedBackground.classList.add('selected');
|
||||
}
|
||||
|
||||
// Max FPS
|
||||
var maxFpsInput = document.querySelector('.max-fps-input');
|
||||
maxFpsInput.value = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
this.addEventListener(maxFpsInput, 'change', this.onMaxFpsChange_);
|
||||
|
||||
// Color format
|
||||
var colorFormat = pskl.UserSettings.get(pskl.UserSettings.COLOR_FORMAT);
|
||||
var colorFormatSelect = document.querySelector('.color-format-select');
|
||||
var selectedColorFormatOption = colorFormatSelect.querySelector('option[value="' + colorFormat + '"]');
|
||||
if (selectedColorFormatOption) {
|
||||
selectedColorFormatOption.setAttribute('selected', 'selected');
|
||||
}
|
||||
this.addEventListener(colorFormatSelect, 'change', this.onColorFormatChange_);
|
||||
|
||||
// Layer preview opacity
|
||||
var layerOpacityInput = document.querySelector('.layer-opacity-input');
|
||||
layerOpacityInput.value = pskl.UserSettings.get(pskl.UserSettings.LAYER_OPACITY);
|
||||
this.addEventListener(layerOpacityInput, 'change', this.onLayerOpacityChange_);
|
||||
this.addEventListener(layerOpacityInput, 'input', this.onLayerOpacityChange_);
|
||||
this.updateLayerOpacityText_(layerOpacityInput.value);
|
||||
};
|
||||
|
||||
ns.MiscPreferencesController.prototype.onBackgroundClick_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var background = target.dataset.background;
|
||||
if (background) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, background);
|
||||
var selected = this.backgroundContainer.querySelector('.selected');
|
||||
if (selected) {
|
||||
selected.classList.remove('selected');
|
||||
}
|
||||
target.classList.add('selected');
|
||||
}
|
||||
};
|
||||
|
||||
ns.MiscPreferencesController.prototype.onColorFormatChange_ = function (evt) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.COLOR_FORMAT, evt.target.value);
|
||||
};
|
||||
|
||||
ns.MiscPreferencesController.prototype.onMaxFpsChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var fps = parseInt(target.value, 10);
|
||||
if (fps && !isNaN(fps)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.MAX_FPS, fps);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.MAX_FPS);
|
||||
}
|
||||
};
|
||||
|
||||
ns.MiscPreferencesController.prototype.onLayerOpacityChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var opacity = parseFloat(target.value);
|
||||
if (!isNaN(opacity)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.LAYER_OPACITY, opacity);
|
||||
pskl.UserSettings.set(pskl.UserSettings.LAYER_PREVIEW, opacity !== 0);
|
||||
this.updateLayerOpacityText_(opacity);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.LAYER_OPACITY);
|
||||
}
|
||||
};
|
||||
|
||||
ns.MiscPreferencesController.prototype.updateLayerOpacityText_ = function (opacity) {
|
||||
var layerOpacityText = document.querySelector('.layer-opacity-text');
|
||||
layerOpacityText.innerHTML = (opacity * 1).toFixed(2);
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,47 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.controller.settings.preferences');
|
||||
|
||||
ns.TilePreferencesController = function (piskelController, preferencesController) {
|
||||
this.piskelController = piskelController;
|
||||
this.preferencesController = preferencesController;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.TilePreferencesController, pskl.controller.settings.AbstractSettingController);
|
||||
|
||||
ns.TilePreferencesController.prototype.init = function () {
|
||||
// Tile mode
|
||||
var tileMode = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
var tileModeCheckbox = document.querySelector('.tile-mode-checkbox');
|
||||
if (tileMode) {
|
||||
tileModeCheckbox.setAttribute('checked', tileMode);
|
||||
}
|
||||
this.addEventListener(tileModeCheckbox, 'change', this.onTileModeChange_);
|
||||
|
||||
// Tile mask opacity
|
||||
var tileMaskOpacityInput = document.querySelector('.tile-mask-opacity-input');
|
||||
tileMaskOpacityInput.value = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_OPACITY);
|
||||
this.addEventListener(tileMaskOpacityInput, 'change', this.onTileMaskOpacityChange_);
|
||||
this.addEventListener(tileMaskOpacityInput, 'input', this.onTileMaskOpacityChange_);
|
||||
this.updateTileMaskOpacityText_(tileMaskOpacityInput.value);
|
||||
};
|
||||
|
||||
ns.TilePreferencesController.prototype.onTileModeChange_ = function (evt) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.SEAMLESS_MODE, evt.currentTarget.checked);
|
||||
};
|
||||
|
||||
ns.TilePreferencesController.prototype.onTileMaskOpacityChange_ = function (evt) {
|
||||
var target = evt.target;
|
||||
var opacity = parseFloat(target.value);
|
||||
if (!isNaN(opacity)) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.SEAMLESS_OPACITY, opacity);
|
||||
this.updateTileMaskOpacityText_(opacity);
|
||||
} else {
|
||||
target.value = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_OPACITY);
|
||||
}
|
||||
};
|
||||
|
||||
ns.TilePreferencesController.prototype.updateTileMaskOpacityText_ = function (opacity) {
|
||||
var seamlessOpacityText = document.querySelector('.tile-mask-opacity-text');
|
||||
seamlessOpacityText.innerHTML = (opacity * 1).toFixed(2);
|
||||
};
|
||||
})();
|
|
@ -69,7 +69,9 @@
|
|||
resizeContent: this.resizeContentCheckbox.checked
|
||||
});
|
||||
|
||||
pskl.app.piskelController.setPiskel(piskel, true);
|
||||
pskl.app.piskelController.setPiskel(piskel, {
|
||||
preserveState: true
|
||||
});
|
||||
|
||||
$.publish(Events.CLOSE_SETTINGS_DRAWER);
|
||||
};
|
||||
|
|
258
src/js/database/BackupDatabase.js
Normal file
|
@ -0,0 +1,258 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.database');
|
||||
|
||||
var DB_NAME = 'PiskelSessionsDatabase';
|
||||
var DB_VERSION = 1;
|
||||
|
||||
// Simple wrapper to promisify a request.
|
||||
var _requestPromise = function (req) {
|
||||
var deferred = Q.defer();
|
||||
req.onsuccess = deferred.resolve.bind(deferred);
|
||||
req.onerror = deferred.reject.bind(deferred);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* The BackupDatabase handles all the database interactions related
|
||||
* to piskel snapshots continuously saved while during the usage of
|
||||
* Piskel.
|
||||
*/
|
||||
ns.BackupDatabase = function () {
|
||||
this.db = null;
|
||||
};
|
||||
|
||||
ns.BackupDatabase.DB_NAME = DB_NAME;
|
||||
|
||||
/**
|
||||
* Open and initialize the database.
|
||||
* Returns a promise that resolves when the database is opened.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.init = function () {
|
||||
var request = window.indexedDB.open(DB_NAME, DB_VERSION);
|
||||
request.onupgradeneeded = this.onUpgradeNeeded_.bind(this);
|
||||
|
||||
return _requestPromise(request).then(function (event) {
|
||||
this.db = event.target.result;
|
||||
return this.db;
|
||||
}.bind(this)).catch(function (e) {
|
||||
console.log('Could not initialize the piskel backup database');
|
||||
});
|
||||
};
|
||||
|
||||
ns.BackupDatabase.prototype.onUpgradeNeeded_ = function (event) {
|
||||
// Set this.db early to allow migration scripts to access it in oncomplete.
|
||||
this.db = event.target.result;
|
||||
|
||||
// Create an object store "piskels" with the autoIncrement flag set as true.
|
||||
var objectStore = this.db.createObjectStore('snapshots', { keyPath: 'id', autoIncrement : true });
|
||||
|
||||
objectStore.createIndex('session_id', 'session_id', { unique: false });
|
||||
objectStore.createIndex('date', 'date', { unique: false });
|
||||
objectStore.createIndex('session_id, date', ['session_id', 'date'], { unique: false });
|
||||
|
||||
objectStore.transaction.oncomplete = function(event) {
|
||||
// Nothing to do at the moment!
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
ns.BackupDatabase.prototype.openObjectStore_ = function () {
|
||||
return this.db.transaction(['snapshots'], 'readwrite').objectStore('snapshots');
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an add request for the provided snapshot.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.createSnapshot = function (snapshot) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
var request = objectStore.add(snapshot);
|
||||
return _requestPromise(request);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a put request for the provided snapshot.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.updateSnapshot = function (snapshot) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
var request = objectStore.put(snapshot);
|
||||
return _requestPromise(request);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a delete request for the provided snapshot.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.deleteSnapshot = function (snapshot) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
var request = objectStore.delete(snapshot.id);
|
||||
return _requestPromise(request);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a get request for the provided snapshotId.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.getSnapshot = function (snapshotId) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
var request = objectStore.get(snapshotId);
|
||||
return _requestPromise(request).then(function (event) {
|
||||
return event.target.result;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the last (most recent) snapshot that satisfies the accept filter provided.
|
||||
* Returns a promise that will resolve with the first matching snapshot (or null
|
||||
* if no valid snapshot is found).
|
||||
*
|
||||
* @param {Function} accept:
|
||||
* Filter method that takes a snapshot as argument and should return true
|
||||
* if the snapshot is valid.
|
||||
*/
|
||||
ns.BackupDatabase.prototype.findLastSnapshot = function (accept) {
|
||||
// Create the backup promise.
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Open a transaction to the snapshots object store.
|
||||
var objectStore = this.db.transaction(['snapshots']).objectStore('snapshots');
|
||||
|
||||
var index = objectStore.index('date');
|
||||
var range = IDBKeyRange.upperBound(Infinity);
|
||||
index.openCursor(range, 'prev').onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
var snapshot = cursor && cursor.value;
|
||||
|
||||
// Resolve null if we couldn't find a matching snapshot.
|
||||
if (!snapshot) {
|
||||
deferred.resolve(null);
|
||||
} else if (accept(snapshot)) {
|
||||
deferred.resolve(snapshot);
|
||||
} else {
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve all the snapshots for a given session id, sorted by descending date order.
|
||||
* Returns a promise that resolves with an array of snapshots.
|
||||
*
|
||||
* @param {String} sessionId
|
||||
* The session id
|
||||
*/
|
||||
ns.BackupDatabase.prototype.getSnapshotsBySessionId = function (sessionId) {
|
||||
// Create the backup promise.
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Open a transaction to the snapshots object store.
|
||||
var objectStore = this.db.transaction(['snapshots']).objectStore('snapshots');
|
||||
|
||||
// Loop on all the saved snapshots for the provided piskel id
|
||||
var index = objectStore.index('session_id, date');
|
||||
var keyRange = IDBKeyRange.bound(
|
||||
[sessionId, 0],
|
||||
[sessionId, Infinity]
|
||||
);
|
||||
|
||||
var snapshots = [];
|
||||
// Ordered by date in descending order.
|
||||
index.openCursor(keyRange, 'prev').onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
snapshots.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
// Consumed all piskel snapshots
|
||||
deferred.resolve(snapshots);
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ns.BackupDatabase.prototype.getSessions = function () {
|
||||
// Create the backup promise.
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Open a transaction to the snapshots object store.
|
||||
var objectStore = this.db.transaction(['snapshots']).objectStore('snapshots');
|
||||
|
||||
var sessions = {};
|
||||
|
||||
var _createSession = function (snapshot) {
|
||||
sessions[snapshot.session_id] = {
|
||||
startDate: snapshot.date,
|
||||
endDate: snapshot.date,
|
||||
name: snapshot.name,
|
||||
description: snapshot.description,
|
||||
id: snapshot.session_id,
|
||||
count: 1
|
||||
};
|
||||
};
|
||||
|
||||
var _updateSession = function (snapshot) {
|
||||
var s = sessions[snapshot.session_id];
|
||||
s.startDate = Math.min(s.startDate, snapshot.date);
|
||||
s.endDate = Math.max(s.endDate, snapshot.date);
|
||||
s.count++;
|
||||
|
||||
if (s.endDate === snapshot.date) {
|
||||
// If the endDate was updated, update also the session metadata to
|
||||
// reflect the latest state.
|
||||
s.name = snapshot.name;
|
||||
s.description = snapshot.description;
|
||||
}
|
||||
};
|
||||
|
||||
var index = objectStore.index('date');
|
||||
var range = IDBKeyRange.upperBound(Infinity);
|
||||
index.openCursor(range, 'prev').onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
var snapshot = cursor && cursor.value;
|
||||
if (!snapshot) {
|
||||
deferred.resolve(sessions);
|
||||
} else {
|
||||
if (sessions[snapshot.session_id]) {
|
||||
_updateSession(snapshot);
|
||||
} else {
|
||||
_createSession(snapshot);
|
||||
}
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise.then(function (sessions) {
|
||||
// Convert the sessions map to an array.
|
||||
return Object.keys(sessions).map(function (key) {
|
||||
return sessions[key];
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ns.BackupDatabase.prototype.deleteSnapshotsForSession = function (sessionId) {
|
||||
// Create the backup promise.
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Open a transaction to the snapshots object store.
|
||||
var objectStore = this.openObjectStore_();
|
||||
|
||||
// Loop on all the saved snapshots for the provided piskel id
|
||||
var index = objectStore.index('session_id');
|
||||
var keyRange = IDBKeyRange.only(sessionId);
|
||||
|
||||
index.openCursor(keyRange).onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
})();
|
139
src/js/database/PiskelDatabase.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.database');
|
||||
|
||||
var DB_NAME = 'PiskelDatabase';
|
||||
var DB_VERSION = 1;
|
||||
|
||||
// Simple wrapper to promisify a request.
|
||||
var _requestPromise = function (req) {
|
||||
var deferred = Q.defer();
|
||||
req.onsuccess = deferred.resolve.bind(deferred);
|
||||
req.onerror = deferred.reject.bind(deferred);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* The PiskelDatabase handles all the database interactions related
|
||||
* to the local piskel saved that can be performed in-browser.
|
||||
*/
|
||||
ns.PiskelDatabase = function (options) {
|
||||
this.db = null;
|
||||
};
|
||||
|
||||
ns.PiskelDatabase.DB_NAME = DB_NAME;
|
||||
|
||||
ns.PiskelDatabase.prototype.init = function () {
|
||||
var request = window.indexedDB.open(DB_NAME, DB_VERSION);
|
||||
request.onupgradeneeded = this.onUpgradeNeeded_.bind(this);
|
||||
|
||||
return _requestPromise(request).then(function (event) {
|
||||
this.db = event.target.result;
|
||||
return this.db;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.PiskelDatabase.prototype.onUpgradeNeeded_ = function (event) {
|
||||
// Set this.db early to allow migration scripts to access it in oncomplete.
|
||||
this.db = event.target.result;
|
||||
|
||||
// Create an object store "piskels" with the autoIncrement flag set as true.
|
||||
var objectStore = this.db.createObjectStore('piskels', { keyPath : 'name' });
|
||||
objectStore.transaction.oncomplete = function(event) {
|
||||
pskl.database.migrate.MigrateLocalStorageToIndexedDb.migrate(this);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
ns.PiskelDatabase.prototype.openObjectStore_ = function () {
|
||||
return this.db.transaction(['piskels'], 'readwrite').objectStore('piskels');
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a get request for the provided name.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.PiskelDatabase.prototype.get = function (name) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
return _requestPromise(objectStore.get(name)).then(function (event) {
|
||||
return event.target.result;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* List all locally saved piskels.
|
||||
* Returns a promise that resolves an array of objects:
|
||||
* - name: name of the piskel
|
||||
* - description: description of the piskel
|
||||
* - date: save date
|
||||
*
|
||||
* The sprite content is not contained in the object and
|
||||
* needs to be retrieved with a separate get.
|
||||
*/
|
||||
ns.PiskelDatabase.prototype.list = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var piskels = [];
|
||||
var objectStore = this.openObjectStore_();
|
||||
var cursor = objectStore.openCursor();
|
||||
cursor.onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
piskels.push({
|
||||
name: cursor.value.name,
|
||||
date: cursor.value.date,
|
||||
description: cursor.value.description
|
||||
});
|
||||
cursor.continue();
|
||||
} else {
|
||||
// Cursor consumed all availabled piskels
|
||||
deferred.resolve(piskels);
|
||||
}
|
||||
};
|
||||
|
||||
cursor.onerror = function () {
|
||||
deferred.reject();
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an put request for the provided args.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.PiskelDatabase.prototype.update = function (name, description, date, serialized) {
|
||||
var data = {};
|
||||
|
||||
data.name = name;
|
||||
data.serialized = serialized;
|
||||
data.date = date;
|
||||
data.description = description;
|
||||
|
||||
var objectStore = this.openObjectStore_();
|
||||
return _requestPromise(objectStore.put(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an add request for the provided args.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.PiskelDatabase.prototype.create = function (name, description, date, serialized) {
|
||||
var data = {};
|
||||
|
||||
data.name = name;
|
||||
data.serialized = serialized;
|
||||
data.date = date;
|
||||
data.description = description;
|
||||
|
||||
var objectStore = this.openObjectStore_();
|
||||
return _requestPromise(objectStore.add(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a saved piskel for the provided name.
|
||||
* Returns a promise that resolves the request event.
|
||||
*/
|
||||
ns.PiskelDatabase.prototype.delete = function (name) {
|
||||
var objectStore = this.openObjectStore_();
|
||||
return _requestPromise(objectStore.delete(name));
|
||||
};
|
||||
})();
|
76
src/js/database/migrate/MigrateLocalStorageToIndexedDb.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.database.migrate');
|
||||
|
||||
// Simple migration helper to move local storage saves to indexed db.
|
||||
ns.MigrateLocalStorageToIndexedDb = {};
|
||||
|
||||
ns.MigrateLocalStorageToIndexedDb.migrate = function (piskelDatabase) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var localStorageService = pskl.app.localStorageService;
|
||||
|
||||
var localStorageKeys = localStorageService.getKeys();
|
||||
var migrationData = localStorageKeys.map(function (key) {
|
||||
return {
|
||||
name: key.name,
|
||||
description: key.description,
|
||||
date: key.date,
|
||||
serialized: localStorageService.getPiskel(key.name)
|
||||
};
|
||||
});
|
||||
|
||||
// Define the sequential migration process.
|
||||
// Wait for each sprite to be saved before saving the next one.
|
||||
var success = true;
|
||||
var migrateSprite = function (index) {
|
||||
var data = migrationData[index];
|
||||
if (!data) {
|
||||
console.log('Data migration from local storage to indexed db finished.');
|
||||
if (success) {
|
||||
console.log('Local storage piskels successfully migrated. Old copies will be deleted.');
|
||||
ns.MigrateLocalStorageToIndexedDb.deleteLocalStoragePiskels();
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
} else {
|
||||
ns.MigrateLocalStorageToIndexedDb.save_(piskelDatabase, data)
|
||||
.then(function () {
|
||||
migrateSprite(index + 1);
|
||||
})
|
||||
.catch(function (e) {
|
||||
var success = false;
|
||||
console.error('Failed to migrate local storage sprite for name: ' + data.name);
|
||||
migrateSprite(index + 1);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Start the migration.
|
||||
migrateSprite(0);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ns.MigrateLocalStorageToIndexedDb.save_ = function (piskelDatabase, piskelData) {
|
||||
return piskelDatabase.get(piskelData.name).then(function (data) {
|
||||
if (typeof data !== 'undefined') {
|
||||
return piskelDatabase.update(piskelData.name, piskelData.description, piskelData.date, piskelData.serialized);
|
||||
} else {
|
||||
return piskelDatabase.create(piskelData.name, piskelData.description, piskelData.date, piskelData.serialized);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ns.MigrateLocalStorageToIndexedDb.deleteLocalStoragePiskels = function () {
|
||||
var localStorageKeys = pskl.app.localStorageService.getKeys();
|
||||
|
||||
// Remove all sprites.
|
||||
localStorageKeys.forEach(function (key) {
|
||||
window.localStorage.removeItem('piskel.' + key.name);
|
||||
});
|
||||
|
||||
// Remove keys.
|
||||
window.localStorage.removeItem('piskel.keys');
|
||||
};
|
||||
|
||||
})();
|
|
@ -32,6 +32,7 @@
|
|||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.setupInitialState_ = function () {
|
||||
|
||||
var size = this.initialState.size;
|
||||
var piskel = this.createPiskel_(size.width, size.height);
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
|
@ -39,9 +40,10 @@
|
|||
$.publish(Events.SELECT_PRIMARY_COLOR, [this.initialState.primaryColor]);
|
||||
$.publish(Events.SELECT_SECONDARY_COLOR, [this.initialState.secondaryColor]);
|
||||
$.publish(Events.SELECT_TOOL, [this.initialState.selectedTool]);
|
||||
if (this.initialState.penSize) {
|
||||
pskl.app.penSizeService.setPenSize(this.initialState.penSize);
|
||||
}
|
||||
|
||||
// Old tests do not have penSize stored in initialState, fallback to 1.
|
||||
var penSize = this.initialState.penSize || 1;
|
||||
pskl.app.penSizeService.setPenSize(this.initialState.penSize);
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.createPiskel_ = function (width, height) {
|
||||
|
@ -108,6 +110,8 @@
|
|||
this.playTransformToolEvent_(recordEvent);
|
||||
} else if (recordEvent.type === 'instrumented-event') {
|
||||
this.playInstrumentedEvent_(recordEvent);
|
||||
} else if (recordEvent.type === 'clipboard-event') {
|
||||
this.playClipboardEvent_(recordEvent);
|
||||
}
|
||||
|
||||
// Record the time spent replaying the event
|
||||
|
@ -169,6 +173,16 @@
|
|||
pskl.app.piskelController[recordEvent.methodName].apply(pskl.app.piskelController, recordEvent.args);
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.playClipboardEvent_ = function (recordEvent) {
|
||||
$.publish(recordEvent.event.type, {
|
||||
preventDefault: function () {},
|
||||
clipboardData: {
|
||||
items: [],
|
||||
setData: function () {}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.onTestEnd_ = function () {
|
||||
this.removeMouseShim_();
|
||||
// Restore the original drawing loop.
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
$.subscribe(Events.TRANSFORMATION_EVENT, this.onTransformationEvent_.bind(this));
|
||||
$.subscribe(Events.PRIMARY_COLOR_SELECTED, this.onColorEvent_.bind(this, true));
|
||||
$.subscribe(Events.SECONDARY_COLOR_SELECTED, this.onColorEvent_.bind(this, false));
|
||||
$.subscribe(Events.CLIPBOARD_COPY, this.onClipboardEvent_.bind(this));
|
||||
$.subscribe(Events.CLIPBOARD_CUT, this.onClipboardEvent_.bind(this));
|
||||
$.subscribe(Events.CLIPBOARD_PASTE, this.onClipboardEvent_.bind(this));
|
||||
|
||||
|
||||
for (var key in this.piskelController) {
|
||||
if (typeof this.piskelController[key] == 'function') {
|
||||
|
@ -85,7 +89,7 @@
|
|||
which : domEvent.which,
|
||||
shiftKey : domEvent.shiftKey,
|
||||
altKey : domEvent.altKey,
|
||||
ctrlKey : domEvent.ctrlKey,
|
||||
ctrlKey : domEvent.ctrlKey || domEvent.metaKey,
|
||||
target : {
|
||||
nodeName : domEvent.target.nodeName
|
||||
}
|
||||
|
@ -136,12 +140,27 @@
|
|||
}
|
||||
};
|
||||
|
||||
ns.DrawingTestRecorder.prototype.onClipboardEvent_ = function (evt) {
|
||||
if (this.isRecording) {
|
||||
var recordEvent = {};
|
||||
recordEvent.type = 'clipboard-event';
|
||||
recordEvent.event = evt;
|
||||
this.events.push(recordEvent);
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingTestRecorder.prototype.onInstrumentedMethod_ = function (callee, methodName, args) {
|
||||
if (this.isRecording) {
|
||||
var recordEvent = {};
|
||||
recordEvent.type = 'instrumented-event';
|
||||
recordEvent.methodName = methodName;
|
||||
recordEvent.args = Array.prototype.slice.call(args, 0);
|
||||
|
||||
if (methodName === 'setPiskel' && args[1].noSnapshot) {
|
||||
// Skip recording calls to setPiskel that don't trigger a save.
|
||||
return;
|
||||
}
|
||||
|
||||
this.events.push(recordEvent);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
this.descriptor = descriptor;
|
||||
this.savePath = null;
|
||||
this.fps = fps;
|
||||
|
||||
this.hiddenFrames = [];
|
||||
} else {
|
||||
throw 'Missing arguments in Piskel constructor : ' + Array.prototype.join.call(arguments, ',');
|
||||
}
|
||||
|
@ -82,20 +82,28 @@
|
|||
this.layers.splice(index, 0, layer);
|
||||
};
|
||||
|
||||
ns.Piskel.prototype.moveLayerUp = function (layer) {
|
||||
ns.Piskel.prototype.moveLayerUp = function (layer, toTop) {
|
||||
var index = this.layers.indexOf(layer);
|
||||
if (index > -1 && index < this.layers.length - 1) {
|
||||
this.layers[index] = this.layers[index + 1];
|
||||
this.layers[index + 1] = layer;
|
||||
}
|
||||
var toIndex = toTop ? this.layers.length - 1 : index + 1;
|
||||
this.moveLayer_(index, toIndex);
|
||||
};
|
||||
|
||||
ns.Piskel.prototype.moveLayerDown = function (layer) {
|
||||
ns.Piskel.prototype.moveLayerDown = function (layer, toBottom) {
|
||||
var index = this.layers.indexOf(layer);
|
||||
if (index > 0) {
|
||||
this.layers[index] = this.layers[index - 1];
|
||||
this.layers[index - 1] = layer;
|
||||
var toIndex = toBottom ? 0 : index - 1;
|
||||
this.moveLayer_(index, toIndex);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the layer at the provided index to the provided toIndex.
|
||||
*/
|
||||
ns.Piskel.prototype.moveLayer_ = function (fromIndex, toIndex) {
|
||||
if (fromIndex == -1 || toIndex == -1 || fromIndex == toIndex) {
|
||||
return;
|
||||
}
|
||||
toIndex = pskl.utils.Math.minmax(toIndex, 0, this.layers.length - 1);
|
||||
var layer = this.layers.splice(fromIndex, 1)[0];
|
||||
this.layers.splice(toIndex, 0, layer);
|
||||
};
|
||||
|
||||
ns.Piskel.prototype.removeLayer = function (layer) {
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
this.container = container;
|
||||
this.setZoom(zoom);
|
||||
|
||||
var containerEl = container.get(0);
|
||||
var containerDocument = containerEl.ownerDocument;
|
||||
var containerDocument = container.ownerDocument;
|
||||
this.frameContainer = containerDocument.createElement('div');
|
||||
this.frameContainer.classList.add('background-image-frame-container');
|
||||
container.get(0).appendChild(this.frameContainer);
|
||||
container.appendChild(this.frameContainer);
|
||||
|
||||
this.cachedFrameProcessor = new pskl.model.frame.CachedFrameProcessor();
|
||||
this.cachedFrameProcessor.setFrameProcessor(this.frameToDataUrl_.bind(this));
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
var serializedFrame = [
|
||||
this.getZoom(),
|
||||
this.getGridWidth(),
|
||||
this.getGridSpacing(),
|
||||
this.getGridColor(),
|
||||
pskl.UserSettings.get('SEAMLESS_MODE'),
|
||||
pskl.UserSettings.get('SEAMLESS_OPACITY'),
|
||||
offset.x, offset.y,
|
||||
|
|
|
@ -55,7 +55,8 @@
|
|||
this.displayCanvas = null;
|
||||
this.setDisplaySize(renderingOptions.width, renderingOptions.height);
|
||||
|
||||
this.setGridWidth(pskl.UserSettings.get(pskl.UserSettings.GRID_WIDTH));
|
||||
this.setGridWidth(this.getUserGridWidth_());
|
||||
this.setGridSpacing(this.getUserGridSpacing_());
|
||||
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
};
|
||||
|
@ -106,7 +107,7 @@
|
|||
this.displayWidth = width;
|
||||
this.displayHeight = height;
|
||||
if (this.displayCanvas) {
|
||||
$(this.displayCanvas).remove();
|
||||
this.displayCanvas.parentNode.removeChild(this.displayCanvas);
|
||||
this.displayCanvas = null;
|
||||
}
|
||||
this.createDisplayCanvas_();
|
||||
|
@ -138,10 +139,18 @@
|
|||
this.offset.y = y;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.getGridColor = function () {
|
||||
return pskl.UserSettings.get(pskl.UserSettings.GRID_COLOR);
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.setGridWidth = function (value) {
|
||||
this.gridWidth_ = value;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.setGridSpacing = function (value) {
|
||||
this.gridSpacing_ = value;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.getGridWidth = function () {
|
||||
if (!this.supportGridRendering) {
|
||||
return 0;
|
||||
|
@ -150,15 +159,29 @@
|
|||
return this.gridWidth_;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.getGridSpacing = function () {
|
||||
if (!this.supportGridRendering) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.gridSpacing_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute a grid width value best suited to the current display context,
|
||||
* particularly for the current zoom level
|
||||
*/
|
||||
ns.FrameRenderer.prototype.computeGridWidthForDisplay_ = function () {
|
||||
var gridSpacing = this.getGridSpacing();
|
||||
if (this.zoom * gridSpacing < 6) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var gridWidth = this.getGridWidth();
|
||||
while (this.zoom < 6 * gridWidth) {
|
||||
while (gridWidth > 1 && this.zoom < 6 * gridWidth) {
|
||||
gridWidth--;
|
||||
}
|
||||
|
||||
return gridWidth;
|
||||
};
|
||||
|
||||
|
@ -176,24 +199,40 @@
|
|||
|
||||
this.displayCanvas = pskl.utils.CanvasUtils.createCanvas(width, height, this.classList);
|
||||
pskl.utils.CanvasUtils.disableImageSmoothing(this.displayCanvas);
|
||||
this.container.append(this.displayCanvas);
|
||||
this.container.appendChild(this.displayCanvas);
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.onUserSettingsChange_ = function (evt, settingName, settingValue) {
|
||||
if (settingName == pskl.UserSettings.GRID_WIDTH) {
|
||||
this.setGridWidth(settingValue);
|
||||
var settings = pskl.UserSettings;
|
||||
if (settingName == settings.GRID_WIDTH ||
|
||||
settingName == settings.GRID_SPACING ||
|
||||
settingName == settings.GRID_ENABLED) {
|
||||
this.setGridWidth(this.getUserGridWidth_());
|
||||
this.setGridSpacing(this.getUserGridSpacing_());
|
||||
}
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.getUserGridWidth_ = function () {
|
||||
var gridEnabled = pskl.UserSettings.get(pskl.UserSettings.GRID_ENABLED);
|
||||
var width = pskl.UserSettings.get(pskl.UserSettings.GRID_WIDTH);
|
||||
return gridEnabled ? width : 0;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.getUserGridSpacing_ = function () {
|
||||
var gridEnabled = pskl.UserSettings.get(pskl.UserSettings.GRID_ENABLED);
|
||||
var spacing = pskl.UserSettings.get(pskl.UserSettings.GRID_SPACING);
|
||||
return gridEnabled ? spacing : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform a screen pixel-based coordinate (relative to the top-left corner of the rendered
|
||||
* frame) into a sprite coordinate in column and row.
|
||||
* @public
|
||||
*/
|
||||
ns.FrameRenderer.prototype.getCoordinates = function(x, y) {
|
||||
var containerOffset = this.container.offset();
|
||||
x = x - containerOffset.left;
|
||||
y = y - containerOffset.top;
|
||||
var containerRect = this.container.getBoundingClientRect();
|
||||
x = x - containerRect.left;
|
||||
y = y - containerRect.top;
|
||||
|
||||
// apply margins
|
||||
x = x - this.margin.x;
|
||||
|
@ -222,9 +261,9 @@
|
|||
x = x + this.margin.x;
|
||||
y = y + this.margin.y;
|
||||
|
||||
var containerOffset = this.container.offset();
|
||||
x = x + containerOffset.left;
|
||||
y = y + containerOffset.top;
|
||||
var containerRect = this.container.getBoundingClientRect();
|
||||
x = x + containerRect.left;
|
||||
y = y + containerRect.top;
|
||||
|
||||
return {
|
||||
x : x + (cellSize / 2),
|
||||
|
@ -258,7 +297,7 @@
|
|||
var isZoomedOut = translateX > 0 || translateY > 0;
|
||||
// Draw the background / zoomed-out color only if needed. Otherwise the clearRect
|
||||
// happening after that will clear "out of bounds" and seems to be doing nothing
|
||||
// on some chromebooks (cf https://github.com/juliandescottes/piskel/issues/651)
|
||||
// on some chromebooks (cf https://github.com/piskelapp/piskel/issues/651)
|
||||
if (isZoomedOut) {
|
||||
// Draw background
|
||||
displayContext.fillStyle = Constants.ZOOMED_OUT_BACKGROUND_COLOR;
|
||||
|
@ -287,16 +326,31 @@
|
|||
|
||||
// Draw grid.
|
||||
var gridWidth = this.computeGridWidthForDisplay_();
|
||||
var gridSpacing = this.getGridSpacing();
|
||||
if (gridWidth > 0) {
|
||||
var gridColor = this.getGridColor();
|
||||
// Scale out before drawing the grid.
|
||||
displayContext.scale(1 / z, 1 / z);
|
||||
// Clear vertical lines.
|
||||
for (var i = 1 ; i < frame.getWidth() ; i++) {
|
||||
displayContext.clearRect((i * z) - (gridWidth / 2), 0, gridWidth, h * z);
|
||||
|
||||
var drawOrClear;
|
||||
if (gridColor === Constants.TRANSPARENT_COLOR) {
|
||||
drawOrClear = displayContext.clearRect.bind(displayContext);
|
||||
} else {
|
||||
displayContext.fillStyle = gridColor;
|
||||
drawOrClear = displayContext.fillRect.bind(displayContext);
|
||||
}
|
||||
// Clear horizontal lines.
|
||||
|
||||
// Draw or clear vertical lines.
|
||||
for (var i = 1 ; i < frame.getWidth() ; i++) {
|
||||
if (i % gridSpacing == 0) {
|
||||
drawOrClear((i * z) - (gridWidth / 2), 0, gridWidth, h * z);
|
||||
}
|
||||
}
|
||||
// Draw or clear horizontal lines.
|
||||
for (var j = 1 ; j < frame.getHeight() ; j++) {
|
||||
displayContext.clearRect(0, (j * z) - (gridWidth / 2), w * z, gridWidth);
|
||||
if (j % gridSpacing == 0) {
|
||||
drawOrClear(0, (j * z) - (gridWidth / 2), w * z, gridWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
this.updateLayersCanvasOpacity_(pskl.UserSettings.get(pskl.UserSettings.LAYER_OPACITY));
|
||||
|
||||
$.subscribe(Events.PISKEL_RESET, this.flush.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
|
||||
};
|
||||
|
||||
pskl.utils.inherit(pskl.rendering.layer.LayersRenderer, pskl.rendering.CompositeRenderer);
|
||||
|
|
|
@ -5,9 +5,23 @@
|
|||
this.reset();
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.stringify = function () {
|
||||
return JSON.stringify({
|
||||
pixels: this.pixels,
|
||||
time: this.time
|
||||
});
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.parse = function (str) {
|
||||
var selectionData = JSON.parse(str);
|
||||
this.pixels = selectionData.pixels;
|
||||
this.time = selectionData.time;
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.reset = function () {
|
||||
this.pixels = [];
|
||||
this.hasPastedContent = false;
|
||||
this.time = -1;
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.move = function (colDiff, rowDiff) {
|
||||
|
@ -30,5 +44,8 @@
|
|||
});
|
||||
|
||||
this.hasPastedContent = true;
|
||||
// Keep track of the selection time to compare between local selection and
|
||||
// paste event selections.
|
||||
this.time = Date.now();
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -14,21 +14,18 @@
|
|||
};
|
||||
|
||||
ns.SelectionManager.prototype.init = function () {
|
||||
$.subscribe(Events.SELECTION_CREATED, $.proxy(this.onSelectionCreated_, this));
|
||||
$.subscribe(Events.SELECTION_DISMISSED, $.proxy(this.onSelectionDismissed_, this));
|
||||
$.subscribe(Events.SELECTION_MOVE_REQUEST, $.proxy(this.onSelectionMoved_, this));
|
||||
$.subscribe(Events.SELECTION_COPY, this.copy.bind(this));
|
||||
$.subscribe(Events.SELECTION_CUT, this.cut.bind(this));
|
||||
$.subscribe(Events.SELECTION_PASTE, this.paste.bind(this));
|
||||
$.subscribe(Events.SELECTION_CREATED, this.onSelectionCreated_.bind(this));
|
||||
$.subscribe(Events.SELECTION_DISMISSED, this.onSelectionDismissed_.bind(this));
|
||||
$.subscribe(Events.SELECTION_MOVE_REQUEST, this.onSelectionMoved_.bind(this));
|
||||
$.subscribe(Events.CLIPBOARD_COPY, this.copy.bind(this));
|
||||
$.subscribe(Events.CLIPBOARD_CUT, this.copy.bind(this));
|
||||
$.subscribe(Events.CLIPBOARD_PASTE, this.paste.bind(this));
|
||||
|
||||
var shortcuts = pskl.service.keyboard.Shortcuts;
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.SELECTION.PASTE, this.paste.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.SELECTION.CUT, this.cut.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.SELECTION.COPY, this.copy.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.SELECTION.DELETE, this.onDeleteShortcut_.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.SELECTION.COMMIT, this.commit.bind(this));
|
||||
|
||||
$.subscribe(Events.TOOL_SELECTED, $.proxy(this.onToolSelected_, this));
|
||||
$.subscribe(Events.TOOL_SELECTED, this.onToolSelected_.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -78,29 +75,85 @@
|
|||
scope : this,
|
||||
replay : {
|
||||
type : SELECTION_REPLAY.ERASE,
|
||||
pixels : JSON.parse(JSON.stringify(pixels.slice(0)))
|
||||
pixels : JSON.parse(JSON.stringify(pixels))
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.cut = function() {
|
||||
if (this.currentSelection) {
|
||||
// Put cut target into the selection:
|
||||
ns.SelectionManager.prototype.copy = function(event, domEvent) {
|
||||
if (this.currentSelection && this.piskelController.getCurrentFrame()) {
|
||||
this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());
|
||||
this.erase();
|
||||
if (domEvent) {
|
||||
domEvent.clipboardData.setData('text/plain', this.currentSelection.stringify());
|
||||
domEvent.preventDefault();
|
||||
}
|
||||
if (event.type === Events.CLIPBOARD_CUT) {
|
||||
this.erase();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.paste = function() {
|
||||
if (!this.currentSelection || !this.currentSelection.hasPastedContent) {
|
||||
if (window.localStorage.getItem('piskel.clipboard')) {
|
||||
this.currentSelection = JSON.parse(window.localStorage.getItem('piskel.clipboard'));
|
||||
} else {
|
||||
return;
|
||||
ns.SelectionManager.prototype.paste = function(event, domEvent) {
|
||||
var items = domEvent ? domEvent.clipboardData.items : [];
|
||||
|
||||
try {
|
||||
for (var i = 0 ; i < items.length ; i++) {
|
||||
var item = items[i];
|
||||
|
||||
if (/^image/i.test(item.type)) {
|
||||
this.pasteImage_(item);
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (/^text\/plain/i.test(item.type)) {
|
||||
this.pasteText_(item);
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Some of the clipboard APIs are not available on Safari/IE
|
||||
// Allow Piskel to fallback on local currentSelection pasting.
|
||||
}
|
||||
|
||||
var pixels = this.currentSelection.pixels;
|
||||
// temporarily keeping this code path for tests and fallbacks.
|
||||
if (this.currentSelection && this.currentSelection.hasPastedContent) {
|
||||
this.pastePixelsOnCurrentFrame_(this.currentSelection.pixels);
|
||||
}
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.pasteImage_ = function(clipboardItem) {
|
||||
var blob = clipboardItem.getAsFile();
|
||||
pskl.utils.FileUtils.readImageFile(blob, function (image) {
|
||||
pskl.app.fileDropperService.dropPosition_ = {x: 0, y: 0};
|
||||
pskl.app.fileDropperService.onImageLoaded_(image, blob);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.pasteText_ = function(clipboardItem) {
|
||||
var blob = clipboardItem.getAsString(function (selectionString) {
|
||||
var selectionData = JSON.parse(selectionString);
|
||||
var time = selectionData.time;
|
||||
var pixels = selectionData.pixels;
|
||||
|
||||
if (this.currentSelection && this.currentSelection.time >= time) {
|
||||
// If the local selection is newer or equal to the one coming from the clipboard event
|
||||
// use the local one. The reason is that the "move" information is only updated locally
|
||||
// without synchronizing it to the clipboard.
|
||||
// TODO: the selection should store the origin of the selection and the selection itself
|
||||
// separately.
|
||||
pixels = this.currentSelection.pixels;
|
||||
}
|
||||
|
||||
if (pixels) {
|
||||
// If the current clipboard data is some random text, pixels will not be defined.
|
||||
this.pastePixelsOnCurrentFrame_(pixels);
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.pastePixelsOnCurrentFrame_ = function (pixels) {
|
||||
var frame = this.piskelController.getCurrentFrame();
|
||||
|
||||
this.pastePixels_(frame, pixels);
|
||||
|
@ -123,8 +176,7 @@
|
|||
var tool = pskl.app.drawingController.currentToolBehavior;
|
||||
var isSelectionTool = tool instanceof pskl.tools.drawing.selection.BaseSelect;
|
||||
if (isSelectionTool) {
|
||||
var overlay = pskl.app.drawingController.overlayFrame;
|
||||
tool.commitSelection(overlay);
|
||||
tool.commitSelection();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -147,13 +199,6 @@
|
|||
});
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.copy = function() {
|
||||
if (this.currentSelection && this.piskelController.getCurrentFrame()) {
|
||||
this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());
|
||||
window.localStorage.setItem('piskel.clipboard', JSON.stringify(this.currentSelection));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
|
@ -1,65 +1,161 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.service');
|
||||
|
||||
// 1 minute = 1000 * 60
|
||||
var BACKUP_INTERVAL = 1000 * 60;
|
||||
var ONE_SECOND = 1000;
|
||||
var ONE_MINUTE = 60 * ONE_SECOND;
|
||||
|
||||
ns.BackupService = function (piskelController) {
|
||||
// Save every minute = 1000 * 60
|
||||
var BACKUP_INTERVAL = ONE_MINUTE;
|
||||
// Store a new snapshot every 5 minutes.
|
||||
var SNAPSHOT_INTERVAL = ONE_MINUTE * 5;
|
||||
// Store up to 12 snapshots for a piskel session, min. 1 hour of work
|
||||
var MAX_SNAPSHOTS_PER_SESSION = 12;
|
||||
var MAX_SESSIONS = 10;
|
||||
|
||||
ns.BackupService = function (piskelController, backupDatabase) {
|
||||
this.piskelController = piskelController;
|
||||
this.lastHash = null;
|
||||
// Immediately store the current when initializing the Service to avoid storing
|
||||
// empty sessions.
|
||||
this.lastHash = this.piskelController.getPiskel().getHash();
|
||||
this.nextSnapshotDate = -1;
|
||||
|
||||
// backupDatabase can be provided for testing purposes.
|
||||
this.backupDatabase = backupDatabase || new pskl.database.BackupDatabase();
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.init = function () {
|
||||
var previousPiskel = window.localStorage.getItem('bkp.next.piskel');
|
||||
var previousInfo = window.localStorage.getItem('bkp.next.info');
|
||||
if (previousPiskel && previousInfo) {
|
||||
this.savePiskel_('prev', previousPiskel, previousInfo);
|
||||
}
|
||||
this.backupDatabase.init().then(function () {
|
||||
window.setInterval(this.backup.bind(this), BACKUP_INTERVAL);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
window.setInterval(this.backup.bind(this), BACKUP_INTERVAL);
|
||||
// This is purely exposed for testing, so that backup dates can be set programmatically.
|
||||
ns.BackupService.prototype.currentDate_ = function () {
|
||||
return Date.now();
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.backup = function () {
|
||||
var piskel = this.piskelController.getPiskel();
|
||||
var descriptor = piskel.getDescriptor();
|
||||
var hash = piskel.getHash();
|
||||
var info = {
|
||||
name : descriptor.name,
|
||||
description : descriptor.info,
|
||||
date : Date.now(),
|
||||
hash : hash
|
||||
};
|
||||
|
||||
// Do not save an unchanged piskel
|
||||
if (hash !== this.lastHash) {
|
||||
this.lastHash = hash;
|
||||
var serializedPiskel = pskl.utils.serialization.Serializer.serialize(piskel);
|
||||
this.savePiskel_('next', serializedPiskel, JSON.stringify(info));
|
||||
if (hash === this.lastHash) {
|
||||
return Q.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.getPreviousPiskelInfo = function () {
|
||||
var previousInfo = window.localStorage.getItem('bkp.prev.info');
|
||||
if (previousInfo) {
|
||||
return JSON.parse(previousInfo);
|
||||
}
|
||||
};
|
||||
// Update the hash
|
||||
// TODO: should only be done after a successful save.
|
||||
this.lastHash = hash;
|
||||
|
||||
ns.BackupService.prototype.load = function() {
|
||||
var previousPiskel = window.localStorage.getItem('bkp.prev.piskel');
|
||||
previousPiskel = JSON.parse(previousPiskel);
|
||||
// Prepare the backup snapshot.
|
||||
var descriptor = piskel.getDescriptor();
|
||||
var date = this.currentDate_();
|
||||
var snapshot = {
|
||||
session_id: pskl.app.sessionId,
|
||||
date: date,
|
||||
name: descriptor.name,
|
||||
description: descriptor.description,
|
||||
frames: piskel.getFrameCount(),
|
||||
width: piskel.getWidth(),
|
||||
height: piskel.getHeight(),
|
||||
fps: piskel.getFPS(),
|
||||
serialized: pskl.utils.serialization.Serializer.serialize(piskel)
|
||||
};
|
||||
|
||||
pskl.utils.serialization.Deserializer.deserialize(previousPiskel, function (piskel) {
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
return this.getSnapshotsBySessionId(pskl.app.sessionId).then(function (snapshots) {
|
||||
var latest = snapshots[0];
|
||||
|
||||
if (latest && date < this.nextSnapshotDate) {
|
||||
// update the latest snapshot
|
||||
snapshot.id = latest.id;
|
||||
return this.backupDatabase.updateSnapshot(snapshot);
|
||||
} else {
|
||||
// add a new snapshot
|
||||
this.nextSnapshotDate = date + SNAPSHOT_INTERVAL;
|
||||
return this.backupDatabase.createSnapshot(snapshot).then(function () {
|
||||
if (snapshots.length >= MAX_SNAPSHOTS_PER_SESSION) {
|
||||
// remove oldest snapshot
|
||||
return this.backupDatabase.deleteSnapshot(snapshots[snapshots.length - 1]);
|
||||
}
|
||||
}.bind(this)).then(function () {
|
||||
var isNewSession = !latest;
|
||||
if (!isNewSession) {
|
||||
return;
|
||||
}
|
||||
return this.backupDatabase.getSessions().then(function (sessions) {
|
||||
if (sessions.length <= MAX_SESSIONS) {
|
||||
// If MAX_SESSIONS has not been reached, no need to delete
|
||||
// previous sessions.
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare an array containing all the ids of the sessions to be deleted.
|
||||
var sessionIdsToDelete = sessions.sort(function (s1, s2) {
|
||||
return s1.startDate - s2.startDate;
|
||||
}).map(function (s) {
|
||||
return s.id;
|
||||
}).slice(0, sessions.length - MAX_SESSIONS);
|
||||
|
||||
// Delete all the extra sessions.
|
||||
return Q.all(sessionIdsToDelete.map(function (id) {
|
||||
return this.deleteSession(id);
|
||||
}.bind(this)));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this)).catch(function (e) {
|
||||
console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.savePiskel_ = function (type, piskel, info) {
|
||||
try {
|
||||
window.localStorage.setItem('bkp.' + type + '.piskel', piskel);
|
||||
window.localStorage.setItem('bkp.' + type + '.info', info);
|
||||
} catch (e) {
|
||||
console.error('Could not save piskel backup in localStorage.', e);
|
||||
}
|
||||
ns.BackupService.prototype.getSnapshotsBySessionId = function (sessionId) {
|
||||
return this.backupDatabase.getSnapshotsBySessionId(sessionId);
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.deleteSession = function (sessionId) {
|
||||
return this.backupDatabase.deleteSnapshotsForSession(sessionId);
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.getPreviousPiskelInfo = function () {
|
||||
return this.backupDatabase.findLastSnapshot(function (snapshot) {
|
||||
return snapshot.session_id !== pskl.app.sessionId;
|
||||
});
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.list = function() {
|
||||
return this.backupDatabase.getSessions();
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.loadSnapshotById = function(snapshotId) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
this.backupDatabase.getSnapshot(snapshotId).then(function (snapshot) {
|
||||
pskl.utils.serialization.Deserializer.deserialize(
|
||||
JSON.parse(snapshot.serialized),
|
||||
function (piskel) {
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
deferred.resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
// Load "latest" backup snapshot.
|
||||
ns.BackupService.prototype.load = function() {
|
||||
var deferred = Q.defer();
|
||||
|
||||
this.getPreviousPiskelInfo().then(function (snapshot) {
|
||||
pskl.utils.serialization.Deserializer.deserialize(
|
||||
JSON.parse(snapshot.serialized),
|
||||
function (piskel) {
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
deferred.resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -30,9 +30,11 @@
|
|||
};
|
||||
|
||||
ns.BeforeUnloadService.prototype.onBeforeUnload = function (evt) {
|
||||
// Attempt one last backup. Some of it may fail due to the asynchronous
|
||||
// nature of IndexedDB.
|
||||
pskl.app.backupService.backup();
|
||||
if (pskl.app.savedStatusService.isDirty()) {
|
||||
var confirmationMessage = 'Your Piskel seems to have unsaved changes';
|
||||
var confirmationMessage = 'Your current sprite has unsaved changes. Are you sure you want to quit?';
|
||||
|
||||
evt = evt || window.event;
|
||||
if (evt) {
|
||||
|
|
25
src/js/service/ClipboardService.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.service');
|
||||
|
||||
ns.ClipboardService = function (piskelController) {
|
||||
this.piskelController = piskelController;
|
||||
};
|
||||
|
||||
ns.ClipboardService.prototype.init = function () {
|
||||
window.addEventListener('copy', this._onCopy.bind(this), true);
|
||||
window.addEventListener('cut', this._onCut.bind(this), true);
|
||||
window.addEventListener('paste', this._onPaste.bind(this), true);
|
||||
};
|
||||
|
||||
ns.ClipboardService.prototype._onCut = function (event) {
|
||||
$.publish(Events.CLIPBOARD_CUT, event);
|
||||
};
|
||||
|
||||
ns.ClipboardService.prototype._onCopy = function (event) {
|
||||
$.publish(Events.CLIPBOARD_COPY, event);
|
||||
};
|
||||
|
||||
ns.ClipboardService.prototype._onPaste = function (event) {
|
||||
$.publish(Events.CLIPBOARD_PASTE, event);
|
||||
};
|
||||
})();
|
|
@ -83,21 +83,21 @@
|
|||
batchAll(frames, function (frame) {
|
||||
return this.cachedFrameProcessor.get(frame);
|
||||
}.bind(this))
|
||||
.then(function (results) {
|
||||
var colors = {};
|
||||
results.forEach(function (result) {
|
||||
Object.keys(result).forEach(function (color) {
|
||||
colors[color] = true;
|
||||
.then(function (results) {
|
||||
var colors = {};
|
||||
results.forEach(function (result) {
|
||||
Object.keys(result).forEach(function (color) {
|
||||
colors[color] = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
// Remove transparent color from used colors
|
||||
delete colors[pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR)];
|
||||
// Remove transparent color from used colors
|
||||
delete colors[pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR)];
|
||||
|
||||
var hexColors = Object.keys(colors).map(function (color) {
|
||||
return pskl.utils.intToHex(color);
|
||||
});
|
||||
this.setCurrentColors(hexColors);
|
||||
}.bind(this));
|
||||
var hexColors = Object.keys(colors).map(function (color) {
|
||||
return pskl.utils.intToHex(color);
|
||||
});
|
||||
this.setCurrentColors(hexColors);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.CurrentColorsService.prototype.isCurrentColorsPaletteSelected_ = function () {
|
||||
|
|
|
@ -35,13 +35,9 @@
|
|||
var isPiskel = /\.piskel$/i.test(file.name);
|
||||
var isPalette = /\.(gpl|txt|pal)$/i.test(file.name);
|
||||
if (isImage) {
|
||||
$.publish(Events.DIALOG_SHOW, {
|
||||
dialogId : 'import',
|
||||
initArgs : {
|
||||
rawFiles: [file]
|
||||
}
|
||||
});
|
||||
// pskl.utils.FileUtils.readImageFile(file, this.onImageLoaded_.bind(this));
|
||||
pskl.utils.FileUtils.readImageFile(file, function (image) {
|
||||
this.onImageLoaded_(image, file);
|
||||
}.bind(this));
|
||||
} else if (isPiskel) {
|
||||
pskl.utils.PiskelFileUtils.loadFromFile(file, this.onPiskelFileLoaded_, this.onPiskelFileError_);
|
||||
} else if (isPalette) {
|
||||
|
@ -56,7 +52,7 @@
|
|||
};
|
||||
|
||||
ns.FileDropperService.prototype.onPiskelFileLoaded_ = function (piskel) {
|
||||
if (window.confirm('This will replace your current animation')) {
|
||||
if (window.confirm(Constants.CONFIRM_OVERWRITE)) {
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
}
|
||||
};
|
||||
|
@ -65,10 +61,23 @@
|
|||
$.publish(Events.PISKEL_FILE_IMPORT_FAILED, [reason]);
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.onImageLoaded_ = function (importedImage) {
|
||||
ns.FileDropperService.prototype.onImageLoaded_ = function (importedImage, file) {
|
||||
var piskelWidth = pskl.app.piskelController.getWidth();
|
||||
var piskelHeight = pskl.app.piskelController.getHeight();
|
||||
|
||||
if (this.isMultipleFiles_) {
|
||||
this.piskelController.addFrameAtCurrentIndex();
|
||||
this.piskelController.selectNextFrame();
|
||||
} else if (importedImage.width > piskelWidth || importedImage.height > piskelHeight) {
|
||||
// For single file imports, if the file is too big, trigger the import wizard.
|
||||
$.publish(Events.DIALOG_SHOW, {
|
||||
dialogId : 'import',
|
||||
initArgs : {
|
||||
rawFiles: [file]
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var currentFrame = this.piskelController.getCurrentFrame();
|
||||
|
|
|
@ -15,10 +15,17 @@
|
|||
data : imageData
|
||||
};
|
||||
|
||||
var protocol = pskl.utils.Environment.isHttps() ? 'https' : 'http';
|
||||
var wrappedSuccess = function (response) {
|
||||
success(Constants.IMAGE_SERVICE_GET_URL + response.responseText);
|
||||
var getUrl = pskl.utils.Template.replace(Constants.IMAGE_SERVICE_GET_URL, {
|
||||
protocol: protocol
|
||||
});
|
||||
success(getUrl + response.responseText);
|
||||
};
|
||||
|
||||
pskl.utils.Xhr.post(Constants.IMAGE_SERVICE_UPLOAD_URL, data, wrappedSuccess, error);
|
||||
var uploadUrl = pskl.utils.Template.replace(Constants.IMAGE_SERVICE_UPLOAD_URL, {
|
||||
protocol: protocol
|
||||
});
|
||||
pskl.utils.Xhr.post(uploadUrl, data, wrappedSuccess, error);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -97,15 +97,15 @@
|
|||
* @private
|
||||
*/
|
||||
ns.ImportService.prototype.createImagesFromSheet_ = function (image,
|
||||
frameSizeX, frameSizeY, frameOffsetX, frameOffsetY) {
|
||||
frameSizeX, frameSizeY, frameOffsetX, frameOffsetY) {
|
||||
return pskl.utils.CanvasUtils.createFramesFromImage(
|
||||
image,
|
||||
frameOffsetX,
|
||||
frameOffsetY,
|
||||
frameSizeX,
|
||||
frameSizeY,
|
||||
/*useHorizonalStrips=*/ true,
|
||||
/*ignoreEmptyFrames=*/ true);
|
||||
image,
|
||||
frameOffsetX,
|
||||
frameOffsetY,
|
||||
frameSizeX,
|
||||
frameSizeY,
|
||||
/*useHorizonalStrips=*/ true,
|
||||
/*ignoreEmptyFrames=*/ true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -116,7 +116,7 @@
|
|||
* @private
|
||||
*/
|
||||
ns.ImportService.prototype.createPiskelFromImages_ = function (images, name,
|
||||
frameSizeX, frameSizeY, smoothing) {
|
||||
frameSizeX, frameSizeY, smoothing) {
|
||||
name = name || 'Imported piskel';
|
||||
var frames = this.createFramesFromImages_(images, frameSizeX, frameSizeY, smoothing);
|
||||
var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
8 : 'back',
|
||||
13 : 'enter',
|
||||
27 : 'esc',
|
||||
32 : 'space',
|
||||
37 : 'left',
|
||||
38 : 'up',
|
||||
39 : 'right',
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* @public
|
||||
*/
|
||||
ns.ShortcutService.prototype.init = function() {
|
||||
$(document.body).keydown($.proxy(this.onKeyDown_, this));
|
||||
document.body.addEventListener('keydown', this.onKeyDown_.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|