diff --git a/js/Events.js b/js/Events.js
index 64985864..e7e5e7ea 100644
--- a/js/Events.js
+++ b/js/Events.js
@@ -39,6 +39,9 @@ var Events = {
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
+ /* Listened to by SettingsController */
+ 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.
diff --git a/js/app.js b/js/app.js
index def736df..3262db09 100644
--- a/js/app.js
+++ b/js/app.js
@@ -33,7 +33,7 @@
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
this.layersListController.init();
- this.settingsController = new pskl.controller.SettingsController(this.piskelController);
+ this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
@@ -94,7 +94,7 @@
finishInitAppEngine_ : function () {
if (pskl.framesheetData_ && pskl.framesheetData_.content) {
var piskel = pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);
- piskel.app.PiskelController.setPiskel(piskel);
+ pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
}
},
diff --git a/js/controller/settings/GifExportController.js b/js/controller/settings/GifExportController.js
index 58ed6ca6..42e57d3d 100644
--- a/js/controller/settings/GifExportController.js
+++ b/js/controller/settings/GifExportController.js
@@ -24,9 +24,9 @@
];
ns.GifExportController.prototype.init = function () {
- this.radioTemplate_ = pskl.utils.Template.get("export-gif-radio-template");
+ this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template");
- this.previewContainerEl = document.querySelectorAll(".export-gif-preview div")[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]");
@@ -56,7 +56,7 @@
};
ns.GifExportController.prototype.updatePreview_ = function (src) {
- this.previewContainerEl.innerHTML = "";
+ this.previewContainerEl.innerHTML = "
";
};
ns.GifExportController.prototype.getSelectedDpi_ = function () {
diff --git a/js/controller/settings/ImportController.js b/js/controller/settings/ImportController.js
new file mode 100644
index 00000000..f28166f9
--- /dev/null
+++ b/js/controller/settings/ImportController.js
@@ -0,0 +1,168 @@
+(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.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 piskel = pskl.utils.Serializer.createPiskel([frame]);
+ 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;
+ };
+
+})();
\ No newline at end of file
diff --git a/js/controller/SettingsController.js b/js/controller/settings/SettingsController.js
similarity index 79%
rename from js/controller/SettingsController.js
rename to js/controller/settings/SettingsController.js
index 611eebf2..708b3fa1 100644
--- a/js/controller/SettingsController.js
+++ b/js/controller/settings/SettingsController.js
@@ -1,14 +1,18 @@
(function () {
- var ns = $.namespace("pskl.controller");
-
+ var ns = $.namespace("pskl.controller.settings");
+
var settings = {
- user : {
- template : 'templates/settings-application.html',
- controller : ns.settings.ApplicationSettingsController
+ 'user' : {
+ template : 'templates/settings/application.html',
+ controller : ns.ApplicationSettingsController
},
- gif : {
- template : 'templates/settings-export-gif.html',
- controller : ns.settings.GifExportController
+ 'gif' : {
+ template : 'templates/settings/export-gif.html',
+ controller : ns.GifExportController
+ },
+ 'import' : {
+ template : 'templates/settings/import.html',
+ controller : ns.ImportController
}
};
@@ -44,14 +48,16 @@
this.closeDrawer();
}
}.bind(this));
+
+ $.subscribe(Events.CLOSE_SETTINGS_DRAWER, this.closeDrawer.bind(this));
};
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);
@@ -66,5 +72,5 @@
this.expanded = false;
this.currentSetting = null;
};
-
+
})();
\ No newline at end of file
diff --git a/js/utils/FileUtils.js b/js/utils/FileUtils.js
new file mode 100644
index 00000000..74f25892
--- /dev/null
+++ b/js/utils/FileUtils.js
@@ -0,0 +1,13 @@
+(function () {
+ var ns = $.namespace('pskl.utils');
+
+ ns.FileUtils = {
+ readFile : function (file, callback) {
+ var reader = new FileReader();
+ reader.onload = function(event){
+ callback(event.target.result);
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+})();
\ No newline at end of file
diff --git a/js/utils/FrameUtils.js b/js/utils/FrameUtils.js
index cb615527..ef14f935 100644
--- a/js/utils/FrameUtils.js
+++ b/js/utils/FrameUtils.js
@@ -20,6 +20,62 @@
frameA.setPixel(col, row, p);
}
});
+ },
+
+ /**
+ * Create a pskl.model.Frame from an Image object.
+ * Transparent pixels will either be converted to completely opaque or completely transparent pixels.
+ * @param {Image} image source image
+ * @return {pskl.model.Frame} corresponding frame
+ */
+ createFromImage : function (image) {
+ var w = image.width,
+ h = image.height;
+ var canvas = pskl.CanvasUtils.createCanvas(w, h);
+ var context = canvas.getContext('2d');
+
+ context.drawImage(image, 0,0,w,h,0,0,w,h);
+ var imgData = context.getImageData(0,0,w,h).data;
+ // Draw the zoomed-up pixels to a different canvas context
+ var frame = [];
+ for (var x=0;x
+
+
+
diff --git a/templates/layers-list.html b/templates/layers-list.html
index 12636bfe..f9ccfec1 100644
--- a/templates/layers-list.html
+++ b/templates/layers-list.html
@@ -1,10 +1,10 @@
Layers
-
-
-
-
+
+
+
+
-
+
+
-
+
\ No newline at end of file
diff --git a/templates/settings/import.html b/templates/settings/import.html
new file mode 100644
index 00000000..405cd092
--- /dev/null
+++ b/templates/settings/import.html
@@ -0,0 +1,31 @@
+