diff --git a/Gruntfile.js b/Gruntfile.js index b948be15..2201f43e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -12,6 +12,7 @@ module.exports = function(grunt) { var piskelScripts = require('./piskel-script-list.js').scripts; + var piskelStyles = require('./piskel-style-list.js').styles; var getGhostConfig = function (delay) { return { filesSrc : ['tests/integration/casperjs/*_test.js'], @@ -68,12 +69,16 @@ module.exports = function(grunt) { local : getGhostConfig(50) }, concat : { - options : { - separator : ';' - }, - dist : { + js : { + options : { + separator : ';' + }, src : piskelScripts, dest : 'build/piskel-packaged.js' + }, + css : { + src : piskelStyles, + dest : 'build/piskel-style-packaged.css' } }, uglify : { @@ -171,8 +176,10 @@ module.exports = function(grunt) { // Compile JS code (eg verify JSDoc annotation and types, no actual minified code generated). grunt.registerTask('compile', ['closureCompiler:compile']); + grunt.registerTask('merge', ['concat:js', 'concat:css', 'uglify']); + // Validate & Build - grunt.registerTask('default', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles', 'jshint', 'concat', 'compile', 'uglify']); + grunt.registerTask('default', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles', 'jshint', 'concat:js', 'concat:css', 'compile', 'uglify']); // Start webserver grunt.registerTask('serve', ['connect:serve']); diff --git a/css/forms.css b/css/forms.css index 2d364327..cb912f01 100644 --- a/css/forms.css +++ b/css/forms.css @@ -1,3 +1,7 @@ +.row { + display: block; +} + .textfield { background : black; border : 1px solid #888; diff --git a/css/settings.css b/css/settings.css index a07be5b4..171579c9 100644 --- a/css/settings.css +++ b/css/settings.css @@ -8,6 +8,7 @@ -webkit-transition: all 200ms ease-out; -moz-transition: all 200ms ease-out; -ms-transition: all 200ms ease-out; + -o-transition: all 200ms ease-out; transition: all 200ms ease-out; } @@ -51,6 +52,10 @@ text-shadow: 1px 1px #000; } +.settings-section .button { + margin: 0; +} + .settings-title { margin-top: 20px; margin-bottom: 10px; @@ -59,7 +64,9 @@ padding-bottom: 5px; } -.settings-item {} +.settings-form-section { + margin-bottom: 10px; +} .background-picker-wrapper { overflow: hidden; @@ -179,7 +186,26 @@ text-shadow: none; } +.save-field { + width: 100%; +} + +#save-status { + margin-left: 10px; +} +.status { + height: 1.5rem; + + word-break : break-all; + vertical-align: middle; + font-weight: normal; + text-shadow: none; +} + [name=smooth-resize-checkbox] { margin : 0 8px; +} + +[name*=checkbox] { vertical-align: middle; } \ No newline at end of file diff --git a/index.html b/index.html index 69bbc344..b6bb7fff 100644 --- a/index.html +++ b/index.html @@ -7,18 +7,6 @@ - - - - - - - - - - - -
diff --git a/js/app.js b/js/app.js index f9d43a46..3f37fcc0 100644 --- a/js/app.js +++ b/js/app.js @@ -10,12 +10,19 @@ ns.app = { init : function () { + /** + * True when piskel is running in static mode (no back end needed). + * When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl + */ + this.isAppEngineVersion = !!pskl.appEngineToken_; + this.shortcutService = new pskl.service.keyboard.ShortcutService(); this.shortcutService.init(); var size = this.readSizeFromURL_(); - var piskel = new pskl.model.Piskel(size.width, size.height); - piskel.setDescriptor("New Piskel", "Some text ..."); + + var descriptor = new pskl.model.piskel.Descriptor('New Piskel', ''); + var piskel = new pskl.model.Piskel(size.width, size.height, descriptor); var layer = new pskl.model.Layer("Layer 1"); var frame = new pskl.model.Frame(size.width, size.height); @@ -65,31 +72,31 @@ this.imageUploadService = new pskl.service.ImageUploadService(); this.imageUploadService.init(); - this.cheatsheetService = new pskl.service.keyboard.CheatsheetService(); this.cheatsheetService.init(); + if (this.isAppEngineVersion) { + this.storageService = new pskl.service.AppEngineStorageService(this.piskelController); + } else { + this.storageService = new pskl.service.GithubStorageService(this.piskelController); + } + this.storageService.init(); + var drawingLoop = new pskl.rendering.DrawingLoop(); drawingLoop.addCallback(this.render, this); drawingLoop.start(); - this.initBootstrapTooltips_(); + this.initTooltips_(); - /** - * True when piskel is running in static mode (no back end needed). - * When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl - */ - this.isStaticVersion = !pskl.appEngineToken_; - - if (this.isStaticVersion) { - this.finishInitStatic_(); - } else { + if (this.isAppEngineVersion) { this.finishInitAppEngine_(); + } else { + this.finishInitGithub_(); } }, - finishInitStatic_ : function () { + finishInitGithub_ : function () { var framesheetId = this.readFramesheetIdFromURL_(); if (framesheetId) { $.publish(Events.SHOW_NOTIFICATION, [{ @@ -102,15 +109,16 @@ }, finishInitAppEngine_ : function () { - if (pskl.framesheetData_ && pskl.framesheetData_.content) { - pskl.utils.serialization.Deserializer.deserialize(pskl.framesheetData_.content, function (piskel) { + if (pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.piskel) { + pskl.utils.serialization.Deserializer.deserialize(pskl.appEnginePiskelData_.piskel, function (piskel) { + piskel.setDescriptor(pskl.appEnginePiskelData_.descriptor); pskl.app.piskelController.setPiskel(piskel); - pskl.app.animationController.setFPS(pskl.framesheetData_.fps); + pskl.app.animationController.setFPS(pskl.appEnginePiskelData_.fps); }); } }, - initBootstrapTooltips_ : function () { + initTooltips_ : function () { $('body').tooltip({ selector: '[rel=tooltip]' }); @@ -123,8 +131,8 @@ }, readSizeFromURL_ : function () { - var sizeParam = this.readUrlParameter_("size"), - size; + var sizeParam = this.readUrlParameter_("size"); + var size; // parameter expected as size=64x48 => size=widthxheight var parts = sizeParam.split("x"); if (parts && parts.length == 2 && !isNaN(parts[0]) && !isNaN(parts[1])) { @@ -149,13 +157,12 @@ }, readUrlParameter_ : function (paramName) { - var searchString = window.location.search.substring(1), - i, val, params = searchString.split("&"); - - for (i = 0; i < params.length; i++) { - val = params[i].split("="); - if (val[0] == paramName) { - return window.unescape(val[1]); + var searchString = window.location.search.substring(1); + var params = searchString.split("&"); + for (var i = 0; i < params.length; i++) { + var param = params[i].split("="); + if (param[0] == paramName) { + return window.unescape(param[1]); } } return ""; @@ -182,65 +189,8 @@ xhr.send(); }, - storeSheet : function (event) { - if (this.isStaticVersion) { - this.storeSheetStatic_(); - } else { - this.storeSheetAppEngine_(); - } - - if(event) { - event.stopPropagation(); - event.preventDefault(); - } - return false; - }, - - storeSheetStatic_ : function () { - var xhr = new XMLHttpRequest(); - var formData = new FormData(); - formData.append('framesheet_content', this.piskelController.serialize()); - formData.append('fps_speed', $('#preview-fps').val()); - - xhr.open('POST', Constants.STATIC.URL.SAVE, true); - - xhr.onload = function(e) { - if (this.status == 200) { - var baseUrl = window.location.href.replace(window.location.search, ""); - window.location.href = baseUrl + "?frameId=" + this.responseText; - } else { - this.onerror(e); - } - }; - xhr.onerror = function(e) { - $.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]); - }; - xhr.send(formData); - }, - - storeSheetAppEngine_ : function () { - var xhr = new XMLHttpRequest(); - var formData = new FormData(); - formData.append('framesheet_content', this.piskelController.serialize()); - formData.append('fps_speed', $('#preview-fps').val()); - formData.append('name', $('#piskel-name').val()); - formData.append('frames', this.piskelController.getFrameCount()); - formData.append('preview', this.getFirstFrameAsPng()); - formData.append('framesheet', this.getFramesheetAsPng()); - - xhr.open('POST', Constants.APPENGINE.URL.SAVE, true); - - xhr.onload = function(e) { - if (this.status == 200) { - $.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]); - } else { - this.onerror(e); - } - }; - xhr.onerror = function(e) { - $.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]); - }; - xhr.send(formData); + store : function (callbacks) { + this.storageService.store(callbacks); }, getFirstFrameAsPng : function () { diff --git a/js/controller/NotificationController.js b/js/controller/NotificationController.js index 8b3ba25a..e6554d55 100644 --- a/js/controller/NotificationController.js +++ b/js/controller/NotificationController.js @@ -15,6 +15,8 @@ * @private */ ns.NotificationController.prototype.displayMessage_ = function (evt, messageInfo) { + this.removeMessage_(); + var message = document.createElement('div'); message.id = "user-message"; message.className = "user-message"; diff --git a/js/controller/settings/ImportController.js b/js/controller/settings/ImportController.js index 3c94eaa5..0186adfe 100644 --- a/js/controller/settings/ImportController.js +++ b/js/controller/settings/ImportController.js @@ -154,7 +154,9 @@ var frame = pskl.utils.FrameUtils.createFromImage(image); var layer = pskl.model.Layer.fromFrames('Layer 1', [frame]); - var piskel = pskl.model.Piskel.fromLayers([layer]); + + 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); diff --git a/js/controller/settings/SaveController.js b/js/controller/settings/SaveController.js index 11b330cb..0b10eebe 100644 --- a/js/controller/settings/SaveController.js +++ b/js/controller/settings/SaveController.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace("pskl.controller.settings"); + var ns = $.namespace('pskl.controller.settings'); ns.SaveController = function (piskelController) { this.piskelController = piskelController; @@ -9,10 +9,67 @@ * @public */ ns.SaveController.prototype.init = function () { - this.titleInput = document.getElementById("save-title"); - this.descriptionInput = document.getElementById("save-description"); + this.saveForm = $('form[name=save-form]'); + this.nameInput = $('#save-name'); + this.descriptionInput = $('#save-description'); + this.isPublicCheckbox = $('input[name=save-public-checkbox]'); + this.saveButton = $('#save-button'); + this.status = $('#save-status'); - this.titleInput.value = this.piskelController.piskel.getDescriptor().name; - this.descriptionInput.value = this.piskelController.piskel.getDescriptor().description; + var descriptor = this.piskelController.piskel.getDescriptor(); + this.nameInput.val(descriptor.name); + this.descriptionInput.val(descriptor.description); + + this.isPublicCheckbox.prop('checked', descriptor.isPublic); + + if (!pskl.app.isAppEngineVersion) { + this.nameInput.attr('disabled', 'disabled'); + this.descriptionInput.attr('disabled', 'disabled'); + this.isPublicCheckbox.attr('disabled', 'disabled'); + } + + this.saveForm.submit(this.onSaveFormSubmit_.bind(this)); + }; + + ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) { + evt.preventDefault(); + evt.stopPropagation(); + + var name = this.nameInput.val(); + var description = this.descriptionInput.val(); + var isPublic = !!this.isPublicCheckbox.prop('checked'); + + var descriptor = new pskl.model.piskel.Descriptor(name, description, isPublic); + this.piskelController.piskel.setDescriptor(descriptor); + + this.beforeSaving_(); + pskl.app.store({ + success : this.onSaveSuccess_.bind(this), + error : this.onSaveError_.bind(this), + after : this.afterSaving_.bind(this) + }); + }; + + ns.SaveController.prototype.beforeSaving_ = function () { + this.saveButton.attr('disabled', true); + this.status.html('Saving ...'); + $('.piskel-name').get(0).classList.add('piskel-name-saving'); + }; + + ns.SaveController.prototype.onSaveSuccess_ = function () { + $.publish(Events.CLOSE_SETTINGS_DRAWER); + $.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]); + }; + + ns.SaveController.prototype.onSaveError_ = function (status) { + $.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+status+")"}]); + }; + + ns.SaveController.prototype.afterSaving_ = function () { + this.saveButton.attr('disabled', false); + this.status.html(''); + $('.piskel-name').get(0).classList.remove('piskel-name-saving'); + + window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 2000); }; })(); \ No newline at end of file diff --git a/js/lib/iframeLoader.js b/js/lib/iframeLoader.js index 4820fb16..b2cb5adf 100644 --- a/js/lib/iframeLoader.js +++ b/js/lib/iframeLoader.js @@ -37,7 +37,11 @@ var storeFrame = function (iframe) { var script=document.createElement("script"); script.setAttribute("type", "text/html"); - script.setAttribute("id", iframe.getAttribute("src")); + if (window.pskl && window.pskl.appEngineToken_) { + script.setAttribute("id", iframe.getAttribute("src").replace('../','')); + } else { + script.setAttribute("id", iframe.getAttribute("src")); + } script.innerHTML = iframe.contentWindow.document.body.innerHTML; iframe.parentNode.removeChild(iframe); document.body.appendChild(script); diff --git a/js/model/Piskel.js b/js/model/Piskel.js index 11f520d7..974e8ece 100644 --- a/js/model/Piskel.js +++ b/js/model/Piskel.js @@ -8,8 +8,8 @@ * @param {String} name * @param {String} description */ - ns.Piskel = function (width, height) { - if (width && height) { + ns.Piskel = function (width, height, descriptor) { + if (width && height && descriptor) { /** @type {Array} */ this.layers = []; @@ -19,7 +19,7 @@ /** @type {Number} */ this.height = height; - this.descriptor = null; + this.descriptor = descriptor; } else { throw 'Missing arguments in Piskel constructor : ' + Array.prototype.join.call(arguments, ","); } @@ -31,11 +31,11 @@ * @param {Array