Create 2 new dialog controllers

Image import is now triggering a popup after selecting the file.
Same for local saves.

Drag and drop of .piskel files opens the piskel immediately !

Remains to do :
- redesign the dialog for import image and browse local
- create dialog for recover session
- improve recover session to handle more than the last session
This commit is contained in:
jdescottes
2014-07-12 15:34:50 +02:00
parent 18ff6f88a7
commit ad3dd935e0
20 changed files with 424 additions and 237 deletions

View File

@ -0,0 +1,18 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
ns.AbstractDialogController = function () {};
ns.AbstractDialogController.prototype.init = function () {
this.closeButton = document.querySelector('.dialog-close');
this.closeButton.addEventListener('click', this.closeDialog.bind(this));
};
ns.AbstractDialogController.prototype.destroy = function () {};
ns.AbstractDialogController.prototype.closeDialog = function () {
$.publish(Events.DIALOG_HIDE);
};
})();

View File

@ -0,0 +1,57 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
ns.BrowseLocalController = function (piskelController) {
};
pskl.utils.inherit(ns.BrowseLocalController, ns.AbstractDialogController);
ns.BrowseLocalController.prototype.init = function () {
this.superclass.init.call(this);
this.localStorageItemTemplate_ = pskl.utils.Template.get("local-storage-item-template");
this.service_ = pskl.app.localStorageService;
this.piskelsList = $('.local-piskels-list');
this.prevSessionContainer = $('.previous-session');
this.fillLocalPiskelsList_();
this.piskelsList.click(this.onPiskelsListClick_.bind(this));
};
ns.BrowseLocalController.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);
this.closeDialog();
}
} else if (action === 'delete') {
if (window.confirm('This will permanently DELETE this piskel from your computer. Continue ?')) {
this.service_.remove(name);
this.fillLocalPiskelsList_();
}
}
};
ns.BrowseLocalController.prototype.fillLocalPiskelsList_ = function () {
var html = "";
var keys = this.service_.getKeys();
keys.sort(function (k1, k2) {
if (k1.date < k2.date) {return 1;}
if (k1.date > k2.date) {return -1;}
return 0;
});
keys.forEach((function (key) {
var date = pskl.utils.DateUtils.format(key.date, "{{Y}}/{{M}}/{{D}} {{H}}:{{m}}");
html += pskl.utils.Template.replace(this.localStorageItemTemplate_, {name : key.name, date : date});
}).bind(this));
var tableBody_ = this.piskelsList.get(0).tBodies[0];
tableBody_.innerHTML = html;
};
})();

View File

@ -5,6 +5,14 @@
'manage-palettes' : {
template : 'templates/dialogs/manage-palettes.html',
controller : ns.PaletteManagerController
},
'browse-local' : {
template : 'templates/dialogs/browse-local.html',
controller : ns.BrowseLocalController
},
'import-image' : {
template : 'templates/dialogs/import-image.html',
controller : ns.ImportImageController
}
};
@ -22,13 +30,20 @@
pskl.app.shortcutService.addShortcut('alt+P', this.onDialogDisplayEvent_.bind(this, null, 'manage-palettes'));
};
ns.DialogsController.prototype.onDialogDisplayEvent_ = function (evt, dialogId) {
ns.DialogsController.prototype.onDialogDisplayEvent_ = function (evt, args) {
var dialogId, initArgs;
if (typeof args === 'string') {
dialogId = args;
} else {
dialogId = args.dialogId;
initArgs = args.initArgs;
}
if (!this.isDisplayed()) {
var config = dialogs[dialogId];
if (config) {
this.dialogContainer_.innerHTML = pskl.utils.Template.get(config.template);
var controller = new config.controller(this.piskelController);
controller.init();
controller.init(initArgs);
this.showDialogWrapper_();
this.currentDialog_ = {

View File

@ -0,0 +1,154 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
var PREVIEW_HEIGHT = 60;
ns.ImportImageController = function (piskelController) {
this.importedImage_ = null;
this.file_ = null;
};
pskl.utils.inherit(ns.ImportImageController, ns.AbstractDialogController);
ns.ImportImageController.prototype.init = function (file) {
this.superclass.init.call(this);
this.file_ = file;
this.importPreview = $('.import-section-preview');
this.fileNameContainer = $('.import-image-file-name');
this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]');
this.smoothResize = $('[name=smooth-resize-checkbox]');
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
this.importImageForm = $('[name=import-image-form]');
this.importImageForm.submit(this.onImportFormSubmit_.bind(this));
pskl.utils.FileUtils.readFile(this.file_, this.processImageSource_.bind(this));
};
ns.ImportImageController.prototype.onImportFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.importImageToPiskel_();
};
ns.ImportImageController.prototype.onResizeInputKeyUp_ = function (from, evt) {
if (this.importedImage_) {
this.synchronizeResizeFields_(evt.target.value, from);
}
};
ns.ImportImageController.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));
}
};
/**
* 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.ImportImageController.prototype.processImageSource_ = function (imageSource) {
this.importedImage_ = new Image();
this.importedImage_.onload = this.onImageLoaded_.bind(this);
this.importedImage_.src = imageSource;
};
ns.ImportImageController.prototype.onImageLoaded_ = function (evt) {
var w = this.importedImage_.width,
h = this.importedImage_.height;
// FIXME : We remove the onload callback here because JsGif will insert
// the image again and we want to avoid retriggering the image onload
this.importedImage_.onload = function () {};
var fileName = this.extractFileNameFromPath_(this.file_.name);
this.fileNameContainer.html(fileName);
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.importPreview.width('auto');
this.importPreview.html('');
this.importPreview.append(this.createImagePreview_());
};
ns.ImportImageController.prototype.createImagePreview_ = function () {
var image = document.createElement('IMG');
image.src = this.importedImage_.src;
image.setAttribute('height', PREVIEW_HEIGHT);
return image;
};
ns.ImportImageController.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.ImportImageController.prototype.importImageToPiskel_ = function () {
var image = this.importedImage_;
if (image) {
if (window.confirm('You are about to create a new Piskel, unsaved changes will be lost.')) {
var gifLoader = new window.SuperGif({
gif : image
});
gifLoader.load({
success : function(){
var images = gifLoader.getFrames().map(function (frame) {
return pskl.CanvasUtils.createFromImageData(frame.data);
});
this.createPiskelFromImages_(images);
this.closeDialog();
}.bind(this),
error : function () {
this.createPiskelFromImages_([image]);
this.closeDialog();
}.bind(this)
});
}
}
};
ns.ImportImageController.prototype.createFramesFromImages_ = function (images) {
var w = this.resizeWidth.val();
var h = this.resizeHeight.val();
var smoothing = !!this.smoothResize.prop('checked');
var frames = images.map(function (image) {
var resizedImage = pskl.utils.ImageResizer.resize(image, w, h, smoothing);
return pskl.utils.FrameUtils.createFromImage(resizedImage);
});
return frames;
};
ns.ImportImageController.prototype.createPiskelFromImages_ = function (images) {
var frames = this.createFramesFromImages_(images);
var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
var descriptor = new pskl.model.piskel.Descriptor('Imported piskel', '');
var piskel = pskl.model.Piskel.fromLayers([layer], descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
};
})();

View File

@ -18,13 +18,16 @@
this.spectrumContainers = [];
};
pskl.utils.inherit(ns.PaletteManagerController, ns.AbstractDialogController);
ns.PaletteManagerController.prototype.init = function () {
this.superclass.init.call(this);
this.palettesList = document.querySelector('.palette-manager-list');
this.paletteBody = document.querySelector('.palette-manager-details-body');
this.paletteHead = document.querySelector('.palette-manager-details-head');
this.createButton = document.querySelector('.palette-manager-actions-button[data-action="create"]');
this.saveAllButton = document.querySelector('.palette-manager-actions-button[data-action="save-all"]');
this.closeButton = document.querySelector('.palette-manager-close');
this.colorCardTemplate = pskl.utils.Template.get('palette-color-card-template');
this.newColorTemplate = pskl.utils.Template.get('palette-new-color-template');
@ -37,7 +40,6 @@
this.paletteHead.addEventListener('click', this.delegatedPaletteHeadClick.bind(this));
this.createButton.addEventListener('click', this.onCreateClick_.bind(this));
this.saveAllButton.addEventListener('click', this.saveAll.bind(this));
this.closeButton.addEventListener('click', this.closeDialog.bind(this));
// Init markup
this.createPaletteListMarkup();
@ -52,10 +54,6 @@
this.destroySpectrumPickers();
};
ns.PaletteManagerController.prototype.closeDialog = function () {
$.publish(Events.DIALOG_HIDE);
};
ns.PaletteManagerController.prototype.onCreateClick_ = function (evt) {
this.createPalette();
};

View File

@ -1,7 +1,5 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = '';
var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) {
this.piskelController = piskelController;
@ -12,61 +10,22 @@
this.hiddenOpenPiskelInput = $('[name=open-piskel-input]');
this.openPiskelInputButton = $('.open-piskel-button');
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.browseLocalButton = document.querySelector('.browse-local-button');
this.browseLocalButton.addEventListener('click', this.onBrowseLocalClick_.bind(this));
this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]');
this.smoothResize = $('[name=smooth-resize-checkbox]');
$('.import-options').hide();
this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.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.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
};
ns.ImportController.prototype.reset_ = function () {
this.importForm.get(0).reset();
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
ns.ImportController.prototype.closeDrawer_ = function () {
$.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.importPictureFromFile_();
};
@ -83,40 +42,39 @@
this.hiddenOpenPiskelInput.click();
};
ns.ImportController.prototype.onBrowseLocalClick_ = function (evt) {
$.publish(Events.DIALOG_DISPLAY, 'browse-local');
this.closeDrawer_();
};
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);
});
pskl.utils.PiskelFileUtils.loadFromFile(file, function (piskel, descriptor, fps) {
piskel.setDescriptor(descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(fps);
});
this.reset_();
this.closeDrawer_();
}
}
};
ns.ImportController.prototype.importPictureFromFile_ = 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_();
$.publish(Events.DIALOG_DISPLAY, {
dialogId : 'import-image',
initArgs : file
});
this.closeDrawer_();
} else {
this.reset_();
this.closeDrawer_();
throw 'File is not an image : ' + file.type;
}
}
@ -128,108 +86,6 @@
$('.import-options').show();
};
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;
// FIXME : We remove the onload callback here because JsGif will insert
// the image again and we want to avoid retriggering the image onload
this.importedImage_.onload = function () {};
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 () {
var image = this.importedImage_;
if (image) {
if (window.confirm('You are about to create a new Piskel, unsaved changes will be lost.')) {
var gifLoader = new window.SuperGif({
gif : image
});
gifLoader.load({
success : function(){
var images = gifLoader.getFrames().map(function (frame) {
return pskl.CanvasUtils.createFromImageData(frame.data);
});
this.createPiskelFromImages_(images);
}.bind(this),
error : function () {
this.createPiskelFromImages_([image]);
}.bind(this)
});
}
}
};
ns.ImportController.prototype.createFramesFromImages_ = function (images) {
var w = this.resizeWidth.val();
var h = this.resizeHeight.val();
var smoothing = !!this.smoothResize.prop('checked');
var frames = images.map(function (image) {
var resizedImage = pskl.utils.ImageResizer.resize(image, w, h, smoothing);
return pskl.utils.FrameUtils.createFromImage(resizedImage);
});
return frames;
};
ns.ImportController.prototype.createPiskelFromImages_ = function (images) {
var frames = this.createFramesFromImages_(images);
var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
var descriptor = new pskl.model.piskel.Descriptor('Imported piskel', '');
var piskel = pskl.model.Piskel.fromLayers([layer], descriptor);
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;
};