diff --git a/css/settings.css b/css/settings.css index 41bf99ce..829a727e 100644 --- a/css/settings.css +++ b/css/settings.css @@ -56,6 +56,13 @@ position: relative; } +.tool-icon.local-storage-icon { + background-image: url(../img/local-storage-icon.png); + background-position: 10px 12px; + background-size: 30px; + position: relative; +} + .tool-icon.import-icon { background-image: url(../img/import-icon.png); background-position: 10px 5px; @@ -247,12 +254,10 @@ } #save-status { - margin-left: 10px; + margin-top: 10px; } .status { height: 1.5rem; - - word-break : break-all; vertical-align: middle; font-weight: normal; text-shadow: none; @@ -264,4 +269,33 @@ [name*=checkbox] { vertical-align: middle; +} + +.local-piskels-list { + width: 100%; +} + +.local-piskel-item { + height: 3em; +} + +.local-piskel-name { + width: 40%; +} + +.local-piskel-save-date { + font-weight : normal; +} + +.local-piskels-list a { + text-decoration: none; +} +.local-piskels-list a:hover { + text-decoration: underline; +} +.local-piskel-load-link { + color : gold; +} +.local-piskel-delete-link { + color : red; } \ No newline at end of file diff --git a/index.html b/index.html index 08bbd11b..05d758b1 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,8 @@ -
+
diff --git a/js/app.js b/js/app.js index 3f37fcc0..a2179814 100644 --- a/js/app.js +++ b/js/app.js @@ -103,8 +103,6 @@ "content" : "Loading animation with id : [" + framesheetId + "]" }]); this.loadFramesheetFromService(framesheetId); - } else { - this.localStorageService.displayRestoreNotification(); } }, @@ -118,6 +116,10 @@ } }, + isLoggedIn : function () { + return pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.isLoggedIn; + }, + initTooltips_ : function () { $('body').tooltip({ selector: '[rel=tooltip]' diff --git a/js/controller/settings/SaveController.js b/js/controller/settings/SaveController.js index 0b10eebe..57d9c577 100644 --- a/js/controller/settings/SaveController.js +++ b/js/controller/settings/SaveController.js @@ -13,7 +13,12 @@ this.nameInput = $('#save-name'); this.descriptionInput = $('#save-description'); this.isPublicCheckbox = $('input[name=save-public-checkbox]'); - this.saveButton = $('#save-button'); + this.saveCloudButton = $('#save-cloud-button'); + this.saveLocalButton = $('#save-local-button'); + + // Only available in app-engine mode ... + this.piskelName = $('.piskel-name').get(0); + this.status = $('#save-status'); var descriptor = this.piskelController.piskel.getDescriptor(); @@ -22,21 +27,22 @@ 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'); + if (!pskl.app.isLoggedIn()) { + this.saveCloudButton.attr('disabled', 'disabled'); + this.status.html('You are not logged in. Only Local Save is available.'); + } else { + this.saveForm.submit(this.onSaveFormSubmit_.bind(this)); } - this.saveForm.submit(this.onSaveFormSubmit_.bind(this)); + this.saveLocalButton.click(this.onSaveLocalClick_.bind(this)); }; ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) { evt.preventDefault(); evt.stopPropagation(); - var name = this.nameInput.val(); - var description = this.descriptionInput.val(); + var name = this.getName(); + var description = this.getDescription(); var isPublic = !!this.isPublicCheckbox.prop('checked'); var descriptor = new pskl.model.piskel.Descriptor(name, description, isPublic); @@ -50,10 +56,40 @@ }); }; + ns.SaveController.prototype.onSaveLocalClick_ = function (evt) { + var localStorageService = pskl.app.localStorageService; + var isOk = true; + var name = this.getName(); + var description = this.getDescription(); + if (localStorageService.getPiskel(name)) { + isOk = window.confirm('There is already a piskel saved as ' + name + '. Override ?'); + } + + if (isOk) { + this.beforeSaving_(); + localStorageService.save(name, description, pskl.app.piskelController.serialize()); + window.setTimeout(function () { + this.onSaveSuccess_(); + this.afterSaving_(); + }.bind(this), 1000); + } + }; + + ns.SaveController.prototype.getName = function () { + return this.nameInput.val(); + }; + + ns.SaveController.prototype.getDescription = function () { + return this.descriptionInput.val(); + }; + ns.SaveController.prototype.beforeSaving_ = function () { - this.saveButton.attr('disabled', true); + this.saveCloudButton.attr('disabled', true); this.status.html('Saving ...'); - $('.piskel-name').get(0).classList.add('piskel-name-saving'); + + if (this.piskelName) { + this.piskelName.classList.add('piskel-name-saving'); + } }; ns.SaveController.prototype.onSaveSuccess_ = function () { @@ -66,9 +102,12 @@ }; ns.SaveController.prototype.afterSaving_ = function () { - this.saveButton.attr('disabled', false); + this.saveCloudButton.attr('disabled', false); this.status.html(''); - $('.piskel-name').get(0).classList.remove('piskel-name-saving'); + + if (this.piskelName) { + this.piskelName.classList.remove('piskel-name-saving'); + } window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 2000); }; diff --git a/js/controller/settings/SettingsController.js b/js/controller/settings/SettingsController.js index bc1f0318..de4a0bd9 100644 --- a/js/controller/settings/SettingsController.js +++ b/js/controller/settings/SettingsController.js @@ -18,6 +18,10 @@ template : 'templates/settings/import.html', controller : ns.ImportController }, + 'localstorage' : { + template : 'templates/settings/localstorage.html', + controller : ns.LocalStorageController + }, 'save' : { template : 'templates/settings/save.html', controller : ns.SaveController diff --git a/js/service/LocalStorageService.js b/js/service/LocalStorageService.js index c68cee38..62a174a9 100644 --- a/js/service/LocalStorageService.js +++ b/js/service/LocalStorageService.js @@ -7,83 +7,74 @@ throw "Bad LocalStorageService initialization: "; } this.piskelController = piskelController; - this.localStorageThrottler_ = null; }; - /** - * @public - */ - ns.LocalStorageService.prototype.init = function(piskelController) { - $.subscribe(Events.LOCALSTORAGE_REQUEST, $.proxy(this.persistToLocalStorageRequest_, this)); + ns.LocalStorageService.prototype.init = function() {}; + +// localStorage.setItem('piskel_bkp', pskl.app.piskelController.serialize()) + + ns.LocalStorageService.prototype.save = function(name, description, piskel) { + this.removeFromKeys_(name); + this.addToKeys_(name, description, Date.now()); + window.localStorage.setItem('piskel.' + name, piskel); }; - /** - * @private - */ - ns.LocalStorageService.prototype.persistToLocalStorageRequest_ = function () { - // Persist to localStorage when drawing. We throttle localStorage accesses - // for high frequency drawing (eg mousemove). - if(this.localStorageThrottler_ !== null) { - window.clearTimeout(this.localStorageThrottler_); - } - this.localStorageThrottler_ = window.setTimeout($.proxy(function() { - this.persistToLocalStorage_(); - this.localStorageThrottler_ = null; - }, this), 1000); - }; + ns.LocalStorageService.prototype.load = function(name) { + var piskelString = this.getPiskel(name); + var key = this.getKey_(name); - /** - * @private - */ - ns.LocalStorageService.prototype.persistToLocalStorage_ = function() { - console.log('[LocalStorage service]: Snapshot stored'); - window.localStorage.snapShot = this.piskelController.serialize(); - }; - - /** - * @private - */ - ns.LocalStorageService.prototype.restoreFromLocalStorage_ = function() { - var framesheet = JSON.parse(window.localStorage.snapShot); - - pskl.utils.serialization.Deserializer.deserialize(framesheet, function (piskel) { + pskl.utils.serialization.Deserializer.deserialize(JSON.parse(piskelString), function (piskel) { + piskel.setDescriptor(new pskl.model.piskel.Descriptor(name, key.description, true)); pskl.app.piskelController.setPiskel(piskel); }); }; - /** - * @private - */ - ns.LocalStorageService.prototype.cleanLocalStorage_ = function() { - console.log('[LocalStorage service]: Snapshot removed'); - delete window.localStorage.snapShot; + ns.LocalStorageService.prototype.remove = function(name) { + this.removeFromKeys_(name); + window.localStorage.removeItem('piskel.' + name); }; - /** - * @public - */ - ns.LocalStorageService.prototype.displayRestoreNotification = function() { - if(window.localStorage && window.localStorage.snapShot) { - var reloadLink = "reload"; - var discardLink = "discard"; - var content = "Non saved version found. " + reloadLink + " or " + discardLink; + ns.LocalStorageService.prototype.saveKeys_ = function(keys) { + window.localStorage.setItem('piskel.keys', JSON.stringify(keys)); + }; - $.publish(Events.SHOW_NOTIFICATION, [{ - "content": content, - "behavior": $.proxy(function(rootNode) { - rootNode = $(rootNode); - rootNode.click($.proxy(function(evt) { - var target = $(evt.target); - if(target.hasClass("localstorage-restore")) { - this.restoreFromLocalStorage_(); - } - else if (target.hasClass("localstorage-discard")) { - this.cleanLocalStorage_(); - } - $.publish(Events.HIDE_NOTIFICATION); - }, this)); - }, this) - }]); + ns.LocalStorageService.prototype.removeFromKeys_ = function(name) { + var keys = this.getKeys(); + var otherKeys = keys.filter(function (key) { + return key.name !== name; + }); + + this.saveKeys_(otherKeys); + }; + + ns.LocalStorageService.prototype.getKey_ = function(name) { + var matches = this.getKeys().filter(function (key) { + return key.name === name; + }); + if (matches.length > 0) { + return matches[0]; + } else { + return null; } }; + + ns.LocalStorageService.prototype.addToKeys_ = function(name, description, date) { + var keys = this.getKeys(); + keys.push({ + name : name, + description : description, + date : date + }); + this.saveKeys_(keys); + }; + + ns.LocalStorageService.prototype.getPiskel = function(name) { + return window.localStorage.getItem('piskel.' + name); + }; + + ns.LocalStorageService.prototype.getKeys = function(name) { + var keysString = window.localStorage.getItem('piskel.keys'); + return JSON.parse(keysString) || []; + }; + })(); \ No newline at end of file diff --git a/js/utils/serialization/Deserializer.js b/js/utils/serialization/Deserializer.js index 799b1f09..5c35322a 100644 --- a/js/utils/serialization/Deserializer.js +++ b/js/utils/serialization/Deserializer.js @@ -20,11 +20,12 @@ deserializer.deserialize(); }; - ns.Deserializer.prototype.deserialize = function () { + ns.Deserializer.prototype.deserialize = function (name) { var data = this.data_; var piskelData = data.piskel; + name = name || 'Deserialized piskel'; - var descriptor = new pskl.model.piskel.Descriptor('Deserialized piskel', ''); + var descriptor = new pskl.model.piskel.Descriptor(name, ''); this.piskel_ = new pskl.model.Piskel(piskelData.width, piskelData.height, descriptor); this.layersToLoad_ = piskelData.layers.length; diff --git a/piskel-script-list.js b/piskel-script-list.js index 27286eaa..0e92ebb6 100644 --- a/piskel-script-list.js +++ b/piskel-script-list.js @@ -72,6 +72,7 @@ exports.scripts = [ "js/controller/settings/ApplicationSettingsController.js", "js/controller/settings/ResizeController.js", "js/controller/settings/GifExportController.js", + "js/controller/settings/LocalStorageController.js", "js/controller/settings/SaveController.js", "js/controller/settings/ImportController.js", // Settings controller diff --git a/templates/settings.html b/templates/settings.html index 80ac056c..bb12a438 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -27,6 +27,13 @@ rel="tooltip" data-placement="left"> +
+
+
- - + + +
\ No newline at end of file