diff --git a/Gruntfile.js b/Gruntfile.js
index 497085a8..f11c60fd 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -37,8 +37,7 @@ module.exports = function(grunt) {
undef : true,
latedef : true,
browser : true,
- jquery : true,
- globals : {'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true}
+ globals : {'$':true, 'jQuery' : true, 'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true}
},
files: [
'Gruntfile.js',
diff --git a/css/settings.css b/css/settings.css
new file mode 100644
index 00000000..d872702a
--- /dev/null
+++ b/css/settings.css
@@ -0,0 +1,118 @@
+
+/** Righty sticky drawer expanded state. */
+
+.right-sticky-section.sticky-section {
+ right: 0;
+ width: 47px;
+
+ -webkit-transition: all 200ms ease-out;
+ -moz-transition: all 200ms ease-out;
+ -ms-transition: all 200ms ease-out;
+ transition: all 200ms ease-out;
+}
+
+.right-sticky-section.expanded {
+ right: 280px;
+}
+
+.right-sticky-section .tool-icon {
+ float: right;
+ margin-right: 0;
+}
+
+.drawer-content {
+ overflow: hidden;
+ background-color: #444;
+ height: 550px;
+ max-height: 100%;
+ width: 280px;
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.right-sticky-section.expanded .tool-icon {
+ margin-right: 1px;
+}
+
+.right-sticky-section .tool-icon.has-expanded-drawer {
+ position: relative;
+ background-color: #444;
+ margin-right: 0;
+ padding-right: 1px;
+}
+
+.settings-section {
+ margin: 10px 20px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #ccc;
+ text-shadow: 1px 1px #000;
+}
+
+.settings-title {
+ margin-top: 20px;
+ margin-bottom: 10px;
+ text-transform: uppercase;
+ border-bottom: 1px #aaa solid;
+ padding-bottom: 5px;
+}
+
+.settings-item {}
+
+.background-picker-wrapper {
+ overflow: hidden;
+ padding: 10px 5px 20px 5px;
+}
+
+.background-picker {
+ cursor: pointer;
+ float: left;
+ height: 35px;
+ width: 35px;
+ background-color: transparent;
+ margin-right: 15px;
+ padding: 1px;
+ position: relative;
+}
+
+.background-picker:after {
+ content: " ";
+ position: absolute;
+ top: -2px;
+ right: -2px;
+ bottom: -2px;
+ left: -2px;
+}
+
+.background-picker:hover:after {
+ border: #eee 1px solid;
+}
+
+.background-picker.selected:after {
+ border: gold 1px solid;
+}
+
+/* Gif Export Setting panel*/
+.export-gif-upload-button {
+ margin-top : 10px;
+}
+
+.export-gif-preview {
+ margin-top:20px;
+ max-width:240px;
+ position:relative;
+}
+
+.preview-upload-ongoing:before{
+ content: "Upload ongoing ...";
+ position: absolute;
+ display: block;
+ height: 100%;
+ width: 100%;
+ text-align: center;
+ padding-top: 45%;
+ box-sizing:border-box;
+ -moz-box-sizing:border-box;
+ background: rgba(0,0,0,0.5);
+ color: white;
+}
\ No newline at end of file
diff --git a/css/style.css b/css/style.css
index 453b2751..50428da5 100644
--- a/css/style.css
+++ b/css/style.css
@@ -98,103 +98,6 @@ body {
float: left;
}
-.right-sticky-section.sticky-section {
- right: 0;
- width: 47px;
-
- -webkit-transition: all 200ms ease-out;
- -moz-transition: all 200ms ease-out;
- -ms-transition: all 200ms ease-out;
- transition: all 200ms ease-out;
-}
-
-.right-sticky-section .tool-icon {
- float: right;
- margin-right: 0;
-}
-
-.drawer {
-
-}
-
-.drawer-content {
- overflow: hidden;
- background-color: #444;
- height: 550px;
- max-height: 100%;
- width: 280px;
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
-}
-
-/** Righty sticky drawer expanded state. */
-
-.right-sticky-section.expanded {
- right: 280px;
-}
-
-.right-sticky-section.expanded .tool-icon {
- margin-right: 1px;
-}
-
-.right-sticky-section .tool-icon.has-expanded-drawer {
- position: relative;
- background-color: #444;
- margin-right: 0;
- padding-right: 1px;
-}
-
-.settings-section {
- margin: 10px 20px;
- font-size: 12px;
- font-weight: bold;
- color: #ccc;
- text-shadow: 1px 1px #000;
-}
-
-.settings-title {
- margin-top: 20px;
- margin-bottom: 10px;
- text-transform: uppercase;
- border-bottom: 1px #aaa solid;
- padding-bottom: 5px;
-}
-
-.settings-item {}
-
-.background-picker-wrapper {
- overflow: hidden;
- padding: 10px 5px 20px 5px;
-}
-
-.background-picker {
- cursor: pointer;
- float: left;
- height: 35px;
- width: 35px;
- background-color: transparent;
- margin-right: 15px;
- padding: 1px;
- position: relative;
-}
-
-.background-picker:after {
- content: " ";
- position: absolute;
- top: -2px;
- right: -2px;
- bottom: -2px;
- left: -2px;
-}
-
-.background-picker:hover:after {
- border: #eee 1px solid;
-}
-
-.background-picker.selected:after {
- border: gold 1px solid;
-}
-
/**
* Canvases layout
*/
diff --git a/index.html b/index.html
index 9a178db9..3035a2cc 100644
--- a/index.html
+++ b/index.html
@@ -9,6 +9,7 @@
+
@@ -22,37 +23,34 @@
diff --git a/js/controller/SettingsController.js b/js/controller/SettingsController.js
index 353d201d..2a5b4c01 100644
--- a/js/controller/SettingsController.js
+++ b/js/controller/SettingsController.js
@@ -15,9 +15,10 @@
var SEL_SETTING_CLS = 'has-expanded-drawer';
var EXP_DRAWER_CLS = 'expanded';
- ns.SettingsController = function () {
+ ns.SettingsController = function (framesheet) {
+ this.framesheet = framesheet;
this.drawerContainer = document.getElementById("drawer-container");
- this.settingsContainer = $('.right-sticky-section');
+ this.settingsContainer = $('[data-pskl-controller=settings]');
this.expanded = false;
this.currentSetting = null;
};
@@ -28,7 +29,7 @@
ns.SettingsController.prototype.init = function() {
// Expand drawer when clicking 'Settings' tab.
$('[data-setting]').click(function(evt) {
- var el = event.currentTarget;
+ var el = evt.originalEvent.currentTarget;
var setting = el.dataset.setting;
if (this.currentSetting != setting) {
this.loadSetting(setting);
@@ -36,17 +37,25 @@
this.closeDrawer();
}
}.bind(this));
+
+ $('body').click(function (evt) {
+ var isInSettingsContainer = $.contains(this.settingsContainer.get(0), evt.target);
+ if (this.expanded && !isInSettingsContainer) {
+ this.closeDrawer();
+ }
+ }.bind(this));
};
ns.SettingsController.prototype.loadSetting = function (setting) {
- this.drawerContainer.innerHTML = this.getTemplate_(settings[setting].template);
- (new settings[setting].controller()).init();
+ this.drawerContainer.innerHTML = pskl.utils.Template.get(settings[setting].template);
+ (new settings[setting].controller(this.framesheet)).init();
this.settingsContainer.addClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
$('[data-setting='+setting+']').addClass(SEL_SETTING_CLS);
+ this.expanded = true;
this.currentSetting = setting;
};
@@ -54,16 +63,8 @@
this.settingsContainer.removeClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
+ this.expanded = false;
this.currentSetting = null;
};
-
- ns.SettingsController.prototype.getTemplate_ = function (templateId) {
- var template = document.getElementById(templateId);
- if (template) {
- return template.innerHTML;
- } else {
- console.error("Could not find template for id :", templateId);
- }
- };
})();
\ No newline at end of file
diff --git a/js/controller/settings/GifExportController.js b/js/controller/settings/GifExportController.js
index c3c36d4d..ff59abf2 100644
--- a/js/controller/settings/GifExportController.js
+++ b/js/controller/settings/GifExportController.js
@@ -1,8 +1,107 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
- ns.GifExportController = function () {
-
+ ns.GifExportController = function (framesheet) {
+ this.framesheet = framesheet;
};
- ns.GifExportController.prototype.init = function () {};
+ ns.GifExportController.prototype.init = function () {
+ this.initRadioElements_();
+
+ this.previewContainer = document.querySelectorAll(".export-gif-preview div")[0];
+ this.uploadForm = $("[name=gif-export-upload-form]");
+
+ this.uploadForm.submit(this.upload.bind(this));
+ };
+
+ ns.GifExportController.prototype.upload = function (evt) {
+ evt.originalEvent.preventDefault();
+ var selectedDpi = this.getSelectedDpi_(),
+ fps = pskl.app.animationController.fps,
+ dpi = selectedDpi;
+
+ this.renderAsImageDataAnimatedGIF(dpi, fps, function (imageData) {
+ this.updatePreview_(imageData);
+ this.previewContainer.classList.add("preview-upload-ongoing");
+ pskl.app.imageUploadService.upload(imageData, function (imageUrl) {
+ this.updatePreview_(imageUrl);
+ this.previewContainer.classList.remove("preview-upload-ongoing");
+ }.bind(this));
+ }.bind(this));
+ };
+
+ ns.GifExportController.prototype.updatePreview_ = function (src) {
+ this.previewContainer.innerHTML = "
";
+ };
+
+ ns.GifExportController.prototype.getSelectedDpi_ = function () {
+ var radiosColl = this.uploadForm.get(0).querySelectorAll("[name=gif-dpi]"),
+ 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 dpi";
+ }
+ };
+
+ ns.GifExportController.prototype.initRadioElements_ = function () {
+ var dpis = [
+ [1],
+ [5],
+ [10,true] //default
+ ];
+
+ var radioTpl = $("#export-gif-radio-template").get(0);
+ for (var i = 0 ; i < dpis.length ; i++) {
+ var dpi = dpis[i];
+ var radio = this.createRadioForDpi_(dpi, radioTpl.innerHTML);
+ radioTpl.parentNode.insertBefore(radio, radioTpl);
+ }
+ };
+
+ ns.GifExportController.prototype.createRadioForDpi_ = function (dpi, template) {
+ var label = dpi[0]*this.framesheet.getWidth() + "x" + dpi[0]*this.framesheet.getHeight();
+ var value = dpi[0];
+ var radioHTML = pskl.utils.Template.replace(template, {value : value, label : label});
+ var radio = pskl.utils.Template.createFromHTML(radioHTML);
+
+ if (dpi[1]) {
+ radio.getElementsByTagName("input")[0].setAttribute("checked", "checked");
+ }
+
+ return radio;
+ };
+
+ ns.GifExportController.prototype.blobToBase64_ = function(blob, cb) {
+ var reader = new FileReader();
+ reader.onload = function() {
+ var dataUrl = reader.result;
+ cb(dataUrl);
+ };
+ reader.readAsDataURL(blob);
+ };
+
+ ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(dpi, fps, cb) {
+ var gif = new window.GIF({
+ workers: 2,
+ quality: 10,
+ width: this.framesheet.getWidth()*dpi,
+ height: this.framesheet.getHeight()*dpi
+ });
+
+ for (var i = 0; i < this.framesheet.frames.length; i++) {
+ var frame = this.framesheet.frames[i];
+ var renderer = new pskl.rendering.CanvasRenderer(frame, dpi);
+ gif.addFrame(renderer.render(), {
+ delay: 1000 / fps
+ });
+ }
+
+ gif.on('finished', function(blob) {
+ this.blobToBase64_(blob, cb);
+ }.bind(this));
+
+ gif.render();
+ };
})();
\ No newline at end of file
diff --git a/js/piskel.js b/js/piskel.js
index 923e51fa..9f32ad99 100644
--- a/js/piskel.js
+++ b/js/piskel.js
@@ -34,7 +34,7 @@
this.previewsController = new pskl.controller.PreviewFilmController(frameSheet, $('#preview-list'));
this.previewsController.init();
- this.settingsController = new pskl.controller.SettingsController();
+ this.settingsController = new pskl.controller.SettingsController(frameSheet);
this.settingsController.init();
this.selectionManager = new pskl.selection.SelectionManager(frameSheet);
@@ -52,6 +52,9 @@
this.localStorageService = new pskl.service.LocalStorageService(frameSheet);
this.localStorageService.init();
+ this.imageUploadService = new pskl.service.ImageUploadService();
+ this.imageUploadService.init();
+
this.toolController = new pskl.controller.ToolController();
this.toolController.init();
@@ -211,34 +214,18 @@
return false;
},
- uploadToScreenletstore : function (imageData) {
- var xhr = new XMLHttpRequest();
- var formData = new FormData();
- formData.append('data', imageData);
- xhr.open('POST', "http://screenletstore.appspot.com/__/upload", true);
- var cloudURL;
- var that = this;
- xhr.onload = function (e) {
- if (this.status == 200) {
- cloudURL = "http://screenletstore.appspot.com/img/" + this.responseText;
- that.openWindow(cloudURL);
- }
- };
-
- xhr.send(formData);
- },
-
uploadAsAnimatedGIF : function () {
var fps = pskl.app.animationController.fps;
var renderer = new pskl.rendering.SpritesheetRenderer(frameSheet);
- var cb = this.uploadToScreenletstore.bind(this);
- renderer.renderAsImageDataAnimatedGIF(fps, cb);
+ renderer.renderAsImageDataAnimatedGIF(fps, function (imageData) {
+ this.imageUploadService.upload(imageData, this.openWindow);
+ }.bind(this));
},
uploadAsSpritesheetPNG : function () {
var imageData = (new pskl.rendering.SpritesheetRenderer(frameSheet)).renderAsImageDataSpritesheetPNG();
- this.uploadToScreenletstore(imageData);
+ this.imageUploadService.upload(imageData, this.openWindow);
},
openWindow : function (url) {
diff --git a/js/rendering/SpritesheetRenderer.js b/js/rendering/SpritesheetRenderer.js
index e751c4c2..0a966e42 100644
--- a/js/rendering/SpritesheetRenderer.js
+++ b/js/rendering/SpritesheetRenderer.js
@@ -15,40 +15,6 @@
return canvas.toDataURL("image/png");
};
- ns.SpritesheetRenderer.prototype.blobToBase64_ = function(blob, cb) {
- var reader = new FileReader();
- reader.onload = function() {
- var dataUrl = reader.result;
- cb(dataUrl);
- };
- reader.readAsDataURL(blob);
- };
-
- ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function(fps, cb) {
- var dpi = 10;
- var gif = new window.GIF({
- workers: 2,
- quality: 10,
- width: 320,
- height: 320
- });
-
- for (var i = 0; i < this.framesheet.frames.length; i++) {
- var frame = this.framesheet.frames[i];
- var renderer = new pskl.rendering.CanvasRenderer(frame, dpi);
- gif.addFrame(renderer.render(), {
- delay: 1000 / fps
- });
- }
-
- gif.on('finished', function(blob) {
- this.blobToBase64_(blob, cb);
- }.bind(this));
-
- gif.render();
- };
-
-
/**
* TODO(juliandescottes): Mutualize with code already present in FrameRenderer
*/
diff --git a/js/service/ImageUploadService.js b/js/service/ImageUploadService.js
new file mode 100644
index 00000000..30954a07
--- /dev/null
+++ b/js/service/ImageUploadService.js
@@ -0,0 +1,34 @@
+(function () {
+ var ns = $.namespace("pskl.service");
+ ns.ImageUploadService = function () {
+ this.serviceUrl_ = "http://screenletstore.appspot.com/__/upload";
+ };
+
+ ns.ImageUploadService.prototype.init = function () {
+ // service interface
+ };
+
+ /**
+ * Upload a base64 image data to distant service. If successful, will call provided callback with the image URL as first argument;
+ * @param {String} imageData base64 image data (such as the return value of canvas.toDataUrl())
+ * @param {Function} cbSuccess success callback. 1st argument will be the uploaded image URL
+ * @param {Function} cbError error callback
+ */
+ ns.ImageUploadService.prototype.upload = function (imageData, cbSuccess, cbError) {
+ var xhr = new XMLHttpRequest();
+ var formData = new FormData();
+ formData.append('data', imageData);
+ xhr.open('POST', this.serviceUrl_, true);
+ xhr.onload = function (e) {
+ if (this.status == 200) {
+ var imageUrl = "http://screenletstore.appspot.com/img/" + this.responseText;
+ cbSuccess(imageUrl);
+ } else {
+ cbError();
+ }
+ };
+
+ xhr.send(formData);
+ };
+
+})();
\ No newline at end of file
diff --git a/js/utils/Template.js b/js/utils/Template.js
new file mode 100644
index 00000000..3eda787d
--- /dev/null
+++ b/js/utils/Template.js
@@ -0,0 +1,30 @@
+(function () {
+ var ns = $.namespace("pskl");
+
+ ns.utils.Template = {
+ get : function (templateId) {
+ var template = document.getElementById(templateId);
+ if (template) {
+ return template.innerHTML;
+ } else {
+ console.error("Could not find template for id :", templateId);
+ }
+ },
+
+ createFromHTML : function (html) {
+ var dummyEl = document.createElement("div");
+ dummyEl.innerHTML = html;
+ return dummyEl.children[0];
+ },
+
+ replace : function (template, dict) {
+ for (var key in dict) {
+ if (dict.hasOwnProperty(key)) {
+ var value = dict[key];
+ template = template.replace(new RegExp('\\{\\{'+key+'\\}\\}', 'g'), value);
+ }
+ }
+ return template;
+ }
+ };
+})();
\ No newline at end of file
diff --git a/piskel-boot.js b/piskel-boot.js
index 2ac97da7..2c2149ce 100644
--- a/piskel-boot.js
+++ b/piskel-boot.js
@@ -9,6 +9,9 @@
if (window.location.href.indexOf("debug") != -1) {
window.exports = {};
+ //debug shortcuts
+ cl = function () {console.log.apply(console, arguments)};
+ d = function () {debugger};
var scriptIndex = 0;
window.loadNextScript = function () {
if (scriptIndex == exports.scripts.length) {
diff --git a/piskel-script-list.js b/piskel-script-list.js
index f0daebef..fb53bda2 100644
--- a/piskel-script-list.js
+++ b/piskel-script-list.js
@@ -13,6 +13,7 @@ exports.scripts = [
// Libraries
"js/utils/core.js",
+ "js/utils/Template.js",
"js/utils/PixelUtils.js",
"js/utils/CanvasUtils.js",
"js/utils/UserSettings.js",
@@ -49,6 +50,7 @@ exports.scripts = [
"js/service/LocalStorageService.js",
"js/service/HistoryService.js",
"js/service/KeyboardEventService.js",
+ "js/service/ImageUploadService.js",
// Tools
"js/drawingtools/BaseTool.js",
diff --git a/templates/settings-export-gif.html b/templates/settings-export-gif.html
index 41e2196c..12fff2d6 100644
--- a/templates/settings-export-gif.html
+++ b/templates/settings-export-gif.html
@@ -1 +1,16 @@
-LOLOLOLOL
\ No newline at end of file
+
+
+ Export to Animated GIF
+
+
+
+
+
+
+
\ No newline at end of file