mirror of
https://github.com/piskelapp/piskel.git
synced 2023-08-10 21:12:52 +03:00
Compare commits
250 Commits
v0.8.3
...
greenkeepe
Author | SHA1 | Date | |
---|---|---|---|
5e46d8b7e2 | |||
e15d7b2422 | |||
a8c85b8a29 | |||
89199f2d6a | |||
98768b2e5b | |||
62b1b8baf0 | |||
11a063de12 | |||
6f4413f353 | |||
974450837e | |||
c6c64af2fd | |||
c68b82339c | |||
099ff80155 | |||
f30e16386d | |||
08b97cb6f0 | |||
b6fa769ba1 | |||
47b09b10c5 | |||
f039a89572 | |||
e74329f04e | |||
8959b201e9 | |||
f5491dc557 | |||
9ce3d44cc6 | |||
8928f3e626 | |||
e04fde7084 | |||
a25648f8e2 | |||
1918227c81 | |||
8b1b21368c | |||
d781df3290 | |||
35bfcb5f00 | |||
cce4cde98b | |||
4706587abb | |||
c0182952c9 | |||
ba779a97a6 | |||
dc149f88d6 | |||
ac08ff8b82 | |||
9be332fad3 | |||
11db5ca45c | |||
37445202f3 | |||
f8b77403cd | |||
74d8aa8c9c | |||
acad4df49e | |||
af92ee6bd7 | |||
a740699775 | |||
6ce2c00acf | |||
93db679dd7 | |||
5bde3c471d | |||
94b1a1df4a | |||
8806c41892 | |||
8cad3bb607 | |||
552d4fa710 | |||
b11f7a430d | |||
a4952d0db2 | |||
42f4812b46 | |||
bf91c4ef62 | |||
03f37d46ac | |||
0abd779348 | |||
33259e5522 | |||
e8207aba57 | |||
b03b4a7c60 | |||
2f47bc433c | |||
9cd3fa03a0 | |||
d4b4192d45 | |||
d5199d38aa | |||
ab082b9f4a | |||
2ec6c4b848 | |||
2cdc999875 | |||
4c6d2c1e48 | |||
95256071e1 | |||
b86ec72a4e | |||
70085bc056 | |||
a328e4d20e | |||
569b508fd5 | |||
d30f6a05d1 | |||
76ae797a9e | |||
61c76f980a | |||
5bd113b38f | |||
aa5d4d4090 | |||
398d93557a | |||
0733a5a142 | |||
835cee37aa | |||
2dea7faea0 | |||
809737908c | |||
e43cee3c94 | |||
b869d63211 | |||
c0f7e7be52 | |||
98527c6ded | |||
9518d570e6 | |||
01b9898181 | |||
184b2e48aa | |||
e97a641e95 | |||
7eea5d672a | |||
537234b1d1 | |||
57936d90b1 | |||
ef8060c07d | |||
8551a8546a | |||
eb84e87a13 | |||
ca3b789747 | |||
22dd474799 | |||
16151e8e95 | |||
2f62be4927 | |||
fbaccb03f2 | |||
dd9b1e0189 | |||
dda566a218 | |||
2bcd354342 | |||
6cc41ee07b | |||
eca9191f29 | |||
83c7e950f0 | |||
32070efcc1 | |||
c743334a31 | |||
84419a1550 | |||
66c941dd25 | |||
dba62d2b0d | |||
156161f7c8 | |||
58bfe16b27 | |||
57b37971f6 | |||
8dab74ceea | |||
d0cca52f47 | |||
58cf9f8390 | |||
49109e51c9 | |||
27b26fe4f3 | |||
b21fc0490d | |||
25ede9ffff | |||
37d2861352 | |||
2fe0b842a5 | |||
dd7ab574b9 | |||
c2fdb65e37 | |||
aef88c8713 | |||
b3c13f1630 | |||
6b0eda2998 | |||
16cfffa898 | |||
732c3c2d76 | |||
21c7425cbc | |||
9ac9583455 | |||
9608995d9c | |||
5faa985dea | |||
5544f734cf | |||
9d0d38e081 | |||
ad5c8182e4 | |||
9dd403a54e | |||
f2424ed16e | |||
da0e8dec84 | |||
a5fc491e92 | |||
e53ee0604c | |||
e1671202af | |||
5e3bb9d79c | |||
c5c336e12c | |||
85b141df52 | |||
a65eeee2e0 | |||
39dba2b70f | |||
0cb4a7831b | |||
696d18748a | |||
e328b4c7b8 | |||
5996216ae7 | |||
8cb7a4aaf6 | |||
136506da40 | |||
d08c101b11 | |||
4b50dfdb5b | |||
e3b363d757 | |||
440c28d0fd | |||
a560872df7 | |||
aca6c28b4b | |||
4a01f5625c | |||
8f493a8147 | |||
73986c4e61 | |||
2a7957bce2 | |||
0586756b92 | |||
2b78456314 | |||
4b378d48a2 | |||
5faf4af7c9 | |||
d947fe1c05 | |||
85336f9b9a | |||
91f637d2fd | |||
eb69c91d43 | |||
7ae40d8794 | |||
5a39d6850e | |||
f342b72e1b | |||
1624792f61 | |||
dd58af30b9 | |||
4e515f4820 | |||
71ec809aea | |||
9538e03928 | |||
e647826095 | |||
5fe9081df1 | |||
d4f038f97b | |||
23a0768f8d | |||
4cc434edd6 | |||
26b3cf3a23 | |||
7e9c632efd | |||
6c50816830 | |||
38cbfe4fce | |||
d775609183 | |||
74f36daa7c | |||
ffca6aa44c | |||
6ea9c248d8 | |||
9c96c7eaf2 | |||
c97ce34e6e | |||
43f989919c | |||
0ec8036670 | |||
3a2d837f5a | |||
0f404d2b25 | |||
5577c3ab5a | |||
baf8049ea6 | |||
74b7f93ddb | |||
fb97533cc9 | |||
ebab5ecb3f | |||
37443ecdc2 | |||
91f0d98d63 | |||
fa716d7a6c | |||
ac3e43bf61 | |||
502eb1aac4 | |||
218c0b511b | |||
a3fd2f0d89 | |||
c522d48c8f | |||
c2b283be67 | |||
dbbd57ae28 | |||
fefc635e25 | |||
8910bf0dbd | |||
f607c65789 | |||
2962a838d3 | |||
7eab386122 | |||
0c0aa5f2c9 | |||
bb7d5c862f | |||
f0f79754f1 | |||
a097d0b897 | |||
a1ab693fb5 | |||
7707aca03e | |||
f829d589d5 | |||
712262b82a | |||
0bbb4ff2d5 | |||
6ee23e22df | |||
f5c98cf0b3 | |||
06abdca62e | |||
2ca97b48e7 | |||
132487704b | |||
f6be33d5bf | |||
8a031d771a | |||
3be2b3d3bc | |||
7b512f6412 | |||
b1058c452e | |||
7bc5d20b2e | |||
e9ef6eb5b3 | |||
a2d5e6ff04 | |||
4d12908363 | |||
682ef9cf91 | |||
cbacd68414 | |||
3b65b90de1 | |||
e7ed2f9b24 | |||
0238dffe71 | |||
52eaa8b170 | |||
53d728cf3a | |||
d01f4ef83e |
25
.codeclimate.yml
Normal file
25
.codeclimate.yml
Normal file
@ -0,0 +1,25 @@
|
||||
engines:
|
||||
csslint:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- javascript
|
||||
eslint:
|
||||
enabled: true
|
||||
checks:
|
||||
wrap-iife:
|
||||
enabled: false
|
||||
fixme:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.css"
|
||||
- "**.js"
|
||||
exclude_paths:
|
||||
- .github/
|
||||
- bin/
|
||||
- misc/
|
||||
- src/js/lib/
|
||||
- test/
|
19
.github/ISSUE_TEMPLATE
vendored
Normal file
19
.github/ISSUE_TEMPLATE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Bug description
|
||||
|
||||
(this template is intended for bug reports, erase it when requesting features/enhancements)
|
||||
|
||||
### Steps to reproduce the bug
|
||||
1. go to piskelapp.com
|
||||
2. select a tool
|
||||
3. ...
|
||||
|
||||
### Environment details
|
||||
* operating system:
|
||||
* browser (or offline application version):
|
||||
|
||||
### Sprite details
|
||||
* number of frames:
|
||||
* sprite resolution:
|
||||
* session duration:
|
||||
|
||||
Feel free to include a screenshot of the bug, a .piskel file of the sprite or anything else that can help us investigate.
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,6 +28,7 @@ build/closure/closure_compiled_binary.js
|
||||
|
||||
# spriting artifacts
|
||||
src/img/icons.png
|
||||
src/img/icons@2x.png
|
||||
src/css/icons.css
|
||||
|
||||
# plato report directory
|
||||
|
@ -6,7 +6,7 @@ before_install:
|
||||
- npm install -g grunt-cli
|
||||
- git clone git://github.com/n1k0/casperjs.git ~/casperjs
|
||||
- cd ~/casperjs
|
||||
- git checkout tags/1.0.2
|
||||
- git checkout tags/1.1.3
|
||||
- export PATH=$PATH:`pwd`/bin
|
||||
- cd -
|
||||
before_script:
|
||||
|
124
Gruntfile.js
124
Gruntfile.js
@ -9,8 +9,6 @@ module.exports = function(grunt) {
|
||||
TEST : 9991
|
||||
};
|
||||
|
||||
var DEV_MODE = '?debug';
|
||||
|
||||
// create a version based on the build timestamp
|
||||
var dateFormat = require('dateformat');
|
||||
var version = '-' + dateFormat(new Date(), "yyyy-mm-dd-hh-MM");
|
||||
@ -34,26 +32,19 @@ module.exports = function(grunt) {
|
||||
var stylePaths = require('./src/piskel-style-list.js').styles;
|
||||
var piskelStyles = prefixPaths(stylePaths, "src/");
|
||||
|
||||
var getCasperConfig = function (suiteName, delay, host) {
|
||||
var testPaths = require('./test/casperjs/' + suiteName).tests;
|
||||
var tests = prefixPaths(testPaths, "test/casperjs/");
|
||||
// Casper JS tests
|
||||
var casperjsOptions = [
|
||||
'--baseUrl=http://' + hostname + ':' + PORT.TEST,
|
||||
'--mode=?debug',
|
||||
'--verbose=false',
|
||||
'--includes=test/casperjs/integration/include.js',
|
||||
'--log-level=info',
|
||||
'--print-command=false',
|
||||
'--print-file-paths=true',
|
||||
];
|
||||
|
||||
return {
|
||||
filesSrc : tests,
|
||||
options : {
|
||||
args : {
|
||||
baseUrl : 'http://' + host + ':' + PORT.TEST,
|
||||
mode : DEV_MODE,
|
||||
delay : delay
|
||||
},
|
||||
async : false,
|
||||
direct : false,
|
||||
logLevel : 'info',
|
||||
printCommand : false,
|
||||
printFilePaths : true
|
||||
}
|
||||
};
|
||||
};
|
||||
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') {
|
||||
@ -110,13 +101,15 @@ module.exports = function(grunt) {
|
||||
browser : true,
|
||||
trailing : true,
|
||||
curly : true,
|
||||
globals : {'$':true, 'jQuery' : true, 'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true, 'Q':true}
|
||||
globals : {'$':true, 'jQuery' : true, 'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true, 'Q':true, 'Promise': true}
|
||||
},
|
||||
files: [
|
||||
// Includes
|
||||
'Gruntfile.js',
|
||||
'package.json',
|
||||
'src/js/**/*.js',
|
||||
'!src/js/**/lib/**/*.js' // Exclude lib folder (note the leading !)
|
||||
// Excludes
|
||||
'!src/js/**/lib/**/*.js'
|
||||
]
|
||||
},
|
||||
|
||||
@ -135,7 +128,7 @@ module.exports = function(grunt) {
|
||||
path : 'http://' + hostname + ':' + PORT.PROD + '/'
|
||||
},
|
||||
dev : {
|
||||
path : 'http://' + hostname + ':' + PORT.DEV + '/' + DEV_MODE
|
||||
path : 'http://' + hostname + ':' + PORT.DEV + '/?debug'
|
||||
}
|
||||
},
|
||||
|
||||
@ -180,7 +173,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
css : {
|
||||
src : piskelStyles,
|
||||
dest : 'dest/prod/css/piskel-style-packaged' + version + '.css'
|
||||
dest : 'dest/tmp/css/piskel-style-packaged' + version + '.css'
|
||||
}
|
||||
},
|
||||
|
||||
@ -231,6 +224,19 @@ module.exports = function(grunt) {
|
||||
{src: ['dest/tmp/index.html'], dest: 'dest/prod/piskelapp-partials/main-partial.html'}
|
||||
]
|
||||
},
|
||||
|
||||
css: {
|
||||
options: {
|
||||
patterns: [{
|
||||
match: /var\(--highlight-color\)/g,
|
||||
replacement: "gold",
|
||||
}]
|
||||
},
|
||||
files: [{
|
||||
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: {
|
||||
@ -282,9 +288,23 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
|
||||
ghost : {
|
||||
'travis' : getCasperConfig('TravisTestSuite.js', 10000, hostname),
|
||||
'local' : getCasperConfig('LocalTestSuite.js', 50, hostname)
|
||||
casperjs : {
|
||||
drawing : {
|
||||
files : {
|
||||
src: ['test/casperjs/DrawingTest.js']
|
||||
},
|
||||
options : {
|
||||
casperjsOptions: casperjsOptions
|
||||
}
|
||||
},
|
||||
integration : {
|
||||
files : {
|
||||
src: integrationTests
|
||||
},
|
||||
options : {
|
||||
casperjsOptions: casperjsOptions
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -294,7 +314,8 @@ module.exports = function(grunt) {
|
||||
nwjs: {
|
||||
windows : {
|
||||
options: {
|
||||
version : "0.15.4",
|
||||
downloadUrl: 'https://dl.nwjs.io/',
|
||||
version : "0.19.4",
|
||||
build_dir: './dest/desktop/', // destination folder of releases.
|
||||
win: true,
|
||||
linux32: true,
|
||||
@ -304,8 +325,9 @@ module.exports = function(grunt) {
|
||||
},
|
||||
macos : {
|
||||
options: {
|
||||
downloadUrl: 'https://dl.nwjs.io/',
|
||||
osx64: true,
|
||||
version : "0.15.4",
|
||||
version : "0.19.4",
|
||||
build_dir: './dest/desktop/'
|
||||
},
|
||||
src: ['./dest/prod/**/*', "./package.json", "!./dest/desktop/"]
|
||||
@ -313,38 +335,42 @@ module.exports = function(grunt) {
|
||||
}
|
||||
});
|
||||
|
||||
// Validate
|
||||
// TEST TASKS
|
||||
// Run linting
|
||||
grunt.registerTask('lint', ['jscs:js', 'leadingIndent:css', 'jshint']);
|
||||
|
||||
// karma/unit-tests task
|
||||
// Run unit-tests
|
||||
grunt.registerTask('unit-test', ['karma']);
|
||||
// Run integration tests
|
||||
grunt.registerTask('integration-test', ['build-dev', 'connect:test', 'casperjs:integration']);
|
||||
// Run linting, unit tests, drawing tests and integration tests
|
||||
grunt.registerTask('test', ['lint', 'unit-test', 'build-dev', 'connect:test', 'casperjs:drawing', 'casperjs:integration']);
|
||||
|
||||
// Validate & Test
|
||||
grunt.registerTask('test-travis', ['lint', 'unit-test', 'build-dev', 'connect:test', 'ghost:travis']);
|
||||
// Validate & Test (faster version) will NOT work on travis !!
|
||||
grunt.registerTask('test-local', ['lint', 'unit-test', 'build-dev', 'connect:test', 'ghost:local']);
|
||||
grunt.registerTask('test-local-nolint', ['unit-test', 'build-dev', 'connect:test', 'ghost:local']);
|
||||
// Run the tests, even if the linting fails
|
||||
grunt.registerTask('test-nolint', ['unit-test', 'build-dev', 'connect:test', 'casperjs:drawing', 'casperjs:integration']);
|
||||
|
||||
grunt.registerTask('test', ['test-travis']);
|
||||
grunt.registerTask('precommit', ['test-local']);
|
||||
// Used by optional precommit hook
|
||||
grunt.registerTask('precommit', ['test']);
|
||||
|
||||
// BUILD TASKS
|
||||
grunt.registerTask('build-index.html', ['includereplace']);
|
||||
grunt.registerTask('merge-statics', ['concat:js', 'concat:css', 'uglify']);
|
||||
grunt.registerTask('build', ['clean:prod', 'sprite', 'merge-statics', 'build-index.html', 'replace:mainPartial', 'copy:prod']);
|
||||
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']);
|
||||
|
||||
// Validate & Build
|
||||
grunt.registerTask('default', ['lint', 'build']);
|
||||
|
||||
// Build stand alone app with nodewebkit
|
||||
grunt.registerTask('desktop', ['clean:desktop', 'default', 'replace:desktop', 'nwjs:windows']);
|
||||
grunt.registerTask('desktop-mac', ['clean:desktop', 'default', 'replace:desktop', 'nwjs:macos']);
|
||||
|
||||
// SERVER TASKS
|
||||
// Start webserver and watch for changes
|
||||
grunt.registerTask('serve', ['build', 'connect:prod', 'open:prod', 'watch:prod']);
|
||||
// Start webserver on src folder, in debug mode
|
||||
grunt.registerTask('serve-dev', ['build-dev', 'connect:dev', 'open:dev', 'watch:dev']);
|
||||
grunt.registerTask('play', ['build-dev', 'connect:dev', 'open:dev', 'watch:dev']);
|
||||
|
||||
grunt.registerTask('serve-debug', ['serve-dev']);
|
||||
grunt.registerTask('play', ['serve-dev']);
|
||||
// ALIASES, kept for backward compatibility
|
||||
grunt.registerTask('serve-debug', ['play']);
|
||||
grunt.registerTask('serve-dev', ['play']);
|
||||
grunt.registerTask('test-travis', ['test']);
|
||||
grunt.registerTask('test-local', ['test']);
|
||||
|
||||
// Default task
|
||||
grunt.registerTask('default', ['lint', 'build']);
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
Piskel
|
||||
======
|
||||
|
||||
[](https://greenkeeper.io/)
|
||||
|
||||
[](https://travis-ci.org/juliandescottes/piskel) [](http://gruntjs.com/)
|
||||
|
||||
A simple web-based tool for Spriting and Pixel art.
|
||||
|
62
bin/copy-to-piskel-website.js
Normal file
62
bin/copy-to-piskel-website.js
Normal file
@ -0,0 +1,62 @@
|
||||
const rmdir = require('rmdir');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const fse = require('fs-extra');
|
||||
|
||||
const PISKEL_PATH = path.resolve(__dirname, '..');
|
||||
const PISKELAPP_PATH = path.resolve(__dirname, '../../piskel-website');
|
||||
|
||||
// Callbacks sorted by call sequence.
|
||||
function onCopy(err) {
|
||||
if (err) {
|
||||
console.error('Failed to copy static files...');
|
||||
return console.error(err);
|
||||
}
|
||||
|
||||
console.log('Copied static files to piskel-website...');
|
||||
let previousPartialPath = path.resolve(PISKELAPP_PATH, 'templates/editor/main-partial.html');
|
||||
fs.unlink(previousPartialPath, onDeletePreviousPartial);
|
||||
}
|
||||
|
||||
function onDeletePreviousPartial(err) {
|
||||
if (err) {
|
||||
console.error('Failed to delete previous main partial...');
|
||||
return console.error(err);
|
||||
}
|
||||
|
||||
console.log('Previous main partial deleted...');
|
||||
fse.copy(
|
||||
path.resolve(PISKELAPP_PATH, "static/editor/piskelapp-partials/main-partial.html"),
|
||||
path.resolve(PISKELAPP_PATH, "templates/editor/main-partial.html"),
|
||||
onCopyNewPartial
|
||||
);
|
||||
}
|
||||
|
||||
function onCopyNewPartial(err) {
|
||||
if (err) {
|
||||
console.error('Failed to delete previous main partial...');
|
||||
return console.error(err);
|
||||
}
|
||||
|
||||
console.log('Main partial copied...');
|
||||
rmdir(
|
||||
path.resolve(PISKELAPP_PATH, "static/editor/piskelapp-partials/"),
|
||||
onDeleteTempPartial
|
||||
);
|
||||
}
|
||||
|
||||
function onDeleteTempPartial(err) {
|
||||
if (err) {
|
||||
console.error('Failed to delete temporary main partial...');
|
||||
return console.error(err);
|
||||
}
|
||||
|
||||
console.log('Temporary main partial deleted...');
|
||||
console.log('Finished!');
|
||||
}
|
||||
|
||||
fse.copy(
|
||||
path.resolve(PISKEL_PATH, "dest/prod"),
|
||||
path.resolve(PISKELAPP_PATH, "static/editor"),
|
||||
onCopy
|
||||
);
|
@ -8,7 +8,10 @@ module.exports = function(config) {
|
||||
var piskelScripts = require('./src/piskel-script-list.js').scripts.map(mapToSrcFolder);
|
||||
piskelScripts.push('test/js/testutils/**/*.js');
|
||||
piskelScripts.push('test/js/**/*.js');
|
||||
|
||||
|
||||
// Polyfill for Object.assign (missing in PhantomJS)
|
||||
piskelScripts.push('./node_modules/phantomjs-polyfill-object-assign/object-assign-polyfill.js');
|
||||
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
@ -21,7 +24,9 @@ module.exports = function(config) {
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: piskelScripts,
|
||||
files: piskelScripts.concat([
|
||||
'./node_modules/promise-polyfill/promise.js'
|
||||
]),
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
|
64
misc/icons/source/common-warning-red.svg
Normal file
64
misc/icons/source/common-warning-red.svg
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<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="42"
|
||||
height="42"
|
||||
viewBox="0 0 41.999999 41.999999"
|
||||
enable-background="new 0 0 99.997 69.373"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="common-warning-red.svg"
|
||||
inkscape:export-filename="C:\Development\git\piskel\misc\icons\source\tool-move.png"
|
||||
inkscape:export-xdpi="45"
|
||||
inkscape:export-ydpi="45"><metadata
|
||||
id="metadata9"><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="defs7" /><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="namedview5"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="85.612634"
|
||||
inkscape:cy="3.0742543"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 21,4.2499995 c -0.868171,0 -1.517453,0.2705716 -2,1.03125 L 3.9999999,34.999999 c -0.6703157,1.211185 0.6156981,2.999959 2,3 l 29.9999991,0 c 1.377219,0.0098 2.645421,-1.78334 2,-3 l -15,-29.6874995 C 22.632848,4.6114627 21.868171,4.2499995 21,4.2499995 z m 0,6.2187495 11.999999,23.53125 -23.9999992,0 z"
|
||||
id="rect3765"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="zcccccczcccc" /><path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 20.572432,16.003047 c -0.858253,0.109012 -1.577503,1.040604 -1.572402,2.036618 L 20,25.963381 c 9.2e-5,1.066342 -0.158856,2.036512 0.765534,2.036618 l 0.468961,0 c 0.92439,-1.06e-4 0.765412,-0.970276 0.765504,-2.036618 l 1,-7.923716 c -9.2e-5,-1.066342 -0.841114,-2.036512 -1.765504,-2.036618 l -0.468961,0 c -0.0643,-0.0041 -0.128799,-0.0041 -0.193102,0 z"
|
||||
id="rect3772"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" /><path
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:#00ffff;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:1.60000000000000010"
|
||||
d="m 20.999298,29.000086 c 0.620953,-0.006 0.971639,0.298697 0.999273,1.499911 0.02763,1.201212 -0.347092,1.493936 -0.999273,1.499909 -0.65218,0.006 -1.002864,-0.275261 -0.999271,-1.499909 0.0036,-1.22465 0.378318,-1.493938 0.999271,-1.499911 z"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="zzzzz" /></svg>
|
After Width: | Height: | Size: 4.1 KiB |
@ -1,21 +0,0 @@
|
||||
@ECHO off
|
||||
|
||||
SETLOCAL
|
||||
|
||||
SET PISKEL_PATH="C:\Development\git\piskel"
|
||||
SET PISKELAPP_PATH="C:\Development\git\piskel-website"
|
||||
|
||||
ECHO "Copying files to piskelapp"
|
||||
XCOPY "%PISKEL_PATH%\dest\prod" "%PISKELAPP_PATH%\static\editor" /e /i /h /y
|
||||
|
||||
ECHO "Delete previous partial"
|
||||
DEL "%PISKELAPP_PATH%\templates\editor\main-partial.html"
|
||||
ECHO "Copy new partial"
|
||||
MOVE "%PISKELAPP_PATH%\static\editor\piskelapp-partials\main-partial.html" "%PISKELAPP_PATH%\templates\editor"
|
||||
ECHO "Delete temp partial"
|
||||
RMDIR "%PISKELAPP_PATH%\static\editor\piskelapp-partials\" /S /Q
|
||||
|
||||
PAUSE
|
||||
explorer "%PISKELAPP_PATH%\"
|
||||
|
||||
ENDLOCAL
|
44
package.json
44
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "piskel",
|
||||
"version": "0.9.0-SNAPSHOT",
|
||||
"version": "0.10.0",
|
||||
"description": "Pixel art editor",
|
||||
"author": "Julian Descottes <julian.descottes@gmail.com>",
|
||||
"contributors": [
|
||||
@ -23,35 +23,39 @@
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"start": "nodewebkit",
|
||||
"preversion": "grunt test-local build",
|
||||
"preversion": "grunt test build",
|
||||
"postversion": "git push && git push --tags && npm publish"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dateformat": "1.0.11",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-clean": "1.0.0",
|
||||
"dateformat": "2.0.0",
|
||||
"fs-extra": "3.0.1",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-casperjs": "^2.2.1",
|
||||
"grunt-contrib-clean": "1.1.0",
|
||||
"grunt-contrib-concat": "1.0.1",
|
||||
"grunt-contrib-connect": "1.0.2",
|
||||
"grunt-contrib-copy": "1.0.0",
|
||||
"grunt-contrib-jshint": "1.0.0",
|
||||
"grunt-contrib-uglify": "1.0.1",
|
||||
"grunt-contrib-jshint": "1.1.0",
|
||||
"grunt-contrib-uglify": "2.3.0",
|
||||
"grunt-contrib-watch": "1.0.0",
|
||||
"grunt-ghost": "1.1.0",
|
||||
"grunt-include-replace": "4.0.1",
|
||||
"grunt-jscs": "2.8.0",
|
||||
"grunt-karma": "1.0.0",
|
||||
"grunt-include-replace": "5.0.0",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-karma": "2.0.0",
|
||||
"grunt-leading-indent": "0.2.0",
|
||||
"grunt-nw-builder": "2.0.3",
|
||||
"grunt-nw-builder": "3.1.0",
|
||||
"grunt-open": "0.2.3",
|
||||
"grunt-replace": "1.0.1",
|
||||
"grunt-spritesmith": "6.3.0",
|
||||
"jasmine-core": "2.1.0",
|
||||
"karma": "0.13.21",
|
||||
"karma-chrome-launcher": "1.0.1",
|
||||
"karma-jasmine": "1.0.2",
|
||||
"karma-phantomjs-launcher": "0.2.3",
|
||||
"load-grunt-tasks": "3.5.0",
|
||||
"phantomjs": "1.9.19"
|
||||
"grunt-spritesmith": "6.4.0",
|
||||
"jasmine-core": "2.6.1",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "2.1.1",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-phantomjs-launcher": "1.0.4",
|
||||
"load-grunt-tasks": "3.5.2",
|
||||
"phantomjs": "2.1.7",
|
||||
"phantomjs-polyfill-object-assign": "0.0.2",
|
||||
"promise-polyfill": "6.0.2",
|
||||
"rmdir": "1.2.0"
|
||||
},
|
||||
"window": {
|
||||
"title": "Piskel",
|
||||
|
@ -1,3 +1,9 @@
|
||||
@keyframes fade {
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0% { opacity: 0.66; }
|
||||
50% { opacity: 1; }
|
||||
100% { opacity: 0.66; }
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
.local-piskel-list-head {
|
||||
font-weight: bold;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.local-piskel-load-button,
|
||||
|
@ -3,7 +3,7 @@
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
|
||||
color : gold;
|
||||
color : var(--highlight-color);
|
||||
font-weight: bold;
|
||||
font-size : 1.25em;
|
||||
line-height: 20px;
|
||||
@ -97,7 +97,7 @@
|
||||
border-radius: 2px;
|
||||
|
||||
text-align: center;
|
||||
font-family:Courier;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
font-size : 18px;
|
||||
color: white;
|
||||
@ -113,8 +113,8 @@
|
||||
}
|
||||
|
||||
.cheatsheet-shortcut-editable .cheatsheet-key {
|
||||
border-color: gold;
|
||||
color: gold;
|
||||
border-color: var(--highlight-color);
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.cheatsheet-shortcut-editing .cheatsheet-key {
|
||||
@ -140,7 +140,7 @@
|
||||
padding : 10px;
|
||||
overflow: hidden;
|
||||
|
||||
background-color : gold;
|
||||
background-color : var(--highlight-color);
|
||||
}
|
||||
|
||||
.cheatsheet-helptext {
|
||||
|
@ -103,7 +103,7 @@
|
||||
transition : border-color 0.2s;
|
||||
}
|
||||
.create-palette-color:hover {
|
||||
border:1px solid gold;
|
||||
border:1px solid var(--highlight-color);
|
||||
}
|
||||
|
||||
.colors-list-drop-proxy {
|
||||
@ -111,17 +111,17 @@
|
||||
}
|
||||
|
||||
.create-palette-new-color {
|
||||
border:2px dotted gold;
|
||||
border:2px dotted var(--highlight-color);
|
||||
|
||||
border-radius: 2px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.create-palette-color.selected {
|
||||
border:2px solid gold;
|
||||
border:2px solid var(--highlight-color);
|
||||
}
|
||||
|
||||
.create-palette-remove-color {
|
||||
|
@ -81,7 +81,7 @@
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
text-shadow: none;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
[name=smooth-resize-checkbox] {
|
||||
|
68
src/css/dialogs-performance-info.css
Normal file
68
src/css/dialogs-performance-info.css
Normal file
@ -0,0 +1,68 @@
|
||||
.performance-link {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
z-index: 11000;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition : opacity 0.3s;
|
||||
}
|
||||
|
||||
.performance-link.visible {
|
||||
display: block;
|
||||
opacity: 0.66;
|
||||
animation: glow 2s infinite;
|
||||
}
|
||||
|
||||
.performance-link.visible:hover {
|
||||
opacity: 1;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
#dialog-container.performance-info {
|
||||
width: 500px;
|
||||
height: 525px;
|
||||
top : 50%;
|
||||
left : 50%;
|
||||
position : absolute;
|
||||
margin-left: -250px;
|
||||
margin-top: -260px;
|
||||
|
||||
}
|
||||
|
||||
.dialog-performance-info-body {
|
||||
font-size: 13px;
|
||||
letter-spacing: 1px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.dialog-performance-info-body ul {
|
||||
border: 1px solid #666;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.dialog-performance-info-body li {
|
||||
list-style-type: initial;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.dialog-performance-info-body sup {
|
||||
color: var(--highlight-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.show #dialog-container.performance-info {
|
||||
margin-top: -300px;
|
||||
}
|
||||
|
||||
.dialog-performance-info-body .warning-icon {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dialog-performance-info-body .warning-icon-info {
|
||||
overflow: hidden;
|
||||
margin-left: 30px;
|
||||
}
|
32
src/css/dialogs-unsupported-browser.css
Normal file
32
src/css/dialogs-unsupported-browser.css
Normal file
@ -0,0 +1,32 @@
|
||||
/************************************************************************************************/
|
||||
/* Unsupported browser dialog */
|
||||
/************************************************************************************************/
|
||||
|
||||
#dialog-container.unsupported-browser {
|
||||
width: 600px;
|
||||
height: 260px;
|
||||
top : 50%;
|
||||
left : 50%;
|
||||
position : absolute;
|
||||
margin-top: -130px;
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
.unsupported-browser .dialog-content {
|
||||
font-size:1.2em;
|
||||
letter-spacing: 1px;
|
||||
padding:10px 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.unsupported-browser .supported-browser-list {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.unsupported-browser .supported-browser-list li {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
#current-user-agent {
|
||||
color: var(--highlight-color);
|
||||
}
|
@ -39,7 +39,7 @@
|
||||
-moz-box-sizing : border-box;
|
||||
|
||||
border-radius: 3px;
|
||||
border : 3px solid gold;
|
||||
border : 3px solid var(--highlight-color);
|
||||
background: #444;
|
||||
overflow: auto;
|
||||
}
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
.dialog-head {
|
||||
width: 100%;
|
||||
background: gold;
|
||||
background: var(--highlight-color);
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
color: black;
|
||||
|
@ -22,6 +22,11 @@
|
||||
background : #3a3a3a;
|
||||
}
|
||||
|
||||
.textfield:focus {
|
||||
border-color: var(--highlight-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.textfield-small {
|
||||
width : 50px;
|
||||
}
|
||||
@ -29,17 +34,13 @@
|
||||
.button {
|
||||
box-sizing: border-box;
|
||||
height: 24px;
|
||||
|
||||
background-color: #3f3f3f;
|
||||
border: 1px solid #333;
|
||||
border-top-color: #666;
|
||||
border-bottom-color: #222;
|
||||
|
||||
background-color: #666;
|
||||
border-style: none;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
color: white;
|
||||
text-shadow: 0 -1px 0 black;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
@ -48,25 +49,17 @@
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
text-decoration: none;
|
||||
background-color: #484848;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background-color: rgb(255,215,0); /* gold */
|
||||
|
||||
border-color: rgb(179, 164, 0);
|
||||
border-top-color: white;
|
||||
border-bottom-color: rgb(151, 133, 0);
|
||||
|
||||
background-color: var(--highlight-color);
|
||||
color: black;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.button-primary:hover {
|
||||
background-color: rgb(255,235,20);
|
||||
color: #333;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.button[disabled],
|
||||
@ -74,10 +67,6 @@
|
||||
cursor:default;
|
||||
background-color: #aaa;
|
||||
color: #777;
|
||||
text-shadow: 0 1px 0 #bbb;
|
||||
border-color: #666;
|
||||
border-top-color: #999;
|
||||
border-bottom-color: #555;
|
||||
}
|
||||
|
||||
.import-size-field,
|
||||
|
@ -79,7 +79,7 @@
|
||||
}
|
||||
|
||||
.add-frame-action:hover {
|
||||
border-color: gold;
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.preview-tile {
|
||||
@ -153,7 +153,7 @@
|
||||
}
|
||||
|
||||
.preview-tile.selected {
|
||||
border-color: gold;
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.preview-tile.selected:after {
|
||||
@ -162,7 +162,7 @@
|
||||
top: 38px;
|
||||
right: -9px;
|
||||
border: transparent 4px solid;
|
||||
border-left-color: gold;
|
||||
border-left-color: var(--highlight-color);
|
||||
border-width: 6px 0 6px 6px;
|
||||
border-style: solid;
|
||||
}
|
||||
@ -173,6 +173,6 @@
|
||||
*/
|
||||
|
||||
.preview-tile-drop-proxy {
|
||||
border: 3px dashed gold;
|
||||
border: 3px dashed var(--highlight-color);
|
||||
background-color: rgba(255, 215,0, 0.2);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@
|
||||
color:#888;
|
||||
font-size:12px;
|
||||
font-weight:bold;
|
||||
font-family:Courier;
|
||||
font-family:monospace;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,8 +1,8 @@
|
||||
.minimap-crop-frame {
|
||||
position:absolute;
|
||||
border:1px solid rgba(255,255,255,0.5);
|
||||
z-index:100;
|
||||
box-sizing : border-box;
|
||||
-moz-box-sizing : border-box;
|
||||
cursor : pointer;
|
||||
position: absolute;
|
||||
border: 2px solid gold;
|
||||
z-index: 100;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
@ -6,12 +6,12 @@
|
||||
max-width: 300px;
|
||||
|
||||
border-top-left-radius: 7px;
|
||||
border: #F0C36D 1px solid;
|
||||
border: #e1a325 2px solid;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
|
||||
color: #222;
|
||||
background-color: #F9EDBE;
|
||||
background-color: var(--highlight-color);
|
||||
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
@ -24,8 +24,6 @@
|
||||
top: 6px;
|
||||
right: 17px;
|
||||
|
||||
color: gray;
|
||||
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
|
||||
@ -43,7 +41,7 @@
|
||||
padding: 10px;
|
||||
width: 360px;
|
||||
border-top-right-radius: 2px;
|
||||
border: gold 2px solid;
|
||||
border: var(--highlight-color) 2px solid;
|
||||
border-left: 0;
|
||||
border-bottom: 0;
|
||||
background-color: #444;
|
||||
@ -69,6 +67,6 @@
|
||||
margin-top: 8px;
|
||||
height : 4px;
|
||||
width : 300px;
|
||||
background : linear-gradient(to left, gold, gold) no-repeat -300px 0;
|
||||
background : linear-gradient(to left, var(--highlight-color), var(--highlight-color)) no-repeat -300px 0;
|
||||
background-color : black;
|
||||
}
|
@ -16,17 +16,17 @@
|
||||
}
|
||||
|
||||
.pen-size-option[data-size='1'] {
|
||||
padding: 6px;
|
||||
}
|
||||
.pen-size-option[data-size='2'] {
|
||||
padding: 5px;
|
||||
}
|
||||
.pen-size-option[data-size='3'] {
|
||||
.pen-size-option[data-size='2'] {
|
||||
padding: 4px;
|
||||
}
|
||||
.pen-size-option[data-size='4'] {
|
||||
.pen-size-option[data-size='3'] {
|
||||
padding: 3px;
|
||||
}
|
||||
.pen-size-option[data-size='4'] {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.pen-size-option:before {
|
||||
content: '';
|
||||
@ -34,6 +34,9 @@
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.pen-size-option:hover {
|
||||
@ -41,9 +44,14 @@
|
||||
}
|
||||
|
||||
.pen-size-option.selected:before {
|
||||
background-color: gold;
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.pen-size-option.selected {
|
||||
border-color: gold;
|
||||
}
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.pen-size-option.labeled:before {
|
||||
content: attr(real-pen-size);
|
||||
color: black;
|
||||
}
|
||||
|
@ -47,5 +47,5 @@ input[type="range"] {
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color:gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
@ -27,16 +27,23 @@
|
||||
}
|
||||
|
||||
.background-picker.selected {
|
||||
border-color: gold;
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.settings-opacity-input {
|
||||
margin: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.layer-opacity-input {
|
||||
margin: 5px;
|
||||
vertical-align: middle;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.layer-opacity-text {
|
||||
.seamless-opacity-input {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.settings-opacity-text {
|
||||
height: 31px;
|
||||
display: inline-block;
|
||||
line-height: 30px;
|
||||
@ -47,7 +54,8 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.grid-width-select {
|
||||
.grid-width-select,
|
||||
.color-format-select {
|
||||
margin: 5px 5px 0 5px;
|
||||
}
|
||||
|
||||
@ -55,3 +63,7 @@
|
||||
/* Override the default 10px margin bottom for this panel */
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.settings-section-application .button-primary {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -113,7 +113,7 @@
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
z-index: 0;
|
||||
background-color: gold;
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.export-tab {
|
||||
@ -121,6 +121,7 @@
|
||||
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;
|
||||
@ -128,11 +129,12 @@
|
||||
|
||||
.export-tab.selected,
|
||||
.export-tab:hover {
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.export-tab.selected {
|
||||
border-color: gold gold #444 gold;
|
||||
border-color: var(--highlight-color);
|
||||
border-bottom-color: #444;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
}
|
||||
|
||||
.resize-origin-option.selected {
|
||||
border : 3px solid gold;
|
||||
border : 3px solid var(--highlight-color);
|
||||
}
|
||||
|
||||
.resize-origin-option:before {
|
||||
@ -65,7 +65,7 @@
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: gold;
|
||||
background: var(--highlight-color);
|
||||
}
|
||||
|
||||
.disabled .resize-origin-option.selected:before {
|
||||
@ -85,23 +85,23 @@
|
||||
}
|
||||
|
||||
.resize-origin-option[data-neighbor="bottom"]:before {
|
||||
border-top-color: gold;
|
||||
border-top-color: var(--highlight-color);
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.resize-origin-option[data-neighbor="left"]:before {
|
||||
border-right-color: gold;
|
||||
border-right-color: var(--highlight-color);
|
||||
margin-top: -4px;
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
.resize-origin-option[data-neighbor="top"]:before {
|
||||
border-bottom-color: gold;
|
||||
border-bottom-color: var(--highlight-color);
|
||||
margin-top: -6px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.resize-origin-option[data-neighbor="right"]:before {
|
||||
border-left-color: gold;
|
||||
border-left-color: var(--highlight-color);
|
||||
margin-top: -4px;
|
||||
}
|
@ -24,3 +24,13 @@
|
||||
color: white;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.save-status-warning-icon {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.save-status-warning-icon {
|
||||
overflow: hidden;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
background-color: #444;
|
||||
margin-right: 0;
|
||||
padding-right: 2px;
|
||||
border-left : 3px solid gold;
|
||||
border-left : 3px solid var(--highlight-color);
|
||||
}
|
||||
|
||||
/************************************************************************************************/
|
||||
@ -64,7 +64,6 @@
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
text-shadow: 1px 1px #000;
|
||||
}
|
||||
|
||||
.settings-section .button {
|
||||
@ -77,7 +76,7 @@
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px #aaa solid;
|
||||
padding-bottom: 5px;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.settings-description {
|
||||
|
@ -100,8 +100,8 @@
|
||||
}
|
||||
|
||||
.sp-palette .sp-thumb-el.sp-thumb-active {
|
||||
border-color: gold;
|
||||
box-shadow: 0 0 0px 1px gold;
|
||||
border-color: var(--highlight-color);
|
||||
box-shadow: 0 0 0px 1px var(--highlight-color);
|
||||
}
|
||||
|
||||
.sp-input {
|
||||
@ -110,7 +110,7 @@
|
||||
background: #111;
|
||||
border-radius: 2px;
|
||||
color: #D3D3D3;
|
||||
font-family: Courier!important;
|
||||
font-family: monospace!important;
|
||||
}
|
||||
|
||||
.sp-input.sp-validation-error {
|
||||
|
@ -17,11 +17,11 @@ body {
|
||||
}
|
||||
|
||||
.no-overflow {
|
||||
overflow : hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-link {
|
||||
color : gold;
|
||||
.highlight {
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.pull-top,
|
||||
@ -59,7 +59,7 @@ body {
|
||||
* TOOLTIPS
|
||||
*/
|
||||
.tooltip-shortcut {
|
||||
color:gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
.tooltip-container {
|
||||
|
@ -79,7 +79,7 @@
|
||||
|
||||
.preview-toggle-onion-skin-enabled,
|
||||
.preview-toggle-onion-skin-enabled:hover {
|
||||
color : gold;
|
||||
color : var(--highlight-color);
|
||||
}
|
||||
|
||||
.preview-contextual-actions {
|
||||
@ -98,7 +98,9 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.original-size-button {
|
||||
.preview-contextual-action {
|
||||
float: left;
|
||||
|
||||
width : 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
@ -113,28 +115,86 @@
|
||||
font-family: Tahoma;
|
||||
}
|
||||
|
||||
.original-size-button-enabled {
|
||||
color: gold;
|
||||
border-color: gold;
|
||||
.preview-contextual-action-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-contextual-action {
|
||||
float: left;
|
||||
.preview-contextual-action:hover {
|
||||
color: var(--highlight-color);
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-down in preview size selection
|
||||
*/
|
||||
.preview-drop-down {
|
||||
float: left;
|
||||
position: relative;
|
||||
width : 22px;
|
||||
min-height: 22px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.preview-drop-down.preview-drop-down-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.preview-disable-overlay{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-drop-down.preview-drop-down-disabled .preview-disable-overlay {
|
||||
display: block;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.preview-drop-down .preview-contextual-action {
|
||||
position: relative;
|
||||
margin: 0 0 -100% 0;
|
||||
opacity: 0;
|
||||
transition: opacity linear .2s,
|
||||
margin linear .2s;
|
||||
transition-delay: 0s, .2s;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.preview-drop-down:hover .preview-contextual-action {
|
||||
margin: 0 0 5px 0;
|
||||
opacity: 1;
|
||||
transition-delay: 0s, 0s;
|
||||
}
|
||||
|
||||
.preview-drop-down .size-button-selected {
|
||||
opacity: 1;
|
||||
color: gold;
|
||||
border-color: gold;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
|
||||
.open-popup-preview-button {
|
||||
border : 2px solid white;
|
||||
background-color : rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.open-popup-preview-button:hover {
|
||||
border-color: gold;
|
||||
border-color: var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* The regular image is provided bby the sprite icons.png+icons.css
|
||||
* The regular image is provided by the sprite icons.png+icons.css
|
||||
*/
|
||||
.icon-minimap-popup-preview-arrow-white:hover {
|
||||
background-image: url(../img/icons/minimap/minimap-popup-preview-arrow-gold.png);
|
||||
background-position: 0 0;
|
||||
background-size: 18px 18px;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2),
|
||||
(min-resolution: 192dpi) {
|
||||
background-image: url(../img/icons/minimap/minimap-popup-preview-arrow-gold@2x.png);
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
.layers-toggle-preview-enabled,
|
||||
.layers-toggle-preview-enabled:hover {
|
||||
color : gold;
|
||||
color : var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,24 +59,34 @@
|
||||
|
||||
.layer-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height:24px;
|
||||
line-height: 24px;
|
||||
padding: 0 0 0 10px;
|
||||
border-top: 1px solid #444;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layer-item .layer-name {
|
||||
padding: 0 0 0 10px;
|
||||
flex: 1 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.layer-item .layer-name.overflowing-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.layer-item:hover {
|
||||
background : #222;
|
||||
}
|
||||
|
||||
.layer-item-opacity {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.current-layer-item,
|
||||
.current-layer-item:hover {
|
||||
background : #333;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
}
|
@ -127,7 +127,7 @@
|
||||
right: 0;
|
||||
|
||||
background-color: black;
|
||||
color: gold;
|
||||
color: var(--highlight-color);
|
||||
|
||||
font-family: Tahoma;
|
||||
font-size: 0.5em;
|
||||
|
@ -26,7 +26,17 @@
|
||||
|
||||
.toolbox-buttons .button {
|
||||
/* Override border propery on .button elements from form.css */
|
||||
border-style: solid;
|
||||
border-color: #333;
|
||||
border-width: 0 1px 0 0;
|
||||
border-radius: 0;
|
||||
|
||||
background-color: #3f3f3f;
|
||||
}
|
||||
|
||||
.toolbox-buttons .button[disabled],
|
||||
.toolbox-buttons .button[disabled]:hover {
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
.toolbox-buttons button:last-child {
|
||||
|
@ -17,7 +17,7 @@
|
||||
position : absolute;
|
||||
height : 100%;
|
||||
width : 100%;
|
||||
border: 3px solid gold;
|
||||
border: 3px solid var(--highlight-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
3
src/css/variables.css
Normal file
3
src/css/variables.css
Normal file
@ -0,0 +1,3 @@
|
||||
html, body {
|
||||
--highlight-color: gold;
|
||||
}
|
BIN
src/img/icons/common/common-warning-red.png
Normal file
BIN
src/img/icons/common/common-warning-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 446 B |
BIN
src/img/icons/common/common-warning-red@2x.png
Normal file
BIN
src/img/icons/common/common-warning-red@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 851 B |
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -71,13 +71,18 @@
|
||||
@@include('templates/misc-templates.html', {})
|
||||
@@include('templates/popup-preview.html', {})
|
||||
|
||||
<span class="cheatsheet-link icon-common-keyboard-gold" rel="tooltip" data-placement="right" title="Keyboard shortcuts"> </span>
|
||||
<span class="cheatsheet-link icon-common-keyboard-gold"
|
||||
rel="tooltip" data-placement="right" title="Keyboard shortcuts"> </span>
|
||||
<div class="performance-link icon-common-warning-red"
|
||||
rel="tooltip" data-placement="left" title="Performance problem detected, learn more."> </div>
|
||||
|
||||
<!-- dialogs partials -->
|
||||
@@include('templates/dialogs/create-palette.html', {})
|
||||
@@include('templates/dialogs/import-image.html', {})
|
||||
@@include('templates/dialogs/browse-local.html', {})
|
||||
@@include('templates/dialogs/cheatsheet.html', {})
|
||||
@@include('templates/dialogs/performance-info.html', {})
|
||||
@@include('templates/dialogs/unsupported-browser.html', {})
|
||||
|
||||
<!-- settings-panel partials -->
|
||||
@@include('templates/settings/application.html', {})
|
||||
|
@ -13,13 +13,16 @@ var Constants = {
|
||||
MAX_WIDTH : 1024,
|
||||
|
||||
MAX_PALETTE_COLORS : 100,
|
||||
// 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,
|
||||
|
||||
DEFAULT_PEN_COLOR : '#000000',
|
||||
TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)',
|
||||
SEAMLESS_MODE_OVERLAY_COLOR : 'rgba(255, 255, 255, 0.5)',
|
||||
SEAMLESS_MODE_OVERLAY_COLOR : 'rgba(255, 255, 255, 0)',
|
||||
|
||||
CURRENT_COLORS_PALETTE_ID : '__current-colors',
|
||||
|
||||
@ -49,6 +52,12 @@ var Constants = {
|
||||
// TESTS
|
||||
DRAWING_TEST_FOLDER : 'drawing',
|
||||
|
||||
// Maximum size of a sprite that can be saved on piskelapp datastore.
|
||||
// This size will be compared to the length of the stringified serialization of the sprite.
|
||||
// This is an approximation at best but gives correct results in most cases.
|
||||
// The datastore limit is 1 MiB, which we roughly approximate to 1 million characters.
|
||||
APPENGINE_SAVE_LIMIT : 1 * 1024 * 1024,
|
||||
|
||||
// SERVICE URLS
|
||||
APPENGINE_SAVE_URL : 'save',
|
||||
IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-b.appspot.com/__/upload',
|
||||
|
@ -59,6 +59,7 @@ var Events = {
|
||||
AFTER_SAVING_PISKEL: 'AFTER_SAVING_PISKEL',
|
||||
|
||||
FRAME_SIZE_CHANGED : 'FRAME_SIZE_CHANGED',
|
||||
FPS_CHANGED : 'FPS_CHANGED',
|
||||
|
||||
SELECTION_CREATED: 'SELECTION_CREATED',
|
||||
SELECTION_MOVE_REQUEST: 'SELECTION_MOVE_REQUEST',
|
||||
@ -79,6 +80,10 @@ var Events = {
|
||||
|
||||
CURRENT_COLORS_UPDATED : 'CURRENT_COLORS_UPDATED',
|
||||
|
||||
PERFORMANCE_REPORT_CHANGED : 'PERFORMANCE_REPORT_CHANGED',
|
||||
|
||||
PISKEL_FILE_IMPORT_FAILED : 'PISKEL_FILE_IMPORT_FAILED',
|
||||
|
||||
// Tests
|
||||
MOUSE_EVENT : 'MOUSE_EVENT',
|
||||
KEYBOARD_EVENT : 'KEYBOARD_EVENT',
|
||||
|
@ -19,8 +19,9 @@
|
||||
this.shortcutService.init();
|
||||
|
||||
var size = pskl.UserSettings.get(pskl.UserSettings.DEFAULT_SIZE);
|
||||
var fps = Constants.DEFAULT.FPS;
|
||||
var descriptor = new pskl.model.piskel.Descriptor('New Piskel', '');
|
||||
var piskel = new pskl.model.Piskel(size.width, size.height, descriptor);
|
||||
var piskel = new pskl.model.Piskel(size.width, size.height, fps, descriptor);
|
||||
|
||||
var layer = new pskl.model.Layer('Layer 1');
|
||||
var frame = new pskl.model.Frame(size.width, size.height);
|
||||
@ -35,6 +36,8 @@
|
||||
this.piskelController.init();
|
||||
|
||||
this.paletteImportService = new pskl.service.palette.PaletteImportService();
|
||||
this.paletteImportService.init();
|
||||
|
||||
this.paletteService = new pskl.service.palette.PaletteService();
|
||||
this.paletteService.addDynamicPalette(new pskl.service.palette.CurrentColorsPalette());
|
||||
|
||||
@ -58,7 +61,6 @@
|
||||
|
||||
this.drawingController = new pskl.controller.DrawingController(
|
||||
this.piskelController,
|
||||
this.paletteController,
|
||||
$('#drawing-canvas-container'));
|
||||
this.drawingController.init();
|
||||
|
||||
@ -76,7 +78,7 @@
|
||||
|
||||
this.framesListController = new pskl.controller.FramesListController(
|
||||
this.piskelController,
|
||||
$('#preview-list'));
|
||||
$('#preview-list-wrapper').get(0));
|
||||
this.framesListController.init();
|
||||
|
||||
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
|
||||
@ -94,7 +96,7 @@
|
||||
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
|
||||
this.selectionManager.init();
|
||||
|
||||
this.historyService = new pskl.service.HistoryService(this.corePiskelController);
|
||||
this.historyService = new pskl.service.HistoryService(this.piskelController);
|
||||
this.historyService.init();
|
||||
|
||||
this.notificationController = new pskl.controller.NotificationController();
|
||||
@ -124,12 +126,15 @@
|
||||
this.storageService = new pskl.service.storage.StorageService(this.piskelController);
|
||||
this.storageService.init();
|
||||
|
||||
this.importService = new pskl.service.ImportService(this.piskelController, this.previewController);
|
||||
this.importService = new pskl.service.ImportService(this.piskelController);
|
||||
this.importService.init();
|
||||
|
||||
this.imageUploadService = new pskl.service.ImageUploadService();
|
||||
this.imageUploadService.init();
|
||||
|
||||
this.savedStatusService = new pskl.service.SavedStatusService(this.piskelController, this.historyService);
|
||||
this.savedStatusService = new pskl.service.SavedStatusService(
|
||||
this.piskelController,
|
||||
this.historyService);
|
||||
this.savedStatusService.init();
|
||||
|
||||
this.backupService = new pskl.service.BackupService(this.piskelController);
|
||||
@ -138,7 +143,9 @@
|
||||
this.beforeUnloadService = new pskl.service.BeforeUnloadService(this.piskelController);
|
||||
this.beforeUnloadService.init();
|
||||
|
||||
this.headerController = new pskl.controller.HeaderController(this.piskelController, this.savedStatusService);
|
||||
this.headerController = new pskl.controller.HeaderController(
|
||||
this.piskelController,
|
||||
this.savedStatusService);
|
||||
this.headerController.init();
|
||||
|
||||
this.penSizeService = new pskl.service.pensize.PenSizeService();
|
||||
@ -147,20 +154,26 @@
|
||||
this.penSizeController = new pskl.controller.PenSizeController();
|
||||
this.penSizeController.init();
|
||||
|
||||
this.fileDropperService = new pskl.service.FileDropperService(
|
||||
this.piskelController,
|
||||
document.querySelector('#drawing-canvas-container'));
|
||||
this.fileDropperService = new pskl.service.FileDropperService(this.piskelController);
|
||||
this.fileDropperService.init();
|
||||
|
||||
var drawingLoop = new pskl.rendering.DrawingLoop();
|
||||
drawingLoop.addCallback(this.render, this);
|
||||
drawingLoop.start();
|
||||
this.userWarningController = new pskl.controller.UserWarningController(this.piskelController);
|
||||
this.userWarningController.init();
|
||||
|
||||
this.performanceReportService = new pskl.service.performance.PerformanceReportService(
|
||||
this.piskelController,
|
||||
this.currentColorsService);
|
||||
this.performanceReportService.init();
|
||||
|
||||
this.drawingLoop = new pskl.rendering.DrawingLoop();
|
||||
this.drawingLoop.addCallback(this.render, this);
|
||||
this.drawingLoop.start();
|
||||
|
||||
this.initTooltips_();
|
||||
|
||||
var piskelData = this.getPiskelInitData_();
|
||||
if (piskelData && piskelData.piskel) {
|
||||
this.loadPiskel_(piskelData.piskel, piskelData.descriptor, piskelData.fps);
|
||||
this.loadPiskel_(piskelData);
|
||||
}
|
||||
|
||||
if (pskl.devtools) {
|
||||
@ -173,13 +186,23 @@
|
||||
mb.createMacBuiltin('Piskel');
|
||||
gui.Window.get().menu = mb;
|
||||
}
|
||||
|
||||
if (pskl.utils.UserAgent.isUnsupported()) {
|
||||
$.publish(Events.DIALOG_DISPLAY, {
|
||||
dialogId : 'unsupported-browser'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadPiskel_ : function (serializedPiskel, descriptor, fps) {
|
||||
loadPiskel_ : function (piskelData) {
|
||||
var serializedPiskel = piskelData.piskel;
|
||||
pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, function (piskel) {
|
||||
piskel.setDescriptor(descriptor);
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
pskl.app.previewController.setFPS(fps);
|
||||
$.publish(Events.PISKEL_SAVED);
|
||||
if (piskelData.descriptor) {
|
||||
// Backward compatibility for v2 or older
|
||||
piskel.setDescriptor(piskelData.descriptor);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -2,14 +2,12 @@
|
||||
|
||||
var ns = $.namespace('pskl.controller');
|
||||
|
||||
ns.DrawingController = function (piskelController, paletteController, container) {
|
||||
ns.DrawingController = function (piskelController, container) {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
this.piskelController = piskelController;
|
||||
|
||||
this.paletteController = paletteController;
|
||||
|
||||
this.dragHandler = new ns.drawing.DragHandler(this);
|
||||
|
||||
/**
|
||||
@ -165,6 +163,9 @@
|
||||
|
||||
if (event.button === Constants.MIDDLE_BUTTON) {
|
||||
this.dragHandler.startDrag(event.clientX, event.clientY);
|
||||
} else if (event.altKey && !this.currentToolBehavior.supportsAlt()) {
|
||||
this.currentToolBehavior.hideHighlightedPixel(this.overlayFrame);
|
||||
this.isPickingColor = true;
|
||||
} else {
|
||||
this.currentToolBehavior.hideHighlightedPixel(this.overlayFrame);
|
||||
$.publish(Events.TOOL_PRESSED);
|
||||
@ -212,6 +213,8 @@
|
||||
if (this.isClicked) {
|
||||
if (pskl.app.mouseStateService.isMiddleButtonPressed()) {
|
||||
this.dragHandler.updateDrag(x, y);
|
||||
} else if (this.isPickingColor) {
|
||||
// Nothing to do on mousemove when picking a color with ALT+click.
|
||||
} else {
|
||||
$.publish(Events.MOUSE_EVENT, [event, this]);
|
||||
this.currentToolBehavior.moveToolAt(
|
||||
@ -238,13 +241,15 @@
|
||||
var evt = jQueryEvent.originalEvent;
|
||||
// Ratio between wheelDeltaY (mousewheel event) and deltaY (wheel event) is -40
|
||||
var delta;
|
||||
if (pskl.utils.UserAgent.isChrome) {
|
||||
delta = evt.wheelDeltaY;
|
||||
} else if (pskl.utils.UserAgent.isIE11) {
|
||||
if (pskl.utils.UserAgent.isIE11) {
|
||||
delta = evt.wheelDelta;
|
||||
} else if (pskl.utils.UserAgent.isFirefox) {
|
||||
delta = -40 * evt.deltaY;
|
||||
} else {
|
||||
delta = evt.wheelDeltaY;
|
||||
}
|
||||
|
||||
delta = delta || 0;
|
||||
var modifier = (delta / 120);
|
||||
|
||||
if (pskl.utils.UserAgent.isMac ? evt.metaKey : evt.ctrlKey) {
|
||||
@ -294,38 +299,63 @@
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onMouseup_ = function (event) {
|
||||
var frame = this.piskelController.getCurrentFrame();
|
||||
if (!this.isClicked) {
|
||||
return;
|
||||
}
|
||||
|
||||
var coords = this.getSpriteCoordinates(event.clientX, event.clientY);
|
||||
if (event.changedTouches && event.changedTouches[0]) {
|
||||
coords = this.getSpriteCoordinates(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
|
||||
}
|
||||
if (this.isClicked) {
|
||||
// A mouse button was clicked on the drawing canvas before this mouseup event,
|
||||
// the user was probably drawing on the canvas.
|
||||
// Note: The mousemove movement (and the mouseup) may end up outside
|
||||
// of the drawing canvas.
|
||||
|
||||
this.isClicked = false;
|
||||
// A mouse button was clicked on the drawing canvas before this mouseup event,
|
||||
// the user was probably drawing on the canvas.
|
||||
// Note: The mousemove movement (and the mouseup) may end up outside
|
||||
// of the drawing canvas.
|
||||
|
||||
if (pskl.app.mouseStateService.isMiddleButtonPressed()) {
|
||||
if (this.dragHandler.isDragging()) {
|
||||
this.dragHandler.stopDrag();
|
||||
} else if (frame.containsPixel(coords.x, coords.y)) {
|
||||
$.publish(Events.SELECT_PRIMARY_COLOR, [frame.getPixel(coords.x, coords.y)]);
|
||||
}
|
||||
} else {
|
||||
this.currentToolBehavior.releaseToolAt(
|
||||
coords.x,
|
||||
coords.y,
|
||||
this.piskelController.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
event
|
||||
);
|
||||
this.isClicked = false;
|
||||
|
||||
$.publish(Events.TOOL_RELEASED);
|
||||
}
|
||||
$.publish(Events.MOUSE_EVENT, [event, this]);
|
||||
var isMiddleButton = pskl.app.mouseStateService.isMiddleButtonPressed();
|
||||
var isMiddleClick = isMiddleButton && !this.dragHandler.isDragging();
|
||||
var isMiddleDrag = isMiddleButton && this.dragHandler.isDragging();
|
||||
|
||||
if (this.isPickingColor || isMiddleClick) {
|
||||
// Picking color after ALT+click or middle mouse button click.
|
||||
this.pickColorAt_(coords);
|
||||
this.isPickingColor = false;
|
||||
} else if (isMiddleDrag) {
|
||||
// Stop the drag handler after a middle button drag action.
|
||||
this.dragHandler.stopDrag();
|
||||
} else {
|
||||
// Regular tool click, release the current tool.
|
||||
this.currentToolBehavior.releaseToolAt(
|
||||
coords.x,
|
||||
coords.y,
|
||||
this.piskelController.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
event
|
||||
);
|
||||
$.publish(Events.TOOL_RELEASED);
|
||||
}
|
||||
|
||||
$.publish(Events.MOUSE_EVENT, [event, this]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a COLOR selection event for the color contained at the provided coordinates.
|
||||
* No-op if the coordinate is outside of the drawing canvas.
|
||||
* @param {Object} coords {x: Number, y: Number}
|
||||
*/
|
||||
ns.DrawingController.prototype.pickColorAt_ = function (coords) {
|
||||
var frame = this.piskelController.getCurrentFrame();
|
||||
if (!frame.containsPixel(coords.x, coords.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var color = pskl.utils.intToColor(frame.getPixel(coords.x, coords.y));
|
||||
var isRightButton = pskl.app.mouseStateService.isRightButtonPressed();
|
||||
var evt = isRightButton ? Events.SELECT_SECONDARY_COLOR : Events.SELECT_PRIMARY_COLOR;
|
||||
$.publish(evt, [color]);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -11,29 +11,39 @@
|
||||
ns.FramesListController = function (piskelController, container) {
|
||||
this.piskelController = piskelController;
|
||||
this.container = container;
|
||||
this.previewList = container.querySelector('#preview-list');
|
||||
this.refreshZoom_();
|
||||
|
||||
this.redrawFlag = true;
|
||||
this.regenerateDomFlag = true;
|
||||
this.justDropped = false;
|
||||
|
||||
this.cachedFrameProcessor = new pskl.model.frame.CachedFrameProcessor();
|
||||
this.cachedFrameProcessor.setFrameProcessor(this.frameToPreviewCanvas_.bind(this));
|
||||
this.cachedFrameProcessor.setOutputCloner(this.clonePreviewCanvas_.bind(this));
|
||||
|
||||
this.initDragndropBehavior_();
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.init = function() {
|
||||
$.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this));
|
||||
$.subscribe(Events.PISKEL_RESET, this.flagForRedraw_.bind(this));
|
||||
$.subscribe(Events.PISKEL_RESET, this.flagForRedraw_.bind(this, true));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, this.flagForRedraw_.bind(this));
|
||||
|
||||
$.subscribe(Events.PISKEL_RESET, this.refreshZoom_.bind(this));
|
||||
|
||||
$('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this));
|
||||
this.container.get(0).addEventListener('click', this.onContainerClick_.bind(this));
|
||||
this.previewListScroller = document.querySelector('#preview-list-scroller');
|
||||
this.previewListScroller.addEventListener('scroll', this.updateScrollerOverflows.bind(this));
|
||||
this.container.addEventListener('click', this.onContainerClick_.bind(this));
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.flagForRedraw_ = function () {
|
||||
ns.FramesListController.prototype.flagForRedraw_ = function (regenerateDom) {
|
||||
this.redrawFlag = true;
|
||||
|
||||
if (regenerateDom) {
|
||||
this.regenerateDomFlag = true;
|
||||
}
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.refreshZoom_ = function () {
|
||||
@ -42,17 +52,25 @@
|
||||
|
||||
ns.FramesListController.prototype.render = function () {
|
||||
if (this.redrawFlag) {
|
||||
this.createPreviews_();
|
||||
if (this.regenerateDomFlag) {
|
||||
this.tiles = [];
|
||||
this.addFrameTile = null;
|
||||
this.createPreviews_();
|
||||
|
||||
this.regenerateDomFlag = false;
|
||||
}
|
||||
|
||||
this.updatePreviews_();
|
||||
this.redrawFlag = false;
|
||||
}
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.updateScrollerOverflows = function () {
|
||||
var scroller = $('#preview-list-scroller');
|
||||
var scrollerHeight = scroller.height();
|
||||
var scrollTop = scroller.scrollTop();
|
||||
var scrollerContentHeight = $('#preview-list').height();
|
||||
var treshold = $('.top-overflow').height();
|
||||
var scroller = this.previewListScroller;
|
||||
var scrollerHeight = scroller.offsetHeight;
|
||||
var scrollTop = scroller.scrollTop;
|
||||
var scrollerContentHeight = this.previewList.offsetHeight;
|
||||
var treshold = this.container.querySelector('.top-overflow').offsetHeight;
|
||||
var overflowTop = false;
|
||||
var overflowBottom = false;
|
||||
|
||||
@ -65,9 +83,8 @@
|
||||
overflowBottom = true;
|
||||
}
|
||||
}
|
||||
var wrapper = $('#preview-list-wrapper');
|
||||
wrapper.toggleClass('top-overflow-visible', overflowTop);
|
||||
wrapper.toggleClass('bottom-overflow-visible', overflowBottom);
|
||||
this.container.classList.toggle('top-overflow-visible', overflowTop);
|
||||
this.container.classList.toggle('bottom-overflow-visible', overflowBottom);
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.onContainerClick_ = function (event) {
|
||||
@ -80,27 +97,79 @@
|
||||
|
||||
if (action === ACTION.CLONE) {
|
||||
this.piskelController.duplicateFrameAt(index);
|
||||
var clonedTile = this.createPreviewTile_(index + 1);
|
||||
this.previewList.insertBefore(clonedTile, this.tiles[index].nextSibling);
|
||||
this.tiles.splice(index, 0, clonedTile);
|
||||
this.updateScrollerOverflows();
|
||||
} else if (action === ACTION.DELETE) {
|
||||
this.piskelController.removeFrameAt(index);
|
||||
this.previewList.removeChild(this.tiles[index]);
|
||||
this.tiles.splice(index, 1);
|
||||
this.updateScrollerOverflows();
|
||||
} else if (action === ACTION.SELECT) {
|
||||
} else if (action === ACTION.SELECT && !this.justDropped) {
|
||||
this.piskelController.setCurrentFrameIndex(index);
|
||||
} else if (action === ACTION.NEW_FRAME) {
|
||||
this.piskelController.addFrame();
|
||||
var newtile = this.createPreviewTile_(this.tiles.length);
|
||||
this.tiles.push(newtile);
|
||||
this.previewList.insertBefore(newtile, this.addFrameTile);
|
||||
this.updateScrollerOverflows();
|
||||
}
|
||||
|
||||
this.flagForRedraw_();
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.updatePreviews_ = function () {
|
||||
var i;
|
||||
var length;
|
||||
|
||||
for (i = 0, length = this.tiles.length; i < length; i++) {
|
||||
// Remove selected class
|
||||
this.tiles[i].classList.remove('selected');
|
||||
|
||||
// Update tile numbers
|
||||
this.tiles[i].setAttribute('data-tile-number', i);
|
||||
this.tiles[i].querySelector('.tile-count').innerHTML = (i + 1);
|
||||
|
||||
// Check if any tile is updated
|
||||
var hash = this.piskelController.getCurrentLayer().getFrameAt(i).getHash();
|
||||
if (this.tiles[i].getAttribute('data-tile-hash') !== hash) {
|
||||
if (this.tiles[i].querySelector('canvas')) {
|
||||
this.tiles[i].querySelector('.canvas-container').replaceChild(
|
||||
this.getCanvasForFrame(this.piskelController.getCurrentLayer().getFrameAt(i)),
|
||||
this.tiles[i].querySelector('canvas')
|
||||
);
|
||||
} else {
|
||||
this.tiles[i].querySelector('.canvas-container').appendChild(
|
||||
this.getCanvasForFrame(this.piskelController.getCurrentLayer().getFrameAt(i))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide/Show buttons if needed
|
||||
var buttons = this.container.querySelectorAll('.delete-frame-action, .dnd-action');
|
||||
var display = (this.piskelController.getFrameCount() > 1) ? 'block' : 'none';
|
||||
for (i = 0, length = buttons.length; i < length; i++) {
|
||||
buttons[i].style.display = display;
|
||||
}
|
||||
|
||||
// Add selected class
|
||||
this.tiles[this.piskelController.getCurrentFrameIndex()].classList.add('selected');
|
||||
};
|
||||
|
||||
ns.FramesListController.prototype.createPreviews_ = function () {
|
||||
this.container.html('');
|
||||
this.previewList.innerHTML = '';
|
||||
|
||||
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
|
||||
$('.tooltip').remove();
|
||||
|
||||
var frameCount = this.piskelController.getFrameCount();
|
||||
|
||||
for (var i = 0 ; i < frameCount ; i++) {
|
||||
this.container.append(this.createPreviewTile_(i));
|
||||
var tile = this.createPreviewTile_(i);
|
||||
this.previewList.appendChild(tile);
|
||||
this.tiles[i] = tile;
|
||||
}
|
||||
// Append 'new empty frame' button
|
||||
var newFrameButton = document.createElement('div');
|
||||
@ -109,12 +178,9 @@
|
||||
newFrameButton.setAttribute('data-tile-action', ACTION.NEW_FRAME);
|
||||
newFrameButton.innerHTML = '<div class="add-frame-action-icon icon-frame-plus-white">' +
|
||||
'</div><div class="label">Add new frame</div>';
|
||||
this.container.append(newFrameButton);
|
||||
this.previewList.appendChild(newFrameButton);
|
||||
this.addFrameTile = newFrameButton;
|
||||
|
||||
var needDragndropBehavior = (frameCount > 1);
|
||||
if (needDragndropBehavior) {
|
||||
this.initDragndropBehavior_();
|
||||
}
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
@ -122,15 +188,15 @@
|
||||
* @private
|
||||
*/
|
||||
ns.FramesListController.prototype.initDragndropBehavior_ = function () {
|
||||
|
||||
$('#preview-list').sortable({
|
||||
$(this.previewList).sortable({
|
||||
placeholder: 'preview-tile preview-tile-drop-proxy',
|
||||
update: $.proxy(this.onUpdate_, this),
|
||||
stop: $.proxy(this.onSortableStop_, this),
|
||||
items: '.preview-tile',
|
||||
axis: 'y',
|
||||
tolerance: 'pointer'
|
||||
});
|
||||
$('#preview-list').disableSelection();
|
||||
$(this.previewList).disableSelection();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -142,6 +208,21 @@
|
||||
|
||||
this.piskelController.moveFrame(originFrameId, targetInsertionId);
|
||||
this.piskelController.setCurrentFrameIndex(targetInsertionId);
|
||||
|
||||
var tile = this.tiles.splice(originFrameId, 1)[0];
|
||||
this.tiles.splice(targetInsertionId, 0, tile);
|
||||
this.flagForRedraw_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FramesListController.prototype.onSortableStop_ = function (event, ui) {
|
||||
this.justDropped = true;
|
||||
|
||||
this.resizeTimer = window.setTimeout($.proxy(function() {
|
||||
this.justDropped = false;
|
||||
}, this), 200);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -153,6 +234,7 @@
|
||||
|
||||
var previewTileRoot = document.createElement('li');
|
||||
previewTileRoot.setAttribute('data-tile-number', tileNumber);
|
||||
previewTileRoot.setAttribute('data-tile-hash', currentFrame.getHash());
|
||||
previewTileRoot.setAttribute('data-tile-action', ACTION.SELECT);
|
||||
previewTileRoot.classList.add('preview-tile');
|
||||
if (this.piskelController.getCurrentFrame() == currentFrame) {
|
||||
@ -171,10 +253,14 @@
|
||||
canvasContainer.style.marginLeft = verticalMargin + 'px';
|
||||
canvasContainer.style.marginRight = verticalMargin + 'px';
|
||||
|
||||
// Add canvas background and canvas
|
||||
var canvasBackground = document.createElement('div');
|
||||
canvasBackground.className = 'canvas-background';
|
||||
canvasContainer.appendChild(canvasBackground);
|
||||
canvasContainer.appendChild(this.getCanvasForFrame(currentFrame));
|
||||
previewTileRoot.appendChild(canvasContainer);
|
||||
|
||||
// Add clone button
|
||||
var cloneFrameButton = document.createElement('button');
|
||||
cloneFrameButton.setAttribute('rel', 'tooltip');
|
||||
cloneFrameButton.setAttribute('data-placement', 'right');
|
||||
@ -184,25 +270,22 @@
|
||||
cloneFrameButton.className = 'tile-overlay duplicate-frame-action icon-frame-duplicate-white';
|
||||
previewTileRoot.appendChild(cloneFrameButton);
|
||||
|
||||
canvasContainer.appendChild(this.getCanvasForFrame(currentFrame));
|
||||
previewTileRoot.appendChild(canvasContainer);
|
||||
// Add delete button
|
||||
var deleteButton = document.createElement('button');
|
||||
deleteButton.setAttribute('rel', 'tooltip');
|
||||
deleteButton.setAttribute('data-placement', 'right');
|
||||
deleteButton.setAttribute('title', 'Delete this frame');
|
||||
deleteButton.setAttribute('data-tile-number', tileNumber);
|
||||
deleteButton.setAttribute('data-tile-action', ACTION.DELETE);
|
||||
deleteButton.className = 'tile-overlay delete-frame-action icon-frame-recyclebin-white';
|
||||
previewTileRoot.appendChild(deleteButton);
|
||||
|
||||
if (tileNumber > 0 || this.piskelController.getFrameCount() > 1) {
|
||||
// Add 'remove frame' button.
|
||||
var deleteButton = document.createElement('button');
|
||||
deleteButton.setAttribute('rel', 'tooltip');
|
||||
deleteButton.setAttribute('data-placement', 'right');
|
||||
deleteButton.setAttribute('title', 'Delete this frame');
|
||||
deleteButton.setAttribute('data-tile-number', tileNumber);
|
||||
deleteButton.setAttribute('data-tile-action', ACTION.DELETE);
|
||||
deleteButton.className = 'tile-overlay delete-frame-action icon-frame-recyclebin-white';
|
||||
previewTileRoot.appendChild(deleteButton);
|
||||
// Add 'dragndrop handle'.
|
||||
var dndHandle = document.createElement('div');
|
||||
dndHandle.className = 'tile-overlay dnd-action icon-frame-dragndrop-white' ;
|
||||
previewTileRoot.appendChild(dndHandle);
|
||||
|
||||
// Add 'dragndrop handle'.
|
||||
var dndHandle = document.createElement('div');
|
||||
dndHandle.className = 'tile-overlay dnd-action icon-frame-dragndrop-white' ;
|
||||
previewTileRoot.appendChild(dndHandle);
|
||||
}
|
||||
// Add tile count
|
||||
var tileCount = document.createElement('div');
|
||||
tileCount.className = 'tile-overlay tile-count';
|
||||
tileCount.innerHTML = tileNumber + 1;
|
||||
|
@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
if (this.piskelName_) {
|
||||
this.piskelName_.innerHTML = name;
|
||||
this.piskelName_.textContent = name;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Could not update header : ' + e.message);
|
||||
|
@ -27,15 +27,21 @@
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.renderLayerList_ = function () {
|
||||
// Backup scroll before refresh.
|
||||
var scrollTop = this.layersListEl.scrollTop;
|
||||
|
||||
this.layersListEl.innerHTML = '';
|
||||
var layers = this.piskelController.getLayers();
|
||||
layers.forEach(this.addLayerItem.bind(this));
|
||||
this.updateButtonStatus_();
|
||||
|
||||
// Restore scroll
|
||||
this.layersListEl.scrollTop = scrollTop;
|
||||
|
||||
// Ensure the currently the selected layer is visible.
|
||||
var currentLayerEl = this.layersListEl.querySelector('.current-layer-item');
|
||||
if (currentLayerEl) {
|
||||
currentLayerEl.scrollIntoView();
|
||||
currentLayerEl.scrollIntoViewIfNeeded(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -99,6 +105,12 @@
|
||||
});
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
ns.LayersListController.prototype.onClick_ = function (evt) {
|
||||
@ -106,8 +118,8 @@
|
||||
var index;
|
||||
if (el.classList.contains('button')) {
|
||||
this.onButtonClick_(el);
|
||||
} else if (el.classList.contains('layer-item')) {
|
||||
index = el.dataset.layerIndex;
|
||||
} else if (el.classList.contains('layer-name')) {
|
||||
index = pskl.utils.Dom.getData(el, 'layerIndex');
|
||||
this.piskelController.setCurrentLayerIndex(parseInt(index, 10));
|
||||
} else if (el.classList.contains('layer-item-opacity')) {
|
||||
index = pskl.utils.Dom.getData(el, 'layerIndex');
|
||||
|
@ -1,6 +1,10 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.controller');
|
||||
|
||||
/**
|
||||
* The PaletteController is responsible for handling the two color picker
|
||||
* widgets found in the left column, below the tools.
|
||||
*/
|
||||
ns.PaletteController = function () {};
|
||||
|
||||
/**
|
||||
@ -92,7 +96,8 @@
|
||||
};
|
||||
|
||||
ns.PaletteController.prototype.resetColors = function () {
|
||||
pskl.app.selectedColorsService.reset();
|
||||
this.setPrimaryColor_(Constants.DEFAULT_PEN_COLOR);
|
||||
this.setSecondaryColor_(Constants.TRANSPARENT_COLOR);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -100,7 +100,7 @@
|
||||
ns.PalettesListController.prototype.getCurrentColorIndex_ = function () {
|
||||
var currentIndex = 0;
|
||||
var selectedColor = document.querySelector('.' + PRIMARY_COLOR_CLASSNAME);
|
||||
if (selectedColor) {
|
||||
if (selectedColor) {
|
||||
currentIndex = parseInt(selectedColor.dataset.colorIndex, 10);
|
||||
}
|
||||
return currentIndex;
|
||||
|
@ -25,9 +25,17 @@
|
||||
};
|
||||
|
||||
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 = this.container.querySelector('[data-size="' + size + '"]');
|
||||
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');
|
||||
}
|
||||
|
59
src/js/controller/UserWarningController.js
Normal file
59
src/js/controller/UserWarningController.js
Normal file
@ -0,0 +1,59 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.controller');
|
||||
|
||||
ns.UserWarningController = function (piskelController, currentColorsService) {
|
||||
this.piskelController = piskelController;
|
||||
this.currentColorsService = currentColorsService;
|
||||
};
|
||||
|
||||
// This method is not attached to the prototype because we want to trigger it
|
||||
// from markup generated for a notification message.
|
||||
ns.UserWarningController.showPerformanceInfoDialog = function () {
|
||||
$.publish(Events.DIALOG_DISPLAY, {
|
||||
dialogId: 'performance-info'
|
||||
});
|
||||
};
|
||||
|
||||
ns.UserWarningController.prototype.init = function () {
|
||||
$.subscribe(Events.PERFORMANCE_REPORT_CHANGED, this.onPerformanceReportChanged_.bind(this));
|
||||
|
||||
this.performanceLinkEl = document.querySelector('.performance-link');
|
||||
pskl.utils.Event.addEventListener(
|
||||
this.performanceLinkEl,
|
||||
'click',
|
||||
ns.UserWarningController.showPerformanceInfoDialog,
|
||||
this
|
||||
);
|
||||
};
|
||||
|
||||
ns.UserWarningController.prototype.destroy = function () {
|
||||
pskl.utils.Event.removeAllEventListeners(this);
|
||||
this.performanceLinkEl = null;
|
||||
};
|
||||
|
||||
ns.UserWarningController.prototype.onPerformanceReportChanged_ = function (event, report) {
|
||||
var shouldDisplayWarning = report.hasProblem();
|
||||
|
||||
// Check if a performance warning is already displayed.
|
||||
var isWarningDisplayed = this.performanceLinkEl.classList.contains('visible');
|
||||
|
||||
// Show/hide the performance warning link depending on the received report.
|
||||
if (shouldDisplayWarning) {
|
||||
this.performanceLinkEl.classList.add('visible');
|
||||
} else {
|
||||
this.performanceLinkEl.classList.remove('visible');
|
||||
}
|
||||
|
||||
// Show a notification message if the new report indicates a performance issue
|
||||
// and we were not displaying a warning before.
|
||||
if (shouldDisplayWarning && !isWarningDisplayed) {
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{
|
||||
'content': 'Performance problem detected, ' +
|
||||
'<a href="#" style="color:red;"' +
|
||||
'onclick="pskl.controller.UserWarningController.showPerformanceInfoDialog()">' +
|
||||
'learn more?</a>',
|
||||
'hideDelay' : 5000
|
||||
}]);
|
||||
}
|
||||
};
|
||||
})();
|
@ -48,7 +48,10 @@
|
||||
|
||||
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});
|
||||
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {
|
||||
name : key.name,
|
||||
date : date
|
||||
});
|
||||
}).bind(this));
|
||||
|
||||
var tableBody_ = this.piskelList.get(0).tBodies[0];
|
||||
|
@ -152,10 +152,10 @@
|
||||
key = key.replace('ctrl', 'cmd');
|
||||
key = key.replace('alt', 'option');
|
||||
}
|
||||
key = key.replace(/left/i, '←');
|
||||
key = key.replace(/up/i, '↑');
|
||||
key = key.replace(/right/i, '→');
|
||||
key = key.replace(/down/i, '↓');
|
||||
key = key.replace(/left/i, '←');
|
||||
key = key.replace(/up/i, '↑');
|
||||
key = key.replace(/right/i, '→');
|
||||
key = key.replace(/down/i, '↓');
|
||||
key = key.replace(/>/g, '>');
|
||||
key = key.replace(/</g, '<');
|
||||
// add spaces around '+' delimiters
|
||||
|
@ -17,6 +17,14 @@
|
||||
'import-image' : {
|
||||
template : 'templates/dialogs/import-image.html',
|
||||
controller : ns.ImportImageController
|
||||
},
|
||||
'performance-info' : {
|
||||
template : 'templates/dialogs/performance-info.html',
|
||||
controller : ns.PerformanceInfoController
|
||||
},
|
||||
'unsupported-browser' : {
|
||||
template : 'templates/dialogs/unsupported-browser.html',
|
||||
controller : ns.UnsupportedBrowserController
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,6 +50,8 @@
|
||||
|
||||
// adding the .animated class here instead of in the markup to avoid an animation during app startup
|
||||
this.dialogWrapper_.classList.add('animated');
|
||||
pskl.utils.Event.addEventListener(this.dialogWrapper_, 'click', this.onWrapperClicked_, this);
|
||||
|
||||
};
|
||||
|
||||
ns.DialogsController.prototype.onCreatePaletteShortcut_ = function () {
|
||||
@ -69,6 +79,12 @@
|
||||
this.showDialog(args.dialogId, args.initArgs);
|
||||
};
|
||||
|
||||
ns.DialogsController.prototype.onWrapperClicked_ = function (evt) {
|
||||
if (evt.target === this.dialogWrapper_) {
|
||||
this.hideDialog();
|
||||
}
|
||||
};
|
||||
|
||||
ns.DialogsController.prototype.showDialog = function (dialogId, initArgs) {
|
||||
if (this.isDisplayingDialog_()) {
|
||||
return;
|
||||
|
@ -135,7 +135,7 @@
|
||||
this.importedImage_.onload = function () {};
|
||||
|
||||
var fileName = this.extractFileNameFromPath_(this.file_.name);
|
||||
this.fileNameContainer.html(fileName);
|
||||
this.fileNameContainer.text(fileName);
|
||||
this.fileNameContainer.attr('title', fileName);
|
||||
|
||||
this.resizeWidth.val(w);
|
||||
|
11
src/js/controller/dialogs/PerformanceInfoController.js
Normal file
11
src/js/controller/dialogs/PerformanceInfoController.js
Normal file
@ -0,0 +1,11 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.controller.dialogs');
|
||||
|
||||
ns.PerformanceInfoController = function () {};
|
||||
|
||||
pskl.utils.inherit(ns.PerformanceInfoController, ns.AbstractDialogController);
|
||||
|
||||
ns.PerformanceInfoController.prototype.init = function () {
|
||||
this.superclass.init.call(this);
|
||||
};
|
||||
})();
|
13
src/js/controller/dialogs/UnsupportedBrowserController.js
Normal file
13
src/js/controller/dialogs/UnsupportedBrowserController.js
Normal file
@ -0,0 +1,13 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.controller.dialogs');
|
||||
|
||||
ns.UnsupportedBrowserController = function () {};
|
||||
|
||||
pskl.utils.inherit(ns.UnsupportedBrowserController, ns.AbstractDialogController);
|
||||
|
||||
ns.UnsupportedBrowserController.prototype.init = function () {
|
||||
this.superclass.init.call(this);
|
||||
var currentUserAgentElement = document.querySelector('#current-user-agent');
|
||||
currentUserAgentElement.innerText = pskl.utils.UserAgent.getDisplayName();
|
||||
};
|
||||
})();
|
@ -35,15 +35,16 @@
|
||||
return this.piskel.getWidth();
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO : this should be removed
|
||||
* FPS should be stored in the Piskel model and not in the
|
||||
* previewController
|
||||
* Then piskelController should be able to return this information
|
||||
* @return {Number} Frames per second for the current animation
|
||||
*/
|
||||
ns.PiskelController.prototype.getFPS = function () {
|
||||
return pskl.app.previewController.getFPS();
|
||||
return this.piskel.fps;
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.setFPS = function (fps) {
|
||||
if (typeof fps !== 'number') {
|
||||
return;
|
||||
}
|
||||
this.piskel.fps = fps;
|
||||
$.publish(Events.FPS_CHANGED);
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.getLayers = function () {
|
||||
@ -269,16 +270,20 @@
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.removeLayerAt = function (index) {
|
||||
if (this.getLayers().length > 1) {
|
||||
var layer = this.getLayerAt(index);
|
||||
if (layer) {
|
||||
this.piskel.removeLayer(layer);
|
||||
this.setCurrentLayerIndex(0);
|
||||
}
|
||||
if (!this.hasLayerAt(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var layer = this.getLayerAt(index);
|
||||
this.piskel.removeLayer(layer);
|
||||
|
||||
// Update the selected layer if needed.
|
||||
if (this.getCurrentLayerIndex() === index) {
|
||||
this.setCurrentLayerIndex(Math.max(0, index - 1));
|
||||
}
|
||||
};
|
||||
|
||||
ns.PiskelController.prototype.serialize = function (expanded) {
|
||||
return pskl.utils.Serializer.serializePiskel(this.piskel, expanded);
|
||||
ns.PiskelController.prototype.serialize = function () {
|
||||
return pskl.utils.serialization.Serializer.serialize(this.piskel);
|
||||
};
|
||||
})();
|
||||
|
@ -1,6 +1,12 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.controller.piskel');
|
||||
|
||||
/**
|
||||
* The PublicPiskelController is a decorator on PiskelController, provides the same API
|
||||
* but will fire RESET/SAVE events when appropriate so that other objects get notified
|
||||
* when important changes are made on the current Piskel.
|
||||
* @param {PiskelController} piskelController the wrapped PiskelController
|
||||
*/
|
||||
ns.PublicPiskelController = function (piskelController) {
|
||||
this.piskelController = piskelController;
|
||||
pskl.utils.wrap(this, this.piskelController);
|
||||
@ -38,6 +44,10 @@
|
||||
pskl.app.shortcutService.registerShortcut(shortcuts.MISC.DUPLICATE_FRAME, this.duplicateCurrentFrame.bind(this));
|
||||
};
|
||||
|
||||
ns.PublicPiskelController.prototype.getWrappedPiskelController = function () {
|
||||
return this.piskelController;
|
||||
};
|
||||
|
||||
ns.PublicPiskelController.prototype.setPiskel = function (piskel, preserveState) {
|
||||
this.piskelController.setPiskel(piskel, preserveState);
|
||||
|
||||
|
@ -3,9 +3,7 @@
|
||||
|
||||
// Preview is a square of PREVIEW_SIZE x PREVIEW_SIZE
|
||||
var PREVIEW_SIZE = 200;
|
||||
|
||||
var ONION_SKIN_SHORTCUT = 'alt+O';
|
||||
var ORIGINAL_SIZE_SHORTCUT = 'alt+1';
|
||||
var RENDER_MINIMUM_DELAY = 300;
|
||||
|
||||
ns.PreviewController = function (piskelController, container) {
|
||||
this.piskelController = piskelController;
|
||||
@ -15,27 +13,33 @@
|
||||
this.currentIndex = 0;
|
||||
|
||||
this.onionSkinShortcut = pskl.service.keyboard.Shortcuts.MISC.ONION_SKIN;
|
||||
this.originalSizeShortcut = pskl.service.keyboard.Shortcuts.MISC.X1_PREVIEW;
|
||||
|
||||
this.lastRenderTime = 0;
|
||||
this.renderFlag = true;
|
||||
|
||||
/**
|
||||
* !! WARNING !! ALL THE INITIALISATION BELOW SHOULD BE MOVED TO INIT()
|
||||
* IT WILL STAY HERE UNTIL WE CAN REMOVE SETFPS (see comment below)
|
||||
*/
|
||||
this.fpsRangeInput = document.querySelector('#preview-fps');
|
||||
this.fpsCounterDisplay = document.querySelector('#display-fps');
|
||||
this.openPopupPreview = document.querySelector('.open-popup-preview-button');
|
||||
this.originalSizeButton = document.querySelector('.original-size-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');
|
||||
|
||||
/**
|
||||
* !! WARNING !! THIS SHOULD REMAIN HERE UNTIL, BECAUSE THE PREVIEW CONTROLLER
|
||||
* IS THE SOURCE OF TRUTH AT THE MOMENT WHEN IT COMES TO FPSs
|
||||
* IT WILL BE QUERIED BY OTHER OBJECTS SO DEFINE IT AS SOON AS POSSIBLE
|
||||
*/
|
||||
this.setFPS(Constants.DEFAULT.FPS);
|
||||
|
||||
this.renderer = new pskl.rendering.frame.BackgroundImageFrameRenderer(this.container);
|
||||
this.popupPreviewController = new ns.PopupPreviewController(piskelController);
|
||||
};
|
||||
@ -46,42 +50,117 @@
|
||||
|
||||
document.querySelector('.right-column').style.width = Constants.ANIMATED_PREVIEW_WIDTH + 'px';
|
||||
|
||||
pskl.utils.Event.addEventListener(this.toggleOnionSkinButton, 'click', this.toggleOnionSkin_, this);
|
||||
pskl.utils.Event.addEventListener(this.openPopupPreview, 'click', this.onOpenPopupPreviewClick_, this);
|
||||
pskl.utils.Event.addEventListener(this.originalSizeButton, 'click', this.onOriginalSizeButtonClick_, this);
|
||||
var addEvent = pskl.utils.Event.addEventListener;
|
||||
addEvent(this.toggleOnionSkinButton, 'click', this.toggleOnionSkin_, this);
|
||||
addEvent(this.openPopupPreview, 'click', this.onOpenPopupPreviewClick_, this);
|
||||
|
||||
pskl.app.shortcutService.registerShortcut(this.onionSkinShortcut, this.toggleOnionSkin_.bind(this));
|
||||
pskl.app.shortcutService.registerShortcut(this.originalSizeShortcut, this.onOriginalSizeButtonClick_.bind(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);
|
||||
}
|
||||
}
|
||||
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, 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.initTooltips_();
|
||||
this.updatePreviewSizeButtons_();
|
||||
this.popupPreviewController.init();
|
||||
|
||||
this.updateZoom_();
|
||||
this.updateOnionSkinPreview_();
|
||||
this.updateOriginalSizeButton_();
|
||||
this.selectPreviewSizeButton_();
|
||||
this.updateFPS_();
|
||||
this.updateMaxFPS_();
|
||||
this.updateContainerDimensions_();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.initTooltips_ = function () {
|
||||
var onionSkinTooltip = pskl.utils.TooltipFormatter.format('Toggle onion skin', this.onionSkinShortcut);
|
||||
this.toggleOnionSkinButton.setAttribute('title', onionSkinTooltip);
|
||||
var originalSizeTooltip = pskl.utils.TooltipFormatter.format('Original size preview', this.originalSizeShortcut);
|
||||
this.originalSizeButton.setAttribute('title', originalSizeTooltip);
|
||||
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 () {
|
||||
this.popupPreviewController.open();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.onOriginalSizeButtonClick_ = function () {
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.ORIGINAL_SIZE_PREVIEW);
|
||||
pskl.UserSettings.set(pskl.UserSettings.ORIGINAL_SIZE_PREVIEW, !isEnabled);
|
||||
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) {
|
||||
@ -89,9 +168,11 @@
|
||||
this.updateOnionSkinPreview_();
|
||||
} else if (name == pskl.UserSettings.MAX_FPS) {
|
||||
this.updateMaxFPS_();
|
||||
} else if (name === pskl.UserSettings.SEAMLESS_MODE) {
|
||||
this.onFrameSizeChange_();
|
||||
} else {
|
||||
this.updateZoom_();
|
||||
this.updateOriginalSizeButton_();
|
||||
this.selectPreviewSizeButton_();
|
||||
this.updateContainerDimensions_();
|
||||
}
|
||||
};
|
||||
@ -99,27 +180,44 @@
|
||||
ns.PreviewController.prototype.updateOnionSkinPreview_ = function () {
|
||||
var enabledClassname = 'preview-toggle-onion-skin-enabled';
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.ONION_SKIN);
|
||||
this.toggleOnionSkinButton.classList.toggle(enabledClassname, isEnabled);
|
||||
|
||||
// classList.toggle is not available on IE11.
|
||||
if (isEnabled) {
|
||||
this.toggleOnionSkinButton.classList.add(enabledClassname);
|
||||
} else {
|
||||
this.toggleOnionSkinButton.classList.remove(enabledClassname);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateOriginalSizeButton_ = function () {
|
||||
var enabledClassname = 'original-size-button-enabled';
|
||||
var isEnabled = pskl.UserSettings.get(pskl.UserSettings.ORIGINAL_SIZE_PREVIEW);
|
||||
this.originalSizeButton.classList.toggle(enabledClassname, isEnabled);
|
||||
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.setFPS(Math.min(this.fps, maxFps));
|
||||
this.piskelController.setFPS(Math.min(maxFps, this.piskelController.getFPS()));
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateZoom_ = function () {
|
||||
var originalSizeEnabled = pskl.UserSettings.get(pskl.UserSettings.ORIGINAL_SIZE_PREVIEW);
|
||||
var seamlessModeEnabled = pskl.UserSettings.get(pskl.UserSettings.SEAMLESS_MODE);
|
||||
var useOriginalSize = originalSizeEnabled || seamlessModeEnabled;
|
||||
var previewSize = pskl.UserSettings.get(pskl.UserSettings.PREVIEW_SIZE);
|
||||
|
||||
var zoom;
|
||||
if (previewSize === 'original') {
|
||||
zoom = 1;
|
||||
} else if (previewSize === 'best') {
|
||||
zoom = Math.floor(this.calculateZoom_());
|
||||
} else if (previewSize === 'full') {
|
||||
zoom = this.calculateZoom_();
|
||||
}
|
||||
|
||||
var zoom = useOriginalSize ? 1 : this.calculateZoom_();
|
||||
this.renderer.setZoom(zoom);
|
||||
this.setRenderFlag_(true);
|
||||
};
|
||||
@ -143,15 +241,17 @@
|
||||
* Event handler triggered on 'input' or 'change' events.
|
||||
*/
|
||||
ns.PreviewController.prototype.onFpsRangeInputUpdate_ = function (evt) {
|
||||
this.setFPS(parseInt(this.fpsRangeInput.value, 10));
|
||||
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.setFPS = function (fps) {
|
||||
if (typeof fps === 'number') {
|
||||
ns.PreviewController.prototype.updateFPS_ = function () {
|
||||
var fps = this.piskelController.getFPS();
|
||||
if (fps !== this.fps) {
|
||||
this.fps = fps;
|
||||
// reset
|
||||
this.fpsRangeInput.value = 0;
|
||||
@ -161,10 +261,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.getFPS = function () {
|
||||
return this.fps;
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.render = function (delta) {
|
||||
this.elapsedTime += delta;
|
||||
var index = this.getNextIndex_(delta);
|
||||
@ -173,6 +269,7 @@
|
||||
var frame = pskl.utils.LayerUtils.mergeFrameAt(this.piskelController.getLayers(), index);
|
||||
this.renderer.render(frame);
|
||||
this.renderFlag = false;
|
||||
this.lastRenderTime = Date.now();
|
||||
|
||||
this.popupPreviewController.render(frame);
|
||||
}
|
||||
@ -205,6 +302,7 @@
|
||||
ns.PreviewController.prototype.onFrameSizeChange_ = function () {
|
||||
this.updateZoom_();
|
||||
this.updateContainerDimensions_();
|
||||
this.updatePreviewSizeButtons_();
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.updateContainerDimensions_ = function () {
|
||||
@ -241,7 +339,8 @@
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.shouldRender_ = function () {
|
||||
return this.renderFlag || this.popupPreviewController.renderFlag;
|
||||
return (this.renderFlag || this.popupPreviewController.renderFlag) &&
|
||||
(Date.now() - this.lastRenderTime > RENDER_MINIMUM_DELAY);
|
||||
};
|
||||
|
||||
ns.PreviewController.prototype.toggleOnionSkin_ = function () {
|
||||
|
@ -39,12 +39,30 @@
|
||||
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_);
|
||||
@ -55,6 +73,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);
|
||||
};
|
||||
@ -65,7 +87,7 @@
|
||||
if (background) {
|
||||
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, background);
|
||||
var selected = this.backgroundContainer.querySelector('.selected');
|
||||
if (selected) {
|
||||
if (selected) {
|
||||
selected.classList.remove('selected');
|
||||
}
|
||||
target.classList.add('selected');
|
||||
@ -94,11 +116,27 @@
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -79,11 +79,15 @@
|
||||
|
||||
ns.ImportController.prototype.openPiskelFile_ = function (file) {
|
||||
if (this.isPiskel_(file)) {
|
||||
pskl.utils.PiskelFileUtils.loadFromFile(file, function (piskel, descriptor, fps) {
|
||||
piskel.setDescriptor(descriptor);
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
pskl.app.previewController.setFPS(fps);
|
||||
});
|
||||
pskl.utils.PiskelFileUtils.loadFromFile(file,
|
||||
// onSuccess
|
||||
function (piskel) {
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
},
|
||||
// onError
|
||||
function (reason) {
|
||||
$.publish(Events.PISKEL_FILE_IMPORT_FAILED, [reason]);
|
||||
});
|
||||
this.closeDrawer_();
|
||||
}
|
||||
};
|
||||
|
@ -46,13 +46,23 @@
|
||||
this.disableSaveButtons_();
|
||||
}
|
||||
|
||||
this.updateSaveToGalleryMessage_();
|
||||
|
||||
$.subscribe(Events.BEFORE_SAVING_PISKEL, this.disableSaveButtons_.bind(this));
|
||||
$.subscribe(Events.AFTER_SAVING_PISKEL, this.enableSaveButtons_.bind(this));
|
||||
};
|
||||
|
||||
ns.SaveController.prototype.updateSaveToGalleryMessage_ = function (spritesheetSize) {
|
||||
var saveToGalleryStatus = document.querySelector('.save-online-status');
|
||||
if (saveToGalleryStatus && pskl.app.performanceReportService.hasProblem()) {
|
||||
var warningPartial = pskl.utils.Template.get('save-gallery-warning-partial');
|
||||
saveToGalleryStatus.innerHTML = warningPartial;
|
||||
}
|
||||
};
|
||||
|
||||
ns.SaveController.prototype.insertSavePartials_ = function () {
|
||||
this.getPartials_().forEach(function (partial) {
|
||||
pskl.utils.Template.insert(this.saveForm, 'beforeend', partial);
|
||||
this.saveForm.insertAdjacentHTML('beforeend', pskl.utils.Template.get(partial));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
@ -150,7 +150,7 @@
|
||||
|
||||
ns.GifExportController.prototype.updateStatus_ = function (imageUrl, error) {
|
||||
if (imageUrl) {
|
||||
var linkTpl = '<a class="image-link" href="{{link}}" target="_blank">{{shortLink}}</a>';
|
||||
var linkTpl = '<a class="highlight" href="{{link}}" target="_blank">{{shortLink}}</a>';
|
||||
var linkHtml = pskl.utils.Template.replace(linkTpl, {
|
||||
link : imageUrl,
|
||||
shortLink : this.shorten_(imageUrl, URL_MAX_LENGTH, '...')
|
||||
|
@ -12,6 +12,9 @@
|
||||
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) {
|
||||
@ -73,4 +76,14 @@
|
||||
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);
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
@ -3,11 +3,9 @@
|
||||
|
||||
var dimensionInfoPattern = '{{width}} x {{height}} px, {{frames}}<br/>{{columns}}, {{rows}}.';
|
||||
|
||||
// Shortcut to pskl.utils.Template.replace
|
||||
var replace = pskl.utils.Template.replace;
|
||||
|
||||
// Helper to return "X items" or "1 item" if X is 1. Can be cnsidered as an overkill,
|
||||
// but the one-liner equivalent is hard to read.
|
||||
// Helper to return "X items" or "1 item" if X is 1.
|
||||
var pluralize = function (word, count) {
|
||||
if (count === 1) {
|
||||
return '1 ' + word;
|
||||
@ -31,6 +29,7 @@
|
||||
this.columnsInput = document.querySelector('#png-export-columns');
|
||||
|
||||
var downloadButton = document.querySelector('.png-download-button');
|
||||
var downloadPixiButton = document.querySelector('.png-pixi-download-button');
|
||||
var dataUriButton = document.querySelector('.datauri-open-button');
|
||||
|
||||
this.initLayoutSection_();
|
||||
@ -38,6 +37,7 @@
|
||||
|
||||
this.addEventListener(this.columnsInput, 'input', this.onColumnsInput_);
|
||||
this.addEventListener(downloadButton, 'click', this.onDownloadClick_);
|
||||
this.addEventListener(downloadPixiButton, 'click', this.onPixiDownloadClick_);
|
||||
this.addEventListener(dataUriButton, 'click', this.onDataUriClick_);
|
||||
$.subscribe(Events.EXPORT_SCALE_CHANGED, this.onScaleChanged_);
|
||||
};
|
||||
@ -118,7 +118,7 @@
|
||||
value = 1;
|
||||
}
|
||||
|
||||
// Force the value to be in bounds, in the user tried to update it by directly typing
|
||||
// Force the value to be in bounds, if the user tried to update it by directly typing
|
||||
// a value.
|
||||
value = pskl.utils.Math.minmax(value, 1, this.piskelController.getFrameCount());
|
||||
this.columnsInput.value = value;
|
||||
@ -145,7 +145,11 @@
|
||||
ns.PngExportController.prototype.onDownloadClick_ = function (evt) {
|
||||
// Create PNG export.
|
||||
var canvas = this.createPngSpritesheet_();
|
||||
this.downloadCanvas_(canvas);
|
||||
};
|
||||
|
||||
// Used and overridden in casper integration tests.
|
||||
ns.PngExportController.prototype.downloadCanvas_ = function (canvas) {
|
||||
// Generate file name
|
||||
var name = this.piskelController.getPiskel().getDescriptor().name;
|
||||
var fileName = name + '.png';
|
||||
@ -156,6 +160,52 @@
|
||||
});
|
||||
};
|
||||
|
||||
ns.PngExportController.prototype.onPixiDownloadClick_ = function () {
|
||||
var zip = new window.JSZip();
|
||||
|
||||
// Create PNG export.
|
||||
var canvas = this.createPngSpritesheet_();
|
||||
var name = this.piskelController.getPiskel().getDescriptor().name;
|
||||
|
||||
zip.file(name + '.png', pskl.utils.CanvasUtils.getBase64FromCanvas(canvas) + '\n', {base64: true});
|
||||
|
||||
var width = canvas.width / this.getColumns_();
|
||||
var height = canvas.height / this.getRows_();
|
||||
|
||||
var numFrames = this.piskelController.getFrameCount();
|
||||
var frames = {};
|
||||
for (var i = 0; i < numFrames; i++) {
|
||||
var column = i % this.getColumns_();
|
||||
var row = (i - column) / this.getColumns_();
|
||||
var frame = {
|
||||
'frame': {'x': width * column,'y': height * row,'w': width,'h': height},
|
||||
'rotated': false,
|
||||
'trimmed': false,
|
||||
'spriteSourceSize': {'x': 0,'y': 0,'w': width,'h': height},
|
||||
'sourceSize': {'w': width,'h': height}
|
||||
};
|
||||
frames[name + i + '.png'] = frame;
|
||||
}
|
||||
|
||||
var json = {
|
||||
'frames': frames,
|
||||
'meta': {
|
||||
'app': 'https://github.com/juliandescottes/piskel/',
|
||||
'version': '1.0',
|
||||
'image': name + '.png',
|
||||
'format': 'RGBA8888',
|
||||
'size': {'w': canvas.width,'h': canvas.height}
|
||||
}
|
||||
};
|
||||
zip.file(name + '.json', JSON.stringify(json));
|
||||
|
||||
var blob = zip.generate({
|
||||
type : 'blob'
|
||||
});
|
||||
|
||||
pskl.utils.FileUtils.downloadAsFile(blob, name + '.zip');
|
||||
};
|
||||
|
||||
ns.PngExportController.prototype.onDataUriClick_ = function (evt) {
|
||||
window.open(this.createPngSpritesheet_().toDataURL('image/png'));
|
||||
};
|
||||
|
@ -64,7 +64,8 @@
|
||||
var resizedLayers = this.piskelController.getLayers().map(this.resizeLayer_.bind(this));
|
||||
|
||||
var currentPiskel = this.piskelController.getPiskel();
|
||||
var piskel = pskl.model.Piskel.fromLayers(resizedLayers, currentPiskel.getDescriptor());
|
||||
var fps = this.piskelController.getFPS();
|
||||
var piskel = pskl.model.Piskel.fromLayers(resizedLayers, fps, currentPiskel.getDescriptor());
|
||||
// propagate savepath to new Piskel
|
||||
piskel.savePath = currentPiskel.savePath;
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
this.step = step || this.initialState.step || ns.DrawingTestPlayer.DEFAULT_STEP;
|
||||
this.callbacks = [];
|
||||
this.shim = null;
|
||||
this.performance = 0;
|
||||
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.DEFAULT_STEP = 50;
|
||||
@ -15,7 +17,16 @@
|
||||
ns.DrawingTestPlayer.prototype.start = function () {
|
||||
this.setupInitialState_();
|
||||
this.createMouseShim_();
|
||||
this.regenerateReferencePng().then(function () {
|
||||
|
||||
// Override the main drawing loop to record the time spent rendering.
|
||||
this.loopBackup = pskl.app.drawingLoop.loop;
|
||||
pskl.app.drawingLoop.loop = function () {
|
||||
var before = window.performance.now();
|
||||
this.loopBackup.call(pskl.app.drawingLoop);
|
||||
this.performance += window.performance.now() - before;
|
||||
}.bind(this);
|
||||
|
||||
this.regenerateReferencePng(function () {
|
||||
this.playEvent_(0);
|
||||
}.bind(this));
|
||||
};
|
||||
@ -35,7 +46,7 @@
|
||||
|
||||
ns.DrawingTestPlayer.prototype.createPiskel_ = function (width, height) {
|
||||
var descriptor = new pskl.model.piskel.Descriptor('TestPiskel', '');
|
||||
var piskel = new pskl.model.Piskel(width, height, descriptor);
|
||||
var piskel = new pskl.model.Piskel(width, height, 12, descriptor);
|
||||
var layer = new pskl.model.Layer('Layer 1');
|
||||
var frame = new pskl.model.Frame(width, height);
|
||||
|
||||
@ -45,21 +56,13 @@
|
||||
return piskel;
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.regenerateReferencePng = function () {
|
||||
ns.DrawingTestPlayer.prototype.regenerateReferencePng = function (callback) {
|
||||
var image = new Image();
|
||||
var then = function () {};
|
||||
|
||||
image.onload = function () {
|
||||
this.referencePng = pskl.utils.CanvasUtils.createFromImage(image).toDataURL();
|
||||
then();
|
||||
this.referenceCanvas = pskl.utils.CanvasUtils.createFromImage(image);
|
||||
callback();
|
||||
}.bind(this);
|
||||
image.src = this.referencePng;
|
||||
|
||||
return {
|
||||
then : function (cb) {
|
||||
then = cb;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,11 +87,13 @@
|
||||
this.timer = window.setTimeout(function () {
|
||||
var recordEvent = this.events[index];
|
||||
|
||||
// All events have already been replayed, finish the test.
|
||||
if (!recordEvent) {
|
||||
this.onTestEnd_();
|
||||
return;
|
||||
}
|
||||
|
||||
var before = window.performance.now();
|
||||
if (recordEvent.type === 'mouse-event') {
|
||||
this.playMouseEvent_(recordEvent);
|
||||
} else if (recordEvent.type === 'keyboard-event') {
|
||||
@ -105,6 +110,9 @@
|
||||
this.playInstrumentedEvent_(recordEvent);
|
||||
}
|
||||
|
||||
// Record the time spent replaying the event
|
||||
this.performance += window.performance.now() - before;
|
||||
|
||||
this.playEvent_(index + 1);
|
||||
}.bind(this), this.step);
|
||||
};
|
||||
@ -129,8 +137,8 @@
|
||||
|
||||
ns.DrawingTestPlayer.prototype.playKeyboardEvent_ = function (recordEvent) {
|
||||
var event = recordEvent.event;
|
||||
if (pskl.utils.UserAgent.isMac && event.ctrlKey) {
|
||||
event.metaKey = true;
|
||||
if (pskl.utils.UserAgent.isMac) {
|
||||
event.metaKey = event.ctrlKey;
|
||||
}
|
||||
|
||||
event.preventDefault = function () {};
|
||||
@ -163,16 +171,33 @@
|
||||
|
||||
ns.DrawingTestPlayer.prototype.onTestEnd_ = function () {
|
||||
this.removeMouseShim_();
|
||||
// Restore the original drawing loop.
|
||||
pskl.app.drawingLoop.loop = this.loopBackup;
|
||||
|
||||
// Retrieve the imageData corresponding to the spritesheet created by the test.
|
||||
var renderer = new pskl.rendering.PiskelRenderer(pskl.app.piskelController);
|
||||
var png = renderer.renderAsCanvas().toDataURL();
|
||||
var canvas = renderer.renderAsCanvas();
|
||||
var testData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
var success = png === this.referencePng;
|
||||
// Retrieve the reference imageData corresponding to the reference data-url png stored for this test.
|
||||
var refCanvas = this.referenceCanvas;
|
||||
this.referenceData = refCanvas.getContext('2d').getImageData(0, 0, refCanvas.width, refCanvas.height);
|
||||
|
||||
$.publish(Events.TEST_RECORD_END, [success, png, this.referencePng]);
|
||||
// Compare the two imageData arrays.
|
||||
var success = true;
|
||||
for (var i = 0 ; i < this.referenceData.data.length ; i++) {
|
||||
if (this.referenceData.data[i] != testData.data[i]) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
$.publish(Events.TEST_RECORD_END, [success]);
|
||||
this.callbacks.forEach(function (callback) {
|
||||
callback(success, png, this.referencePng);
|
||||
});
|
||||
callback({
|
||||
success: success,
|
||||
performance: this.performance
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.DrawingTestPlayer.prototype.addEndTestCallback = function (callback) {
|
||||
|
@ -64,7 +64,7 @@
|
||||
events : this.events,
|
||||
initialState : this.initialState,
|
||||
png : png
|
||||
});
|
||||
}, null, ' ');
|
||||
|
||||
this.reset();
|
||||
|
||||
|
@ -14,11 +14,11 @@
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ns.DrawingTestRunner.prototype.onTestRecordEnd_ = function (evt, success, png) {
|
||||
ns.DrawingTestRunner.prototype.onTestRecordEnd_ = function (evt, success) {
|
||||
var testResult = document.createElement('div');
|
||||
testResult.id = 'drawing-test-result';
|
||||
testResult.setAttribute('data-test-name', this.testName);
|
||||
testResult.innerHTML = success ? 'OK' : ('KO:' + png);
|
||||
testResult.innerHTML = success ? 'OK' : 'KO';
|
||||
document.body.appendChild(testResult);
|
||||
};
|
||||
})();
|
||||
|
@ -47,25 +47,33 @@
|
||||
this.testSuiteRunner.start();
|
||||
};
|
||||
|
||||
ns.DrawingTestSuiteController.prototype.onTestCaseEnd_ = function (evt, testPath, status) {
|
||||
ns.DrawingTestSuiteController.prototype.onTestCaseEnd_ = function (evt, testPath, success, performance) {
|
||||
var testCaseStatus = document.createElement('li');
|
||||
|
||||
testCaseStatus.innerHTML = pskl.utils.Template.replace(
|
||||
'[{{path}}] finished : <b style="color:{{color}}">{{status}}</b>',
|
||||
{path : this.shortenPath_(testPath), status : status ? 'OK' : 'KO', color : status ? 'green' : 'red'}
|
||||
'[{{path}}] finished : <b style="color:{{color}}">{{status}} ({{performance}})</b>',
|
||||
{
|
||||
path : this.shortenPath_(testPath),
|
||||
status : success ? 'OK' : 'KO',
|
||||
color : success ? 'green' : 'red',
|
||||
performance: performance.toFixed(2)
|
||||
}
|
||||
);
|
||||
this.testListElt.appendChild(testCaseStatus);
|
||||
};
|
||||
|
||||
ns.DrawingTestSuiteController.prototype.onTestSuiteEnd_ = function (evt, status) {
|
||||
console.log('on test suite end');
|
||||
ns.DrawingTestSuiteController.prototype.onTestSuiteEnd_ = function (evt, status, performance) {
|
||||
var elapsed = Date.now() - this.startTime_;
|
||||
elapsed = (elapsed / 1000).toFixed(4);
|
||||
|
||||
var testSuiteStatus = document.createElement('li');
|
||||
testSuiteStatus.innerHTML = pskl.utils.Template.replace(
|
||||
'<b>Test finished : {{status}}</b> ({{elapsed}} seconds)',
|
||||
{status : status, elapsed : elapsed}
|
||||
'<b>Test finished : {{status}}</b> ({{elapsed}}s, performance: {{performance}})',
|
||||
{
|
||||
status : status,
|
||||
elapsed : elapsed,
|
||||
performance: performance.toFixed(2)
|
||||
}
|
||||
);
|
||||
this.testListElt.appendChild(testSuiteStatus);
|
||||
};
|
||||
|
@ -44,20 +44,25 @@
|
||||
testPlayer.start();
|
||||
};
|
||||
|
||||
ns.DrawingTestSuiteRunner.prototype.onTestEnd_ = function (success, png, referencePng) {
|
||||
ns.DrawingTestSuiteRunner.prototype.onTestEnd_ = function (data /* {success, performance} */) {
|
||||
var path = this.testPaths[this.currentIndex];
|
||||
this.testStatus[path] = success;
|
||||
$.publish(Events.TEST_CASE_END, [path, success]);
|
||||
this.testStatus[path] = data;
|
||||
|
||||
$.publish(Events.TEST_CASE_END, [path, data.success, data.performance]);
|
||||
|
||||
this.runTest(this.currentIndex + 1);
|
||||
};
|
||||
|
||||
ns.DrawingTestSuiteRunner.prototype.onTestSuiteEnd_ = function () {
|
||||
var success = this.testPaths.every(function (path) {
|
||||
return this.testStatus[path];
|
||||
return this.testStatus[path].success;
|
||||
}.bind(this));
|
||||
|
||||
var performance = this.testPaths.reduce(function (p, path) {
|
||||
return this.testStatus[path].performance + p;
|
||||
}.bind(this), 0);
|
||||
|
||||
this.status = success ? ns.DrawingTestSuiteRunner.STATUS.SUCCESS : ns.DrawingTestSuiteRunner.STATUS.ERROR;
|
||||
$.publish(Events.TEST_SUITE_END, [this.status]);
|
||||
$.publish(Events.TEST_SUITE_END, [this.status, performance]);
|
||||
};
|
||||
})();
|
||||
|
27
src/js/lib/scrollifneeded/scrollifneeded.js
Normal file
27
src/js/lib/scrollifneeded/scrollifneeded.js
Normal file
@ -0,0 +1,27 @@
|
||||
if (!Element.prototype.scrollIntoViewIfNeeded) {
|
||||
Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
|
||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
||||
|
||||
var parent = this.parentNode,
|
||||
parentComputedStyle = window.getComputedStyle(parent, null),
|
||||
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
|
||||
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
|
||||
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
|
||||
overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
|
||||
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
|
||||
overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
|
||||
alignWithTop = overTop && !overBottom;
|
||||
|
||||
if ((overTop || overBottom) && centerIfNeeded) {
|
||||
parent.scrollTop = this.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + this.clientHeight / 2;
|
||||
}
|
||||
|
||||
if ((overLeft || overRight) && centerIfNeeded) {
|
||||
parent.scrollLeft = this.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + this.clientWidth / 2;
|
||||
}
|
||||
|
||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||
this.scrollIntoView(alignWithTop);
|
||||
}
|
||||
};
|
||||
}
|
20
src/js/lib/smoothscroll/LICENSE.txt
Normal file
20
src/js/lib/smoothscroll/LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Dustan Kasten
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
277
src/js/lib/smoothscroll/smoothscroll.js
Normal file
277
src/js/lib/smoothscroll/smoothscroll.js
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* smoothscroll polyfill - v0.3.3
|
||||
* https://iamdustan.github.io/smoothscroll
|
||||
* 2016 (c) Dustan Kasten, Jeremias Menichelli - MIT License
|
||||
*/
|
||||
|
||||
(function(w, d, undefined) {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* aliases
|
||||
* w: window global object
|
||||
* d: document
|
||||
* undefined: undefined
|
||||
*/
|
||||
|
||||
// polyfill
|
||||
function polyfill() {
|
||||
// return when scrollBehavior interface is supported
|
||||
if ('scrollBehavior' in d.documentElement.style) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* globals
|
||||
*/
|
||||
var Element = w.HTMLElement || w.Element;
|
||||
var SCROLL_TIME = 468;
|
||||
|
||||
/*
|
||||
* object gathering original scroll methods
|
||||
*/
|
||||
var original = {
|
||||
scroll: w.scroll || w.scrollTo,
|
||||
scrollBy: w.scrollBy,
|
||||
scrollIntoView: Element.prototype.scrollIntoView
|
||||
};
|
||||
|
||||
/*
|
||||
* define timing method
|
||||
*/
|
||||
var now = w.performance && w.performance.now
|
||||
? w.performance.now.bind(w.performance) : Date.now;
|
||||
|
||||
/**
|
||||
* changes scroll position inside an element
|
||||
* @method scrollElement
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
function scrollElement(x, y) {
|
||||
this.scrollLeft = x;
|
||||
this.scrollTop = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns result of applying ease math function to a number
|
||||
* @method ease
|
||||
* @param {Number} k
|
||||
* @returns {Number}
|
||||
*/
|
||||
function ease(k) {
|
||||
return 0.5 * (1 - Math.cos(Math.PI * k));
|
||||
}
|
||||
|
||||
/**
|
||||
* indicates if a smooth behavior should be applied
|
||||
* @method shouldBailOut
|
||||
* @param {Number|Object} x
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function shouldBailOut(x) {
|
||||
if (typeof x !== 'object'
|
||||
|| x.behavior === undefined
|
||||
|| x.behavior === 'auto'
|
||||
|| x.behavior === 'instant') {
|
||||
// first arg not an object, or behavior is auto, instant or undefined
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof x === 'object'
|
||||
&& x.behavior === 'smooth') {
|
||||
// first argument is an object and behavior is smooth
|
||||
return false;
|
||||
}
|
||||
|
||||
// throw error when behavior is not supported
|
||||
throw new TypeError('behavior not valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* finds scrollable parent of an element
|
||||
* @method findScrollableParent
|
||||
* @param {Node} el
|
||||
* @returns {Node} el
|
||||
*/
|
||||
function findScrollableParent(el) {
|
||||
do {
|
||||
el = el.parentNode;
|
||||
} while (el !== d.body
|
||||
&& !(el.clientHeight < el.scrollHeight
|
||||
|| el.clientWidth < el.scrollWidth));
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* self invoked function that, given a context, steps through scrolling
|
||||
* @method step
|
||||
* @param {Object} context
|
||||
*/
|
||||
function step(context) {
|
||||
// call method again on next available frame
|
||||
context.frame = w.requestAnimationFrame(step.bind(w, context));
|
||||
|
||||
var time = now();
|
||||
var value;
|
||||
var currentX;
|
||||
var currentY;
|
||||
var elapsed = (time - context.startTime) / SCROLL_TIME;
|
||||
|
||||
// avoid elapsed times higher than one
|
||||
elapsed = elapsed > 1 ? 1 : elapsed;
|
||||
|
||||
// apply easing to elapsed time
|
||||
value = ease(elapsed);
|
||||
|
||||
currentX = context.startX + (context.x - context.startX) * value;
|
||||
currentY = context.startY + (context.y - context.startY) * value;
|
||||
|
||||
context.method.call(context.scrollable, currentX, currentY);
|
||||
|
||||
// return when end points have been reached
|
||||
if (currentX === context.x && currentY === context.y) {
|
||||
w.cancelAnimationFrame(context.frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scrolls window with a smooth behavior
|
||||
* @method smoothScroll
|
||||
* @param {Object|Node} el
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
function smoothScroll(el, x, y) {
|
||||
var scrollable;
|
||||
var startX;
|
||||
var startY;
|
||||
var method;
|
||||
var startTime = now();
|
||||
var frame;
|
||||
|
||||
// define scroll context
|
||||
if (el === d.body) {
|
||||
scrollable = w;
|
||||
startX = w.scrollX || w.pageXOffset;
|
||||
startY = w.scrollY || w.pageYOffset;
|
||||
method = original.scroll;
|
||||
} else {
|
||||
scrollable = el;
|
||||
startX = el.scrollLeft;
|
||||
startY = el.scrollTop;
|
||||
method = scrollElement;
|
||||
}
|
||||
|
||||
// cancel frame when a scroll event's happening
|
||||
if (frame) {
|
||||
w.cancelAnimationFrame(frame);
|
||||
}
|
||||
|
||||
// scroll looping over a frame
|
||||
step({
|
||||
scrollable: scrollable,
|
||||
method: method,
|
||||
startTime: startTime,
|
||||
startX: startX,
|
||||
startY: startY,
|
||||
x: x,
|
||||
y: y,
|
||||
frame: frame
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ORIGINAL METHODS OVERRIDES
|
||||
*/
|
||||
|
||||
// w.scroll and w.scrollTo
|
||||
w.scroll = w.scrollTo = function() {
|
||||
// avoid smooth behavior if not required
|
||||
if (shouldBailOut(arguments[0])) {
|
||||
original.scroll.call(
|
||||
w,
|
||||
arguments[0].left || arguments[0],
|
||||
arguments[0].top || arguments[1]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
smoothScroll.call(
|
||||
w,
|
||||
d.body,
|
||||
~~arguments[0].left,
|
||||
~~arguments[0].top
|
||||
);
|
||||
};
|
||||
|
||||
// w.scrollBy
|
||||
w.scrollBy = function() {
|
||||
// avoid smooth behavior if not required
|
||||
if (shouldBailOut(arguments[0])) {
|
||||
original.scrollBy.call(
|
||||
w,
|
||||
arguments[0].left || arguments[0],
|
||||
arguments[0].top || arguments[1]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
smoothScroll.call(
|
||||
w,
|
||||
d.body,
|
||||
~~arguments[0].left + (w.scrollX || w.pageXOffset),
|
||||
~~arguments[0].top + (w.scrollY || w.pageYOffset)
|
||||
);
|
||||
};
|
||||
|
||||
// Element.prototype.scrollIntoView
|
||||
Element.prototype.scrollIntoView = function() {
|
||||
// avoid smooth behavior if not required
|
||||
if (shouldBailOut(arguments[0])) {
|
||||
original.scrollIntoView.call(this, arguments[0] || true);
|
||||
return;
|
||||
}
|
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
var scrollableParent = findScrollableParent(this);
|
||||
var parentRects = scrollableParent.getBoundingClientRect();
|
||||
var clientRects = this.getBoundingClientRect();
|
||||
|
||||
if (scrollableParent !== d.body) {
|
||||
// reveal element inside parent
|
||||
smoothScroll.call(
|
||||
this,
|
||||
scrollableParent,
|
||||
scrollableParent.scrollLeft + clientRects.left - parentRects.left,
|
||||
scrollableParent.scrollTop + clientRects.top - parentRects.top
|
||||
);
|
||||
// reveal parent in viewport
|
||||
w.scrollBy({
|
||||
left: parentRects.left,
|
||||
top: parentRects.top,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
// reveal element in viewport
|
||||
w.scrollBy({
|
||||
left: clientRects.left,
|
||||
top: clientRects.top,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof exports === 'object') {
|
||||
// commonjs
|
||||
module.exports = { polyfill: polyfill };
|
||||
} else {
|
||||
// global
|
||||
polyfill();
|
||||
}
|
||||
})(window, document);
|
@ -671,7 +671,8 @@
|
||||
|
||||
// Update the text entry input as it changes happen
|
||||
if (opts.showInput) {
|
||||
textInput.val(realColor.toString(format));
|
||||
var displayFormat = pskl.UserSettings.get(pskl.UserSettings.COLOR_FORMAT);
|
||||
textInput.val(realColor.toString(displayFormat));
|
||||
}
|
||||
|
||||
if (opts.showPalette) {
|
||||
|
@ -14,28 +14,54 @@
|
||||
}
|
||||
};
|
||||
|
||||
ns.Frame.fromPixelGrid = function (pixels) {
|
||||
if (pixels.length && pixels[0].length) {
|
||||
var w = pixels.length;
|
||||
var h = pixels[0].length;
|
||||
ns.Frame.fromPixelGrid = function (pixels, width, height) {
|
||||
if (pixels.length) {
|
||||
var w;
|
||||
var h;
|
||||
var buffer;
|
||||
|
||||
if (pixels[0].length) {
|
||||
w = pixels.length;
|
||||
h = pixels[0].length;
|
||||
buffer = [];
|
||||
for (var y = 0; y < h; y++) {
|
||||
for (var x = 0; x < w; x++) {
|
||||
if (typeof pixels[x][y] == 'string') {
|
||||
buffer[y * w + x] = pskl.utils.colorToInt(pixels[x][y]);
|
||||
} else {
|
||||
buffer[y * w + x] = pixels[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (width && height) {
|
||||
w = width;
|
||||
h = height;
|
||||
buffer = pixels;
|
||||
} else {
|
||||
throw 'Bad arguments in pskl.model.frame.fromPixelGrid, missing width and height';
|
||||
}
|
||||
|
||||
var frame = new pskl.model.Frame(w, h);
|
||||
frame.setPixels(pixels);
|
||||
frame.setPixels(buffer);
|
||||
return frame;
|
||||
} else {
|
||||
throw 'Bad arguments in pskl.model.Frame.fromPixelGrid : ' + pixels;
|
||||
throw 'Bad arguments in pskl.model.Frame.fromPixelGrid';
|
||||
}
|
||||
};
|
||||
|
||||
var _emptyPixelGridCache = {};
|
||||
ns.Frame.createEmptyPixelGrid_ = function (width, height) {
|
||||
var pixels = [];
|
||||
for (var columnIndex = 0 ; columnIndex < width ; columnIndex++) {
|
||||
var columnArray = [];
|
||||
for (var heightIndex = 0 ; heightIndex < height ; heightIndex++) {
|
||||
columnArray.push(Constants.TRANSPARENT_COLOR);
|
||||
}
|
||||
pixels[columnIndex] = columnArray;
|
||||
var pixels;
|
||||
var key = width + '-' + height;
|
||||
if (_emptyPixelGridCache[key]) {
|
||||
pixels = _emptyPixelGridCache[key];
|
||||
} else {
|
||||
pixels = _emptyPixelGridCache[key] = new Uint32Array(width * height);
|
||||
var transparentColorInt = pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR);
|
||||
pixels.fill(transparentColorInt);
|
||||
}
|
||||
return pixels;
|
||||
|
||||
return new Uint32Array(pixels);
|
||||
};
|
||||
|
||||
ns.Frame.createEmptyFromFrame = function (frame) {
|
||||
@ -44,7 +70,7 @@
|
||||
|
||||
ns.Frame.prototype.clone = function () {
|
||||
var clone = new ns.Frame(this.width, this.height);
|
||||
clone.setPixels(this.getPixels());
|
||||
clone.setPixels(this.pixels);
|
||||
return clone;
|
||||
};
|
||||
|
||||
@ -64,8 +90,8 @@
|
||||
};
|
||||
|
||||
ns.Frame.prototype.clear = function () {
|
||||
var pixels = ns.Frame.createEmptyPixelGrid_(this.getWidth(), this.getHeight());
|
||||
this.setPixels(pixels);
|
||||
this.pixels = ns.Frame.createEmptyPixelGrid_(this.getWidth(), this.getHeight());
|
||||
this.version++;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -73,11 +99,7 @@
|
||||
* @private
|
||||
*/
|
||||
ns.Frame.prototype.clonePixels_ = function (pixels) {
|
||||
var clonedPixels = [];
|
||||
for (var col = 0 ; col < pixels.length ; col++) {
|
||||
clonedPixels[col] = pixels[col].slice(0 , pixels[col].length);
|
||||
}
|
||||
return clonedPixels;
|
||||
return new Uint32Array(pixels);
|
||||
};
|
||||
|
||||
ns.Frame.prototype.getHash = function () {
|
||||
@ -86,9 +108,12 @@
|
||||
|
||||
ns.Frame.prototype.setPixel = function (x, y, color) {
|
||||
if (this.containsPixel(x, y)) {
|
||||
var p = this.pixels[x][y];
|
||||
var index = y * this.width + x;
|
||||
var p = this.pixels[index];
|
||||
color = pskl.utils.colorToInt(color);
|
||||
|
||||
if (p !== color) {
|
||||
this.pixels[x][y] = color || Constants.TRANSPARENT_COLOR;
|
||||
this.pixels[index] = color || pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR);
|
||||
this.version++;
|
||||
}
|
||||
}
|
||||
@ -96,7 +121,7 @@
|
||||
|
||||
ns.Frame.prototype.getPixel = function (x, y) {
|
||||
if (this.containsPixel(x, y)) {
|
||||
return this.pixels[x][y];
|
||||
return this.pixels[y * this.width + x];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -105,10 +130,9 @@
|
||||
ns.Frame.prototype.forEachPixel = function (callback) {
|
||||
var width = this.getWidth();
|
||||
var height = this.getHeight();
|
||||
for (var x = 0 ; x < width ; x++) {
|
||||
for (var y = 0 ; y < height ; y++) {
|
||||
callback(this.pixels[x][y], x, y, this);
|
||||
}
|
||||
var length = width * height;
|
||||
for (var i = 0; i < length ; i++) {
|
||||
callback(this.pixels[i], i % width, Math.floor(i / width), this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -37,10 +37,13 @@
|
||||
};
|
||||
|
||||
ns.Layer.prototype.setOpacity = function (opacity) {
|
||||
if (typeof opacity == 'string') {
|
||||
opacity = parseFloat(opacity);
|
||||
}
|
||||
if (opacity === null || isNaN(opacity) || opacity < 0 || opacity > 1) {
|
||||
return;
|
||||
}
|
||||
this.opacity = opacity;
|
||||
this.opacity = +opacity.toFixed(3);
|
||||
};
|
||||
|
||||
ns.Layer.prototype.isTransparent = function () {
|
||||
|
@ -8,21 +8,14 @@
|
||||
* @param {String} name
|
||||
* @param {String} description
|
||||
*/
|
||||
ns.Piskel = function (width, height, descriptor) {
|
||||
ns.Piskel = function (width, height, fps, descriptor) {
|
||||
if (width && height && descriptor) {
|
||||
/** @type {Array} */
|
||||
this.layers = [];
|
||||
|
||||
/** @type {Number} */
|
||||
this.width = width;
|
||||
|
||||
/** @type {Number} */
|
||||
this.height = height;
|
||||
|
||||
this.descriptor = descriptor;
|
||||
|
||||
/** @type {String} */
|
||||
this.savePath = null;
|
||||
this.fps = fps;
|
||||
|
||||
} else {
|
||||
throw 'Missing arguments in Piskel constructor : ' + Array.prototype.join.call(arguments, ',');
|
||||
@ -35,11 +28,11 @@
|
||||
* @param {Array<pskl.model.Layer>} layers
|
||||
* @return {pskl.model.Piskel}
|
||||
*/
|
||||
ns.Piskel.fromLayers = function (layers, descriptor) {
|
||||
ns.Piskel.fromLayers = function (layers, fps, descriptor) {
|
||||
var piskel = null;
|
||||
if (layers.length > 0 && layers[0].size() > 0) {
|
||||
var sampleFrame = layers[0].getFrameAt(0);
|
||||
piskel = new pskl.model.Piskel(sampleFrame.getWidth(), sampleFrame.getHeight(), descriptor);
|
||||
piskel = new pskl.model.Piskel(sampleFrame.getWidth(), sampleFrame.getHeight(), fps, descriptor);
|
||||
layers.forEach(piskel.addLayer.bind(piskel));
|
||||
} else {
|
||||
throw 'Piskel.fromLayers expects array of non empty pskl.model.Layer as first argument';
|
||||
@ -59,6 +52,10 @@
|
||||
return this.width;
|
||||
};
|
||||
|
||||
ns.Piskel.prototype.getFPS = function () {
|
||||
return this.fps;
|
||||
};
|
||||
|
||||
ns.Piskel.prototype.getLayers = function () {
|
||||
return this.layers;
|
||||
};
|
||||
|
@ -30,20 +30,9 @@
|
||||
var key1 = frame.getHash();
|
||||
if (cache[key1]) {
|
||||
processedFrame = cache[key1];
|
||||
} else if (frame instanceof pskl.model.frame.RenderedFrame) {
|
||||
// Cannot use 2nd level cache with rendered frames
|
||||
var callbackFirstLvlCacheOnly = this.onProcessorComplete_.bind(this, deferred, cache, key1, key1);
|
||||
this.frameProcessor(frame, callbackFirstLvlCacheOnly);
|
||||
} else {
|
||||
var framePixels = JSON.stringify(frame.getPixels());
|
||||
var key2 = pskl.utils.hashCode(framePixels);
|
||||
if (cache[key2]) {
|
||||
processedFrame = this.outputCloner(cache[key2], frame);
|
||||
cache[key1] = processedFrame;
|
||||
} else {
|
||||
var callback = this.onProcessorComplete_.bind(this, deferred, cache, key1, key2);
|
||||
this.frameProcessor(frame, callback);
|
||||
}
|
||||
var callback = this.onProcessorComplete_.bind(this, deferred, cache, key1);
|
||||
this.frameProcessor(frame, callback);
|
||||
}
|
||||
|
||||
if (processedFrame) {
|
||||
@ -53,9 +42,8 @@
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ns.AsyncCachedFrameProcessor.prototype.onProcessorComplete_ = function (deferred, cache, key1, key2, result) {
|
||||
ns.AsyncCachedFrameProcessor.prototype.onProcessorComplete_ = function (deferred, cache, key1, result) {
|
||||
cache[key1] = result;
|
||||
cache[key2] = result;
|
||||
deferred.resolve(result);
|
||||
};
|
||||
})();
|
||||
|
@ -1,8 +1,8 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.model.frame');
|
||||
|
||||
// 10 * 60 * 1000 = 10 minutes
|
||||
var DEFAULT_CLEAR_INTERVAL = 10 * 60 * 1000;
|
||||
// Maximum number of cache entries
|
||||
var MAX_CACHE_ENTRIES = 100;
|
||||
|
||||
var DEFAULT_FRAME_PROCESSOR = function (frame) {
|
||||
return pskl.utils.FrameUtils.toImage(frame);
|
||||
@ -12,14 +12,16 @@
|
||||
|
||||
var DEFAULT_NAMESPACE = '__cache_default__';
|
||||
|
||||
ns.CachedFrameProcessor = function (cacheResetInterval) {
|
||||
ns.CachedFrameProcessor = function () {
|
||||
// Cache object.
|
||||
this.cache_ = {};
|
||||
this.cacheResetInterval = cacheResetInterval || DEFAULT_CLEAR_INTERVAL;
|
||||
|
||||
// Array of [namespace, key] for each cached frame.
|
||||
this.cacheQueue_ = [];
|
||||
|
||||
this.frameProcessor = DEFAULT_FRAME_PROCESSOR;
|
||||
this.outputCloner = DEFAULT_OUTPUT_CLONER;
|
||||
this.defaultNamespace = DEFAULT_NAMESPACE;
|
||||
|
||||
window.setInterval(this.clear.bind(this), this.cacheResetInterval);
|
||||
};
|
||||
|
||||
ns.CachedFrameProcessor.prototype.clear = function () {
|
||||
@ -66,21 +68,16 @@
|
||||
var cacheKey = frame.getHash();
|
||||
if (cache[cacheKey]) {
|
||||
processedFrame = cache[cacheKey];
|
||||
} else if (frame instanceof pskl.model.frame.RenderedFrame) {
|
||||
// Cannot use 2nd level cache with rendered frames
|
||||
} else {
|
||||
processedFrame = this.frameProcessor(frame);
|
||||
cache[cacheKey] = processedFrame;
|
||||
} else {
|
||||
var framePixels = JSON.stringify(frame.getPixels());
|
||||
var frameAsString = pskl.utils.hashCode(framePixels);
|
||||
if (cache[frameAsString]) {
|
||||
processedFrame = this.outputCloner(cache[frameAsString], frame);
|
||||
} else {
|
||||
processedFrame = this.frameProcessor(frame);
|
||||
cache[frameAsString] = processedFrame;
|
||||
this.cacheQueue_.unshift([namespace, cacheKey]);
|
||||
if (this.cacheQueue_.length > MAX_CACHE_ENTRIES) {
|
||||
var oldestItem = this.cacheQueue_.pop();
|
||||
this.cache_[oldestItem[0]][oldestItem[1]] = null;
|
||||
}
|
||||
cache[cacheKey] = processedFrame;
|
||||
}
|
||||
|
||||
return processedFrame;
|
||||
};
|
||||
})();
|
||||
|
@ -15,6 +15,7 @@
|
||||
scope : scope,
|
||||
args : args
|
||||
};
|
||||
|
||||
this.callbacks.push(callbackObj);
|
||||
return callbackObj;
|
||||
};
|
||||
|
@ -30,12 +30,11 @@
|
||||
|
||||
ns.FramesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
|
||||
var context = canvas.getContext('2d');
|
||||
frame.forEachPixel(function (color, x, y) {
|
||||
if (color != Constants.TRANSPARENT_COLOR) {
|
||||
context.fillStyle = color;
|
||||
context.fillRect(x + offsetWidth, y + offsetHeight, 1, 1);
|
||||
}
|
||||
});
|
||||
var imageData = context.createImageData(frame.getWidth(), frame.getHeight());
|
||||
var pixels = frame.getPixels();
|
||||
var data = new Uint8ClampedArray(pixels.buffer);
|
||||
imageData.data.set(data);
|
||||
context.putImageData(imageData, offsetWidth, offsetHeight);
|
||||
};
|
||||
|
||||
ns.FramesheetRenderer.prototype.createCanvas_ = function (columns, rows) {
|
||||
|
@ -35,6 +35,7 @@
|
||||
this.getZoom(),
|
||||
this.getGridWidth(),
|
||||
pskl.UserSettings.get('SEAMLESS_MODE'),
|
||||
pskl.UserSettings.get('SEAMLESS_OPACITY'),
|
||||
offset.x, offset.y,
|
||||
size.width, size.height,
|
||||
frame.getHash()
|
||||
|
@ -261,27 +261,33 @@
|
||||
this.margin.y - this.offset.y * z
|
||||
);
|
||||
|
||||
// Scale up to draw the canvas content
|
||||
displayContext.scale(z, z);
|
||||
|
||||
if (pskl.UserSettings.get('SEAMLESS_MODE')) {
|
||||
displayContext.clearRect(-1 * w * z, -1 * h * z, 3 * w * z, 3 * h * z);
|
||||
displayContext.clearRect(-1 * w, -1 * h, 3 * w, 3 * h);
|
||||
} else {
|
||||
displayContext.clearRect(0, 0, w * z, h * z);
|
||||
displayContext.clearRect(0, 0, w, h);
|
||||
}
|
||||
|
||||
if (pskl.UserSettings.get('SEAMLESS_MODE')) {
|
||||
this.drawTiledFrames_(displayContext, this.canvas, w, h, 1);
|
||||
}
|
||||
displayContext.drawImage(this.canvas, 0, 0);
|
||||
|
||||
// Draw grid.
|
||||
var gridWidth = this.computeGridWidthForDisplay_();
|
||||
if (gridWidth > 0) {
|
||||
var scaled = pskl.utils.ImageResizer.resizeNearestNeighbour(this.canvas, z, gridWidth);
|
||||
|
||||
if (pskl.UserSettings.get('SEAMLESS_MODE')) {
|
||||
this.drawTiledFrames_(displayContext, scaled, w, h, z);
|
||||
// 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);
|
||||
}
|
||||
displayContext.drawImage(scaled, 0, 0);
|
||||
} else {
|
||||
displayContext.scale(z, z);
|
||||
|
||||
if (pskl.UserSettings.get('SEAMLESS_MODE')) {
|
||||
this.drawTiledFrames_(displayContext, this.canvas, w, h, 1);
|
||||
// Clear horizontal lines.
|
||||
for (var j = 1 ; j < frame.getHeight() ; j++) {
|
||||
displayContext.clearRect(0, (j * z) - (gridWidth / 2), w * z, gridWidth);
|
||||
}
|
||||
displayContext.drawImage(this.canvas, 0, 0);
|
||||
}
|
||||
|
||||
displayContext.restore();
|
||||
@ -293,7 +299,9 @@
|
||||
* differentiate those additional frames from the main frame.
|
||||
*/
|
||||
ns.FrameRenderer.prototype.drawTiledFrames_ = function (context, image, w, h, z) {
|
||||
context.fillStyle = Constants.SEAMLESS_MODE_OVERLAY_COLOR;
|
||||
var opacity = pskl.UserSettings.get('SEAMLESS_OPACITY');
|
||||
opacity = pskl.utils.Math.minmax(opacity, 0, 1);
|
||||
context.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')';
|
||||
[[0, -1], [0, 1], [-1, -1], [-1, 0], [-1, 1], [1, -1], [1, 0], [1, 1]].forEach(function (d) {
|
||||
context.drawImage(image, d[0] * w * z, d[1] * h * z);
|
||||
context.fillRect(d[0] * w * z, d[1] * h * z, w * z, h * z);
|
||||
|
@ -93,7 +93,11 @@
|
||||
|
||||
ns.SelectionManager.prototype.paste = function() {
|
||||
if (!this.currentSelection || !this.currentSelection.hasPastedContent) {
|
||||
return;
|
||||
if (window.localStorage.getItem('piskel.clipboard')) {
|
||||
this.currentSelection = JSON.parse(window.localStorage.getItem('piskel.clipboard'));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var pixels = this.currentSelection.pixels;
|
||||
@ -146,6 +150,7 @@
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
var info = {
|
||||
name : descriptor.name,
|
||||
description : descriptor.info,
|
||||
fps : this.piskelController.getFPS(),
|
||||
date : Date.now(),
|
||||
hash : hash
|
||||
};
|
||||
@ -34,7 +33,8 @@
|
||||
// Do not save an unchanged piskel
|
||||
if (hash !== this.lastHash) {
|
||||
this.lastHash = hash;
|
||||
this.savePiskel_('next', this.piskelController.serialize(), JSON.stringify(info));
|
||||
var serializedPiskel = pskl.utils.serialization.Serializer.serialize(piskel);
|
||||
this.savePiskel_('next', serializedPiskel, JSON.stringify(info));
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,17 +46,11 @@
|
||||
};
|
||||
|
||||
ns.BackupService.prototype.load = function() {
|
||||
|
||||
var previousPiskel = window.localStorage.getItem('bkp.prev.piskel');
|
||||
var previousInfo = window.localStorage.getItem('bkp.prev.info');
|
||||
|
||||
previousPiskel = JSON.parse(previousPiskel);
|
||||
previousInfo = JSON.parse(previousInfo);
|
||||
|
||||
pskl.utils.serialization.Deserializer.deserialize(previousPiskel, function (piskel) {
|
||||
piskel.setDescriptor(new pskl.model.piskel.Descriptor(previousInfo.name, previousInfo.description, true));
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
pskl.app.previewController.setFPS(previousInfo.fps);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -6,15 +6,38 @@
|
||||
};
|
||||
|
||||
ns.BeforeUnloadService.prototype.init = function () {
|
||||
if (pskl.utils.Environment.detectNodeWebkit()) {
|
||||
// Add a dedicated listener to window 'close' event in nwjs environment.
|
||||
var win = require('nw.gui').Window.get();
|
||||
win.on('close', this.onNwWindowClose.bind(this, win));
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', this.onBeforeUnload.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* In nw.js environment "onbeforeunload" is not triggered when closing the window.
|
||||
* Polyfill the behavior here.
|
||||
*/
|
||||
ns.BeforeUnloadService.prototype.onNwWindowClose = function (win) {
|
||||
var msg = this.onBeforeUnload();
|
||||
if (msg) {
|
||||
if (!window.confirm(msg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
win.close(true);
|
||||
};
|
||||
|
||||
ns.BeforeUnloadService.prototype.onBeforeUnload = function (evt) {
|
||||
pskl.app.backupService.backup();
|
||||
if (pskl.app.savedStatusService.isDirty()) {
|
||||
var confirmationMessage = 'Your Piskel seems to have unsaved changes';
|
||||
|
||||
(evt || window.event).returnValue = confirmationMessage;
|
||||
evt = evt || window.event;
|
||||
if (evt) {
|
||||
evt.returnValue = confirmationMessage;
|
||||
}
|
||||
return confirmationMessage;
|
||||
}
|
||||
};
|
||||
|
@ -10,11 +10,16 @@
|
||||
this.cachedFrameProcessor = new pskl.model.frame.AsyncCachedFrameProcessor();
|
||||
this.cachedFrameProcessor.setFrameProcessor(this.getFrameColors_.bind(this));
|
||||
|
||||
this.throttledUpdateCurrentColors_ = pskl.utils.FunctionUtils.throttle(
|
||||
this.updateCurrentColors_.bind(this),
|
||||
1000
|
||||
);
|
||||
|
||||
this.paletteService = pskl.app.paletteService;
|
||||
};
|
||||
|
||||
ns.CurrentColorsService.prototype.init = function () {
|
||||
$.subscribe(Events.HISTORY_STATE_SAVED, this.updateCurrentColors_.bind(this));
|
||||
$.subscribe(Events.HISTORY_STATE_SAVED, this.throttledUpdateCurrentColors_);
|
||||
$.subscribe(Events.HISTORY_STATE_LOADED, this.loadColorsFromCache_.bind(this));
|
||||
};
|
||||
|
||||
@ -67,13 +72,18 @@
|
||||
|
||||
ns.CurrentColorsService.prototype.updateCurrentColors_ = function () {
|
||||
var layers = this.piskelController.getLayers();
|
||||
var frames = layers.map(function (l) {return l.getFrames();}).reduce(function (p, n) {return p.concat(n);});
|
||||
|
||||
var job = function (frame) {
|
||||
// Concatenate all frames in a single array.
|
||||
var frames = layers.map(function (l) {
|
||||
return l.getFrames();
|
||||
}).reduce(function (p, n) {
|
||||
return p.concat(n);
|
||||
});
|
||||
|
||||
batchAll(frames, function (frame) {
|
||||
return this.cachedFrameProcessor.get(frame);
|
||||
}.bind(this);
|
||||
|
||||
batchAll(frames, job).then(function (results) {
|
||||
}.bind(this))
|
||||
.then(function (results) {
|
||||
var colors = {};
|
||||
results.forEach(function (result) {
|
||||
Object.keys(result).forEach(function (color) {
|
||||
@ -81,8 +91,12 @@
|
||||
});
|
||||
});
|
||||
// Remove transparent color from used colors
|
||||
delete colors[Constants.TRANSPARENT_COLOR];
|
||||
this.setCurrentColors(Object.keys(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));
|
||||
};
|
||||
|
||||
@ -103,7 +117,9 @@
|
||||
|
||||
ns.CurrentColorsService.prototype.getFrameColors_ = function (frame, processorCallback) {
|
||||
var frameColorsWorker = new pskl.worker.framecolors.FrameColors(frame,
|
||||
function (event) {processorCallback(event.data.colors);},
|
||||
function (event) {
|
||||
processorCallback(event.data.colors);
|
||||
},
|
||||
function () {},
|
||||
function (event) {processorCallback({});}
|
||||
);
|
||||
|
@ -1,9 +1,8 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.service');
|
||||
|
||||
ns.FileDropperService = function (piskelController, drawingAreaContainer) {
|
||||
ns.FileDropperService = function (piskelController) {
|
||||
this.piskelController = piskelController;
|
||||
this.drawingAreaContainer = drawingAreaContainer;
|
||||
this.dropPosition_ = null;
|
||||
};
|
||||
|
||||
@ -28,6 +27,8 @@
|
||||
};
|
||||
|
||||
var files = event.dataTransfer.files;
|
||||
this.isMultipleFiles_ = (files.length > 1);
|
||||
|
||||
for (var i = 0; i < files.length ; i++) {
|
||||
var file = files[i];
|
||||
var isImage = file.type.indexOf('image') === 0;
|
||||
@ -36,7 +37,7 @@
|
||||
if (isImage) {
|
||||
this.readImageFile_(file);
|
||||
} else if (isPiskel) {
|
||||
pskl.utils.PiskelFileUtils.loadFromFile(file, this.onPiskelFileLoaded_);
|
||||
pskl.utils.PiskelFileUtils.loadFromFile(file, this.onPiskelFileLoaded_, this.onPiskelFileError_);
|
||||
} else if (isPalette) {
|
||||
pskl.app.paletteImportService.read(file, this.onPaletteLoaded_.bind(this));
|
||||
}
|
||||
@ -52,32 +53,39 @@
|
||||
pskl.UserSettings.set(pskl.UserSettings.SELECTED_PALETTE, palette.id);
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.onPiskelFileLoaded_ = function (piskel, descriptor, fps) {
|
||||
ns.FileDropperService.prototype.onPiskelFileLoaded_ = function (piskel) {
|
||||
if (window.confirm('This will replace your current animation')) {
|
||||
piskel.setDescriptor(descriptor);
|
||||
pskl.app.piskelController.setPiskel(piskel);
|
||||
pskl.app.previewController.setFPS(fps);
|
||||
}
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.processImageSource_ = function (imageSource) {
|
||||
this.importedImage_ = new Image();
|
||||
this.importedImage_.onload = this.onImageLoaded_.bind(this);
|
||||
this.importedImage_.src = imageSource;
|
||||
ns.FileDropperService.prototype.onPiskelFileError_ = function (reason) {
|
||||
$.publish(Events.PISKEL_FILE_IMPORT_FAILED, [reason]);
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.onImageLoaded_ = function () {
|
||||
var droppedFrame = pskl.utils.FrameUtils.createFromImage(this.importedImage_);
|
||||
ns.FileDropperService.prototype.processImageSource_ = function (imageSource) {
|
||||
var importedImage = new Image();
|
||||
importedImage.onload = this.onImageLoaded_.bind(this, importedImage);
|
||||
importedImage.src = imageSource;
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.onImageLoaded_ = function (importedImage) {
|
||||
if (this.isMultipleFiles_) {
|
||||
this.piskelController.addFrameAtCurrentIndex();
|
||||
this.piskelController.selectNextFrame();
|
||||
}
|
||||
|
||||
var currentFrame = this.piskelController.getCurrentFrame();
|
||||
// Convert client coordinates to sprite coordinates
|
||||
var spriteDropPosition = pskl.app.drawingController.getSpriteCoordinates(
|
||||
this.dropPosition_.x,
|
||||
this.dropPosition_.y
|
||||
);
|
||||
|
||||
var dropCoordinates = this.adjustDropPosition_(this.dropPosition_, droppedFrame);
|
||||
var x = spriteDropPosition.x;
|
||||
var y = spriteDropPosition.y;
|
||||
|
||||
currentFrame.forEachPixel(function (color, x, y) {
|
||||
var fColor = droppedFrame.getPixel(x - dropCoordinates.x, y - dropCoordinates.y);
|
||||
if (fColor && fColor != Constants.TRANSPARENT_COLOR) {
|
||||
currentFrame.setPixel(x, y, fColor);
|
||||
}
|
||||
});
|
||||
pskl.utils.FrameUtils.addImageToFrame(currentFrame, importedImage, x, y);
|
||||
|
||||
$.publish(Events.PISKEL_RESET);
|
||||
$.publish(Events.PISKEL_SAVE_STATE, {
|
||||
@ -85,28 +93,4 @@
|
||||
});
|
||||
};
|
||||
|
||||
ns.FileDropperService.prototype.adjustDropPosition_ = function (position, droppedFrame) {
|
||||
var framePosition = pskl.app.drawingController.getSpriteCoordinates(position.x, position.y);
|
||||
|
||||
var xCoord = framePosition.x - Math.floor(droppedFrame.width / 2);
|
||||
var yCoord = framePosition.y - Math.floor(droppedFrame.height / 2);
|
||||
|
||||
xCoord = Math.max(0, xCoord);
|
||||
yCoord = Math.max(0, yCoord);
|
||||
|
||||
var currentFrame = this.piskelController.getCurrentFrame();
|
||||
if (droppedFrame.width <= currentFrame.width) {
|
||||
xCoord = Math.min(xCoord, currentFrame.width - droppedFrame.width);
|
||||
}
|
||||
|
||||
if (droppedFrame.height <= currentFrame.height) {
|
||||
yCoord = Math.min(yCoord, currentFrame.height - droppedFrame.height);
|
||||
}
|
||||
|
||||
return {
|
||||
x : xCoord,
|
||||
y : yCoord
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -1,10 +1,13 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.service');
|
||||
|
||||
ns.HistoryService = function (piskelController, shortcutService, deserializer) {
|
||||
this.piskelController = piskelController || pskl.app.piskelController;
|
||||
ns.HistoryService = function (piskelController, shortcutService, deserializer, serializer) {
|
||||
// Use the real piskel controller that will not fire events when calling setters
|
||||
this.piskelController = piskelController.getWrappedPiskelController();
|
||||
|
||||
this.shortcutService = shortcutService || pskl.app.shortcutService;
|
||||
this.deserializer = deserializer || pskl.utils.serialization.Deserializer;
|
||||
this.deserializer = deserializer || pskl.utils.serialization.arraybuffer.ArrayBufferDeserializer;
|
||||
this.serializer = serializer || pskl.utils.serialization.arraybuffer.ArrayBufferSerializer;
|
||||
|
||||
this.stateQueue = [];
|
||||
this.currentIndex = -1;
|
||||
@ -23,6 +26,9 @@
|
||||
// Interval/buffer (in milliseconds) between two state load (ctrl+z/y spamming)
|
||||
ns.HistoryService.LOAD_STATE_INTERVAL = 50;
|
||||
|
||||
// Maximum number of states that can be recorded.
|
||||
ns.HistoryService.MAX_SAVED_STATES = 500;
|
||||
|
||||
ns.HistoryService.prototype.init = function () {
|
||||
$.subscribe(Events.PISKEL_SAVE_STATE, this.onSaveStateEvent.bind(this));
|
||||
|
||||
@ -47,15 +53,24 @@
|
||||
action : action,
|
||||
frameIndex : action.state ? action.state.frameIndex : this.piskelController.currentFrameIndex,
|
||||
layerIndex : action.state ? action.state.layerIndex : this.piskelController.currentLayerIndex,
|
||||
fps : this.piskelController.getFPS(),
|
||||
uuid: pskl.utils.Uuid.generate()
|
||||
};
|
||||
|
||||
var isSnapshot = action.type === ns.HistoryService.SNAPSHOT;
|
||||
var isAtAutoSnapshotInterval = this.currentIndex % ns.HistoryService.SNAPSHOT_PERIOD === 0;
|
||||
if (isSnapshot || isAtAutoSnapshotInterval) {
|
||||
state.piskel = this.piskelController.serialize(true);
|
||||
var piskel = this.piskelController.getPiskel();
|
||||
state.piskel = this.serializer.serialize(piskel);
|
||||
}
|
||||
|
||||
// If the new state pushes over MAX_SAVED_STATES, erase all states between the first and
|
||||
// second snapshot states.
|
||||
if (this.stateQueue.length > ns.HistoryService.MAX_SAVED_STATES) {
|
||||
var firstSnapshotIndex = this.getNextSnapshotIndex_(1);
|
||||
this.stateQueue.splice(0, firstSnapshotIndex);
|
||||
this.currentIndex = this.currentIndex - firstSnapshotIndex;
|
||||
}
|
||||
this.stateQueue.push(state);
|
||||
$.publish(Events.HISTORY_STATE_SAVED);
|
||||
};
|
||||
@ -90,6 +105,13 @@
|
||||
return index;
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.getNextSnapshotIndex_ = function (index) {
|
||||
while (this.stateQueue[index] && !this.stateQueue[index].piskel) {
|
||||
index = index + 1;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.loadState = function (index) {
|
||||
try {
|
||||
if (this.isLoadStateAllowed_(index)) {
|
||||
@ -124,13 +146,7 @@
|
||||
var state = this.stateQueue[stateIndex];
|
||||
var piskelSnapshot = state.piskel;
|
||||
|
||||
// If the snapshot is stringified, parse it and backup the result for faster access next time
|
||||
// FIXME : Memory consumption might go crazy if we keep unpacking big piskels indefinitely
|
||||
// ==> should ensure I remove some of them :)
|
||||
if (typeof piskelSnapshot === 'string') {
|
||||
piskelSnapshot = JSON.parse(piskelSnapshot);
|
||||
state.piskel = piskelSnapshot;
|
||||
}
|
||||
state.piskel = piskelSnapshot;
|
||||
|
||||
return piskelSnapshot;
|
||||
};
|
||||
@ -169,6 +185,7 @@
|
||||
ns.HistoryService.prototype.setupState = function (state) {
|
||||
this.piskelController.setCurrentFrameIndex(state.frameIndex);
|
||||
this.piskelController.setCurrentLayerIndex(state.layerIndex);
|
||||
this.piskelController.setFPS(state.fps);
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.replayState = function (state) {
|
||||
|
@ -4,13 +4,25 @@
|
||||
/**
|
||||
* Image an animation import service supporting the import dialog.
|
||||
* @param {!PiskelController} piskelController
|
||||
* @param {!PreviewController} previewController
|
||||
* @constructor
|
||||
*/
|
||||
ns.ImportService =
|
||||
function (piskelController, previewController) {
|
||||
ns.ImportService = function (piskelController) {
|
||||
this.piskelController_ = piskelController;
|
||||
this.previewController_ = previewController;
|
||||
};
|
||||
|
||||
ns.ImportService.prototype.init = function () {
|
||||
$.subscribe(Events.PISKEL_FILE_IMPORT_FAILED, this.onPiskelFileImportFailed_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a piskel load failed event is published. Display an appropriate error message.
|
||||
* TODO: for some failure reasons, we might want to display a dialog with more details.
|
||||
*/
|
||||
ns.ImportService.prototype.onPiskelFileImportFailed_ = function (evt, reason) {
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{
|
||||
'content': 'Piskel file import failed (' + reason + ')',
|
||||
'hideDelay' : 10000
|
||||
}]);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -101,10 +113,9 @@
|
||||
var frames = this.createFramesFromImages_(images, frameSizeX, frameSizeY, smoothing);
|
||||
var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
|
||||
var descriptor = new pskl.model.piskel.Descriptor('Imported piskel', '');
|
||||
var piskel = pskl.model.Piskel.fromLayers([layer], descriptor);
|
||||
var piskel = pskl.model.Piskel.fromLayers([layer], Constants.DEFAULT.FPS, descriptor);
|
||||
|
||||
this.piskelController_.setPiskel(piskel);
|
||||
this.previewController_.setFPS(Constants.DEFAULT.FPS);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,8 @@
|
||||
var ns = $.namespace('pskl.service');
|
||||
|
||||
ns.SelectedColorsService = function () {
|
||||
this.reset();
|
||||
this.primaryColor_ = Constants.DEFAULT_PEN_COLOR;
|
||||
this.secondaryColor_ = Constants.TRANSPARENT_COLOR;
|
||||
};
|
||||
|
||||
ns.SelectedColorsService.prototype.init = function () {
|
||||
@ -18,11 +19,6 @@
|
||||
return this.secondaryColor_;
|
||||
};
|
||||
|
||||
ns.SelectedColorsService.prototype.reset = function () {
|
||||
this.primaryColor_ = Constants.DEFAULT_PEN_COLOR;
|
||||
this.secondaryColor_ = Constants.TRANSPARENT_COLOR;
|
||||
};
|
||||
|
||||
ns.SelectedColorsService.prototype.onPrimaryColorUpdate_ = function (evt, color) {
|
||||
this.primaryColor_ = color;
|
||||
};
|
||||
|
@ -55,7 +55,9 @@
|
||||
NEW_FRAME : createShortcut('new-frame', 'Create new empty frame', 'N'),
|
||||
DUPLICATE_FRAME : createShortcut('duplicate-frame', 'Duplicate selected frame', 'shift+N'),
|
||||
CHEATSHEET : createShortcut('cheatsheet', 'Open the keyboard shortcut cheatsheet', ['?', 'shift+?']),
|
||||
X1_PREVIEW : createShortcut('x1-preview', 'Toggle original size preview', 'alt+1'),
|
||||
X1_PREVIEW : createShortcut('x1-preview', 'Select original size preview', 'alt+1'),
|
||||
BEST_PREVIEW : createShortcut('best-preview', 'Select best size preview', 'alt+2'),
|
||||
FULL_PREVIEW : createShortcut('full-preview', 'Select full size preview', 'alt+3'),
|
||||
ONION_SKIN : createShortcut('onion-skin', 'Toggle onion skin', 'alt+O'),
|
||||
LAYER_PREVIEW : createShortcut('layer-preview', 'Toggle layer preview', 'alt+L'),
|
||||
CLOSE_POPUP : createShortcut('close-popup', 'Close an opened popup', 'ESC')
|
||||
|
@ -9,6 +9,7 @@
|
||||
};
|
||||
|
||||
ns.PaletteImportService = function () {};
|
||||
ns.PaletteImportService.prototype.init = function () {};
|
||||
|
||||
ns.PaletteImportService.prototype.read = function (file, onSuccess, onError) {
|
||||
var reader = this.getReader_(file, onSuccess, onError);
|
||||
|
@ -2,7 +2,7 @@
|
||||
var ns = $.namespace('pskl.service.pensize');
|
||||
|
||||
var MIN_PENSIZE = 1;
|
||||
var MAX_PENSIZE = 4;
|
||||
var MAX_PENSIZE = 32;
|
||||
|
||||
/**
|
||||
* Service to retrieve and modify the current pen size.
|
||||
|
45
src/js/service/performance/PerformanceReport.js
Normal file
45
src/js/service/performance/PerformanceReport.js
Normal file
@ -0,0 +1,45 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.service.performance');
|
||||
|
||||
/**
|
||||
* We consider that piskel should behave correctly for a sprite with the following specs:
|
||||
* - 256*256
|
||||
* - 30 frames
|
||||
* - 5 layers
|
||||
* - 30 colors
|
||||
* Based on these assumptions, as well as a few arbitrary hard limits we try to check
|
||||
* if the provided sprite might present a performance issue.
|
||||
*
|
||||
* @param {Piskel} piskel the sprite to analyze
|
||||
* @param {Number} colorsCount number of colors for the current sprite
|
||||
* (not part of the piskel model so has to be provided separately).
|
||||
*/
|
||||
ns.PerformanceReport = function (piskel, colorsCount) {
|
||||
var pixels = piskel.getWidth() * piskel.getHeight();
|
||||
this.resolution = pixels > (500 * 500);
|
||||
|
||||
var layersCount = piskel.getLayers().length;
|
||||
this.layers = layersCount > 25;
|
||||
|
||||
var framesCount = piskel.getLayerAt(0).size();
|
||||
this.frames = framesCount > 100;
|
||||
|
||||
this.colors = colorsCount > 100;
|
||||
|
||||
var overallScore = (pixels / 2500) + (layersCount * 4) + framesCount + colorsCount;
|
||||
this.overall = overallScore > 100;
|
||||
};
|
||||
|
||||
ns.PerformanceReport.prototype.equals = function (report) {
|
||||
return (report instanceof ns.PerformanceReport &&
|
||||
this.resolution == report.resolution &&
|
||||
this.layers == report.layers &&
|
||||
this.frames == report.frames &&
|
||||
this.colors == report.colors &&
|
||||
this.overall == report.overall);
|
||||
};
|
||||
|
||||
ns.PerformanceReport.prototype.hasProblem = function () {
|
||||
return this.resolution || this.layers || this.frames || this.colors || this.overall;
|
||||
};
|
||||
})();
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user