Feature : Save Piskel project as File

First commit :
Removed Local storage feature
Added 'download project' 'open project' options

First attempt at simplifying right panel.

To be continued ...
This commit is contained in:
jdescottes 2014-06-23 00:49:54 +02:00
parent 23fd3c464c
commit 89466d582a
23 changed files with 366 additions and 214 deletions

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ diff.txt
# build destination # build destination
dest dest
build/closure/closure_compiled_binary.js
# marked as private # marked as private
*.private.* *.private.*

View file

@ -33,4 +33,8 @@ ul, li {
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background-color: rgba(50, 50, 50, 0.4); background-color: rgba(50, 50, 50, 0.4);
}
a, a:visited {
color:gold;
} }

View file

@ -70,7 +70,7 @@
position: relative; position: relative;
} }
.upload-cloud-icon .label { .tool-icon .label {
position: absolute; position: absolute;
left: 0; left: 0;
bottom: 4px; bottom: 4px;
@ -126,6 +126,7 @@
text-transform: uppercase; text-transform: uppercase;
border-bottom: 1px #aaa solid; border-bottom: 1px #aaa solid;
padding-bottom: 5px; padding-bottom: 5px;
color: gold;
} }
.settings-description { .settings-description {
@ -141,6 +142,10 @@
margin : 10px 0; margin : 10px 0;
} }
[name*=checkbox] {
vertical-align: middle;
}
/************************************************************************************************/ /************************************************************************************************/
/* Application settings */ /* Application settings */
/************************************************************************************************/ /************************************************************************************************/
@ -203,13 +208,6 @@
background:gold; background:gold;
} }
.gif-export-preview,
.png-export-preview {
margin-top:20px;
max-width:240px;
position:relative;
}
.png-export-preview { .png-export-preview {
margin:10px 0; margin:10px 0;
overflow: hidden; overflow: hidden;
@ -223,6 +221,38 @@
margin : 10px 0; margin : 10px 0;
} }
.gif-upload-status,
.gif-export-preview {
float : left;
}
.gif-export-preview {
}
.gif-upload-status {
width: 180px;
margin-left: 5px;
margin-top: 10px;
}
.gif-export-preview,
.png-export-preview {
margin-top:10px;
position:relative;
}
.png-export-preview {
max-width:240px;
}
.gif-export-preview,
.png-export-preview {
max-width:32px;
max-height:32px;
}
.preview-upload-ongoing:before{ .preview-upload-ongoing:before{
content: "Upload ongoing ..."; content: "Upload ongoing ...";
position: absolute; position: absolute;
@ -243,7 +273,7 @@
.import-section, .import-section,
.resize-section { .resize-section {
margin: 15px 0; margin: 10px 0;
} }
.import-section-title { .import-section-title {
@ -296,26 +326,36 @@
text-shadow: none; 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] { [name=smooth-resize-checkbox] {
margin : 0 8px; margin : 0 8px;
} }
[name*=checkbox] { /************************************************************************************************/
/* Save panel */
/************************************************************************************************/
.save-field {
width: 100%;
}
#save-online-status {
margin-top: 10px;
}
#save-local-status {
margin-bottom: 10px;
}
.save-status {
vertical-align: middle; vertical-align: middle;
font-weight: normal;
text-shadow: none;
font-style: italic;
}
.save-local-name {
white-space: nowrap;
font-weight: bold;
color: white;
font-style: normal;
} }

View file

@ -11,6 +11,10 @@ body {
display: none; display: none;
} }
.clearfix {
overflow: hidden;
}
.allow-user-select { .allow-user-select {
-webkit-touch-callout: initial; -webkit-touch-callout: initial;
-webkit-user-select: initial; -webkit-user-select: initial;

View file

@ -54,9 +54,8 @@
<iframe src="templates/settings/application.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/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/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/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.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> </div>

View file

@ -56,8 +56,8 @@ var Constants = {
SAVE : 'save' SAVE : 'save'
} }
}, },
IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-a.appspot.com/__/upload', IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-b.appspot.com/__/upload',
IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-a.appspot.com/img/', IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-b.appspot.com/img/',
ZOOMED_OUT_BACKGROUND_COLOR : '#A0A0A0', ZOOMED_OUT_BACKGROUND_COLOR : '#A0A0A0',

View file

@ -1,7 +1,7 @@
(function () { (function () {
var ns = $.namespace("pskl.controller.settings"); var ns = $.namespace("pskl.controller.settings");
var URL_MAX_LENGTH = 60; var URL_MAX_LENGTH = 30;
var MAX_GIF_COLORS = 256; var MAX_GIF_COLORS = 256;
ns.GifExportController = function (piskelController) { ns.GifExportController = function (piskelController) {
@ -27,12 +27,12 @@
]; ];
ns.GifExportController.prototype.init = function () { ns.GifExportController.prototype.init = function () {
this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template"); this.optionTemplate_ = pskl.utils.Template.get("gif-export-option-template");
this.uploadStatusContainerEl = document.querySelector(".gif-upload-status"); this.uploadStatusContainerEl = document.querySelector(".gif-upload-status");
this.previewContainerEl = document.querySelector(".gif-export-preview"); this.previewContainerEl = document.querySelector(".gif-export-preview");
this.radioGroupEl = document.querySelector(".gif-export-radio-group"); this.selectResolutionEl = document.querySelector(".gif-export-select-resolution");
this.uploadButton = $(".gif-upload-button"); this.uploadButton = $(".gif-upload-button");
this.uploadButton.click(this.onUploadButtonClick_.bind(this)); this.uploadButton.click(this.onUploadButtonClick_.bind(this));
@ -45,7 +45,7 @@
this.exportProgressStatusEl = document.querySelector('.gif-export-progress-status'); this.exportProgressStatusEl = document.querySelector('.gif-export-progress-status');
this.exportProgressBarEl = document.querySelector('.gif-export-progress-bar'); this.exportProgressBarEl = document.querySelector('.gif-export-progress-bar');
this.createRadioElements_(); this.createOptionElements_();
}; };
ns.GifExportController.prototype.onUploadButtonClick_ = function (evt) { ns.GifExportController.prototype.onUploadButtonClick_ = function (evt) {
@ -63,8 +63,8 @@
this.renderAsImageDataAnimatedGIF(zoom, fps, function (imageData) { this.renderAsImageDataAnimatedGIF(zoom, fps, function (imageData) {
pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this)); pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this));
pskl.utils.ImageToBlob.imageDataToBlob(imageData, "image/gif", function(blob) { pskl.utils.BlobUtils.dataToBlob(imageData, "image/gif", function(blob) {
pskl.utils.FileUtils.downloadAsFile(fileName, blob); pskl.utils.FileUtils.downloadAsFile(blob, fileName);
}); });
}.bind(this)); }.bind(this));
}; };
@ -82,43 +82,30 @@
}; };
ns.GifExportController.prototype.updatePreview_ = function (src) { ns.GifExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<div><img style='max-width:240px;' src='"+src+"'/></div>"; this.previewContainerEl.innerHTML = "<div><img style='max-width:32px;' src='"+src+"'/></div>";
}; };
ns.GifExportController.prototype.getSelectedZoom_ = function () { ns.GifExportController.prototype.getSelectedZoom_ = function () {
var radiosColl = this.exportForm.get(0).querySelectorAll("[name=gif-zoom-level]"), return this.selectResolutionEl.value;
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 () { ns.GifExportController.prototype.createOptionElements_ = function () {
var resolutions = ns.GifExportController.RESOLUTIONS; var resolutions = ns.GifExportController.RESOLUTIONS;
for (var i = 0 ; i < resolutions.length ; i++) { for (var i = 0 ; i < resolutions.length ; i++) {
var radio = this.createRadioForResolution_(resolutions[i]); var option = this.createOptionForResolution_(resolutions[i]);
this.radioGroupEl.appendChild(radio); this.selectResolutionEl.appendChild(option);
} }
}; };
ns.GifExportController.prototype.createRadioForResolution_ = function (resolution) { ns.GifExportController.prototype.createOptionForResolution_ = function (resolution) {
var zoom = resolution.zoom; var zoom = resolution.zoom;
var label = zoom*this.piskelController.getWidth() + "x" + zoom*this.piskelController.getHeight(); var label = zoom*this.piskelController.getWidth() + "x" + zoom*this.piskelController.getHeight();
var value = zoom; var value = zoom;
var radioHTML = pskl.utils.Template.replace(this.radioTemplate_, {value : value, label : label}); var optionHTML = pskl.utils.Template.replace(this.optionTemplate_, {value : value, label : label});
var radioEl = pskl.utils.Template.createFromHTML(radioHTML); var optionEl = pskl.utils.Template.createFromHTML(optionHTML);
if (resolution['default']) { return optionEl;
var input = radioEl.getElementsByTagName("input")[0];
input.setAttribute("checked", "checked");
}
return radioEl;
}; };
ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) { ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) {
@ -181,8 +168,10 @@
ns.GifExportController.prototype.shorten_ = function (url, maxLength, suffix) { ns.GifExportController.prototype.shorten_ = function (url, maxLength, suffix) {
if (url.length > maxLength) { if (url.length > maxLength) {
url = url.substring(0, maxLength); var index = Math.round((maxLength-suffix.length) / 2);
url += suffix; var part1 = url.substring(0, index);
var part2 = url.substring(url.length - index, url.length);
url = part1 + suffix + part2;
} }
return url; return url;
}; };

View file

@ -1,6 +1,6 @@
(function () { (function () {
var ns = $.namespace('pskl.controller.settings'); var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = 'No file selected ...'; var DEFAULT_FILE_STATUS = '';
var PREVIEW_HEIGHT = 60; var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) { ns.ImportController = function (piskelController) {
@ -9,6 +9,9 @@
}; };
ns.ImportController.prototype.init = function () { ns.ImportController.prototype.init = function () {
this.hiddenOpenPiskelInput = $('[name=open-piskel-input]');
this.openPiskelInputButton = $('.open-piskel-button');
this.importForm = $('[name=import-form]'); this.importForm = $('[name=import-form]');
this.hiddenFileInput = $('[name=file-upload-input]'); this.hiddenFileInput = $('[name=file-upload-input]');
this.fileInputButton = $('.file-input-button'); this.fileInputButton = $('.file-input-button');
@ -20,12 +23,16 @@
this.resizeWidth = $('[name=resize-width]'); this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]'); this.resizeHeight = $('[name=resize-height]');
this.smoothResize = $('[name=smooth-resize-checkbox]'); this.smoothResize = $('[name=smooth-resize-checkbox]');
this.submitButton = $('[name=import-submit]');
$('.import-options').hide();
this.importForm.submit(this.onImportFormSubmit_.bind(this)); this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.bind(this)); this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));
this.fileInputButton.click(this.onFileInputClick_.bind(this)); this.fileInputButton.click(this.onFileInputClick_.bind(this));
this.hiddenOpenPiskelInput.change(this.onOpenPiskelChange_.bind(this));
this.openPiskelInputButton.click(this.onOpenPiskelClick_.bind(this));
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width')); this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height')); this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
}; };
@ -61,14 +68,47 @@
}; };
ns.ImportController.prototype.onFileUploadChange_ = function (evt) { ns.ImportController.prototype.onFileUploadChange_ = function (evt) {
this.importFromFile_(); this.importPictureFromFile_();
}; };
ns.ImportController.prototype.onFileInputClick_ = function (evt) { ns.ImportController.prototype.onFileInputClick_ = function (evt) {
this.hiddenFileInput.click(); this.hiddenFileInput.click();
}; };
ns.ImportController.prototype.importFromFile_ = function () { ns.ImportController.prototype.onOpenPiskelChange_ = function (evt) {
this.openPiskelFile_();
};
ns.ImportController.prototype.onOpenPiskelClick_ = function (evt) {
this.hiddenOpenPiskelInput.click();
};
ns.ImportController.prototype.openPiskelFile_ = function () {
var files = this.hiddenOpenPiskelInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isPiskel_(file)){
pskl.utils.FileUtils.readFile(file, function (content) {
var rawPiskel = window.atob(content.replace('data:;base64,',''));
var serializedPiskel = JSON.parse(rawPiskel);
var name = serializedPiskel.piskel.name;
var description = serializedPiskel.piskel.description;
var fps = serializedPiskel.piskel.fps;
pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, function (piskel) {
piskel.setDescriptor(new pskl.model.piskel.Descriptor(name, description, true));
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(fps);
});
});
this.reset_();
}
}
};
ns.ImportController.prototype.importPictureFromFile_ = function () {
var files = this.hiddenFileInput.get(0).files; var files = this.hiddenFileInput.get(0).files;
if (files.length == 1) { if (files.length == 1) {
var file = files[0]; var file = files[0];
@ -83,15 +123,9 @@
}; };
ns.ImportController.prototype.enableDisabledSections_ = function () { 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.removeClass('button-primary');
this.fileInputButton.blur(); this.fileInputButton.blur();
$('.import-options').show();
$('.import-section-disabled').removeClass('import-section-disabled');
}; };
ns.ImportController.prototype.readImageFile_ = function (imageFile) { ns.ImportController.prototype.readImageFile_ = function (imageFile) {
@ -200,4 +234,8 @@
return file.type.indexOf('image') === 0; return file.type.indexOf('image') === 0;
}; };
ns.ImportController.prototype.isPiskel_ = function (file) {
return (/\.piskel$/).test(file.name);
};
})(); })();

View file

@ -9,28 +9,23 @@
ns.PngExportController.prototype.init = function () { ns.PngExportController.prototype.init = function () {
this.previewContainerEl = document.querySelectorAll(".png-export-preview")[0]; this.previewContainerEl = document.querySelectorAll(".png-export-preview")[0];
this.uploadStatusContainerEl = document.querySelectorAll(".png-upload-status")[0];
document.querySelector(".png-upload-button").addEventListener('click', this.onPngUploadButtonClick_.bind(this));
document.querySelector(".png-download-button").addEventListener('click', this.onPngDownloadButtonClick_.bind(this)); document.querySelector(".png-download-button").addEventListener('click', this.onPngDownloadButtonClick_.bind(this));
document.querySelector(".zip-generate-button").addEventListener('click', this.onZipButtonClick_.bind(this)); document.querySelector(".zip-generate-button").addEventListener('click', this.onZipButtonClick_.bind(this));
this.updatePreview_(this.getFramesheetAsCanvas().toDataURL("image/png")); this.updatePreview_(this.getFramesheetAsCanvas().toDataURL("image/png"));
(new ns.GifExportController(this.piskelController)).init();
}; };
ns.PngExportController.prototype.onPngDownloadButtonClick_ = function (evt) { ns.PngExportController.prototype.onPngDownloadButtonClick_ = function (evt) {
var fileName = this.getPiskelName_() + '.png'; var fileName = this.getPiskelName_() + '.png';
pskl.utils.ImageToBlob.canvasToBlob(this.getFramesheetAsCanvas(), function(blob) { pskl.utils.BlobUtils.canvasToBlob(this.getFramesheetAsCanvas(), function(blob) {
pskl.utils.FileUtils.downloadAsFile(fileName, blob); pskl.utils.FileUtils.downloadAsFile(blob, fileName);
}); });
}; };
ns.PngExportController.prototype.onPngUploadButtonClick_ = function (evt) {
this.previewContainerEl.classList.add("preview-upload-ongoing");
pskl.app.imageUploadService.upload(this.getFramesheetAsCanvas().toDataURL("image/png"), this.onImageUploadCompleted_.bind(this));
};
ns.PngExportController.prototype.onZipButtonClick_ = function () { ns.PngExportController.prototype.onZipButtonClick_ = function () {
var zip = new window.JSZip(); var zip = new window.JSZip();
@ -43,8 +38,8 @@
var fileName = this.getPiskelName_() + '.zip'; var fileName = this.getPiskelName_() + '.zip';
var fileContent = zip.generate({type:"blob"}); var blob = zip.generate({type:"blob"});
pskl.utils.FileUtils.downloadAsFile(fileName, fileContent); pskl.utils.FileUtils.downloadAsFile(blob, fileName);
}; };
ns.PngExportController.prototype.getFrameAsCanvas_ = function (frame) { ns.PngExportController.prototype.getFrameAsCanvas_ = function (frame) {

View file

@ -13,13 +13,16 @@
this.nameInput = $('#save-name'); this.nameInput = $('#save-name');
this.descriptionInput = $('#save-description'); this.descriptionInput = $('#save-description');
this.isPublicCheckbox = $('input[name=save-public-checkbox]'); this.isPublicCheckbox = $('input[name=save-public-checkbox]');
this.saveCloudButton = $('#save-cloud-button'); this.saveOnlineButton = $('#save-online-button');
this.saveLocalButton = $('#save-local-button'); this.saveLocalButton = $('#save-local-button');
// Only available in app-engine mode ... // Only available in app-engine mode ...
this.piskelName = $('.piskel-name').get(0); this.piskelName = $('.piskel-name').get(0);
this.status = $('#save-status'); this.saveOnlineStatus = $('#save-online-status');
this.saveLocalStatus = $('#save-local-status');
this.timestamp = new Date();
var descriptor = this.piskelController.getPiskel().getDescriptor(); var descriptor = this.piskelController.getPiskel().getDescriptor();
this.nameInput.val(descriptor.name); this.nameInput.val(descriptor.name);
@ -27,27 +30,38 @@
this.isPublicCheckbox.prop('checked', descriptor.isPublic); this.isPublicCheckbox.prop('checked', descriptor.isPublic);
if (!pskl.app.isLoggedIn()) { this.saveLocalButton.click(this.onSaveLocalClick_.bind(this));
this.saveCloudButton.attr('disabled', 'disabled'); this.nameInput.keyup(this.updateLocalStatusFilename_.bind(this));
this.status.html('You are not logged in. Only Local Save is available.');
} else { if (pskl.app.isLoggedIn()) {
this.saveForm.submit(this.onSaveFormSubmit_.bind(this)); this.saveForm.submit(this.onSaveFormSubmit_.bind(this));
} else {
this.saveOnlineButton.hide();
$('.save-public-section').hide();
this.saveOnlineStatus.html(pskl.utils.Template.get('save-please-login-partial'));
this.saveLocalButton.get(0).classList.add('button-primary');
this.saveForm.submit(this.onSaveLocalClick_.bind(this));
} }
this.saveLocalButton.click(this.onSaveLocalClick_.bind(this)); this.updateLocalStatusFilename_();
};
ns.SaveController.prototype.updateLocalStatusFilename_ = function () {
this.saveLocalStatus.html(pskl.utils.Template.getAndReplace('save-local-status-template', {
name : this.getLocalFilename_()
}));
};
ns.SaveController.prototype.getLocalFilename_ = function () {
var piskelName = this.getName();
var timestamp = pskl.utils.DateUtils.format(this.timestamp, "{{Y}}{{M}}{{D}}-{{H}}{{m}}{{s}}");
return piskelName + "-" + timestamp + ".piskel";
}; };
ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) { ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); 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.getPiskel().setDescriptor(descriptor);
this.beforeSaving_(); this.beforeSaving_();
pskl.app.storageService.store({ pskl.app.storageService.store({
success : this.onSaveSuccess_.bind(this), success : this.onSaveSuccess_.bind(this),
@ -57,22 +71,13 @@
}; };
ns.SaveController.prototype.onSaveLocalClick_ = function (evt) { ns.SaveController.prototype.onSaveLocalClick_ = function (evt) {
var localStorageService = pskl.app.localStorageService; this.beforeSaving_();
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) { pskl.utils.BlobUtils.stringToBlob(pskl.app.piskelController.serialize(), function(blob) {
this.beforeSaving_(); pskl.utils.FileUtils.downloadAsFile(blob, this.getLocalFilename_());
localStorageService.save(name, description, pskl.app.piskelController.serialize()); this.onSaveSuccess_();
window.setTimeout(function () { this.afterSaving_();
this.onSaveSuccess_(); }.bind(this), "application/piskel+json");
this.afterSaving_();
}.bind(this), 1000);
}
}; };
ns.SaveController.prototype.getName = function () { ns.SaveController.prototype.getName = function () {
@ -84,14 +89,25 @@
}; };
ns.SaveController.prototype.beforeSaving_ = function () { ns.SaveController.prototype.beforeSaving_ = function () {
this.saveCloudButton.attr('disabled', true); this.updatePiskelDescriptor_();
this.status.html('Saving ...');
this.saveOnlineButton.attr('disabled', true);
this.saveOnlineStatus.html('Saving ...');
if (this.piskelName) { if (this.piskelName) {
this.piskelName.classList.add('piskel-name-saving'); this.piskelName.classList.add('piskel-name-saving');
} }
}; };
ns.SaveController.prototype.updatePiskelDescriptor_ = function () {
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.getPiskel().setDescriptor(descriptor);
};
ns.SaveController.prototype.onSaveSuccess_ = function () { ns.SaveController.prototype.onSaveSuccess_ = function () {
$.publish(Events.CLOSE_SETTINGS_DRAWER); $.publish(Events.CLOSE_SETTINGS_DRAWER);
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]); $.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]);
@ -103,8 +119,8 @@
}; };
ns.SaveController.prototype.afterSaving_ = function () { ns.SaveController.prototype.afterSaving_ = function () {
this.saveCloudButton.attr('disabled', false); this.saveOnlineButton.attr('disabled', false);
this.status.html(''); this.submitButton.html('');
if (this.piskelName) { if (this.piskelName) {
this.piskelName.classList.remove('piskel-name-saving'); this.piskelName.classList.remove('piskel-name-saving');

View file

@ -10,12 +10,8 @@
template : 'templates/settings/resize.html', template : 'templates/settings/resize.html',
controller : ns.ResizeController controller : ns.ResizeController
}, },
'gif' : {
template : 'templates/settings/export-gif.html',
controller : ns.GifExportController
},
'png' : { 'png' : {
template : 'templates/settings/export-png.html', template : 'templates/settings/export.html',
controller : ns.PngExportController controller : ns.PngExportController
}, },
'import' : { 'import' : {

View file

@ -3,8 +3,8 @@
var BASE64_REGEX = /\s*;\s*base64\s*(?:;|$)/i; var BASE64_REGEX = /\s*;\s*base64\s*(?:;|$)/i;
ns.ImageToBlob = { ns.BlobUtils = {
imageDataToBlob : function(dataURI, type, callback) { dataToBlob : function(dataURI, type, callback) {
var header_end = dataURI.indexOf(","), var header_end = dataURI.indexOf(","),
data = dataURI.substring(header_end + 1), data = dataURI.substring(header_end + 1),
isBase64 = BASE64_REGEX.test(dataURI.substring(0, header_end)), isBase64 = BASE64_REGEX.test(dataURI.substring(0, header_end)),
@ -26,13 +26,18 @@
canvasToBlob : function(canvas, callback, type /*, ...args*/) { canvasToBlob : function(canvas, callback, type /*, ...args*/) {
type = type || "image/png"; type = type || "image/png";
if (this.mozGetAsFile) { if (canvas.mozGetAsFile) {
callback(this.mozGetAsFile("canvas", type)); callback(canvas.mozGetAsFile("canvas", type));
} else { } else {
var args = Array.prototype.slice.call(arguments, 2); var args = Array.prototype.slice.call(arguments, 2);
var dataURI = canvas.toDataURL.apply(canvas, args); var dataURI = canvas.toDataURL.apply(canvas, args);
pskl.utils.ImageToBlob.imageDataToBlob(dataURI, type, callback); pskl.utils.BlobUtils.dataToBlob(dataURI, type, callback);
} }
},
stringToBlob : function (string, callback, type) {
type = type || "text/plain";
pskl.utils.BlobUtils.dataToBlob('data:'+type+',' + string, type, callback);
} }
}; };
})(); })();

24
src/js/utils/DateUtils.js Normal file
View file

@ -0,0 +1,24 @@
(function () {
var ns = $.namespace('pskl.utils');
var pad = function (num) {
if (num < 10) {
return "0" + num;
} else {
return "" + num;
}
};
ns.DateUtils = {
format : function (date, format) {
return pskl.utils.Template.replace(format, {
Y : date.getFullYear(),
M : pad(date.getMonth() + 1),
D : pad(date.getDate()),
H : pad(date.getHours()),
m : pad(date.getMinutes()),
s : pad(date.getSeconds())
});
}
};
})();

View file

@ -10,7 +10,7 @@
reader.readAsDataURL(file); reader.readAsDataURL(file);
}, },
downloadAsFile : function (filename, content) { downloadAsFile : function (content, filename) {
var saveAs = window.saveAs || (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator)); var saveAs = window.saveAs || (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator));
if (saveAs) { if (saveAs) {
saveAs(content, filename); saveAs(content, filename);

View file

@ -17,6 +17,15 @@
return dummyEl.children[0]; return dummyEl.children[0];
}, },
getAndReplace : function (templateId, dict) {
var result = "";
var tpl = pskl.utils.Template.get(templateId);
if (tpl) {
result = pskl.utils.Template.replace(tpl, dict);
}
return result;
},
replace : function (template, dict) { replace : function (template, dict) {
for (var key in dict) { for (var key in dict) {
if (dict.hasOwnProperty(key)) { if (dict.hasOwnProperty(key)) {

View file

@ -9,6 +9,9 @@
return JSON.stringify({ return JSON.stringify({
modelVersion : Constants.MODEL_VERSION, modelVersion : Constants.MODEL_VERSION,
piskel : { piskel : {
name : piskel.getDescriptor().name,
description : piskel.getDescriptor().description,
fps : pskl.app.piskelController.getFPS(),
height : piskel.getHeight(), height : piskel.getHeight(),
width : piskel.getWidth(), width : piskel.getWidth(),
layers : serializedLayers, layers : serializedLayers,

View file

@ -24,13 +24,14 @@
"js/utils/core.js", "js/utils/core.js",
"js/utils/UserAgent.js", "js/utils/UserAgent.js",
"js/utils/Base64.js", "js/utils/Base64.js",
"js/utils/BlobUtils.js",
"js/utils/CanvasUtils.js", "js/utils/CanvasUtils.js",
"js/utils/DateUtils.js",
"js/utils/Dom.js", "js/utils/Dom.js",
"js/utils/Math.js", "js/utils/Math.js",
"js/utils/FileUtils.js", "js/utils/FileUtils.js",
"js/utils/FrameUtils.js", "js/utils/FrameUtils.js",
"js/utils/LayerUtils.js", "js/utils/LayerUtils.js",
"js/utils/ImageToBlob.js",
"js/utils/ImageResizer.js", "js/utils/ImageResizer.js",
"js/utils/PixelUtils.js", "js/utils/PixelUtils.js",
"js/utils/Template.js", "js/utils/Template.js",

View file

@ -1,10 +1,5 @@
<div class="vertical-centerer"> <div class="vertical-centerer">
<div
data-setting="save"
class="tool-icon save-icon"
title="Save to gallery"
rel="tooltip" data-placement="left" >
</div>
<div <div
data-setting="user" data-setting="user"
@ -16,37 +11,43 @@
<div <div
data-setting="resize" data-setting="resize"
class="tool-icon resize-icon" class="tool-icon resize-icon"
title="Resize" title="Modify Canvas"
rel="tooltip" data-placement="left"> rel="tooltip" data-placement="left">
</div> </div>
<div
data-setting="save"
class="tool-icon save-icon"
title="Save to gallery"
rel="tooltip" data-placement="left" >
</div>
<div <div
data-setting="import" data-setting="import"
class="tool-icon import-icon" class="tool-icon local-storage-icon"
title="Import an existing picture" title="Import piskel"
rel="tooltip" data-placement="left"> rel="tooltip" data-placement="left">
</div> </div>
<div <!-- <div
data-setting="localstorage" data-setting="localstorage"
class="tool-icon local-storage-icon" class="tool-icon local-storage-icon"
title="Browse piskels saved locally" title="Browse piskels saved locally"
rel="tooltip" data-placement="left"> rel="tooltip" data-placement="left">
</div> </div> -->
<div <!-- <div
data-setting="gif" data-setting="gif"
class="tool-icon upload-cloud-icon" class="tool-icon upload-cloud-icon"
title="Export Animation" title="Export Animation"
rel="tooltip" data-placement="left"> rel="tooltip" data-placement="left">
<span class="label">ANIM</span> <span class="label">ANIM</span>
</div> </div> -->
<div <div
data-setting="png" data-setting="png"
class="tool-icon upload-cloud-icon" class="tool-icon import-icon"
title="Export Spritesheet" title="Export"
rel="tooltip" data-placement="left"> rel="tooltip" data-placement="left">
<span class="label">SHEET</span>
</div> </div>
</div> </div>

View file

@ -1,22 +0,0 @@
<div class="settings-section">
<div class="settings-title">
Export to Animated GIF
</div>
<div class="settings-item">
<form action="" method="POST" class="gif-export-form">
<label>Select resolution:</label>
<script type="text/template" id="gif-export-radio-template">
<label style="display:block"><input type="radio" name="gif-zoom-level" value="{{value}}"/>
{{label}}</label>
</script>
<div class="gif-export-radio-group"></div>
<button type="button" class="button button-primary gif-download-button">Download GIF</button>
<button type="button" class="button button gif-upload-button">Export online</button>
</form>
<span class="gif-export-progress-status"></span>
<div class="gif-export-progress-bar"></div>
<div class="gif-export-preview"></div>
<div class="gif-upload-status"></div>
</div>
</div>

View file

@ -1,23 +0,0 @@
<div class="settings-section">
<div class="settings-title">
Export Spritesheet as PNG
</div>
<div class="settings-item">
<span>Preview : </span>
<div class="png-export-preview"></div>
<div class="png-export-radio-group"></div>
<button type="button" class="button button-primary png-download-button">Download PNG</button>
<button type="button" class="button png-upload-button">Export online</button>
<!-- <input type="button" class="button png-download-button" value="Download" /> -->
<div class="png-upload-status"></div>
</div>
<div class="settings-title">
Export Spritesheet as ZIP
</div>
<div class="settings-item">
<span class="settings-description">A ZIP archive will be created with one PNG file per frame.</span>
<div>
<button type="button" class="button button-primary zip-generate-button"/>Download ZIP</button>
</div>
</div>
</div>

View file

@ -0,0 +1,40 @@
<div class="settings-section">
<div class="settings-title">
Export Spritesheet
</div>
<div class="png-export-preview"></div>
<div class="settings-item">
<span class="settings-description">PNG with all frames side by side.</span>
<button type="button" class="button button-primary png-download-button">Download PNG</button>
</div>
<div class="settings-item">
<span class="settings-description">ZIP with one PNG file per frame.</span>
<div>
<button type="button" class="button button-primary zip-generate-button"/>Download ZIP</button>
</div>
</div>
<div class="settings-title">
Export to Animated GIF
</div>
<div class="settings-item">
<form action="" method="POST" class="gif-export-form">
<div style="margin:10px 0;">
<label>Select resolution:</label>
<select class="gif-export-select-resolution"></select>
<script type="text/template" id="gif-export-option-template">
<option value="{{value}}">{{label}}</option>
</script>
</div>
<div style="margin:10px 0;">
<button type="button" class="button button-primary gif-download-button">Download GIF</button>
<button type="button" class="button button gif-upload-button">Get public URL</button>
</div>
</form>
<div class="clearfix">
<div class="gif-export-preview"></div>
<div class="gif-upload-status"></div>
</div>
<span class="gif-export-progress-status"></span>
<div class="gif-export-progress-bar"></div>
</div>
</div>

View file

@ -1,33 +1,50 @@
<div class="settings-section"> <div class="settings-section">
<div class="settings-title"> <div class="settings-title">
Import Picture Open Piskel Project
</div>
<div class="settings-item">
<form action="" method="POST" name="open-piskel-form">
<span>Load a <span style="font-weight:bold;color:white">.piskel</span> file from your computer.</span>
<div class="import-section">
<button type="button" class="button button-primary open-piskel-button">Open...</button>
<span class="file-input-open-piskel-status"></span>
<input style="display:none"
type="file" name="open-piskel-input"
value="file" accept=".piskel"/>
</div>
</form>
</div>
<div class="settings-title">
Import From Picture
</div> </div>
<div class="settings-item"> <div class="settings-item">
<form action="" method="POST" name="import-form"> <form action="" method="POST" name="import-form">
<div style="margin-top:5px;margin-bottom:5px;">Supports : PNG, JPG, BMP, Animated GIF ...</div>
<div class="import-section"> <div class="import-section">
<span class="import-section-title import-section-title-small">File :</span> <button type="button" class="button button-primary file-input-button">Browse...</button>
<button type="button" class="button button-primary file-input-button">Browse</button>
<span class="file-input-status"></span> <span class="file-input-status"></span>
<input style="display:none" <input style="display:none"
type="file" name="file-upload-input" type="file" name="file-upload-input"
value="file" accept="image/*"/> value="file" accept="image/*"/>
</div> </div>
<div class="import-section import-section-disabled">
<span class="import-section-title import-section-title-small" style="vertical-align:top">Info :</span>
<div class="import-section-preview"></div>
</div>
<div class="import-section import-section-disabled">
<span class="import-section-title import-section-title-small">Size :</span>
<input type="text" disabled="disabled" class="textfield import-size-field" name="resize-width"/>x
<input type="text" disabled="disabled" class="textfield import-size-field" name="resize-height"/>
</div>
<div class="import-section import-section-disabled">
<span class="import-section-title">Smooth resize :</span>
<input type="checkbox" disabled="disabled" checked="checked" name="smooth-resize-checkbox" value="1"/>
</div>
<input type="submit" name="import-submit" disabled="disabled" class="button button-primary import-button" value="Import" />
<span class="settings-description" style="margin-top:5px;">Animated GIFs will be split in several frames. Other images will be imported as a single-frame.</span> <div class="import-options">
<div class="import-section">
<span class="import-section-title import-section-title-small" style="vertical-align:top">Info :</span>
<div class="import-section-preview"></div>
</div>
<div class="import-section">
<span class="import-section-title import-section-title-small">Size :</span>
<input type="text" class="textfield import-size-field" name="resize-width"/>x
<input type="text" class="textfield import-size-field" name="resize-height"/>
</div>
<div class="import-section">
<span class="import-section-title">Smooth resize :</span>
<input type="checkbox" checked="checked" name="smooth-resize-checkbox" value="1"/>
</div>
<input type="submit" name="import-submit" class="button button-primary import-button" value="Import" />
</div>
</form> </form>
</div> </div>

View file

@ -1,7 +1,7 @@
<div class="settings-section"> <div class="settings-section">
<div class="settings-title">Save</div> <form action="" method="POST" name="save-form">
<div class="settings-item"> <div class="settings-title">Describe your piskel</div>
<form action="" method="POST" name="save-form"> <div class="settings-item">
<div class="settings-form-section"> <div class="settings-form-section">
<label class="row">Title : </label> <label class="row">Title : </label>
<input id="save-name" type="text" class="save-field textfield"/> <input id="save-name" type="text" class="save-field textfield"/>
@ -10,14 +10,29 @@
<label class="row">Description :</label> <label class="row">Description :</label>
<textarea id="save-description" class="save-field textfield" placeholder="Your piskel in a few words"></textarea> <textarea id="save-description" class="save-field textfield" placeholder="Your piskel in a few words"></textarea>
</div> </div>
<div class="settings-form-section"> <div class="settings-form-section save-public-section">
<label class="row"> <label class="row">
Public : <input type="checkbox" value="1" name="save-public-checkbox"/> Public : <input type="checkbox" value="1" name="save-public-checkbox"/>
</label> </label>
</div> </div>
<input type="submit" class="button button-primary" id="save-cloud-button" value="Upload" /> </div>
<input type="button" class="button" id="save-local-button" value="Local save" /> <div class="settings-title">Save online</div>
<div id="save-status" class="status"></div> <div class="settings-item">
</form> <input type="submit" class="button button-primary" id="save-online-button" value="Upload" />
</div> <div id="save-online-status" class="save-status"></div>
</div>
<div class="settings-title">Download Project</div>
<div class="settings-item">
<div id="save-local-status" class="save-status"></div>
<input type="button" class="button" id="save-local-button" value="Download Piskel Project" />
</div>
</form>
<script type="text/template" id="save-please-login-partial">
<span>Login to <a href="http://piskelapp.com" target="_blank">piskelapp.com</a> to save and share your sprites online !</span>
</script>
<script type="text/template" id="save-local-status-template">
<span>Your piskel will be downloaded as <span class="save-local-name">{{name}}<span></span>
</script>
</div> </div>