Cleanup project root

This commit is contained in:
jdescottes
2014-03-16 21:15:34 +01:00
parent 50e7d05764
commit 87574a2b30
204 changed files with 29 additions and 245 deletions

View File

@@ -0,0 +1,8 @@
.tooltip.in {
opacity: 0.95;
filter: alpha(opacity=95);
}
.tooltip {
line-height: 20px;
}

112
src/css/bootstrap/bootstrap.css vendored Executable file
View File

@@ -0,0 +1,112 @@
/*!
* Bootstrap v2.1.1
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
line-height: 0;
}
.clearfix:after {
clear: both;
}
.hide-text {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.input-block-level {
display: block;
width: 100%;
min-height: 30px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.tooltip {
position: absolute;
z-index: 1030;
display: block;
visibility: visible;
padding: 5px;
font-size: 11px;
opacity: 0;
filter: alpha(opacity=0);
}
.tooltip.in {
opacity: 0.8;
filter: alpha(opacity=80);
}
.tooltip.top {
margin-top: -3px;
}
.tooltip.right {
margin-left: 3px;
}
.tooltip.bottom {
margin-top: 3px;
}
.tooltip.left {
margin-left: -3px;
}
.tooltip-inner {
max-width: 200px;
padding: 3px 8px;
color: #ffffff;
text-align: center;
text-decoration: none;
background-color: #000000;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.tooltip-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
.tooltip.top .tooltip-arrow {
bottom: 0;
left: 50%;
margin-left: -5px;
border-width: 5px 5px 0;
border-top-color: #000000;
}
.tooltip.right .tooltip-arrow {
top: 50%;
left: 0;
margin-top: -5px;
border-width: 5px 5px 5px 0;
border-right-color: #000000;
}
.tooltip.left .tooltip-arrow {
top: 50%;
right: 0;
margin-top: -5px;
border-width: 5px 0 5px 5px;
border-left-color: #000000;
}
.tooltip.bottom .tooltip-arrow {
top: 0;
left: 50%;
margin-left: -5px;
border-width: 0 5px 5px;
border-bottom-color: #000000;
}
.tooltip {
line-height: 20px;
}

View File

@@ -0,0 +1 @@
Bootstrap custom build containing only the tooltip component

77
src/css/cheatsheet.css Normal file
View File

@@ -0,0 +1,77 @@
#cheatsheet-wrapper {
position: absolute;
z-index: 10000;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 50px;
box-sizing: border-box;
color: white;
}
.cheatsheet-container {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 20px 10%;
border-radius: 3px;
background: rgba(0,0,0,0.9);
overflow: auto;
}
.cheatsheet-container h3 {
font-size:24px;
margin-top: 0;
}
.cheatsheet-section {
float: left;
width : 50%;
}
.cheatsheet-shortcut {
overflow: hidden;
margin: 10px 0;
}
.cheatsheet-icon.tool-icon {
float: left;
display: inline-block;
height: 30px;
width: 30px;
margin: 0 20px 0 0;
background-size: 20px 20px;
background-position: 5px 5px;
}
.cheatsheet-description {
font-family:Courier;
color: white;
font-size : 13px;
margin-left: 20px;
line-height : 30px;
}
.cheatsheet-key {
display : inline-block;
height: 30px;
line-height: 30px;
padding: 0 10px;
border : 1px solid gold;
border-radius: 2px;
box-sizing: border-box;
text-align: center;
font-family:Courier;
font-weight: bold;
font-size : 18px;
color: gold;
}

76
src/css/forms.css Normal file
View File

@@ -0,0 +1,76 @@
.row {
display: block;
}
.textfield {
background : black;
border : 1px solid #888;
border-radius : 2px;
padding : 3px 10px;
color : white;
box-sizing:border-box;
-moz-box-sizing:border-box;
}
.textfield[disabled=disabled] {
background : #3a3a3a;
}
.textfield-small {
width : 50px;
}
.button {
height: 24px;
box-sizing: border-box;
background-color: #3f3f3f;
border: 1px solid #333;
border-top-color: #666;
border-bottom-color: #222;
cursor: pointer;
text-decoration: none;
color: white;
text-shadow: 0px -1px 0 black;
font-weight: bold;
font-size: 1rem;
text-align: center;
transition: background-color 0.2s linear;
}
.button:hover {
text-decoration: none;
background-color: #484848;
color: gold;
}
.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);
color: black;
text-shadow: 0px 1px 0 #fff;
}
.button-primary:hover {
background-color: rgb(255,235,20);
color: #333;
}
.button[disabled=disabled],
.button[disabled=disabled]:hover {
cursor:default;
background-color: #aaa;
color: #777;
text-shadow: 0px 1px 0 #bbb;
border-color: #666;
border-top-color: #999;
border-bottom-color: #555;
}

8
src/css/minimap.css Normal file
View File

@@ -0,0 +1,8 @@
.minimap-crop-frame {
position:absolute;
border:1px solid rgba(255,255,255,0.5);
z-index:9999;
box-sizing : border-box;
-moz-box-sizing : border-box;
cursor : pointer;
}

View File

@@ -0,0 +1,185 @@
.preview-list-wrapper {
position: relative;
height: 100%;
overflow: hidden;
}
.preview-list-scroller {
overflow-y: scroll;
overflow-x: hidden;
height: 100%;
}
.top-overflow,
.bottom-overflow {
height: 20px;
position: absolute;
left: 0;
right: 12px;
-webkit-transition: all 500ms ease-out;
-moz-transition: all 500ms ease-out;
-ms-transition: all 500ms ease-out;
-o-transition: all 500ms ease-out;
transition: all 500ms ease-out;
background-image: linear-gradient(45deg, rgba(0,0,0, 0.8) 25%, transparent 25%, transparent 75%, rgba(0,0,0, 0.8) 75%, rgba(0,0,0, 0.8)),
linear-gradient(-45deg, rgba(0,0,0, 0.8) 25%, transparent 25%, transparent 75%, rgba(0,0,0, 0.8) 75%, rgba(0,0,0, 0.8));
background-size: 29px 45px;
background-repeat: repeat-x;
background-position-x: 3px;
z-index: 10;
}
.top-overflow {
top: -20px;
}
.bottom-overflow {
bottom: -20px;
background-position-x: 0;
background-position-y: -23px;
}
.top-overflow-visible .top-overflow {
top: 0;
}
.bottom-overflow-visible .bottom-overflow {
bottom: 0;
}
.preview-list {
list-style-type: none;
padding-right: 7px;
}
.add-frame-action {
border: #888 solid 4px;
font-size: 13px;
color: #888;
cursor: pointer;
padding: 6px 0;
border-radius: 4px;
margin-top: 8px;
background-image: url(../img/plus.png);
background-repeat: no-repeat;
background-position: 3px 7px;
background-size: 26px 26px;
text-indent: 18px;
background-color: #222;
}
.add-frame-action .label {
width: 80px;
margin: 0 auto;
}
.add-frame-action:hover {
border-color: gold;
}
.preview-tile {
position: relative;
border: #444 3px solid;
border-radius: 3px;
margin: 5px 0;
}
.preview-tile:first-child {
margin-top: 0;
}
.preview-tile:hover {
border: #999 3px solid;
}
.preview-tile .tile-overlay {
z-index: 10;
position: absolute;
background-color: rgba(100, 100, 100, 0.6);
opacity: 0;
height: 30px;
width: 30px;
border: none;
}
.preview-tile:hover .tile-overlay {
opacity: 1.0;
}
.preview-tile .tile-overlay.tile-count {
display: block;
opacity: 1.0;
border-bottom-right-radius: 3px;
top: 0;
left: 0;
font-size: 14px;
line-height: 30px;
color: white;
font-weight: bold;
text-align: center;
cursor: default;
}
.preview-tile .tile-overlay.delete-frame-action {
background-image: url(../img/garbage.png);
background-repeat: no-repeat;
top: 0;
right: 0;
border-bottom-left-radius: 3px;
height: 30px;
width: 30px;
background-position: 5px 5px;
background-size: 20px;
cursor: pointer;
}
.preview-tile .tile-overlay.duplicate-frame-action {
bottom: 0;
right: 0;
border-bottom-left-radius: 3px;
background-image: url(../img/duplicate.png);
background-repeat: no-repeat;
background-position: 5px 5px;
background-size: 20px;
cursor: pointer;
}
.preview-tile .tile-overlay.dnd-action {
bottom: 0;
left: 0;
border-top-right-radius: 3px;
background-image: url(../img/dragndrop.png);
background-repeat: no-repeat;
background-position: 5px 5px;
background-size: 20px;
cursor: move;
}
.preview-tile.selected {
border-color: gold;
}
.preview-tile.selected:after {
content: " ";
position: absolute;
top: 38px;
right: -15px;
border: transparent 4px solid;
border-left-color: gold;
border-width: 6px;
border-style: solid;
}
/**
* Drag n drop styles.
*/
.preview-tile-drop-proxy {
border: 3px dashed gold;
height: 90px;
border-radius: 9px;
background-color: rgba(255, 215,0, 0.2);
}

36
src/css/reset.css Normal file
View File

@@ -0,0 +1,36 @@
html, body {
height : 100%; width: 100%;
margin : 0;
overflow: hidden;
cursor : default;
font-family: arial;
font-size: 11px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
ul, li {
margin : 0;
padding : 0;
list-style-type: none;
}
/* Force apparition of scrollbars on leopard */
::-webkit-scrollbar {
-webkit-appearance: none;
width: 6px;
}
::-webkit-scrollbar-thumb {
border-radius: 2px;
background-color: #666;
}
::-webkit-scrollbar-track {
background-color: rgba(50, 50, 50, 0.4);
}

316
src/css/settings.css Normal file
View File

@@ -0,0 +1,316 @@
/** Righty sticky drawer expanded state. */
.right-sticky-section.sticky-section {
right: 0;
width: 47px;
-webkit-transition: all 200ms ease-out;
-moz-transition: all 200ms ease-out;
-ms-transition: all 200ms ease-out;
-o-transition: all 200ms ease-out;
transition: all 200ms ease-out;
}
.right-sticky-section.expanded {
right: 280px;
}
.right-sticky-section .tool-icon {
float: right;
margin-right: 0;
}
/*
* Settings icons
*/
.tool-icon.gallery-icon {
background-image: url(../img/gallery.png);
background-position: 3px 3px;
background-size: 39px 39px;
}
.tool-icon.resize-icon {
background-image: url(../img/resize-icon.png);
background-position: 10px 10px;
background-size: 26px 26px;
}
.tool-icon.save-icon {
background-image: url(../img/save.png);
background-position: 6px 6px;
background-size: 36px 36px;
}
.tool-icon.gear-icon {
background-image: url(../img/gear.png);
background-position: 6px 7px;
background-size: 32px 32px;
}
.tool-icon.upload-cloud-icon {
background-image: url(../img/cloud_export.png);
background-position: 4px 0px;
background-size: 36px 36px;
position: relative;
}
.tool-icon.local-storage-icon {
background-image: url(../img/local-storage-icon.png);
background-position: 10px 12px;
background-size: 30px;
position: relative;
}
.tool-icon.import-icon {
background-image: url(../img/import-icon.png);
background-position: 10px 5px;
background-size: 26px;
position: relative;
}
.upload-cloud-icon .label {
position: absolute;
left: 0;
bottom: 4px;
right: 0;
font-size: 11px;
text-transform: uppercase;
color: #fff;
text-align: center;
}
.drawer-content {
overflow: hidden;
background-color: #444;
height: 550px;
max-height: 100%;
width: 280px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
box-shadow: 0 0 5px 0 black;
}
.right-sticky-section.expanded .tool-icon {
padding-right: 1px;
}
.right-sticky-section .tool-icon.has-expanded-drawer {
position: relative;
background-color: #444;
margin-right: 0;
padding-right: 2px;
border-left : 3px solid gold;
}
.settings-section {
margin: 10px 20px;
font-size: 12px;
font-weight: bold;
color: #ccc;
text-shadow: 1px 1px #000;
}
.settings-section .button {
margin: 0;
}
.settings-title {
margin-top: 20px;
margin-bottom: 10px;
text-transform: uppercase;
border-bottom: 1px #aaa solid;
padding-bottom: 5px;
}
.settings-form-section {
margin-bottom: 10px;
}
.background-picker-wrapper {
overflow: hidden;
padding: 10px 5px 20px 5px;
}
.background-picker {
cursor: pointer;
float: left;
height: 35px;
width: 35px;
background-color: transparent;
margin-right: 15px;
padding: 1px;
position: relative;
}
.background-picker:after {
content: " ";
position: absolute;
top: -2px;
right: -2px;
bottom: -2px;
left: -2px;
}
.background-picker:hover:after {
border: #eee 1px solid;
}
.background-picker.selected:after {
border: gold 1px solid;
}
/* Gif/Png Export Setting panel*/
/*******************************/
.gif-upload-button,
.gif-render-button {
/*float : right;*/
margin-top : 10px;
margin-right : 10px;
}
.gif-export-radio-group {
margin:10px 0;
}
.gif-export-preview,
.png-export-preview {
margin-top:20px;
max-width:240px;
position:relative;
}
.png-export-preview {
margin:10px 0;
overflow: hidden;
}
.png-export-preview img {
float: left;
}
.png-upload-status {
margin : 10px 0;
}
.preview-upload-ongoing:before{
content: "Upload ongoing ...";
position: absolute;
display: block;
height: 100%;
width: 100%;
text-align: center;
padding-top: 45%;
box-sizing:border-box;
-moz-box-sizing:border-box;
background: rgba(0,0,0,0.5);
color: white;
}
/* Import panel */
.import-section,
.resize-section {
margin: 15px 0;
}
.import-section-title {
display : inline-block;
}
.import-section-title-small {
width: 35px;
}
.import-section-disabled {
color : #888;
}
.import-section-preview {
display : inline-block;
height : 60px;
width: 60px;
border : 1px dashed #999;
border-radius: 3px;
}
.import-size-field,
.resize-size-field {
width: 50px;
margin-right: 8px;
text-align: right;
}
.import-size-field:nth-of-type(2),
.resize-size-field:nth-of-type(2) {
margin-left: 5px;
}
.file-input-button {
margin-right: 8px;
border-radius: 2px;
}
.file-input-status {
display: inline-block;
width: 130px;
overflow: hidden;
height: 1.5rem;
word-break : break-all;
vertical-align: middle;
font-style: italic;
font-weight: normal;
text-shadow: none;
}
.save-field {
width: 100%;
}
#save-status {
margin-top: 10px;
}
.status {
height: 1.5rem;
vertical-align: middle;
font-weight: normal;
text-shadow: none;
}
[name=smooth-resize-checkbox] {
margin : 0 8px;
}
[name*=checkbox] {
vertical-align: middle;
}
.local-piskels-list {
width: 100%;
}
.local-piskel-item {
height: 3em;
}
.local-piskel-name {
width: 40%;
}
.local-piskel-save-date {
font-weight : normal;
}
.local-piskels-list a {
text-decoration: none;
}
.local-piskels-list a:hover {
text-decoration: underline;
}
.local-piskel-load-link {
color : gold;
}
.local-piskel-delete-link {
color : red;
}

View File

@@ -0,0 +1,98 @@
/**
* Piskel specific CSS for spectrum widgets
*/
/* Widget's main container */
.sp-container {
border-radius: 4px;
background-color: #2B2B2B;
border: solid 4px #888;
padding: 5px;
}
.sp-container[data-y-position=bottom] {
border-top-left-radius: 0;
}
.sp-container[data-y-position=top] {
border-bottom-left-radius: 0;
}
/* Color square container */
.sp-color, .sp-hue {
border : 1px solid #222;
}
/* Remove the padding on the input container */
.sp-replacer {
padding: 4px;
height: 100%;
border-width: 0;
box-sizing: border-box;
-moz-box-sizing:border-box;
background-color: #444;
}
.sp-replacer:hover {
background-color: #888;
}
/* Hide the triangle */
.sp-dd {
display: none;
}
/* */
.sp-preview {
margin-right: 0;
height: 100%;
width: 39px;
border-width: 0;
}
.sp-palette-row-selection {
max-width: 62px;
}
.sp-palette .sp-thumb-el {
margin : 0 5px 5px 0;
width: 24px;
height: 24px;
border-color: #444;
}
.sp-picker-container, .sp-palette-container {
padding-top: 5px;
padding-right: 5px;
padding-left: 5px;
border-left-width: 0;
border-right-width: 0;
}
.sp-slider {
height: 5px;
left: -2px;
right: -2px;
border: 2px solid white;
background: rgba(255,255,255,0);
opacity: 1;
border-radius: 2px;
box-shadow: 0 0 1px 1px rgba(0,0,0,0.5), inset 0 0 1px 1px rgba(0,0,0,0.5);
}
.sp-dragger {
border-radius: 8px;
height: 12px;
width: 12px;
border: 2px solid white;
background: none;
box-sizing:border-box;
-moz-box-sizing:border-box;
box-shadow: 0 0 1px 1px rgba(0,0,0,0.5), inset 0 0 1px 1px rgba(0,0,0,0.5);
}
.sp-palette .sp-thumb-el.sp-thumb-active {
border-color: gold;
box-shadow: 0 0 0px 1px gold;
}

View File

@@ -0,0 +1,477 @@
/***
Spectrum Colorpicker v1.1.2
https://github.com/bgrins/spectrum
Author: Brian Grinstead
License: MIT
***/
.sp-container {
position:absolute;
top:0;
left:0;
display:inline-block;
*display: inline;
*zoom: 1;
/* https://github.com/bgrins/spectrum/issues/40 */
z-index: 9999994;
overflow: hidden;
}
.sp-container.sp-flat {
position: relative;
}
/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
.sp-top {
position:relative;
width: 100%;
display:inline-block;
}
.sp-top-inner {
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
}
.sp-color {
position: absolute;
top:0;
left:0;
bottom:0;
right:20%;
}
.sp-hue {
position: absolute;
top:0;
right:0;
bottom:0;
left:84%;
height: 100%;
}
.sp-fill {
padding-top: 80%;
}
.sp-sat, .sp-val {
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.sp-alpha-enabled .sp-top {
margin-bottom: 18px;
}
.sp-alpha-enabled .sp-alpha {
display: block;
}
.sp-alpha-handle {
position:absolute;
top:-4px;
bottom: -4px;
width: 6px;
left: 50%;
cursor: pointer;
border: 1px solid black;
background: white;
opacity: .8;
}
.sp-alpha {
display: none;
position: absolute;
bottom: -14px;
right: 0;
left: 0;
height: 8px;
}
.sp-alpha-inner {
border: solid 1px #333;
}
/* Don't allow text selection */
.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
-webkit-user-select:none;
-moz-user-select: -moz-none;
-o-user-select:none;
user-select: none;
}
.sp-container.sp-input-disabled .sp-input-container {
display: none;
}
.sp-container.sp-buttons-disabled .sp-button-container {
display: none;
}
.sp-palette-only .sp-picker-container {
display: none;
}
.sp-palette-disabled .sp-palette-container {
display: none;
}
.sp-initial-disabled .sp-initial {
display: none;
}
/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
.sp-sat {
background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
}
.sp-val {
background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
}
.sp-hue {
background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}
/* IE filters do not support multiple color stops.
Generate 6 divs, line them up, and do two color gradients for each.
Yes, really.
*/
.sp-1 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
}
.sp-2 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
}
.sp-3 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
}
.sp-4 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
}
.sp-5 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
}
.sp-6 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
}
.sp-hidden {
display: none !important;
}
/* Clearfix hack */
.sp-cf:before, .sp-cf:after { content: ""; display: table; }
.sp-cf:after { clear: both; }
.sp-cf { *zoom: 1; }
/* Mobile devices, make hue slider bigger so it is easier to slide */
@media (max-device-width: 480px) {
.sp-color { right: 40%; }
.sp-hue { left: 63%; }
.sp-fill { padding-top: 60%; }
}
.sp-dragger {
border-radius: 5px;
height: 5px;
width: 5px;
border: 1px solid #fff;
background: #000;
cursor: pointer;
position:absolute;
top:0;
left: 0;
}
.sp-slider {
position: absolute;
top:0;
cursor:pointer;
height: 3px;
left: -1px;
right: -1px;
border: 1px solid #000;
background: white;
opacity: .8;
}
/*
Theme authors:
Here are the basic themeable display options (colors, fonts, global widths).
See http://bgrins.github.io/spectrum/themes/ for instructions.
*/
.sp-container {
border-radius: 0;
background-color: #ECECEC;
border: solid 1px #f0c49B;
padding: 0;
}
.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue
{
font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.sp-top
{
margin-bottom: 3px;
}
.sp-color, .sp-hue
{
border: solid 1px #666;
}
/* Input */
.sp-input-container {
float:right;
width: 100px;
margin-bottom: 4px;
}
.sp-initial-disabled .sp-input-container {
width: 100%;
}
.sp-input {
font-size: 12px !important;
border: 1px inset;
padding: 4px 5px;
margin: 0;
width: 100%;
background:transparent;
border-radius: 3px;
color: #222;
}
.sp-input:focus {
border: 1px solid orange;
}
.sp-input.sp-validation-error
{
border: 1px solid red;
background: #fdd;
}
.sp-picker-container , .sp-palette-container
{
float:left;
position: relative;
padding: 10px;
padding-bottom: 300px;
margin-bottom: -290px;
}
.sp-picker-container
{
width: 172px;
border-left: solid 1px #fff;
}
/* Palettes */
.sp-palette-container
{
border-right: solid 1px #ccc;
}
.sp-palette .sp-thumb-el {
display: block;
position:relative;
float:left;
width: 24px;
height: 15px;
margin: 3px;
cursor: pointer;
border:solid 2px transparent;
}
.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
border-color: orange;
}
.sp-thumb-el
{
position:relative;
}
/* Initial */
.sp-initial
{
float: left;
border: solid 1px #333;
}
.sp-initial span {
width: 30px;
height: 25px;
border:none;
display:block;
float:left;
margin:0;
}
/* Buttons */
.sp-button-container {
float: right;
}
/* Replacer (the little preview div that shows up instead of the <input>) */
.sp-replacer {
margin:0;
overflow:hidden;
cursor:pointer;
padding: 4px;
display:inline-block;
*zoom: 1;
*display: inline;
border: solid 1px #91765d;
background: #eee;
color: #333;
vertical-align: middle;
}
.sp-replacer:hover, .sp-replacer.sp-active {
border-color: #F0C49B;
color: #111;
}
.sp-replacer.sp-disabled {
cursor:default;
border-color: silver;
color: silver;
}
.sp-dd {
padding: 2px 0;
height: 16px;
line-height: 16px;
float:left;
font-size:10px;
}
.sp-preview
{
position:relative;
width:25px;
height: 20px;
border: solid 1px #222;
margin-right: 5px;
float:left;
z-index: 0;
}
.sp-palette
{
*width: 220px;
max-width: 220px;
}
.sp-palette .sp-thumb-el
{
width:16px;
height: 16px;
margin:2px 1px;
border: solid 1px #d0d0d0;
}
.sp-container
{
padding-bottom:0;
}
/* Buttons: http://hellohappy.org/css3-buttons/ */
.sp-container button {
background-color: #eeeeee;
background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
border: 1px solid #ccc;
border-bottom: 1px solid #bbb;
border-radius: 3px;
color: #333;
font-size: 14px;
line-height: 1;
padding: 5px 4px;
text-align: center;
text-shadow: 0 1px 0 #eee;
vertical-align: middle;
}
.sp-container button:hover {
background-color: #dddddd;
background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
border: 1px solid #bbb;
border-bottom: 1px solid #999;
cursor: pointer;
text-shadow: 0 1px 0 #ddd;
}
.sp-container button:active {
border: 1px solid #aaa;
border-bottom: 1px solid #888;
-webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
}
.sp-cancel
{
font-size: 11px;
color: #d93f3f !important;
margin:0;
padding:2px;
margin-right: 5px;
vertical-align: middle;
text-decoration:none;
}
.sp-cancel:hover
{
color: #d93f3f !important;
text-decoration: underline;
}
.sp-palette span:hover, .sp-palette span.sp-thumb-active
{
border-color: #000;
}
.sp-preview, .sp-alpha, .sp-thumb-el
{
position:relative;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
}
.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner
{
display:block;
position:absolute;
top:0;left:0;bottom:0;right:0;
}
.sp-palette .sp-thumb-inner
{
background-position: 50% 50%;
background-repeat: no-repeat;
}
.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner
{
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
}
.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner
{
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
}

306
src/css/style.css Normal file
View File

@@ -0,0 +1,306 @@
body {
background: radial-gradient(circle, #000, #373737);
/* 16/06/2013 : -webkit still needed for
safari, safari mobile and android browser and chrome for android
cf http://caniuse.com/css-gradients */
background: -webkit-radial-gradient(circle, #000, #373737);
}
/* Browser fixes */
::-ms-clear {
display: none;
}
/**
* Application layout
*/
.main-wrapper {
position: absolute;
top: 5px;
right: 0;
bottom: 5px;
left: 0;
}
.column-wrapper {
text-align: center;
font-size: 0;
position: absolute;
left: 100px; /* Reserve room for tools on the left edge of the screen. */
top: 0;
right: 50px; /* Reserve room for actions on the right edge of the screen. */
bottom: 0;
}
.piskel-name-container {
overflow:hidden;
position:fixed;
top:10px;
left:10px;
color:white;
font-family:Tahoma;
z-index: 10000;
}
.piskel-name-container #piskel-name {
border :none;
color : lightgrey;
background: transparent;
font-size:16pt;
}
.column {
display: inline-block;
}
.left-column {
vertical-align: top;
height: 100%;
margin-right: 7px;
}
.main-column {
height: 100%;
position: relative;
}
.right-column {
vertical-align: top;
margin-left: 10px;
}
.drawing-canvas-container {
font-size: 0;
}
.sticky-section {
position: fixed;
top: 0;
bottom: 0;
z-index: 1000;
}
.sticky-section .sticky-section-wrap {
display: table;
height: 100%;
}
.sticky-section .vertical-centerer {
display: table-cell;
vertical-align: middle;
}
.left-sticky-section.sticky-section {
left: 0;
max-width: 100px;
}
.left-sticky-section .tool-icon {
float: left;
}
/**
* Canvases layout
*/
.canvas {
position: relative;
z-index: 1;
}
.canvas-container {
position: relative;
display: block;
}
.canvas-container .canvas-background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.light-picker-background,
.light-canvas-background .canvas-background {
background: url(../img/canvas_background/light_canvas_background.png) repeat;
}
.medium-picker-background,
.medium-canvas-background .canvas-background {
background: url(../img/canvas_background/medium_canvas_background.png) repeat;
}
.lowcont-medium-picker-background,
.lowcont-medium-canvas-background .canvas-background {
background: url(../img/canvas_background/lowcont_medium_canvas_background.png) repeat;
}
.lowcont-dark-picker-background,
.lowcont-dark-canvas-background .canvas-background {
background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
}
.layers-canvas {
opacity: 0.2;
}
.canvas.canvas-overlay,
.canvas.layers-canvas {
position: absolute;
top: 0;
left: 0;
}
/**
* Z-indexes should match the drawing area canvas superposition order :
* - 1 : draw layers below current layer
* - 2 : draw current layer
* - 3 : draw layers above current layer
* - 4 : draw the tools overlay
*/
.canvas.layers-below-canvas {z-index: 7;}
.canvas.drawing-canvas {z-index: 8;}
.canvas.layers-above-canvas {z-index: 9;}
.canvas.canvas-overlay {z-index: 10;}
/**
* Animated preview styles.
*/
.preview-container {
border : 0px Solid black;
border-radius:5px 0px 0px 5px;
box-shadow : 0px 0px 2px rgba(0,0,0,0.2);
font-size: 0;
}
.preview-container .canvas-container {
overflow : hidden;
}
.preview-container canvas {
border : 0px Solid transparent;
}
.display-fps {
float: left;
color: #aaa;
font-size: 12px;
min-width: 55px;
vertical-align: bottom;
line-height: 24px;
}
.range-fps {
overflow: hidden;
width: 120px;
}
/**
* Layers container
*/
.layers-list-container {
border: 4px solid #888;
font-size: medium;
color: white;
text-align: left;
border-radius: 4px;
margin-top: 10px;
}
.layers-title {
padding: 8px;
margin: 0;
font-size: 15px;
background: #222;
background-image: url('../img/layers.svg');
background-size: 22px;
background-repeat: no-repeat;
background-position: 97%;
}
.layers-list {
font-size : 12px;
}
.layer-item {
height:24px;
line-height: 24px;
padding : 0 10px;
border-top: 1px solid #444;
cursor : pointer;
}
.layer-item:hover {
background : #222;
}
.current-layer-item,
.current-layer-item:hover {
background : #333;
color: gold;
}
.layers-button-container {
overflow : hidden;
}
.layers-button-arrow {
font-family : 'Lucida Grande', Calibri;
padding : 2px 6px 0 6px;
}
.layers-button {
margin: 0;
width: 25%;
float : left;
}
/* @override */
.button.layers-button {
border-left-width: 0;
}
.layers-button:last-child {
border-right-width: 0;
}
/**
* User messages
*/
.user-message {
position: absolute;
right: 0;
bottom: 0;
background-color: #F9EDBE;
padding: 10px 47px;
border-top-left-radius: 7px;
color: #222;
border: #F0C36D 1px solid;
border-right: 0;
border-bottom: 0;
font-weight: bold;
font-size: 13px;
z-index: 10000;
max-width: 300px;
}
.user-message .close {
position: absolute;
top: 6px;
right: 17px;
color: gray;
font-weight: bold;
cursor: pointer;
font-size: 18px;
}
.user-message .close:hover {
color: black;
}
.image-link {
color : gold;
}

226
src/css/tools.css Normal file
View File

@@ -0,0 +1,226 @@
.tools-wrapper,
.options-wrapper,
.palette-wrapper {
float: left;
}
.tool-icon {
cursor : pointer;
width: 46px;
height: 46px;
margin: 1px;
background-color: #3a3a3a;
background-repeat: no-repeat;
background-position: 12px 12px;
background-size: 24px 24px;
}
.tool-icon.selected {
cursor: default;
background-color: #444;
border: 3px solid gold;
box-sizing: border-box;
-moz-box-sizing: border-box;
background-position: 9px 9px;
}
.tool-icon:hover {
background-color: #444;
}
/*
* Tool icons:
*/
.tool-icon.tool-pen {
background-image: url(../img/tools/pen.png);
}
.tool-icon.tool-vertical-mirror-pen {
background-image: url(../img/tools/mirror.png);
background-position: 0px 10px;
background-size: 38px 27px;
}
.tool-icon.tool-vertical-mirror-pen.selected {
background-position: -3px 7px;
}
.tool-icon.tool-paint-bucket {
background-image: url(../img/tools/paintbucket.png);
}
.tool-icon.tool-eraser {
background-image: url(../img/tools/eraser.png);
}
.tool-icon.tool-stroke {
background-image: url(../img/tools/stroke.png);
}
.tool-icon.tool-rectangle {
background-image: url(../img/tools/rectangle.png);
background-position: 12px 14px;
background-size: 24px 20px;
}
.tool-icon.tool-circle {
background-image: url(../img/tools/circle.png);
}
.tool-icon.tool-move {
background-image: url(../img/tools/hand.png);
background-size: 24px 24px;
}
.tool-icon.tool-rectangle-select {
background-image: url(../img/tools/rectangle_selection.png);
background-position: 12px 14px;
background-size: 24px 20px;
}
.tool-icon.tool-rectangle.selected,
.tool-icon.tool-rectangle-select.selected {
background-position: 9px 11px;
}
.tool-icon.tool-shape-select {
background-image: url(../img/tools/magicwand.png);
}
.tool-icon.tool-colorpicker {
background-image: url(../img/tools/eyedropper.png);
background-size: 23px 23px;
}
/*
* Tool cursors:
*/
.tool-paint-bucket .drawing-canvas-container:hover {
cursor: url(../img/icons/paint-bucket.png) 12 12, pointer;
}
.tool-vertical-mirror-pen .drawing-canvas-container:hover {
cursor: url(../img/icons/vertical-mirror-pen.png) 5 15, pointer;
}
.tool-pen .drawing-canvas-container:hover {
cursor: url(../img/icons/pen.png) 0 15, pointer;
}
.tool-eraser .drawing-canvas-container:hover {
cursor: url(../img/icons/eraser.png) 0 15, pointer;
}
.tool-stroke .drawing-canvas-container:hover {
cursor: url(../img/icons/pen.png) 0 15, pointer;
}
.tool-rectangle .drawing-canvas-container:hover {
cursor: url(../img/icons/rectangle.png) 0 15, pointer;
}
.tool-circle .drawing-canvas-container:hover {
cursor: url(../img/icons/circle.png) 2 15, pointer;
}
.tool-move .drawing-canvas-container:hover {
cursor: url(../img/icons/hand.png) 15 15, pointer;
}
.tool-rectangle-select .drawing-canvas-container:hover {
cursor: url(../img/icons/select.png) 15 15, pointer;
}
.tool-shape-select .drawing-canvas-container:hover {
cursor: url(../img/icons/wand.png) 15 15, pointer;
}
.tool-colorpicker .drawing-canvas-container:hover {
cursor: url(../img/icons/dropper.png) 0 15, pointer;
}
.swap-colors-icon {
background-image: url(../img/tools/swap-arrow-square-small-grey.png);
position: relative;
top: 50px;
left: 6px;
height: 18px;
width: 18px;
background-size: 18px;
opacity : 0.3;
cursor : pointer;
}
.swap-colors-icon:hover {
opacity : 1;
}
.tool-color-picker {
position:relative;
}
.tool-color-picker:nth-child(1) {
z-index : 100;
}
.tool-color-picker:nth-child(2) {
z-index : 90;
margin-top: 20px;
margin-left:-20px;
}
.tool-color-picker input {
width: 16px;
height: 16px;
text-indent: -10000px;
border: 1px solid black;
background: white;
cursor: pointer;
position : relative;
top: 10px;
margin-left: 2px;
}
.tool-color-picker .secondary-color-picker {
top : 18px;
margin-left: 0;
}
.palette-wrapper {
margin-top: 10px;
margin-left: 10px;
}
.palette-color[data-color=TRANSPARENT] {
position: relative;
top: 10px;
left: 10px;
background-color: white;
height : 16px;
width : 16px;
border: 2px solid #000;
background-size: 16px 16px;
background-position: 0 0;
background-image: -webkit-gradient(
linear,
left top,
right bottom,
color-stop(0, #fff),
color-stop(0.45, #fff),
color-stop(0.5, #ff0000),
color-stop(0.55, #fff),
color-stop(1, #fff)
);
background-image: -moz-linear-gradient(
left top,
#fff 0%,
#fff 45%,
#f00 50%,
#fff 55%,
#fff 100%
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

BIN
src/img/cloud_export.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

BIN
src/img/dragndrop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
src/img/duplicate.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
src/img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

BIN
src/img/gallery.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

BIN
src/img/garbage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/img/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

BIN
src/img/icons/circle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

BIN
src/img/icons/color-palette.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

BIN
src/img/icons/dropper.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

BIN
src/img/icons/eraser.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

BIN
src/img/icons/hand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

BIN
src/img/icons/paint-bucket.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

BIN
src/img/icons/pen.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

BIN
src/img/icons/rectangle.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

BIN
src/img/icons/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/img/icons/stroke.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
src/img/icons/wand.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

BIN
src/img/import-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

62
src/img/layers.svg Normal file
View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
version="1.1"
width="90"
height="87.886002"
id="svg3024">
<defs
id="defs3026" />
<metadata
id="metadata3029">
<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>
<g
transform="translate(-266.63206,-597.79217)"
id="layer1">
<g
transform="translate(262.14286,465.21932)"
id="Captions" />
<g
id="g3825" />
<g
transform="translate(-0.51079959,126.51585)"
id="layer1-9"
style="fill:#ffffff;fill-opacity:1">
<g
transform="translate(262.14286,465.21932)"
id="Captions-4"
style="fill:#ffffff;fill-opacity:1" />
<g
id="g3825-8"
style="fill:#ffffff;fill-opacity:1">
<path
d="m 357.14286,556.72132 c 0,1.346 -1.097,2.441 -2.442,2.441 h -60.239 c -1.347,0 -2.441,-1.096 -2.441,-2.441 v -4.555 h 50.241 c 4.346,0 7.884,-3.539 7.884,-7.885 v -48.128 h 4.555 c 1.346,0 2.442,1.096 2.442,2.441 v 58.127 z"
id="path2989-8"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 279.58086,544.28132 v -4.555 h 6.997 2.722 2.721 37.803 c 4.346,0 7.884,-3.537 7.884,-7.883 v -35.69 -2.721 -2.721 -6.998 h 4.555 c 1.346,0 2.442,1.096 2.442,2.443 v 4.555 2.721 2.721 48.129 c 0,1.346 -1.097,2.443 -2.442,2.443 h -50.242 -2.721 -2.722 -4.555 c -1.347,0 -2.442,-1.098 -2.442,-2.444 z"
id="path2991-2"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 329.82386,471.27632 h -60.239 c -1.347,0 -2.442,1.093 -2.442,2.441 v 58.127 c 0,1.344 1.095,2.441 2.442,2.441 h 4.555 2.721 2.721 6.997 2.722 2.721 37.803 c 1.345,0 2.441,-1.098 2.441,-2.441 v -35.691 -2.721 -2.721 -6.998 -2.721 -2.721 -4.554 c -10e-4,-1.348 -1.098,-2.441 -2.442,-2.441 z"
id="path2993-4"
style="fill:#ffffff;fill-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/img/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

BIN
src/img/resize-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

BIN
src/img/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/img/tools/circle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

BIN
src/img/tools/eraser.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/img/tools/hand-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/img/tools/hand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
src/img/tools/magicwand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
src/img/tools/mirror.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/img/tools/pen-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/img/tools/pen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
src/img/tools/rectangle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

BIN
src/img/tools/stroke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

69
src/index.html Normal file
View File

@@ -0,0 +1,69 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Piskel</title>
<meta name="description" content="">
<meta name="author" content="Julian Descottes">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div
id="loading-mask"
style="
position:fixed;
top:0;right:0;bottom:0;left:0;
background:black;
opacity:1;
text-align:center;
z-index : 20000;
transition:opacity 0.5s;
color:white;">
<span style="top:45%">Loading pixels ...</span>
</div>
<script type="text/javascript" src="js/lib/iframeLoader.js"></script>
<div class="piskel-name-container">
<input readonly id="piskel-name" type="text" value=""/>
</div>
<div id="main-wrapper" class="main-wrapper">
<iframe src="templates/drawing-tools.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<div id="column-wrapper" class="column-wrapper">
<div class='column left-column'>
<iframe src="templates/frames-list.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
</div>
<div class='column main-column'>
<div id="drawing-canvas-container" class="drawing-canvas-container canvas-container">
<div class="canvas-background"></div>
</div>
</div>
<div class="column right-column">
<iframe src="templates/preview.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<iframe src="templates/layers-list.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
</div>
</div>
<div id="application-action-section" data-pskl-controller="settings" class="sticky-section right-sticky-section">
<div class="sticky-section-wrap">
<iframe src="templates/settings.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<div class="drawer vertical-centerer">
<div class="drawer-content" id="drawer-container">
<iframe src="templates/settings/save.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/application.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/resize.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/import.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/localstorage.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/export-gif.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/export-png.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
</div>
</div>
</div>
</div>
</div>
<iframe src="templates/cheatsheet.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<script type="text/javascript" src="piskel-boot.js"></script>
</body>
</html>

59
src/js/Constants.js Normal file
View File

@@ -0,0 +1,59 @@
// TODO(grosbouddha): put under pskl namespace.
var Constants = {
DEFAULT : {
HEIGHT : 32,
WIDTH : 32,
FPS : 12
},
MODEL_VERSION : 2,
MAX_HEIGHT : 1024,
MAX_WIDTH : 1024,
MINIMUM_ZOOM : 1,
PREVIEW_FILM_SIZE : 120,
DEFAULT_PEN_COLOR : '#000000',
TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)',
/*
* Fake semi-transparent color used to highlight transparent
* strokes and rectangles:
*/
SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)',
/*
* When a tool is hovering the drawing canvas, we highlight the eventual
* pixel target with this color:
*/
TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)',
/*
* Default entry point for piskel web service:
*/
STATIC : {
URL : {
SAVE : 'http://3.piskel-app.appspot.com/store',
GET : 'http://3.piskel-app.appspot.com/get'
}
},
APPENGINE : {
URL : {
SAVE : 'save'
}
},
IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-a.appspot.com/__/upload',
IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-a.appspot.com/img/',
GRID_STROKE_WIDTH: 1,
ZOOMED_OUT_BACKGROUND_COLOR : '#A0A0A0',
LEFT_BUTTON : 0,
MIDDLE_BUTTON : 1,
RIGHT_BUTTON : 2,
MOUSEMOVE_THROTTLING : 10,
ABSTRACT_FUNCTION : function () {throw 'abstract method should be implemented';}
};

44
src/js/Events.js Normal file
View File

@@ -0,0 +1,44 @@
// TODO(grosbouddha): put under pskl namespace.
var Events = {
TOOL_SELECTED : "TOOL_SELECTED",
TOOL_RELEASED : "TOOL_RELEASED",
SELECT_PRIMARY_COLOR: "SELECT_PRIMARY_COLOR",
SELECT_SECONDARY_COLOR: "SELECT_SECONDARY_COLOR",
/**
* When this event is emitted, a request is sent to the localstorage
* Service to save the current framesheet. The storage service
* may not immediately store data (internal throttling of requests).
*/
LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST",
/**
* Fired each time a user setting change.
* The payload will be:
* 1st argument: Name of the settings
* 2nd argument: New value
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
CLOSE_SETTINGS_DRAWER : "CLOSE_SETTINGS_DRAWER",
/**
* The framesheet was reseted and is now probably drastically different.
* Number of frames, content of frames, color used for the palette may have changed.
*/
PISKEL_RESET: "PISKEL_RESET",
PISKEL_SAVED: "PISKEL_SAVED",
FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED",
SELECTION_CREATED: "SELECTION_CREATED",
SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST",
SELECTION_DISMISSED: "SELECTION_DISMISSED",
SHOW_NOTIFICATION: "SHOW_NOTIFICATION",
HIDE_NOTIFICATION: "HIDE_NOTIFICATION",
ZOOM_CHANGED : "ZOOM_CHANGED"
};

230
src/js/app.js Normal file
View File

@@ -0,0 +1,230 @@
/**
* @require Constants
* @require Events
*/
(function () {
var ns = $.namespace("pskl");
/**
* Main application controller
*/
ns.app = {
init : function () {
/**
* True when piskel is running in static mode (no back end needed).
* When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl
*/
this.isAppEngineVersion = !!pskl.appEngineToken_;
this.shortcutService = new pskl.service.keyboard.ShortcutService();
this.shortcutService.init();
var size = this.readSizeFromURL_();
var descriptor = new pskl.model.piskel.Descriptor('New Piskel', '');
var piskel = new pskl.model.Piskel(size.width, size.height, descriptor);
var layer = new pskl.model.Layer("Layer 1");
var frame = new pskl.model.Frame(size.width, size.height);
layer.addFrame(frame);
piskel.addLayer(layer);
this.piskelController = new pskl.controller.PiskelController(piskel);
this.piskelController.init();
this.paletteController = new pskl.controller.PaletteController();
this.paletteController.init();
this.drawingController = new pskl.controller.DrawingController(this.piskelController, this.paletteController, $('#drawing-canvas-container'));
this.drawingController.init();
this.animationController = new pskl.controller.AnimatedPreviewController(this.piskelController, $('#preview-canvas-container'));
this.animationController.init();
this.minimapController = new pskl.controller.MinimapController(this.piskelController, this.animationController, this.drawingController, $('#preview-canvas-container'));
this.minimapController.init();
this.previewsController = new pskl.controller.PreviewFilmController(this.piskelController, $('#preview-list'));
this.previewsController.init();
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
this.layersListController.init();
this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.toolController = new pskl.controller.ToolController();
this.toolController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
this.selectionManager.init();
this.historyService = new pskl.service.HistoryService(this.piskelController);
this.historyService.init();
this.notificationController = new pskl.controller.NotificationController();
this.notificationController.init();
this.localStorageService = new pskl.service.LocalStorageService(this.piskelController);
this.localStorageService.init();
this.imageUploadService = new pskl.service.ImageUploadService();
this.imageUploadService.init();
this.cheatsheetService = new pskl.service.keyboard.CheatsheetService();
this.cheatsheetService.init();
this.savedStatusService = new pskl.service.SavedStatusService(this.piskelController);
this.savedStatusService.init();
if (this.isAppEngineVersion) {
this.storageService = new pskl.service.AppEngineStorageService(this.piskelController);
} else {
this.storageService = new pskl.service.GithubStorageService(this.piskelController);
}
this.storageService.init();
var drawingLoop = new pskl.rendering.DrawingLoop();
drawingLoop.addCallback(this.render, this);
drawingLoop.start();
this.initTooltips_();
if (this.isAppEngineVersion) {
this.finishInitAppEngine_();
} else {
this.finishInitGithub_();
}
},
finishInitGithub_ : function () {
var framesheetId = this.readFramesheetIdFromURL_();
if (framesheetId) {
$.publish(Events.SHOW_NOTIFICATION, [{
"content" : "Loading animation with id : [" + framesheetId + "]"
}]);
this.loadFramesheetFromService(framesheetId);
}
},
finishInitAppEngine_ : function () {
if (pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.piskel) {
pskl.utils.serialization.Deserializer.deserialize(pskl.appEnginePiskelData_.piskel, function (piskel) {
piskel.setDescriptor(pskl.appEnginePiskelData_.descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.appEnginePiskelData_.fps);
});
}
},
isLoggedIn : function () {
return pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.isLoggedIn;
},
initTooltips_ : function () {
$('body').tooltip({
selector: '[rel=tooltip]'
});
},
render : function (delta) {
this.drawingController.render(delta);
this.animationController.render(delta);
this.previewsController.render(delta);
},
readSizeFromURL_ : function () {
var sizeParam = this.readUrlParameter_("size");
var size;
// parameter expected as size=64x48 => size=widthxheight
var parts = sizeParam.split("x");
if (parts && parts.length == 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
var width = parseInt(parts[0], 10),
height = parseInt(parts[1], 10);
size = {
height : Math.min(height, Constants.MAX_HEIGHT),
width : Math.min(width, Constants.MAX_WIDTH)
};
} else {
size = {
height : Constants.DEFAULT.HEIGHT,
width : Constants.DEFAULT.WIDTH
};
}
return size;
},
readFramesheetIdFromURL_ : function () {
return this.readUrlParameter_("frameId");
},
readUrlParameter_ : function (paramName) {
var searchString = window.location.search.substring(1);
var params = searchString.split("&");
for (var i = 0; i < params.length; i++) {
var param = params[i].split("=");
if (param[0] == paramName) {
return window.unescape(param[1]);
}
}
return "";
},
loadFramesheetFromService : function (frameId) {
var xhr = new XMLHttpRequest();
xhr.open('GET', Constants.STATIC.URL.GET + '?l=' + frameId, true);
xhr.responseType = 'text';
xhr.onload = function (e) {
var res = JSON.parse(this.responseText);
pskl.utils.serialization.Deserializer.deserialize(res.framesheet, function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(res.fps);
$.publish(Events.HIDE_NOTIFICATION);
});
};
xhr.onerror = function () {
$.publish(Events.HIDE_NOTIFICATION);
};
xhr.send();
},
store : function (callbacks) {
this.storageService.store(callbacks);
},
getFirstFrameAsPng : function () {
var firstFrame = this.piskelController.getFrameAt(0);
var canvasRenderer = new pskl.rendering.CanvasRenderer(firstFrame, 1);
canvasRenderer.drawTransparentAs('rgba(0,0,0,0)');
var firstFrameCanvas = canvasRenderer.render();
return firstFrameCanvas.toDataURL("image/png");
},
getFramesheetAsPng : function () {
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var framesheetCanvas = renderer.renderAsCanvas();
return framesheetCanvas.toDataURL("image/png");
},
uploadAsSpritesheetPNG : function () {
var imageData = this.getFramesheetAsPng();
this.imageUploadService.upload(imageData, this.openWindow.bind(this));
},
openWindow : function (url) {
var options = [
"dialog=yes", "scrollbars=no", "status=no",
"width=" + this.piskelController.getWidth() * this.piskelController.getFrameCount(),
"height=" + this.piskelController.getHeight()
].join(",");
window.open(url, "piskel-export", options);
}
};
})();

View File

@@ -0,0 +1,77 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.AnimatedPreviewController = function (piskelController, container) {
this.piskelController = piskelController;
this.container = container;
this.elapsedTime = 0;
this.currentIndex = 0;
this.setFPS(Constants.DEFAULT.FPS);
var zoom = this.calculateZoom_();
var frame = this.piskelController.getCurrentFrame();
var renderingOptions = {
"zoom": zoom,
"height" : frame.getHeight() * zoom,
"width" : frame.getWidth() * zoom
};
this.renderer = new pskl.rendering.frame.FrameRenderer(this.container, renderingOptions);
$.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this));
};
ns.AnimatedPreviewController.prototype.init = function () {
// the oninput event won't work on IE10 unfortunately, but at least will provide a
// consistent behavior across all other browsers that support the input type range
// see https://bugzilla.mozilla.org/show_bug.cgi?id=853670
$("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this));
};
ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) {
this.setFPS(parseInt($("#preview-fps")[0].value, 10));
};
ns.AnimatedPreviewController.prototype.setFPS = function (fps) {
this.fps = fps;
$("#preview-fps").val(this.fps);
$("#display-fps").html(this.fps + " FPS");
};
ns.AnimatedPreviewController.prototype.getFPS = function () {
return this.fps;
};
ns.AnimatedPreviewController.prototype.render = function (delta) {
this.elapsedTime += delta;
var index = Math.floor(this.elapsedTime / (1000/this.fps));
if (index != this.currentIndex) {
this.currentIndex = index;
if (!this.piskelController.hasFrameAt(this.currentIndex)) {
this.currentIndex = 0;
this.elapsedTime = 0;
}
this.renderer.render(this.piskelController.getFrameAt(this.currentIndex));
}
};
/**
* Calculate the preview zoom depending on the framesheet size
*/
ns.AnimatedPreviewController.prototype.calculateZoom_ = function () {
var frame = this.piskelController.getCurrentFrame();
var previewSize = 200,
hZoom = previewSize / frame.getHeight(),
wZoom = previewSize / frame.getWidth();
return Math.min(hZoom, wZoom);
};
ns.AnimatedPreviewController.prototype.onFrameSizeChange_ = function () {
var frame = this.piskelController.getCurrentFrame();
var zoom = this.calculateZoom_();
this.renderer.setDisplaySize(frame.getWidth() * zoom, frame.getHeight() * zoom);
this.renderer.setZoom(zoom);
this.renderer.setOffset(0, 0);
};
})();

View File

@@ -0,0 +1,335 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.DrawingController = function (piskelController, paletteController, container) {
/**
* @public
*/
this.piskelController = piskelController;
this.paletteController = paletteController;
/**
* @public
*/
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(piskelController.getCurrentFrame());
/**
* @private
*/
this.container = container;
// TODO(vincz): Store user prefs in a localstorage string ?
var renderingOptions = {
"zoom": this.calculateZoom_(),
"supportGridRendering" : true,
"height" : this.getContainerHeight_(),
"width" : this.getContainerWidth_(),
"xOffset" : 0,
"yOffset" : 0
};
this.overlayRenderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["canvas-overlay"]);
this.renderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["drawing-canvas"]);
this.layersRenderer = new pskl.rendering.layer.LayersRenderer(this.container, renderingOptions, piskelController);
this.compositeRenderer = new pskl.rendering.CompositeRenderer();
this.compositeRenderer
.add(this.overlayRenderer)
.add(this.renderer)
.add(this.layersRenderer);
// State of drawing controller:
this.isClicked = false;
this.previousMousemoveTime = 0;
this.currentToolBehavior = null;
};
ns.DrawingController.prototype.init = function () {
this.initMouseBehavior();
$.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
this.currentToolBehavior = toolBehavior;
this.overlayFrame.clear();
}, this));
$(window).resize($.proxy(this.startResizeTimer_, this));
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
$.subscribe(Events.FRAME_SIZE_CHANGED, $.proxy(this.onFrameSizeChanged_, this));
this.centerColumnWrapperHorizontally_();
};
ns.DrawingController.prototype.initMouseBehavior = function() {
var body = $('body');
this.container.mousedown($.proxy(this.onMousedown_, this));
this.container.mouseenter($.proxy(this.onMouseenter_, this));
this.container.mouseleave($.proxy(this.onMouseleave_, this));
if (pskl.utils.UserAgent.isChrome) {
this.container.on('mousewheel', $.proxy(this.onMousewheel_, this));
} else {
this.container.on('wheel', $.proxy(this.onMousewheel_, this));
}
body.mouseup($.proxy(this.onMouseup_, this));
// Deactivate right click:
body.contextmenu(this.onCanvasContextMenu_);
};
ns.DrawingController.prototype.startResizeTimer_ = function () {
if (this.resizeTimer) {
window.clearInterval(this.resizeTimer);
}
this.resizeTimer = window.setTimeout($.proxy(this.afterWindowResize_, this), 200);
};
ns.DrawingController.prototype.afterWindowResize_ = function () {
var initialWidth = this.compositeRenderer.getDisplaySize().width;
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
this.centerColumnWrapperHorizontally_();
var ratio = this.compositeRenderer.getDisplaySize().width / initialWidth;
var newZoom = ratio * this.compositeRenderer.getZoom();
this.compositeRenderer.setZoom(newZoom);
$.publish(Events.ZOOM_CHANGED);
};
/**
* @private
*/
ns.DrawingController.prototype.onUserSettingsChange_ = function (evt, settingsName, settingsValue) {
if(settingsName == pskl.UserSettings.SHOW_GRID) {
console.warn('DrawingController:onUserSettingsChange_ not implemented !');
}
};
ns.DrawingController.prototype.onFrameSizeChanged_ = function () {
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
this.compositeRenderer.setZoom(this.calculateZoom_());
this.compositeRenderer.setOffset(0, 0);
$.publish(Events.ZOOM_CHANGED);
};
/**
* @private
*/
ns.DrawingController.prototype.onMouseenter_ = function (event) {
this.container.bind('mousemove', $.proxy(this.onMousemove_, this));
};
/**
* @private
*/
ns.DrawingController.prototype.onMouseleave_ = function (event) {
this.container.unbind('mousemove');
this.currentToolBehavior.hideHighlightedPixel(this.overlayFrame);
};
/**
* @private
*/
ns.DrawingController.prototype.onMousedown_ = function (event) {
var frame = this.piskelController.getCurrentFrame();
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
if (event.button === Constants.MIDDLE_BUTTON) {
if (frame.containsPixel(coords.x, coords.y)) {
$.publish(Events.SELECT_PRIMARY_COLOR, [frame.getPixel(coords.x, coords.y)]);
}
} else {
this.isClicked = true;
this.currentToolBehavior.hideHighlightedPixel(this.overlayFrame);
this.currentToolBehavior.applyToolAt(
coords.x,
coords.y,
this.getCurrentColor_(event),
frame,
this.overlayFrame,
event
);
$.publish(Events.LOCALSTORAGE_REQUEST);
}
};
/**
* @private
*/
ns.DrawingController.prototype.onMousemove_ = function (event) {
var currentTime = new Date().getTime();
// Throttling of the mousemove event:
if ((currentTime - this.previousMousemoveTime) > Constants.MOUSEMOVE_THROTTLING ) {
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
if (this.isClicked) {
this.currentToolBehavior.moveToolAt(
coords.x,
coords.y,
this.getCurrentColor_(event),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
event
);
// TODO(vincz): Find a way to move that to the model instead of being at the interaction level.
// Eg when drawing, it may make sense to have it here. However for a non drawing tool,
// you don't need to draw anything when mousemoving and you request useless localStorage.
$.publish(Events.LOCALSTORAGE_REQUEST);
} else {
this.currentToolBehavior.moveUnactiveToolAt(
coords.x,
coords.y,
this.getCurrentColor_(event),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
event
);
}
this.previousMousemoveTime = currentTime;
}
};
ns.DrawingController.prototype.onMousewheel_ = function (jQueryEvent) {
var event = jQueryEvent.originalEvent;
var delta = event.wheelDeltaY || (-2 * event.deltaY);
var currentZoom = this.renderer.getZoom();
var perfectZoom = this.calculateZoom_();
var step = perfectZoom / 10;
if (delta > 0) {
this.compositeRenderer.setZoom(currentZoom + step);
} else if (delta < 0) {
this.compositeRenderer.setZoom(currentZoom - step);
}
$.publish(Events.ZOOM_CHANGED);
};
/**
* @private
*/
ns.DrawingController.prototype.onMouseup_ = function (event) {
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;
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
this.currentToolBehavior.releaseToolAt(
coords.x,
coords.y,
this.getCurrentColor_(event),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
event
);
$.publish(Events.TOOL_RELEASED);
}
};
/**
* @private
*/
ns.DrawingController.prototype.getSpriteCoordinates = function(event) {
return this.renderer.getCoordinates(event.clientX, event.clientY);
};
/**
* @private
*/
ns.DrawingController.prototype.getCurrentColor_ = function (event) {
if(event.button == Constants.RIGHT_BUTTON) {
return this.paletteController.getSecondaryColor();
} else if(event.button == Constants.LEFT_BUTTON) {
return this.paletteController.getPrimaryColor();
} else {
return Constants.DEFAULT_PEN_COLOR;
}
};
/**
* @private
*/
ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
if ($(event.target).closest('#drawing-canvas-container').length) {
// Deactivate right click on drawing canvas only.
event.preventDefault();
event.stopPropagation();
event.cancelBubble = true;
return false;
}
};
ns.DrawingController.prototype.render = function () {
var currentFrame = this.piskelController.getCurrentFrame();
if (!currentFrame.isSameSize(this.overlayFrame)) {
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(currentFrame);
}
this.layersRenderer.render();
this.renderer.render(currentFrame);
this.overlayRenderer.render(this.overlayFrame);
};
/**
* @private
*/
ns.DrawingController.prototype.calculateZoom_ = function() {
var frameHeight = this.piskelController.getCurrentFrame().getHeight(),
frameWidth = this.piskelController.getCurrentFrame().getWidth();
return Math.min(this.getAvailableWidth_()/frameWidth, this.getAvailableHeight_()/frameHeight);
};
ns.DrawingController.prototype.getAvailableHeight_ = function () {
return $('#main-wrapper').height();
};
ns.DrawingController.prototype.getAvailableWidth_ = function () {
var leftSectionWidth = $('.left-column').outerWidth(true),
rightSectionWidth = $('.right-column').outerWidth(true),
toolsContainerWidth = $('#tool-section').outerWidth(true),
settingsContainerWidth = $('#application-action-section').outerWidth(true),
availableWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth - toolsContainerWidth - settingsContainerWidth;
return availableWidth-50;
};
ns.DrawingController.prototype.getContainerHeight_ = function () {
return this.calculateZoom_() * this.piskelController.getCurrentFrame().getHeight();
};
ns.DrawingController.prototype.getContainerWidth_ = function () {
return this.calculateZoom_() * this.piskelController.getCurrentFrame().getWidth();
};
/**
* @private
*/
ns.DrawingController.prototype.centerColumnWrapperHorizontally_ = function() {
var containerHeight = this.getContainerHeight_();
var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - containerHeight) / 2);
$('#column-wrapper').css({
'top': verticalGapInPixel + 'px'
});
};
ns.DrawingController.prototype.getRenderer = function () {
return this.compositeRenderer;
};
ns.DrawingController.prototype.setOffset = function (x, y) {
this.compositeRenderer.setOffset(x, y);
$.publish(Events.ZOOM_CHANGED);
};
})();

View File

@@ -0,0 +1,59 @@
(function () {
var ns = $.namespace('pskl.controller');
ns.LayersListController = function (piskelController) {
this.piskelController = piskelController;
};
ns.LayersListController.prototype.init = function () {
this.layerItemTemplate_ = pskl.utils.Template.get('layer-item-template');
this.rootEl = document.querySelectorAll('.layers-list-container')[0];
this.layersListEl = document.querySelectorAll('.layers-list')[0];
this.rootEl.addEventListener('click', this.onClick_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.renderLayerList_.bind(this));
this.renderLayerList_();
};
ns.LayersListController.prototype.renderLayerList_ = function () {
this.layersListEl.innerHTML = '';
var layers = this.piskelController.getLayers();
layers.forEach(this.addLayerItem.bind(this));
};
ns.LayersListController.prototype.addLayerItem = function (layer) {
var layerItemHtml = pskl.utils.Template.replace(this.layerItemTemplate_, {
layername : layer.getName()
});
var layerItem = pskl.utils.Template.createFromHTML(layerItemHtml);
if (this.piskelController.getCurrentLayer() === layer) {
layerItem.classList.add('current-layer-item');
}
this.layersListEl.insertBefore(layerItem, this.layersListEl.firstChild);
};
ns.LayersListController.prototype.onClick_ = function (evt) {
var el = evt.target || evt.srcElement;
if (el.nodeName == 'BUTTON') {
this.onButtonClick_(el);
} else if (el.nodeName == 'LI') {
var layerName = el.getAttribute('data-layer-name');
this.piskelController.selectLayerByName(layerName);
}
};
ns.LayersListController.prototype.onButtonClick_ = function (button) {
var action = button.getAttribute('data-action');
if (action == 'up') {
this.piskelController.moveLayerUp();
} else if (action == 'down') {
this.piskelController.moveLayerDown();
} else if (action == 'add') {
this.piskelController.createLayer();
} else if (action == 'delete') {
this.piskelController.removeCurrentLayer();
}
};
})();

View File

@@ -0,0 +1,90 @@
(function () {
var ns = $.namespace('pskl.controller');
ns.MinimapController = function (piskelController, animationController, drawingController, container) {
this.piskelController = piskelController;
this.animationController = animationController;
this.drawingController = drawingController;
this.container = container;
this.isClicked = false;
};
ns.MinimapController.prototype.init = function () {
// Create minimap DOM elements
this.cropFrame = document.createElement('DIV');
this.cropFrame.className = 'minimap-crop-frame';
this.cropFrame.style.display = 'none';
$(this.container).append(this.cropFrame);
// Init mouse events
$(this.container).mousedown(this.onMinimapMousedown_.bind(this));
$('body').mousemove(this.onMinimapMousemove_.bind(this));
$('body').mouseup(this.onMinimapMouseup_.bind(this));
$.subscribe(Events.ZOOM_CHANGED, $.proxy(this.renderMinimap_, this));
};
ns.MinimapController.prototype.renderMinimap_ = function () {
var zoomRatio = this.getDrawingAreaZoomRatio_();
if (zoomRatio > 1) {
this.displayCropFrame_(zoomRatio, this.drawingController.getRenderer().getOffset());
} else {
this.hideCropFrame_();
}
};
ns.MinimapController.prototype.displayCropFrame_ = function (ratio, offset) {
this.cropFrame.style.display = 'block';
this.cropFrame.style.top = (offset.y * this.animationController.renderer.getZoom()) + 'px';
this.cropFrame.style.left = (offset.x * this.animationController.renderer.getZoom()) + 'px';
var zoomRatio = this.getDrawingAreaZoomRatio_();
this.cropFrame.style.width = (this.container.width() / zoomRatio) + 'px';
this.cropFrame.style.height = (this.container.height() / zoomRatio) + 'px';
};
ns.MinimapController.prototype.hideCropFrame_ = function () {
this.cropFrame.style.display = 'none';
};
ns.MinimapController.prototype.onMinimapMousemove_ = function (evt) {
if (this.isClicked) {
if (this.getDrawingAreaZoomRatio_() > 1) {
var coords = this.getCoordinatesCenteredAround_(evt.clientX, evt.clientY);
this.drawingController.setOffset(coords.x, coords.y);
}
}
};
ns.MinimapController.prototype.onMinimapMousedown_ = function (evt) {
this.isClicked = true;
};
ns.MinimapController.prototype.onMinimapMouseup_ = function (evt) {
this.isClicked = false;
};
ns.MinimapController.prototype.getCoordinatesCenteredAround_ = function (x, y) {
var frameCoords = this.animationController.renderer.getCoordinates(x, y);
var zoomRatio = this.getDrawingAreaZoomRatio_();
var frameWidth = this.piskelController.getCurrentFrame().getWidth();
var frameHeight = this.piskelController.getCurrentFrame().getHeight();
var width = frameWidth / zoomRatio;
var height = frameHeight / zoomRatio;
return {
x : frameCoords.x - (width/2),
y : frameCoords.y - (height/2)
};
};
ns.MinimapController.prototype.getDrawingAreaZoomRatio_ = function () {
var drawingAreaZoom = this.drawingController.getRenderer().getZoom();
var drawingAreaFullHeight = this.piskelController.getCurrentFrame().getHeight() * drawingAreaZoom;
var zoomRatio = drawingAreaFullHeight / this.drawingController.getRenderer().getDisplaySize().height;
return zoomRatio;
};
})();

View File

@@ -0,0 +1,41 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.NotificationController = function () {};
/**
* @public
*/
ns.NotificationController.prototype.init = function() {
$.subscribe(Events.SHOW_NOTIFICATION, $.proxy(this.displayMessage_, this));
$.subscribe(Events.HIDE_NOTIFICATION, $.proxy(this.removeMessage_, this));
};
/**
* @private
*/
ns.NotificationController.prototype.displayMessage_ = function (evt, messageInfo) {
this.removeMessage_();
var message = document.createElement('div');
message.id = "user-message";
message.className = "user-message";
message.innerHTML = messageInfo.content;
message.innerHTML = message.innerHTML + "<div title='Close message' class='close'>x</div>";
document.body.appendChild(message);
$(message).find(".close").click($.proxy(this.removeMessage_, this));
if(messageInfo.behavior) {
messageInfo.behavior(message);
}
};
/**
* @private
*/
ns.NotificationController.prototype.removeMessage_ = function (evt) {
var message = $("#user-message");
if (message.length) {
message.remove();
}
};
})();

View File

@@ -0,0 +1,146 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PaletteController = function () {
this.primaryColor = Constants.DEFAULT_PEN_COLOR;
this.secondaryColor = Constants.TRANSPARENT_COLOR;
};
/**
* @public
*/
ns.PaletteController.prototype.init = function() {
var transparentColorPalette = $(".palette-color[data-color=TRANSPARENT]");
transparentColorPalette.mouseup($.proxy(this.onPaletteColorClick_, this));
$.subscribe(Events.SELECT_PRIMARY_COLOR, this.onColorSelected_.bind(this, {isPrimary:true}));
$.subscribe(Events.SELECT_SECONDARY_COLOR, this.onColorSelected_.bind(this, {isPrimary:false}));
pskl.app.shortcutService.addShortcut('X', this.swapColors.bind(this));
pskl.app.shortcutService.addShortcut('D', this.resetColors.bind(this));
var spectrumCfg = {
showPalette: true,
showButtons: false,
palette: [
['rgba(0,0,0,0)']
],
clickoutFiresChange : true,
beforeShow : function(tinycolor) {
tinycolor.setAlpha(1);
}
};
// Initialize colorpickers:
var colorPicker = $('#color-picker');
colorPicker.spectrum($.extend({color: Constants.DEFAULT_PEN_COLOR}, spectrumCfg));
colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this));
this.setTitleOnPicker_(Constants.DEFAULT_PEN_COLOR, colorPicker);
var secondaryColorPicker = $('#secondary-color-picker');
secondaryColorPicker.spectrum($.extend({color: Constants.TRANSPARENT_COLOR}, spectrumCfg));
secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this));
this.setTitleOnPicker_(Constants.TRANSPARENT_COLOR, secondaryColorPicker);
var swapColorsIcon = $('.swap-colors-icon');
swapColorsIcon.click(this.swapColors.bind(this));
};
/**
* @private
*/
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
var inputPicker = $(evt.target);
if(evt.data.isPrimary) {
this.setPrimaryColor(inputPicker.val());
} else {
this.setSecondaryColor(inputPicker.val());
}
};
/**
* @private
*/
ns.PaletteController.prototype.onColorSelected_ = function(args, evt, color) {
var inputPicker = $(evt.target);
if(args.isPrimary) {
this.setPrimaryColor(color);
} else {
this.setSecondaryColor(color);
}
};
ns.PaletteController.prototype.setPrimaryColor = function (color) {
this.primaryColor = color;
this.updateColorPicker_(color, $('#color-picker'));
};
ns.PaletteController.prototype.setSecondaryColor = function (color) {
this.secondaryColor = color;
this.updateColorPicker_(color, $('#secondary-color-picker'));
};
ns.PaletteController.prototype.getPrimaryColor = function () {
return this.primaryColor;
};
ns.PaletteController.prototype.getSecondaryColor = function () {
return this.secondaryColor;
};
ns.PaletteController.prototype.swapColors = function () {
var primaryColor = this.getPrimaryColor();
this.setPrimaryColor(this.getSecondaryColor());
this.setSecondaryColor(primaryColor);
};
ns.PaletteController.prototype.resetColors = function () {
this.setPrimaryColor(Constants.DEFAULT_PEN_COLOR);
this.setSecondaryColor(Constants.TRANSPARENT_COLOR);
};
/**
* @private
*/
ns.PaletteController.prototype.onPaletteColorClick_ = function (event) {
var selectedColor = $(event.target).data("color");
var isLeftClick = (event.which == 1);
var isRightClick = (event.which == 3);
if (isLeftClick) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [selectedColor]);
} else if (isRightClick) {
$.publish(Events.SECONDARY_COLOR_SELECTED, [selectedColor]);
}
};
/**
* @private
*/
ns.PaletteController.prototype.updateColorPicker_ = function (color, colorPicker) {
if (color == Constants.TRANSPARENT_COLOR) {
// We can set the current palette color to transparent.
// You can then combine this transparent color with an advanced
// tool for customized deletions.
// Eg: bucket + transparent: Delete a colored area
// Stroke + transparent: hollow out the equivalent of a stroke
// The colorpicker can't be set to a transparent state.
// We set its background to white and insert the
// string "TRANSPARENT" to mimic this state:
colorPicker.spectrum("set", Constants.TRANSPARENT_COLOR);
colorPicker.val(Constants.TRANSPARENT_COLOR);
} else {
colorPicker.spectrum("set", color);
}
this.setTitleOnPicker_(color, colorPicker);
};
ns.PaletteController.prototype.setTitleOnPicker_ = function (title, colorPicker) {
var spectrumInputSelector = '.sp-replacer';
colorPicker.next(spectrumInputSelector).attr('title', title);
};
})();

View File

@@ -0,0 +1,227 @@
(function () {
var ns = $.namespace('pskl.controller');
ns.PiskelController = function (piskel) {
if (piskel) {
this.setPiskel(piskel);
} else {
throw 'A piskel instance is mandatory for instanciating PiskelController';
}
};
ns.PiskelController.prototype.setPiskel = function (piskel) {
this.piskel = piskel;
this.currentLayerIndex = 0;
this.currentFrameIndex = 0;
this.layerIdCounter = 1;
$.publish(Events.FRAME_SIZE_CHANGED);
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.init = function () {
pskl.app.shortcutService.addShortcut('up', this.selectPreviousFrame.bind(this));
pskl.app.shortcutService.addShortcut('down', this.selectNextFrame.bind(this));
pskl.app.shortcutService.addShortcut('n', this.addFrameAtCurrentIndex.bind(this));
pskl.app.shortcutService.addShortcut('shift+n', this.duplicateCurrentFrame.bind(this));
};
ns.PiskelController.prototype.getHeight = function () {
return this.piskel.getHeight();
};
ns.PiskelController.prototype.getWidth = function () {
return this.piskel.getWidth();
};
/**
* TODO : this should be removed
* FPS should be stored in the Piskel model and not in the
* animationController
* 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.animationController.getFPS();
};
ns.PiskelController.prototype.getLayers = function () {
return this.piskel.getLayers();
};
ns.PiskelController.prototype.getCurrentLayer = function () {
return this.piskel.getLayerAt(this.currentLayerIndex);
};
ns.PiskelController.prototype.getCurrentFrame = function () {
var layer = this.getCurrentLayer();
return layer.getFrameAt(this.currentFrameIndex);
};
ns.PiskelController.prototype.getFrameAt = function (index) {
var frames = this.getLayers().map(function (l) {
return l.getFrameAt(index);
});
return pskl.utils.FrameUtils.merge(frames);
};
ns.PiskelController.prototype.hasFrameAt = function (index) {
return !!this.getCurrentLayer().getFrameAt(index);
};
ns.PiskelController.prototype.addFrame = function () {
this.addFrameAt(this.getFrameCount());
};
ns.PiskelController.prototype.addFrameAtCurrentIndex = function () {
this.addFrameAt(this.currentFrameIndex + 1);
};
ns.PiskelController.prototype.addFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.addFrameAt(this.createEmptyFrame_(), index);
}.bind(this));
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.createEmptyFrame_ = function () {
var w = this.piskel.getWidth(), h = this.piskel.getHeight();
return new pskl.model.Frame(w, h);
};
ns.PiskelController.prototype.removeFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.removeFrameAt(index);
});
// Current frame index is impacted if the removed frame was before the current frame
if (this.currentFrameIndex >= index && this.currentFrameIndex > 0) {
this.setCurrentFrameIndex(this.currentFrameIndex - 1);
}
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.duplicateCurrentFrame = function () {
this.duplicateFrameAt(this.currentFrameIndex);
};
ns.PiskelController.prototype.duplicateFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.duplicateFrameAt(index);
});
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.moveFrame(fromIndex, toIndex);
});
};
ns.PiskelController.prototype.getFrameCount = function () {
var layer = this.piskel.getLayerAt(0);
return layer.length();
};
ns.PiskelController.prototype.setCurrentFrameIndex = function (index) {
this.currentFrameIndex = index;
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.selectNextFrame = function () {
var nextIndex = this.currentFrameIndex + 1;
if (nextIndex < this.getFrameCount()) {
this.setCurrentFrameIndex(nextIndex);
}
};
ns.PiskelController.prototype.selectPreviousFrame = function () {
var nextIndex = this.currentFrameIndex - 1;
if (nextIndex >= 0) {
this.setCurrentFrameIndex(nextIndex);
}
};
ns.PiskelController.prototype.setCurrentLayerIndex = function (index) {
this.currentLayerIndex = index;
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.selectLayer = function (layer) {
var index = this.getLayers().indexOf(layer);
if (index != -1) {
this.setCurrentLayerIndex(index);
}
};
ns.PiskelController.prototype.selectLayerByName = function (name) {
if (this.hasLayerForName_(name)) {
var layer = this.piskel.getLayersByName(name)[0];
this.selectLayer(layer);
}
};
ns.PiskelController.prototype.generateLayerName_ = function () {
var name = "Layer " + this.layerIdCounter;
while (this.hasLayerForName_(name)) {
this.layerIdCounter++;
name = "Layer " + this.layerIdCounter;
}
return name;
};
ns.PiskelController.prototype.createLayer = function (name) {
if (!name) {
name = this.generateLayerName_();
}
if (!this.hasLayerForName_(name)) {
var layer = new pskl.model.Layer(name);
for (var i = 0 ; i < this.getFrameCount() ; i++) {
layer.addFrame(this.createEmptyFrame_());
}
this.piskel.addLayer(layer);
this.setCurrentLayerIndex(this.piskel.getLayers().length - 1);
} else {
throw 'Layer name should be unique';
}
};
ns.PiskelController.prototype.hasLayerForName_ = function (name) {
return this.piskel.getLayersByName(name).length > 0;
};
ns.PiskelController.prototype.moveLayerUp = function () {
var layer = this.getCurrentLayer();
this.piskel.moveLayerUp(layer);
this.selectLayer(layer);
};
ns.PiskelController.prototype.moveLayerDown = function () {
var layer = this.getCurrentLayer();
this.piskel.moveLayerDown(layer);
this.selectLayer(layer);
};
ns.PiskelController.prototype.removeCurrentLayer = function () {
if (this.getLayers().length > 1) {
var layer = this.getCurrentLayer();
this.piskel.removeLayer(layer);
this.setCurrentLayerIndex(0);
}
};
ns.PiskelController.prototype.serialize = function () {
return pskl.utils.Serializer.serializePiskel(this.piskel);
};
ns.PiskelController.prototype.load = function (data) {
this.deserialize(JSON.stringify(data));
};
})();

View File

@@ -0,0 +1,224 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PreviewFilmController = function (piskelController, container) {
this.piskelController = piskelController;
this.container = container;
this.refreshZoom_();
this.redrawFlag = true;
};
ns.PreviewFilmController.prototype.init = function() {
$.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.flagForRedraw_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.refreshZoom_.bind(this));
$('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this));
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.addFrame = function () {
this.piskelController.addFrame();
this.piskelController.setCurrentFrameIndex(this.piskelController.getFrameCount() - 1);
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.flagForRedraw_ = function () {
this.redrawFlag = true;
};
ns.PreviewFilmController.prototype.refreshZoom_ = function () {
this.zoom = this.calculateZoom_();
};
ns.PreviewFilmController.prototype.render = function () {
if (this.redrawFlag) {
// TODO(vincz): Full redraw on any drawing modification, optimize.
this.createPreviews_();
this.redrawFlag = false;
}
};
ns.PreviewFilmController.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 overflowTop = false,
overflowBottom = false;
if (scrollerHeight < scrollerContentHeight) {
if (scrollTop > treshold) {
overflowTop = true;
}
var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight;
if (scrollBottom > treshold) {
overflowBottom = true;
}
}
var wrapper = $('#preview-list-wrapper');
wrapper.toggleClass('top-overflow-visible', overflowTop);
wrapper.toggleClass('bottom-overflow-visible', overflowBottom);
};
ns.PreviewFilmController.prototype.createPreviews_ = function () {
this.container.html("");
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
$(".tooltip").remove();
var frameCount = this.piskelController.getFrameCount();
for (var i = 0, l = frameCount; i < l ; i++) {
this.container.append(this.createPreviewTile_(i));
}
// Append 'new empty frame' button
var newFrameButton = document.createElement("div");
newFrameButton.id = "add-frame-action";
newFrameButton.className = "add-frame-action";
newFrameButton.innerHTML = "<p class='label'>Add new frame</p>";
this.container.append(newFrameButton);
$(newFrameButton).click(this.addFrame.bind(this));
var needDragndropBehavior = (frameCount > 1);
if(needDragndropBehavior) {
this.initDragndropBehavior_();
}
this.updateScrollerOverflows();
};
/**
* @private
*/
ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () {
$("#preview-list").sortable({
placeholder: "preview-tile-drop-proxy",
update: $.proxy(this.onUpdate_, this),
items: ".preview-tile"
});
$("#preview-list").disableSelection();
};
/**
* @private
*/
ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) {
var originFrameId = parseInt(ui.item.data("tile-number"), 10);
var targetInsertionId = $('.preview-tile').index(ui.item);
this.piskelController.moveFrame(originFrameId, targetInsertionId);
this.piskelController.setCurrentFrameIndex(targetInsertionId);
// TODO(grosbouddha): move localstorage request to the model layer?
$.publish(Events.LOCALSTORAGE_REQUEST);
};
/**
* @private
* TODO(vincz): clean this giant rendering function & remove listeners.
*/
ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) {
var currentFrame = this.piskelController.getCurrentLayer().getFrameAt(tileNumber);
var previewTileRoot = document.createElement("li");
var classname = "preview-tile";
previewTileRoot.setAttribute("data-tile-number", tileNumber);
if (this.piskelController.getCurrentFrame() == currentFrame) {
classname += " selected";
}
previewTileRoot.className = classname;
var canvasContainer = document.createElement("div");
canvasContainer.className = "canvas-container";
var canvasBackground = document.createElement("div");
canvasBackground.className = "canvas-background";
canvasContainer.appendChild(canvasBackground);
previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber));
var cloneFrameButton = document.createElement("button");
cloneFrameButton.setAttribute('rel', 'tooltip');
cloneFrameButton.setAttribute('data-placement', 'right');
cloneFrameButton.setAttribute('title', 'Duplicate this frame');
cloneFrameButton.className = "tile-overlay duplicate-frame-action";
previewTileRoot.appendChild(cloneFrameButton);
cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber));
// TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim
// is to make this update function (#createPreviewTile) less aggressive.
var renderingOptions = {
"zoom" : this.zoom,
"height" : this.piskelController.getCurrentFrame().getHeight() * this.zoom,
"width" : this.piskelController.getCurrentFrame().getWidth() * this.zoom
};
var currentFrameRenderer = new pskl.rendering.frame.FrameRenderer($(canvasContainer), renderingOptions, ["tile-view"]);
currentFrameRenderer.render(currentFrame);
previewTileRoot.appendChild(canvasContainer);
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.className = "tile-overlay delete-frame-action";
deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber));
previewTileRoot.appendChild(deleteButton);
// Add 'dragndrop handle'.
var dndHandle = document.createElement("div");
dndHandle.className = "tile-overlay dnd-action";
previewTileRoot.appendChild(dndHandle);
}
var tileCount = document.createElement("div");
tileCount.className = "tile-overlay tile-count";
tileCount.innerHTML = tileNumber + 1;
previewTileRoot.appendChild(tileCount);
return previewTileRoot;
};
ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) {
// has not class tile-action:
if(!evt.target.classList.contains('tile-overlay')) {
this.piskelController.setCurrentFrameIndex(index);
}
};
ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) {
this.piskelController.removeFrameAt(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) {
this.piskelController.duplicateFrameAt(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.piskelController.setCurrentFrameIndex(index + 1);
this.updateScrollerOverflows();
};
/**
* Calculate the preview zoom depending on the piskel size
*/
ns.PreviewFilmController.prototype.calculateZoom_ = function () {
var curFrame = this.piskelController.getCurrentFrame(),
frameHeight = curFrame.getHeight(),
frameWidth = curFrame.getWidth(),
maxFrameDim = Math.max(frameWidth, frameHeight);
var previewHeight = Constants.PREVIEW_FILM_SIZE * frameHeight / maxFrameDim;
var previewWidth = Constants.PREVIEW_FILM_SIZE * frameWidth / maxFrameDim;
return pskl.PixelUtils.calculateZoom(previewHeight, previewWidth, frameHeight, frameWidth) || 1;
};
})();

View File

@@ -0,0 +1,145 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.ToolController = function () {
var toDescriptor = function (id, shortcut, instance) {
return {id:id, shortcut:shortcut, instance:instance};
};
this.tools = [
toDescriptor('simplePen', 'P', new pskl.drawingtools.SimplePen()),
toDescriptor('verticalMirrorPen', 'V', new pskl.drawingtools.VerticalMirrorPen()),
toDescriptor('eraser', 'E', new pskl.drawingtools.Eraser()),
toDescriptor('paintBucket', 'B', new pskl.drawingtools.PaintBucket()),
toDescriptor('stroke', 'L', new pskl.drawingtools.Stroke()),
toDescriptor('rectangle', 'R', new pskl.drawingtools.Rectangle()),
toDescriptor('circle', 'C', new pskl.drawingtools.Circle()),
toDescriptor('move', 'M', new pskl.drawingtools.Move()),
toDescriptor('rectangleSelect', 'S', new pskl.drawingtools.RectangleSelect()),
toDescriptor('shapeSelect', 'Z', new pskl.drawingtools.ShapeSelect()),
toDescriptor('colorPicker', 'O', new pskl.drawingtools.ColorPicker())
];
this.currentSelectedTool = this.tools[0];
this.previousSelectedTool = this.tools[0];
};
/**
* @public
*/
ns.ToolController.prototype.init = function() {
this.createToolsDom_();
this.addKeyboardShortcuts_();
// Initialize tool:
// Set SimplePen as default selected tool:
this.selectTool_(this.tools[0]);
// Activate listener on tool panel:
$("#tool-section").mousedown($.proxy(this.onToolIconClicked_, this));
};
/**
* @private
*/
ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
var stage = $("body");
var previousSelectedToolClass = stage.data("selected-tool-class");
if(previousSelectedToolClass) {
stage.removeClass(previousSelectedToolClass);
stage.removeClass(pskl.drawingtools.Move.TOOL_ID);
}
stage.addClass(tool.instance.toolId);
stage.data("selected-tool-class", tool.instance.toolId);
};
/**
* @private
*/
ns.ToolController.prototype.selectTool_ = function(tool) {
this.currentSelectedTool = tool;
this.activateToolOnStage_(this.currentSelectedTool);
var selectedToolElement = $('#tool-section .tool-icon.selected');
var toolElement = $('[data-tool-id=' + tool.instance.toolId + ']');
selectedToolElement.removeClass('selected');
toolElement.addClass('selected');
$.publish(Events.TOOL_SELECTED, [tool.instance]);
};
/**
* @private
*/
ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
var target = $(evt.target);
var clickedTool = target.closest(".tool-icon");
if(clickedTool.length) {
var toolId = clickedTool.data().toolId;
var tool = this.getToolById_(toolId);
if (tool) {
this.selectTool_(tool);
}
}
};
ns.ToolController.prototype.onKeyboardShortcut_ = function(charkey) {
for (var i = 0 ; i < this.tools.length ; i++) {
var tool = this.tools[i];
if (tool.shortcut.toLowerCase() === charkey.toLowerCase()) {
this.selectTool_(tool);
}
}
};
ns.ToolController.prototype.getToolById_ = function (toolId) {
for(var i = 0 ; i < this.tools.length ; i++) {
var tool = this.tools[i];
if (tool.instance.toolId == toolId) {
return tool;
}
}
return null;
};
/**
* @private
*/
ns.ToolController.prototype.createToolsDom_ = function() {
var toolMarkup = '';
for(var i = 0 ; i < this.tools.length ; i++) {
toolMarkup += this.getToolMarkup_(this.tools[i]);
}
$('#tools-container').html(toolMarkup);
};
/**
* @private
*/
ns.ToolController.prototype.getToolMarkup_ = function(tool) {
var toolId = tool.instance.toolId;
var classList = ['tool-icon', toolId];
if (this.currentSelectedTool == tool) {
classList.push('selected');
}
var tpl = pskl.utils.Template.get('drawing-tool-item-template');
return pskl.utils.Template.replace(tpl, {
cssclass : classList.join(' '),
toolid : toolId,
title : this.getTooltipText_(tool)
});
};
ns.ToolController.prototype.getTooltipText_ = function (tool) {
return tool.instance.helpText + ' (' + tool.shortcut + ')';
};
ns.ToolController.prototype.addKeyboardShortcuts_ = function () {
for(var i = 0 ; i < this.tools.length ; i++) {
pskl.app.shortcutService.addShortcut(this.tools[i].shortcut, this.onKeyboardShortcut_.bind(this));
}
};
})();

View File

@@ -0,0 +1,43 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
ns.ApplicationSettingsController = function () {};
/**
* @public
*/
ns.ApplicationSettingsController.prototype.init = function() {
// Highlight selected background picker:
var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
$('#background-picker-wrapper')
.find('.background-picker[data-background-class=' + backgroundClass + ']')
.addClass('selected');
// Initial state for grid display:
var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID);
$('#show-grid').prop('checked', show_grid);
// Handle grid display changes:
$('#show-grid').change(this.onShowGridClick.bind(this));
// Handle canvas background changes:
$('#background-picker-wrapper').click(this.onBackgroundClick.bind(this));
};
ns.ApplicationSettingsController.prototype.onShowGridClick = function (evt) {
var checked = $('#show-grid').prop('checked');
pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked);
};
ns.ApplicationSettingsController.prototype.onBackgroundClick = function (evt) {
var target = $(evt.target).closest('.background-picker');
if (target.length) {
var backgroundClass = target.data('background-class');
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass);
$('.background-picker').removeClass('selected');
target.addClass('selected');
}
};
})();

View File

@@ -0,0 +1,159 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
var URL_MAX_LENGTH = 60;
ns.GifExportController = function (piskelController) {
this.piskelController = piskelController;
};
/**
* List of Resolutions applicable for Gif export
* @static
* @type {Array} array of Objects {zoom:{Number}, default:{Boolean}}
*/
ns.GifExportController.RESOLUTIONS = [
{
'zoom' : 1
},{
'zoom' : 5
},{
'zoom' : 10,
'default' : true
},{
'zoom' : 20
}
];
ns.GifExportController.prototype.init = function () {
this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template");
this.uploadStatusContainerEl = document.querySelectorAll(".gif-upload-status")[0];
this.previewContainerEl = document.querySelectorAll(".gif-export-preview")[0];
this.radioGroupEl = document.querySelectorAll(".gif-export-radio-group")[0];
this.uploadForm = $("[name=gif-export-upload-form]");
this.uploadForm.submit(this.onUploadFormSubmit_.bind(this));
this.createRadioElements_();
};
ns.GifExportController.prototype.onUploadFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
var selectedZoom = this.getSelectedZoom_(),
fps = this.piskelController.getFPS(),
zoom = selectedZoom;
this.renderAsImageDataAnimatedGIF(zoom, fps, this.onGifRenderingCompleted_.bind(this));
};
ns.GifExportController.prototype.onGifRenderingCompleted_ = function (imageData) {
this.updatePreview_(imageData);
this.previewContainerEl.classList.add("preview-upload-ongoing");
pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this));
};
ns.GifExportController.prototype.onImageUploadCompleted_ = function (imageUrl) {
this.updatePreview_(imageUrl);
this.updateStatus_(imageUrl);
this.previewContainerEl.classList.remove("preview-upload-ongoing");
};
ns.GifExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<div><img style='max-width:240px;' src='"+src+"'/></div>";
};
ns.GifExportController.prototype.getSelectedZoom_ = function () {
var radiosColl = this.uploadForm.get(0).querySelectorAll("[name=gif-zoom-level]"),
radios = Array.prototype.slice.call(radiosColl,0);
var selectedRadios = radios.filter(function(radio) {return !!radio.checked;});
if (selectedRadios.length == 1) {
return selectedRadios[0].value;
} else {
throw "Unexpected error when retrieving selected zoom";
}
};
ns.GifExportController.prototype.createRadioElements_ = function () {
var resolutions = ns.GifExportController.RESOLUTIONS;
for (var i = 0 ; i < resolutions.length ; i++) {
var radio = this.createRadioForResolution_(resolutions[i]);
this.radioGroupEl.appendChild(radio);
}
};
ns.GifExportController.prototype.createRadioForResolution_ = function (resolution) {
var zoom = resolution.zoom;
var label = zoom*this.piskelController.getWidth() + "x" + zoom*this.piskelController.getHeight();
var value = zoom;
var radioHTML = pskl.utils.Template.replace(this.radioTemplate_, {value : value, label : label});
var radioEl = pskl.utils.Template.createFromHTML(radioHTML);
if (resolution['default']) {
var input = radioEl.getElementsByTagName("input")[0];
input.setAttribute("checked", "checked");
}
return radioEl;
};
ns.GifExportController.prototype.blobToBase64_ = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
cb(dataUrl);
};
reader.readAsDataURL(blob);
};
ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) {
var gif = new window.GIF({
workers: 2,
quality: 10,
width: this.piskelController.getWidth()*zoom,
height: this.piskelController.getHeight()*zoom
});
for (var i = 0; i < this.piskelController.getFrameCount(); i++) {
var frame = this.piskelController.getFrameAt(i);
var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, zoom);
var canvas = canvasRenderer.render();
gif.addFrame(canvas.getContext('2d'), {
delay: 1000 / fps
});
}
gif.on('finished', function(blob) {
this.blobToBase64_(blob, cb);
}.bind(this));
gif.render();
};
// FIXME : HORRIBLE COPY/PASTA
ns.GifExportController.prototype.updateStatus_ = function (imageUrl, error) {
if (imageUrl) {
var linkTpl = "<a class='image-link' href='{{link}}' target='_blank'>{{shortLink}}</a>";
var linkHtml = pskl.utils.Template.replace(linkTpl, {
link : imageUrl,
shortLink : this.shorten_(imageUrl, URL_MAX_LENGTH, '...')
});
this.uploadStatusContainerEl.innerHTML = 'Your image is now available at : ' + linkHtml;
} else {
// FIXME : Should display error message instead
}
};
ns.GifExportController.prototype.shorten_ = function (url, maxLength, suffix) {
if (url.length > maxLength) {
url = url.substring(0, maxLength);
url += suffix;
}
return url;
};
})();

View File

@@ -0,0 +1,172 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = 'No file selected ...';
var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) {
this.piskelController = piskelController;
this.importedImage_ = null;
};
ns.ImportController.prototype.init = function () {
this.importForm = $('[name=import-form]');
this.hiddenFileInput = $('[name=file-upload-input]');
this.fileInputButton = $('.file-input-button');
this.fileInputStatus = $('.file-input-status');
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
this.importPreview = $('.import-section-preview');
this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]');
this.smoothResize = $('[name=smooth-resize-checkbox]');
this.submitButton = $('[name=import-submit]');
this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));
this.fileInputButton.click(this.onFileInputClick_.bind(this));
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
};
ns.ImportController.prototype.reset_ = function () {
this.importForm.get(0).reset();
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
ns.ImportController.prototype.onResizeInputKeyUp_ = function (from, evt) {
if (this.importedImage_) {
this.synchronizeResizeFields_(evt.target.value, from);
}
};
ns.ImportController.prototype.synchronizeResizeFields_ = function (value, from) {
value = parseInt(value, 10);
if (isNaN(value)) {
value = 0;
}
var height = this.importedImage_.height, width = this.importedImage_.width;
if (from === 'width') {
this.resizeHeight.val(Math.round(value * height / width));
} else {
this.resizeWidth.val(Math.round(value * width / height));
}
};
ns.ImportController.prototype.onImportFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.importImageToPiskel_();
};
ns.ImportController.prototype.onFileUploadChange_ = function (evt) {
this.importFromFile_();
};
ns.ImportController.prototype.onFileInputClick_ = function (evt) {
this.hiddenFileInput.click();
};
ns.ImportController.prototype.importFromFile_ = function () {
var files = this.hiddenFileInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isImage_(file)) {
this.readImageFile_(file);
this.enableDisabledSections_();
} else {
this.reset_();
throw 'File is not an image : ' + file.type;
}
}
};
ns.ImportController.prototype.enableDisabledSections_ = function () {
this.resizeWidth.removeAttr('disabled');
this.resizeHeight.removeAttr('disabled');
this.smoothResize.removeAttr('disabled');
this.submitButton.removeAttr('disabled');
this.fileInputButton.removeClass('button-primary');
this.fileInputButton.blur();
$('.import-section-disabled').removeClass('import-section-disabled');
};
ns.ImportController.prototype.readImageFile_ = function (imageFile) {
pskl.utils.FileUtils.readFile(imageFile, this.processImageSource_.bind(this));
};
/**
* Create an image from the given source (url or data-url), and onload forward to onImageLoaded
* TODO : should be a generic utility method, should take a callback
* @param {String} imageSource url or data-url, will be used as src for the image
*/
ns.ImportController.prototype.processImageSource_ = function (imageSource) {
this.importedImage_ = new Image();
this.importedImage_.onload = this.onImageLoaded_.bind(this);
this.importedImage_.src = imageSource;
};
ns.ImportController.prototype.onImageLoaded_ = function (evt) {
var w = this.importedImage_.width,
h = this.importedImage_.height;
var filePath = this.hiddenFileInput.val();
var fileName = this.extractFileNameFromPath_(filePath);
this.fileInputStatus.html(fileName);
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.importPreview.width('auto');
this.importPreview.html('');
this.importPreview.append(this.createImagePreview_());
};
ns.ImportController.prototype.createImagePreview_ = function () {
var image = document.createElement('IMG');
image.src = this.importedImage_.src;
image.setAttribute('height', PREVIEW_HEIGHT);
return image;
};
ns.ImportController.prototype.extractFileNameFromPath_ = function (path) {
var parts = [];
if (path.indexOf('/') !== -1) {
parts = path.split('/');
} else if (path.indexOf('\\') !== -1) {
parts = path.split('\\');
} else {
parts = [path];
}
return parts[parts.length-1];
};
ns.ImportController.prototype.importImageToPiskel_ = function () {
if (this.importedImage_) {
if (window.confirm('You are about to create a new Piskel, unsaved changes will be lost.')) {
var w = this.resizeWidth.val(),
h = this.resizeHeight.val(),
smoothing = !!this.smoothResize.prop('checked');
var image = pskl.utils.ImageResizer.resize(this.importedImage_, w, h, smoothing);
var frame = pskl.utils.FrameUtils.createFromImage(image);
var layer = pskl.model.Layer.fromFrames('Layer 1', [frame]);
var descriptor = new pskl.model.piskel.Descriptor('Imported piskel', '');
var piskel = pskl.model.Piskel.fromLayers([layer], descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
this.reset_();
}
}
};
ns.ImportController.prototype.isImage_ = function (file) {
return file.type.indexOf('image') === 0;
};
})();

View File

@@ -0,0 +1,70 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
ns.LocalStorageController = function () {};
/**
* @public
*/
ns.LocalStorageController.prototype.init = function() {
this.localStorageItemTemplate_ = pskl.utils.Template.get("local-storage-item-template");
this.service_ = pskl.app.localStorageService;
this.piskelsList = $('.local-piskels-list');
this.fillLocalPiskelsList_();
this.piskelsList.click(this.onPiskelsListClick_.bind(this));
};
ns.LocalStorageController.prototype.onPiskelsListClick_ = function (evt) {
var action = evt.target.getAttribute('data-action');
var name = evt.target.getAttribute('data-name');
if (action === 'load') {
if (window.confirm('This will erase your current piskel. Continue ?')) {
this.service_.load(name);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
}
} else if (action === 'delete') {
if (window.confirm('This will permanently DELETE this piskel from your computer. Continue ?')) {
this.service_.remove(name);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
}
}
};
ns.LocalStorageController.prototype.fillLocalPiskelsList_ = function () {
var html = "";
var keys = this.service_.getKeys();
var pad = function (num) {
if (num < 10) {
return "0" + num;
} else {
return "" + num;
}
};
keys.sort(function (k1, k2) {
if (k1.date < k2.date) {return 1;}
if (k1.date > k2.date) {return -1;}
return 0;
});
keys.forEach((function (key) {
var date = new Date(key.date);
var formattedDate = pskl.utils.Template.replace("{{Y}}/{{M}}/{{D}} {{H}}:{{m}}", {
Y : date.getFullYear(),
M : pad(date.getMonth() + 1),
D : pad(date.getDate()),
H : pad(date.getHours()),
m : pad(date.getMinutes())
});
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {name : key.name, date : formattedDate});
}).bind(this));
var tableBody_ = this.piskelsList.get(0).tBodies[0];
tableBody_.innerHTML = html;
};
})();

View File

@@ -0,0 +1,63 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
var URL_MAX_LENGTH = 60;
ns.PngExportController = function (piskelController) {
this.piskelController = piskelController;
};
ns.PngExportController.prototype.init = function () {
this.previewContainerEl = document.querySelectorAll(".png-export-preview")[0];
this.uploadStatusContainerEl = document.querySelectorAll(".png-upload-status")[0];
this.uploadForm = $("[name=png-export-upload-form]");
this.uploadForm.submit(this.onUploadFormSubmit_.bind(this));
this.updatePreview_(this.getFramesheetAsBase64Png());
};
ns.PngExportController.prototype.onUploadFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.previewContainerEl.classList.add("preview-upload-ongoing");
pskl.app.imageUploadService.upload(this.getFramesheetAsBase64Png(), this.onImageUploadCompleted_.bind(this));
};
ns.PngExportController.prototype.getFramesheetAsBase64Png = function () {
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var framesheetCanvas = renderer.renderAsCanvas();
return framesheetCanvas.toDataURL("image/png");
};
ns.PngExportController.prototype.onImageUploadCompleted_ = function (imageUrl) {
this.updatePreview_(imageUrl);
this.updateStatus_(imageUrl);
this.previewContainerEl.classList.remove("preview-upload-ongoing");
};
ns.PngExportController.prototype.updateStatus_ = function (imageUrl, error) {
if (imageUrl) {
var linkTpl = "<a class='image-link' href='{{link}}' target='_blank'>{{shortLink}}</a>";
var linkHtml = pskl.utils.Template.replace(linkTpl, {
link : imageUrl,
shortLink : this.shorten_(imageUrl, URL_MAX_LENGTH, '...')
});
this.uploadStatusContainerEl.innerHTML = 'Your image is now available at : ' + linkHtml;
} else {
// FIXME : Should display error message instead
}
};
ns.PngExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<img class='light-picker-background' style='max-width:240px;' src='"+src+"'/>";
};
ns.PngExportController.prototype.shorten_ = function (url, maxLength, suffix) {
if (url.length > maxLength) {
url = url.substring(0, maxLength);
url += suffix;
}
return url;
};
})();

View File

@@ -0,0 +1,58 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
ns.ResizeController = function (piskelController) {
this.piskelController = piskelController;
};
ns.ResizeController.prototype.init = function () {
this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]');
this.resizeWidth.val(this.piskelController.getWidth());
this.resizeHeight.val(this.piskelController.getHeight());
this.cancelButton = $('.resize-cancel-button');
this.cancelButton.click(this.onCancelButtonClicked_.bind(this));
this.resizeForm = $("[name=resize-form]");
this.resizeForm.submit(this.onResizeFormSubmit_.bind(this));
};
ns.ResizeController.prototype.onResizeFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
var width = parseInt(this.resizeWidth.val(), 10);
var height = parseInt(this.resizeHeight.val(), 10);
var layers = [];
var fromLayers = this.piskelController.getLayers();
for (var i = 0 ; i < fromLayers.length ; i++) {
var frames = [];
var fromFrames = fromLayers[i].getFrames();
for (var j = 0 ; j < fromFrames.length ; j++) {
var frame = new pskl.model.Frame(width, height);
this.copyFromFrameToFrame(fromFrames[j], frame);
frames.push(frame);
}
var layer = pskl.model.Layer.fromFrames(fromLayers[i].getName(), frames);
layers.push(layer);
}
var piskel = pskl.model.Piskel.fromLayers(layers, this.piskelController.piskel.getDescriptor());
pskl.app.piskelController.setPiskel(piskel);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
ns.ResizeController.prototype.copyFromFrameToFrame = function (from, to) {
from.forEachPixel(function (color, x, y) {
if (x < to.getWidth() && y < to.getHeight()) {
to.setPixel(x, y, color);
}
});
};
ns.ResizeController.prototype.onCancelButtonClicked_ = function (evt) {
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
})();

View File

@@ -0,0 +1,115 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
ns.SaveController = function (piskelController) {
this.piskelController = piskelController;
};
/**
* @public
*/
ns.SaveController.prototype.init = function () {
this.saveForm = $('form[name=save-form]');
this.nameInput = $('#save-name');
this.descriptionInput = $('#save-description');
this.isPublicCheckbox = $('input[name=save-public-checkbox]');
this.saveCloudButton = $('#save-cloud-button');
this.saveLocalButton = $('#save-local-button');
// Only available in app-engine mode ...
this.piskelName = $('.piskel-name').get(0);
this.status = $('#save-status');
var descriptor = this.piskelController.piskel.getDescriptor();
this.nameInput.val(descriptor.name);
this.descriptionInput.val(descriptor.description);
this.isPublicCheckbox.prop('checked', descriptor.isPublic);
if (!pskl.app.isLoggedIn()) {
this.saveCloudButton.attr('disabled', 'disabled');
this.status.html('You are not logged in. Only Local Save is available.');
} else {
this.saveForm.submit(this.onSaveFormSubmit_.bind(this));
}
this.saveLocalButton.click(this.onSaveLocalClick_.bind(this));
};
ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) {
evt.preventDefault();
evt.stopPropagation();
var name = this.getName();
var description = this.getDescription();
var isPublic = !!this.isPublicCheckbox.prop('checked');
var descriptor = new pskl.model.piskel.Descriptor(name, description, isPublic);
this.piskelController.piskel.setDescriptor(descriptor);
this.beforeSaving_();
pskl.app.store({
success : this.onSaveSuccess_.bind(this),
error : this.onSaveError_.bind(this),
after : this.afterSaving_.bind(this)
});
};
ns.SaveController.prototype.onSaveLocalClick_ = function (evt) {
var localStorageService = pskl.app.localStorageService;
var isOk = true;
var name = this.getName();
var description = this.getDescription();
if (localStorageService.getPiskel(name)) {
isOk = window.confirm('There is already a piskel saved as ' + name + '. Override ?');
}
if (isOk) {
this.beforeSaving_();
localStorageService.save(name, description, pskl.app.piskelController.serialize());
window.setTimeout(function () {
this.onSaveSuccess_();
this.afterSaving_();
}.bind(this), 1000);
}
};
ns.SaveController.prototype.getName = function () {
return this.nameInput.val();
};
ns.SaveController.prototype.getDescription = function () {
return this.descriptionInput.val();
};
ns.SaveController.prototype.beforeSaving_ = function () {
this.saveCloudButton.attr('disabled', true);
this.status.html('Saving ...');
if (this.piskelName) {
this.piskelName.classList.add('piskel-name-saving');
}
};
ns.SaveController.prototype.onSaveSuccess_ = function () {
$.publish(Events.CLOSE_SETTINGS_DRAWER);
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]);
$.publish(Events.PISKEL_SAVED);
};
ns.SaveController.prototype.onSaveError_ = function (status) {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+status+")"}]);
};
ns.SaveController.prototype.afterSaving_ = function () {
this.saveCloudButton.attr('disabled', false);
this.status.html('');
if (this.piskelName) {
this.piskelName.classList.remove('piskel-name-saving');
}
window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 2000);
};
})();

View File

@@ -0,0 +1,100 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
var settings = {
'user' : {
template : 'templates/settings/application.html',
controller : ns.ApplicationSettingsController
},
'resize' : {
template : 'templates/settings/resize.html',
controller : ns.ResizeController
},
'gif' : {
template : 'templates/settings/export-gif.html',
controller : ns.GifExportController
},
'png' : {
template : 'templates/settings/export-png.html',
controller : ns.PngExportController
},
'import' : {
template : 'templates/settings/import.html',
controller : ns.ImportController
},
'localstorage' : {
template : 'templates/settings/localstorage.html',
controller : ns.LocalStorageController
},
'save' : {
template : 'templates/settings/save.html',
controller : ns.SaveController
}
};
var SEL_SETTING_CLS = 'has-expanded-drawer';
var EXP_DRAWER_CLS = 'expanded';
ns.SettingsController = function (piskelController) {
this.piskelController = piskelController;
this.drawerContainer = document.getElementById('drawer-container');
this.settingsContainer = $('[data-pskl-controller=settings]');
this.isExpanded = false;
this.currentSetting = null;
};
/**
* @public
*/
ns.SettingsController.prototype.init = function() {
$('[data-setting]').click(this.onSettingIconClick.bind(this));
$('body').click(this.onBodyClick.bind(this));
$.subscribe(Events.CLOSE_SETTINGS_DRAWER, this.closeDrawer.bind(this));
};
ns.SettingsController.prototype.onSettingIconClick = function (evt) {
var el = evt.originalEvent.currentTarget;
var setting = el.getAttribute('data-setting');
if (this.currentSetting != setting) {
this.loadSetting(setting);
} else {
this.closeDrawer();
}
evt.originalEvent.stopPropagation();
evt.originalEvent.preventDefault();
};
ns.SettingsController.prototype.onBodyClick = function (evt) {
var target = evt.target;
var isInDrawerContainer = pskl.utils.Dom.isParent(target, this.drawerContainer);
var isInSettingsIcon = target.getAttribute('data-setting');
var isInSettingsContainer = isInDrawerContainer || isInSettingsIcon;
if (this.isExpanded && !isInSettingsContainer) {
this.closeDrawer();
}
};
ns.SettingsController.prototype.loadSetting = function (setting) {
this.drawerContainer.innerHTML = pskl.utils.Template.get(settings[setting].template);
(new settings[setting].controller(this.piskelController)).init();
this.settingsContainer.addClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
$('[data-setting='+setting+']').addClass(SEL_SETTING_CLS);
this.isExpanded = true;
this.currentSetting = setting;
};
ns.SettingsController.prototype.closeDrawer = function () {
this.settingsContainer.removeClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
this.isExpanded = false;
this.currentSetting = null;
};
})();

View File

@@ -0,0 +1,88 @@
/**
* @provide pskl.drawingtools.BaseTool
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.BaseTool = function() {};
ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {};
ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {};
ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) {
if (overlay.containsPixel(col, row)) {
if (!isNaN(this.highlightedPixelCol) &&
!isNaN(this.highlightedPixelRow) &&
(this.highlightedPixelRow != row ||
this.highlightedPixelCol != col)) {
// Clean the previously highlighted pixel:
overlay.clear();
}
// Show the current pixel targeted by the tool:
overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR);
this.highlightedPixelCol = col;
this.highlightedPixelRow = row;
}
};
ns.BaseTool.prototype.hideHighlightedPixel = function(overlay) {
if (this.highlightedPixelRow !== null && this.highlightedPixelCol !== null) {
try {
overlay.setPixel(this.highlightedPixelCol, this.highlightedPixelRow, Constants.TRANSPARENT_COLOR);
} catch (e) {
console.warn('ns.BaseTool.prototype.hideHighlightedPixel failed');
}
this.highlightedPixelRow = null;
this.highlightedPixelCol = null;
}
};
ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {};
/**
* Bresenham line algorihtm: Get an array of pixels from
* start and end coordinates.
*
* http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
* http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript
*
* @private
*/
ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) {
var pixels = [];
var dx = Math.abs(x1-x0);
var dy = Math.abs(y1-y0);
var sx = (x0 < x1) ? 1 : -1;
var sy = (y0 < y1) ? 1 : -1;
var err = dx-dy;
while(true){
// Do what you need to for this
pixels.push({"col": x0, "row": y0});
if ((x0==x1) && (y0==y1)) {
break;
}
var e2 = 2*err;
if (e2>-dy){
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
return pixels;
};
})();

View File

@@ -0,0 +1,85 @@
/**
* @provide pskl.drawingtools.Circle
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Circle = function() {
this.toolId = "tool-circle";
this.helpText = "Circle tool";
// Circle's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Circle, ns.BaseTool);
/**
* @override
*/
ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.startCol = col;
this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
// draw in overlay
this.drawCircle_(col, row, color, overlay);
};
/**
* @override
*/
ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize
this.drawCircle_(col, row, color, frame);
}
};
ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) {
var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
for(var i = 0; i< circlePoints.length; i++) {
// Change model:
targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
}
};
ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
var xC = (coords.x0 + coords.x1)/2;
var yC = (coords.y0 + coords.y1)/2;
var rX = coords.x1 - xC;
var rY = coords.y1 - yC;
var pixels = [];
var x, y, angle;
for (x = coords.x0 ; x < coords.x1 ; x++) {
angle = Math.acos((x - xC)/rX);
y = Math.round(rY * Math.sin(angle) + yC);
pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
}
for (y = coords.y0 ; y < coords.y1 ; y++) {
angle = Math.asin((y - yC)/rY);
x = Math.round(rX * Math.cos(angle) + xC);
pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
}
return pixels;
};
})();

View File

@@ -0,0 +1,29 @@
/**
* @provide pskl.drawingtools.ColorPicker
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.ColorPicker = function() {
this.toolId = "tool-colorpicker";
this.helpText = "Color picker";
};
pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
/**
* @override
*/
ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
if (frame.containsPixel(col, row)) {
var sampledColor = frame.getPixel(col, row);
if (event.button == Constants.LEFT_BUTTON) {
$.publish(Events.SELECT_PRIMARY_COLOR, [sampledColor]);
} else if (event.button == Constants.RIGHT_BUTTON) {
$.publish(Events.SELECT_SECONDARY_COLOR, [sampledColor]);
}
}
};
})();

View File

@@ -0,0 +1,23 @@
/**
* @provide pskl.drawingtools.Eraser
*
* @require Constants
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Eraser = function() {
this.toolId = "tool-eraser";
this.helpText = "Eraser tool";
};
pskl.utils.inherit(ns.Eraser, ns.SimplePen);
/**
* @override
*/
ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay, event);
};
})();

View File

@@ -0,0 +1,56 @@
/**
* @provide pskl.drawingtools.Move
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Move = function() {
this.toolId = ns.Move.TOOL_ID;
this.helpText = "Move tool";
// Stroke's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
ns.Move.TOOL_ID = "tool-move";
pskl.utils.inherit(ns.Move, ns.BaseTool);
/**
* @override
*/
ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.startCol = col;
this.startRow = row;
this.frameClone = frame.clone();
};
ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
this.shiftFrame(colDiff, rowDiff, frame, this.frameClone);
};
ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) {
var color;
for (var col = 0 ; col < frame.getWidth() ; col++) {
for (var row = 0 ; row < frame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff);
} else {
color = Constants.TRANSPARENT_COLOR;
}
frame.setPixel(col, row, color);
}
}
};
/**
* @override
*/
ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
this.moveToolAt(col, row, color, frame, overlay);
};
})();

View File

@@ -0,0 +1,36 @@
/**
* @provide pskl.drawingtools.PaintBucket
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.PaintBucket = function() {
this.toolId = "tool-paint-bucket";
this.helpText = "Paint bucket tool";
};
pskl.utils.inherit(ns.PaintBucket, ns.BaseTool);
/**
* @override
*/
ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color);
};
})();

View File

@@ -0,0 +1,59 @@
/**
* @provide pskl.drawingtools.Rectangle
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Rectangle = function() {
this.toolId = "tool-rectangle";
this.helpText = "Rectangle tool";
// Rectangle's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Rectangle, ns.BaseTool);
/**
* @override
*/
ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.startCol = col;
this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
// draw in overlay
this.drawRectangle_(col, row, color, overlay);
};
/**
* @override
*/
ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize
this.drawRectangle_(col, row, color, frame);
}
};
ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) {
var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row);
for(var i = 0; i< strokePoints.length; i++) {
// Change model:
targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
};
})();

View File

@@ -0,0 +1,49 @@
/**
* @provide pskl.drawingtools.SimplePen
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.SimplePen = function() {
this.toolId = "tool-pen";
this.helpText = "Pen tool";
this.previousCol = null;
this.previousRow = null;
};
pskl.utils.inherit(ns.SimplePen, ns.BaseTool);
/**
* @override
*/
ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
if (frame.containsPixel(col, row)) {
frame.setPixel(col, row, color);
}
this.previousCol = col;
this.previousRow = row;
};
ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
if((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
// The pen movement is too fast for the mousemove frequency, there is a gap between the
// current point and the previously drawn one.
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
for(var i=0, l=interpolatedPixels.length; i<l; i++) {
var coords = interpolatedPixels[i];
this.applyToolAt(coords.col, coords.row, color, frame, overlay);
}
}
else {
this.applyToolAt(col, row, color, frame, overlay);
}
this.previousCol = col;
this.previousRow = row;
};
})();

View File

@@ -0,0 +1,80 @@
/**
* @provide pskl.drawingtools.Stroke
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Stroke = function() {
this.toolId = "tool-stroke";
this.helpText = "Stroke tool";
// Stroke's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Stroke, ns.BaseTool);
/**
* @override
*/
ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.startCol = col;
this.startRow = row;
// When drawing a stroke we don't change the model instantly, since the
// user can move his cursor to change the stroke direction and length
// dynamically. Instead we draw the (preview) stroke in a fake canvas that
// overlay the drawing canvas.
// We wait for the releaseToolAt callback to impact both the
// frame model and canvas rendering.
// The fake canvas where we will draw the preview of the stroke:
// Drawing the first point of the stroke in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
overlay.clear();
// When the user moussemove (before releasing), we dynamically compute the
// pixel to draw the line and draw this line in the overlay canvas:
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
// Drawing current stroke:
for(var i = 0; i< strokePoints.length; i++) {
if(color == Constants.TRANSPARENT_COLOR) {
// When mousemoving the stroke tool, we draw in the canvas overlay above the drawing canvas.
// If the stroke color is transparent, we won't be
// able to see it during the movement.
// We set it to a semi-opaque white during the tool mousemove allowing to see colors below the stroke.
// When the stroke tool will be released, It will draw a transparent stroke,
// eg deleting the equivalent of a stroke.
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
};
/**
* @override
*/
ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
// If the stroke tool is released outside of the canvas, we cancel the stroke:
// TODO: Mutualize this check in common method
if(frame.containsPixel(col, row)) {
// The user released the tool to draw a line. We will compute the pixel coordinate, impact
// the model and draw them in the drawing canvas (not the fake overlay anymore)
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
for(var i = 0; i< strokePoints.length; i++) {
// Change model:
frame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
}
// For now, we are done with the stroke tool and don't need an overlay anymore:
overlay.clear();
};
})();

View File

@@ -0,0 +1,46 @@
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.VerticalMirrorPen = function() {
this.toolId = "tool-vertical-mirror-pen";
this.helpText = "vertical mirror pen tool";
this.swap = null;
this.mirroredPreviousCol = null;
this.mirroredPreviousRow = null;
};
pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen);
ns.VerticalMirrorPen.prototype.setMirrorContext = function() {
this.swap = this.previousCol;
this.previousCol = this.mirroredPreviousCol;
};
ns.VerticalMirrorPen.prototype.unsetMirrorContext = function() {
this.mirroredPreviousCol = this.previousCol;
this.previousCol = this.swap;
};
/**
* @override
*/
ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.superclass.applyToolAt.call(this, col, row, color, frame, overlay);
var mirroredCol = this.getSymmetricCol_(col, frame);
this.mirroredPreviousCol = mirroredCol;
this.setMirrorContext();
this.superclass.applyToolAt.call(this, mirroredCol, row, color, frame, overlay);
this.unsetMirrorContext();
};
/**
* @private
*/
ns.VerticalMirrorPen.prototype.getSymmetricCol_ = function(col, frame) {
return frame.getWidth() - col - 1;
};
})();

View File

@@ -0,0 +1,167 @@
/**
* @provide pskl.drawingtools.BaseSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.BaseSelect = function() {
this.secondaryToolId = pskl.drawingtools.Move.TOOL_ID;
this.BodyRoot = $('body');
// Select's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.BaseSelect, ns.BaseTool);
/**
* @override
*/
ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {
this.startCol = col;
this.startRow = row;
this.lastCol = col;
this.lastRow = row;
// The select tool can be in two different state.
// If the inital click of the tool is not on a selection, we go in "select"
// mode to create a selection.
// If the initial click is on a previous selection, we go in "moveSelection"
// mode to allow to move the selection by drag'n dropping it.
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
this.mode = "select";
this.onSelectStart_(col, row, color, frame, overlay);
}
else {
this.mode = "moveSelection";
this.onSelectionDragStart_(col, row, color, frame, overlay);
}
};
/**
* @override
*/
ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {
if(this.mode == "select") {
this.onSelect_(col, row, color, frame, overlay);
}
else if(this.mode == "moveSelection") {
this.onSelectionDrag_(col, row, color, frame, overlay);
}
};
/**
* @override
*/
ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {
if(this.mode == "select") {
this.onSelectEnd_(col, row, color, frame, overlay);
} else if(this.mode == "moveSelection") {
this.onSelectionDragEnd_(col, row, color, frame, overlay);
}
};
ns.BaseSelect.prototype.hideHighlightedPixel = function () {
// not implemented for selection tools
};
/**
* If we mouseover the selection draw inside the overlay frame, show the 'move' cursor
* instead of the 'select' one. It indicates that we can move the selection by dragndroping it.
* @override
*/
ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) {
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
// We're hovering the selection, show the move tool:
this.BodyRoot.addClass(this.toolId);
this.BodyRoot.removeClass(this.secondaryToolId);
} else {
// We're not hovering the selection, show create selection tool:
this.BodyRoot.addClass(this.secondaryToolId);
this.BodyRoot.removeClass(this.toolId);
}
};
ns.BaseSelect.prototype.hideHighlightedPixel = function() {
// there is no highlighted pixel for selection tools, do nothing
};
/**
* For each pixel in the selection draw it in white transparent on the tool overlay
* @protected
*/
ns.BaseSelect.prototype.drawSelectionOnOverlay_ = function (selection, overlay) {
var pixels = selection.pixels;
for(var i=0, l=pixels.length; i<l; i++) {
overlay.setPixel(pixels[i].col, pixels[i].row, Constants.SELECTION_TRANSPARENT_COLOR);
}
};
/**
* Move the overlay frame filled with semi-transparent pixels that represent the selection.
* @private
*/
ns.BaseSelect.prototype.shiftOverlayFrame_ = function (colDiff, rowDiff, overlayFrame, reference) {
var color;
for (var col = 0 ; col < overlayFrame.getWidth() ; col++) {
for (var row = 0 ; row < overlayFrame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff);
} else {
color = Constants.TRANSPARENT_COLOR;
}
overlayFrame.setPixel(col, row, color);
}
}
};
// The list of callbacks to implement by specialized tools to implement the selection creation behavior.
/** @protected */
ns.BaseSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {};
/** @protected */
ns.BaseSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {};
/** @protected */
ns.BaseSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {};
// The list of callbacks that define the drag'n drop behavior of the selection.
/** @private */
ns.BaseSelect.prototype.onSelectionDragStart_ = function (col, row, color, frame, overlay) {
// Since we will move the overlayFrame in which the current selection is rendered,
// we clone it to have a reference for the later shifting process.
this.overlayFrameReference = overlay.clone();
};
/** @private */
ns.BaseSelect.prototype.onSelectionDrag_ = function (col, row, color, frame, overlay) {
var deltaCol = col - this.lastCol;
var deltaRow = row - this.lastRow;
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
// Shifting selection on overlay frame:
this.shiftOverlayFrame_(colDiff, rowDiff, overlay, this.overlayFrameReference);
// Update selection model:
$.publish(Events.SELECTION_MOVE_REQUEST, [deltaCol, deltaRow]);
this.lastCol = col;
this.lastRow = row;
};
/** @private */
ns.BaseSelect.prototype.onSelectionDragEnd_ = function (col, row, color, frame, overlay) {
this.onSelectionDrag_(col, row, color, frame, overlay);
};
})();

View File

@@ -0,0 +1,52 @@
/**
* @provide pskl.drawingtools.RectangleSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.RectangleSelect = function() {
this.toolId = "tool-rectangle-select";
this.helpText = "Rectangle selection tool";
ns.BaseSelect.call(this);
};
pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect);
/**
* @override
*/
ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
/**
* When creating the rectangle selection, we clear the current overlayFrame and
* redraw the current rectangle based on the orgin coordinate and
* the current mouse coordiinate in sprite.
* @override
*/
ns.RectangleSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
overlay.clear();
if(this.startCol == col &&this.startRow == row) {
$.publish(Events.SELECTION_DISMISSED);
} else {
var selection = new pskl.selection.RectangularSelection(
this.startCol, this.startRow, col, row);
$.publish(Events.SELECTION_CREATED, [selection]);
this.drawSelectionOnOverlay_(selection, overlay);
}
};
/**
* @override
*/
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
this.onSelect_(col, row, color, frame, overlay);
};
})();

View File

@@ -0,0 +1,36 @@
/**
* @provide pskl.drawingtools.ShapeSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.ShapeSelect = function() {
this.toolId = "tool-shape-select";
this.helpText = "Shape selection tool";
ns.BaseSelect.call(this);
};
pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect);
/**
* For the shape select tool, you just need to click one time to create a selection.
* So we jsut need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_)
* @override
*/
ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Clean previous selection:
$.publish(Events.SELECTION_DISMISSED);
overlay.clear();
// From the pixel cliked, get shape using an algorithm similar to the paintbucket one:
var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row);
var selection = new pskl.selection.ShapeSelection(pixels);
$.publish(Events.SELECTION_CREATED, [selection]);
this.drawSelectionOnOverlay_(selection, overlay);
};
})();

BIN
src/js/lib/.DS_Store vendored Normal file

Binary file not shown.

275
src/js/lib/bootstrap/bootstrap.js vendored Executable file
View File

@@ -0,0 +1,275 @@
/* ===========================================================
* bootstrap-tooltip.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function (element, options) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function (type, element, options) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function (options) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) return self.show()
clearTimeout(this.timeout)
self.hoverState = 'in'
this.timeout = setTimeout(function() {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
, leave: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (this.timeout) clearTimeout(this.timeout)
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.hoverState = 'out'
this.timeout = setTimeout(function() {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
return this
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
, trigger: 'hover'
, title: ''
, delay: 0
, html: true
}
}(window.jQuery);

Some files were not shown because too many files have changed in this diff Show More