From ad3dd935e0fbc69137795003dfea18466f40c081 Mon Sep 17 00:00:00 2001 From: jdescottes Date: Sat, 12 Jul 2014 15:34:50 +0200 Subject: [PATCH] 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 --- src/css/dialogs.css | 25 +++ src/css/settings.css | 2 +- src/index.html | 2 + src/js/app.js | 4 +- .../dialogs/AbstractDialogController.js | 18 ++ .../dialogs/BrowseLocalController.js | 57 ++++++ .../controller/dialogs/DialogsController.js | 19 +- .../dialogs/ImportImageController.js | 154 +++++++++++++++ .../dialogs/PaletteManagerController.js | 10 +- .../controller/settings/ImportController.js | 182 ++---------------- ...ropperService.js => FileDropperService.js} | 31 +-- src/js/utils/DateUtils.js | 1 + src/js/utils/PiskelFileUtils.js | 17 ++ src/piskel-script-list.js | 6 +- src/templates/dialogs/browse-local.html | 27 +++ src/templates/dialogs/import-image.html | 27 +++ src/templates/dialogs/manage-palettes.html | 4 +- src/templates/settings.html | 18 +- src/templates/settings/import.html | 55 ++---- src/templates/settings/save.html | 2 +- 20 files changed, 424 insertions(+), 237 deletions(-) create mode 100644 src/js/controller/dialogs/AbstractDialogController.js create mode 100644 src/js/controller/dialogs/BrowseLocalController.js create mode 100644 src/js/controller/dialogs/ImportImageController.js rename src/js/service/{ImageDropperService.js => FileDropperService.js} (67%) create mode 100644 src/js/utils/PiskelFileUtils.js create mode 100644 src/templates/dialogs/browse-local.html create mode 100644 src/templates/dialogs/import-image.html diff --git a/src/css/dialogs.css b/src/css/dialogs.css index d70aff5a..04909a47 100644 --- a/src/css/dialogs.css +++ b/src/css/dialogs.css @@ -40,6 +40,7 @@ -moz-box-sizing : border-box; border-radius: 3px; + border : 3px solid gold; background: rgba(0,0,0,1); overflow: auto; } @@ -48,3 +49,27 @@ margin-top: 0; } +.dialog-wrapper { + position : relative; +} + +.dialog-head { + width: 100%; + background: gold; + margin: 0; + padding: 10px; + color: black; + font-size: 1.8em; + box-sizing: border-box; + -moz-box-sizing: border-box; +} + +.dialog-close { + position: absolute; + top: 0; + right: 0; + line-height: 45px; + margin-right: 10px; + font-size: 1.3em; + cursor: pointer; +} diff --git a/src/css/settings.css b/src/css/settings.css index 895a4650..1f03c652 100644 --- a/src/css/settings.css +++ b/src/css/settings.css @@ -314,7 +314,7 @@ border-radius: 2px; } -.file-input-status { +.import-image-file-name { display: inline-block; width: 130px; overflow: hidden; diff --git a/src/index.html b/src/index.html index d66d8894..553d7e87 100644 --- a/src/index.html +++ b/src/index.html @@ -69,6 +69,8 @@
+ +
diff --git a/src/js/app.js b/src/js/app.js index 0c18c176..6586b793 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -105,8 +105,8 @@ this.beforeUnloadService = new pskl.service.BeforeUnloadService(this.piskelController); this.beforeUnloadService.init(); - this.imageDropperService = new pskl.service.ImageDropperService(this.piskelController, $('#drawing-canvas-container').get(0)); - this.imageDropperService.init(); + this.fileDropperService = new pskl.service.FileDropperService(this.piskelController, $('#drawing-canvas-container').get(0)); + this.fileDropperService.init(); if (this.isAppEngineVersion) { diff --git a/src/js/controller/dialogs/AbstractDialogController.js b/src/js/controller/dialogs/AbstractDialogController.js new file mode 100644 index 00000000..1c8c7715 --- /dev/null +++ b/src/js/controller/dialogs/AbstractDialogController.js @@ -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); + }; + +})(); \ No newline at end of file diff --git a/src/js/controller/dialogs/BrowseLocalController.js b/src/js/controller/dialogs/BrowseLocalController.js new file mode 100644 index 00000000..1fce176a --- /dev/null +++ b/src/js/controller/dialogs/BrowseLocalController.js @@ -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; + }; +})(); \ No newline at end of file diff --git a/src/js/controller/dialogs/DialogsController.js b/src/js/controller/dialogs/DialogsController.js index 6f5c526a..9a8b8c91 100644 --- a/src/js/controller/dialogs/DialogsController.js +++ b/src/js/controller/dialogs/DialogsController.js @@ -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_ = { diff --git a/src/js/controller/dialogs/ImportImageController.js b/src/js/controller/dialogs/ImportImageController.js new file mode 100644 index 00000000..08f6d417 --- /dev/null +++ b/src/js/controller/dialogs/ImportImageController.js @@ -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); + }; +})(); \ No newline at end of file diff --git a/src/js/controller/dialogs/PaletteManagerController.js b/src/js/controller/dialogs/PaletteManagerController.js index 85e7a1ad..dc16ba4d 100644 --- a/src/js/controller/dialogs/PaletteManagerController.js +++ b/src/js/controller/dialogs/PaletteManagerController.js @@ -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(); }; diff --git a/src/js/controller/settings/ImportController.js b/src/js/controller/settings/ImportController.js index 21e4fabf..fece800a 100644 --- a/src/js/controller/settings/ImportController.js +++ b/src/js/controller/settings/ImportController.js @@ -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; }; diff --git a/src/js/service/ImageDropperService.js b/src/js/service/FileDropperService.js similarity index 67% rename from src/js/service/ImageDropperService.js rename to src/js/service/FileDropperService.js index 0db75e6a..d649c05c 100644 --- a/src/js/service/ImageDropperService.js +++ b/src/js/service/FileDropperService.js @@ -1,17 +1,23 @@ (function () { var ns = $.namespace('pskl.service'); - ns.ImageDropperService = function (piskelController, drawingAreaContainer) { + ns.FileDropperService = function (piskelController, drawingAreaContainer) { this.piskelController = piskelController; this.drawingAreaContainer = drawingAreaContainer; }; - ns.ImageDropperService.prototype.init = function () { + ns.FileDropperService.prototype.init = function () { document.body.addEventListener('drop', this.onFileDrop.bind(this), false); document.body.addEventListener('dragover', this.onFileDragOver.bind(this), false); }; - ns.ImageDropperService.prototype.onFileDrop = function (event) { + ns.FileDropperService.prototype.onFileDragOver = function (event) { + event.stopPropagation(); + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + }; + + ns.FileDropperService.prototype.onFileDrop = function (event) { event.preventDefault(); event.stopPropagation(); @@ -25,28 +31,31 @@ var isImage = file.type.indexOf('image') === 0; if (isImage) { this.readImageFile_(file); + } else if (/\.piskel$/i.test(file.name)) { + pskl.utils.PiskelFileUtils.loadFromFile(file, this.onPiskelFileLoaded_); } } }; - ns.ImageDropperService.prototype.onFileDragOver = function (event) { - event.stopPropagation(); - event.preventDefault(); - event.dataTransfer.dropEffect = 'Copy image on the frame'; // Explicitly show this is a copy. + ns.FileDropperService.prototype.onPiskelFileLoaded_ = function (piskel, descriptor, fps) { + if (window.confirm('This will replace your current animation')) { + piskel.setDescriptor(descriptor); + pskl.app.piskelController.setPiskel(piskel); + pskl.app.animationController.setFPS(fps); + } }; - - ns.ImageDropperService.prototype.readImageFile_ = function (imageFile) { + ns.FileDropperService.prototype.readImageFile_ = function (imageFile) { pskl.utils.FileUtils.readFile(imageFile, this.processImageSource_.bind(this)); }; - ns.ImageDropperService.prototype.processImageSource_ = function (imageSource) { + ns.FileDropperService.prototype.processImageSource_ = function (imageSource) { this.importedImage_ = new Image(); this.importedImage_.onload = this.onImageLoaded_.bind(this); this.importedImage_.src = imageSource; }; - ns.ImageDropperService.prototype.onImageLoaded_ = function () { + ns.FileDropperService.prototype.onImageLoaded_ = function () { var frame = pskl.utils.FrameUtils.createFromImage(this.importedImage_); var currentFrame = this.piskelController.getCurrentFrame(); diff --git a/src/js/utils/DateUtils.js b/src/js/utils/DateUtils.js index 3e45e28c..150bb134 100644 --- a/src/js/utils/DateUtils.js +++ b/src/js/utils/DateUtils.js @@ -11,6 +11,7 @@ ns.DateUtils = { format : function (date, format) { + date = new Date(date); return pskl.utils.Template.replace(format, { Y : date.getFullYear(), M : pad(date.getMonth() + 1), diff --git a/src/js/utils/PiskelFileUtils.js b/src/js/utils/PiskelFileUtils.js new file mode 100644 index 00000000..60271b3c --- /dev/null +++ b/src/js/utils/PiskelFileUtils.js @@ -0,0 +1,17 @@ +(function () { + var ns = $.namespace('pskl.utils'); + + ns.PiskelFileUtils = { + loadFromFile : function (file, onSuccess, onError) { + pskl.utils.FileUtils.readFile(file, function (content) { + var rawPiskel = window.atob(content.replace('data:;base64,','')); + var serializedPiskel = JSON.parse(rawPiskel); + var fps = serializedPiskel.piskel.fps; + var descriptor = new pskl.model.piskel.Descriptor(serializedPiskel.piskel.name, serializedPiskel.piskel.description, true); + pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, function (piskel) { + onSuccess(piskel, descriptor, fps); + }); + }); + } + }; +})(); \ No newline at end of file diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index da9b4755..f3d80170 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -23,6 +23,7 @@ "js/utils/LayerUtils.js", "js/utils/ImageResizer.js", "js/utils/PixelUtils.js", + "js/utils/PiskelFileUtils.js", "js/utils/Template.js", "js/utils/UserSettings.js", "js/utils/serialization/Serializer.js", @@ -97,7 +98,10 @@ "js/controller/settings/SettingsController.js", // Dialogs sub-controllers + "js/controller/dialogs/AbstractDialogController.js", "js/controller/dialogs/PaletteManagerController.js", + "js/controller/dialogs/ImportImageController.js", + "js/controller/dialogs/BrowseLocalController.js", // Dialogs controller "js/controller/dialogs/DialogsController.js", @@ -115,7 +119,7 @@ "js/service/keyboard/CheatsheetService.js", "js/service/ImageUploadService.js", "js/service/CurrentColorsService.js", - "js/service/ImageDropperService.js", + "js/service/FileDropperService.js", // Tools "js/drawingtools/BaseTool.js", diff --git a/src/templates/dialogs/browse-local.html b/src/templates/dialogs/browse-local.html new file mode 100644 index 00000000..8b30ff36 --- /dev/null +++ b/src/templates/dialogs/browse-local.html @@ -0,0 +1,27 @@ +
+

+ Browse Local Piskels + X +

+
+ + +
+
+ + +
\ No newline at end of file diff --git a/src/templates/dialogs/import-image.html b/src/templates/dialogs/import-image.html new file mode 100644 index 00000000..ed28214d --- /dev/null +++ b/src/templates/dialogs/import-image.html @@ -0,0 +1,27 @@ +
+

+ Import Image + X +

+
+
+
+ +
+
+ Info : +
+
+
+ Size : + x + +
+
+ Smooth resize : + +
+ +
+
+
\ No newline at end of file diff --git a/src/templates/dialogs/manage-palettes.html b/src/templates/dialogs/manage-palettes.html index 59cb296f..795cbcaa 100644 --- a/src/templates/dialogs/manage-palettes.html +++ b/src/templates/dialogs/manage-palettes.html @@ -1,7 +1,7 @@
-

+

Palette manager - X + X

diff --git a/src/templates/settings.html b/src/templates/settings.html index 8e4d926b..60ed7acc 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -22,19 +22,26 @@ rel="tooltip" data-placement="left" >
+
+
+
- + - -
-
\ No newline at end of file diff --git a/src/templates/settings/import.html b/src/templates/settings/import.html index 954ca5bb..493fe83c 100644 --- a/src/templates/settings/import.html +++ b/src/templates/settings/import.html @@ -5,57 +5,34 @@
Load a local piskel saved in this Browser
- +
Load .piskel file
-
- Load a .piskel file from your computer + Load a .piskel file from your computer -
- - - -
-
+
+ + + +
Import From Picture
-
-
Supports : PNG, JPG, BMP, Animated GIF ...
-
- - - -
- -
-
- Info : -
-
-
- Size : - x - -
-
- Smooth resize : - -
- -
- -
+
Supports : PNG, JPG, BMP, Animated GIF ...
+
+ + +
Recover recent sessions diff --git a/src/templates/settings/save.html b/src/templates/settings/save.html index 4c5d9542..c4ccb36d 100644 --- a/src/templates/settings/save.html +++ b/src/templates/settings/save.html @@ -22,7 +22,7 @@
Save offline in Browser
-
Your piskel will be saved in the browser and will only be accessible from this browser.
+
Your piskel will be saved locally and will only be accessible from this browser.
Save offline as File