Issue #277 : Add global StorageService, enable CTRL+S

This commit is contained in:
jdescottes 2015-09-19 22:54:37 +02:00
parent 758cc6202a
commit 6620f7e5a9
11 changed files with 355 additions and 264 deletions

View File

@ -41,6 +41,8 @@ var Events = {
HISTORY_STATE_LOADED: 'HISTORY_STATE_LOADED',
PISKEL_SAVED: 'PISKEL_SAVED',
BEFORE_SAVING_PISKEL: 'BEFORE_SAVING_PISKEL',
AFTER_SAVING_PISKEL: 'AFTER_SAVING_PISKEL',
FRAME_SIZE_CHANGED : 'FRAME_SIZE_CHANGED',

View File

@ -109,15 +109,21 @@
this.canvasBackgroundController = new pskl.controller.CanvasBackgroundController();
this.canvasBackgroundController.init();
this.galleryStorageService = new pskl.service.storage.GalleryStorageService(this.piskelController);
this.galleryStorageService.init();
this.localStorageService = new pskl.service.storage.LocalStorageService(this.piskelController);
this.localStorageService.init();
this.fileDownloadStorageService = new pskl.service.storage.FileDownloadStorageService(this.piskelController);
this.fileDownloadStorageService.init();
this.desktopStorageService = new pskl.service.storage.DesktopStorageService(this.piskelController);
this.desktopStorageService.init();
this.galleryStorageService = new pskl.service.storage.GalleryStorageService(this.piskelController);
this.galleryStorageService.init();
this.storageService = new pskl.service.storage.StorageService(this.piskelController);
this.storageService.init();
this.imageUploadService = new pskl.service.ImageUploadService();
this.imageUploadService.init();

View File

@ -11,52 +11,61 @@
* @public
*/
ns.SaveController.prototype.init = function () {
// timestamp used to generate unique name when saving as .piskel
this.timestamp = new Date();
var saveForm = document.querySelector('.save-form');
this.getPartials_().forEach(function (partial) {
pskl.utils.Template.insert(saveForm, 'beforeend', partial);
});
this.insertPartials_();
// Only available in app-engine mode
this.piskelName = document.querySelector('.piskel-name');
this.saveOnlineStatus = document.querySelector('#save-online-status');
this.saveFileStatus = document.querySelector('#save-file-status');
this.descriptionInput = document.querySelector('#save-description');
this.nameInput = document.querySelector('#save-name');
this.saveOnlineButton = document.querySelector('#save-online-button');
this.isPublicCheckbox = document.querySelector('input[name=save-public-checkbox]');
this.saveFileButton = document.querySelector('#save-file-button');
this.saveBrowserButton = document.querySelector('#save-browser-button');
var descriptor = this.piskelController.getPiskel().getDescriptor();
this.descriptionInput.value = descriptor.description;
this.nameInput.value = descriptor.name;
if (descriptor.isPublic) {
this.isPublicCheckbox.setAttribute('checked', true);
this.addEventListener(this.saveFileButton, 'click', this.saveFile_);
this.addEventListener(this.saveBrowserButton, 'click', this.saveBrowser_);
this.addEventListener('form[name=save-form]', 'submit', this.onSaveFormSubmit_);
$.subscribe(Events.BEFORE_SAVING_PISKEL, this.disableSaveButtons_.bind(this));
$.subscribe(Events.AFTER_SAVING_PISKEL, this.enableSaveButtons_.bind(this));
if (pskl.app.isLoggedIn()) {
this.authenticatedUserInit_();
}
if (pskl.utils.Environment.detectNodeWebkit()) {
this.addEventListener('#save-as-button', 'click', this.saveAs_);
}
this.addEventListener('#save-file-button', 'click', this.saveFile_);
this.addEventListener('#save-browser-button', 'click', this.saveLocal_);
this.addEventListener(this.saveOnlineButton, 'click', this.saveOnline_);
this.addEventListener('form[name=save-form]', 'submit', this.onSaveFormSubmit_);
if (pskl.app.isLoggedIn()) {
pskl.utils.Template.insert(this.saveOnlineStatus, 'beforeend', 'save-online-status-partial');
} else {
pskl.utils.Template.insert(this.saveOnlineStatus, 'beforeend', 'save-please-login-partial');
var container = document.querySelector('.setting-save-section');
container.classList.add('anonymous');
this.desktopApplicationInit_();
}
};
ns.SaveController.prototype.insertPartials_ = function () {
ns.SaveController.prototype.authenticatedUserInit_ = function () {
var descriptor = this.piskelController.getPiskel().getDescriptor();
this.isPublicCheckbox = document.querySelector('input[name=save-public-checkbox]');
if (descriptor.isPublic) {
this.isPublicCheckbox.setAttribute('checked', true);
}
this.saveOnlineButton = document.querySelector('#save-online-button');
this.addEventListener(this.saveOnlineButton, 'click', this.saveOnline_);
};
ns.SaveController.prototype.desktopApplicationInit_ = function () {
this.saveAsNewButton = document.querySelector('#save-as-button');
this.addEventListener('#save-as-button', 'click', this.saveAs_);
};
ns.SaveController.prototype.getPartials_ = function () {
var partials = [];
if (pskl.utils.Environment.detectNodeWebkit()) {
partials = [
'save-file-nw-partial',
'save-localstorage-partial',
'save-online-partial'
'save-online-unavailable-partial'
];
} else if (pskl.app.isLoggedIn()) {
partials = [
@ -68,21 +77,11 @@
partials = [
'save-file-partial',
'save-localstorage-partial',
'save-online-partial'
'save-online-unavailable-partial'
];
}
var container = document.querySelector('.save-form');
partials.forEach(function (partial) {
pskl.utils.Template.insert(container, 'beforeend', partial);
});
};
ns.SaveController.prototype.getLocalFilename_ = function () {
var piskelName = this.getName();
var timestamp = pskl.utils.DateUtils.format(this.timestamp, '{{Y}}{{M}}{{D}}-{{H}}{{m}}{{s}}');
return piskelName + '-' + timestamp + '.piskel';
return partials;
};
ns.SaveController.prototype.onSaveFormSubmit_ = function (evt) {
@ -94,127 +93,105 @@
} else {
this.saveLocal_();
}
};
ns.SaveController.prototype.saveOnline_ = function () {
var name = this.getName();
if (!name) {
name = window.prompt('Please specify a name', 'New piskel');
}
if (name) {
var description = this.getDescription();
var isPublic = this.isPublic_();
var descriptor = new pskl.model.piskel.Descriptor(name, description, isPublic);
this.piskelController.getPiskel().setDescriptor(descriptor);
this.beforeSaving_();
this.saveOnlineButton.setAttribute('disabled', true);
this.saveOnlineStatus.innerHTML = 'Saving ...';
pskl.app.storageService.store({
success : this.onSaveSuccess_.bind(this),
error : this.onSaveError_.bind(this),
after : this.afterOnlineSaving_.bind(this)
});
}
var piskel = this.piskelController.getPiskel();
pskl.app.storageService.saveToGallery(piskel).then(this.onSaveSuccess_);
};
ns.SaveController.prototype.saveLocal_ = function () {
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) {
ns.SaveController.prototype.saveBrowser_ = function () {
this.beforeSaving_();
localStorageService.save(name, description, pskl.app.piskelController.serialize());
window.setTimeout(function () {
this.onSaveSuccess_();
this.afterSaving_();
}.bind(this), 500);
}
var piskel = this.piskelController.getPiskel();
pskl.app.storageService.saveToLocalStorage(piskel).then(this.onSaveSuccess_);
};
ns.SaveController.prototype.saveFile_ = function () {
// detect if this is running in NodeWebkit
if (pskl.utils.Environment.detectNodeWebkit()) {
pskl.app.desktopStorageService.save();
this.saveFileDesktop_();
} else {
this.saveFileBrowser_();
}
};
ns.SaveController.prototype.saveAs_ = function () {
pskl.app.desktopStorageService.savePiskelAs();
};
ns.SaveController.prototype.saveFileBrowser_ = function () {
this.beforeSaving_();
pskl.utils.BlobUtils.stringToBlob(pskl.app.piskelController.serialize(), function(blob) {
pskl.utils.FileUtils.downloadAsFile(blob, this.getLocalFilename_());
this.onSaveSuccess_();
this.afterSaving_();
}.bind(this), 'application/piskel+json');
var piskel = this.piskelController.getPiskel();
pskl.app.storageService.saveToFileBrowser(piskel).then(this.onSaveSuccess_);
};
ns.SaveController.prototype.getName = function () {
ns.SaveController.prototype.saveFileDesktop_ = function () {
this.beforeSaving_();
var piskel = this.piskelController.getPiskel();
pskl.app.storageService.saveToFileNodeWebkit(piskel).then(this.onSaveSuccess_);
};
ns.SaveController.prototype.saveAs_ = function () {
this.beforeSaving_();
var piskel = this.piskelController.getPiskel();
pskl.app.storageService.saveToFileNodeWebkit(piskel, true).then(this.onSaveSuccess_);
};
ns.SaveController.prototype.getDescriptor_ = function () {
var name = this.getName_();
var description = this.getDescription_();
var isPublic = this.isPublic_();
return new pskl.model.piskel.Descriptor(name, description, isPublic);
};
ns.SaveController.prototype.getName_ = function () {
return this.nameInput.value;
};
ns.SaveController.prototype.getDescription = function () {
ns.SaveController.prototype.getDescription_ = function () {
return this.descriptionInput.value;
};
ns.SaveController.prototype.isPublic_ = function () {
return !!this.isPublicCheckbox.checked;
};
ns.SaveController.prototype.beforeSaving_ = function () {
this.updatePiskelDescriptor_();
if (this.piskelName) {
this.piskelName.classList.add('piskel-name-saving');
if (!this.isPublicCheckbox) {
return true;
}
};
ns.SaveController.prototype.updatePiskelDescriptor_ = function () {
var name = this.getName();
var description = this.getDescription();
var isPublic = this.isPublic_();
var descriptor = new pskl.model.piskel.Descriptor(name, description, isPublic);
this.piskelController.getPiskel().setDescriptor(descriptor);
return !!this.isPublicCheckbox.checked;
};
ns.SaveController.prototype.onSaveSuccess_ = function () {
$.publish(Events.CLOSE_SETTINGS_DRAWER);
$.publish(Events.SHOW_NOTIFICATION, [{'content': 'Successfully saved !'}]);
$.publish(Events.PISKEL_SAVED);
};
ns.SaveController.prototype.onSaveError_ = function (status) {
$.publish(Events.SHOW_NOTIFICATION, [{'content': 'Saving failed (' + status + ')'}]);
ns.SaveController.prototype.beforeSaving_ = function () {
this.piskelController.getPiskel().setDescriptor(this.getDescriptor_());
};
ns.SaveController.prototype.afterOnlineSaving_ = function () {
this.saveOnlineButton.setAttribute('disabled', false);
this.saveOnlineStatus.innerHTML = '';
this.afterSaving_();
ns.SaveController.prototype.disableSaveButtons_ = function () {
this.setDisabled_(this.saveFileButton, true);
this.setDisabled_(this.saveBrowserButton, true);
this.setDisabled_(this.saveOnlineButton, true);
this.setDisabled_(this.saveAsNewButton, true);
};
ns.SaveController.prototype.afterSaving_ = function () {
if (this.piskelName) {
this.piskelName.classList.remove('piskel-name-saving');
ns.SaveController.prototype.enableSaveButtons_ = function () {
this.setDisabled_(this.saveFileButton, false);
this.setDisabled_(this.saveBrowserButton, false);
this.setDisabled_(this.saveOnlineButton, false);
this.setDisabled_(this.saveAsNewButton, false);
};
/**
* Safely update the disabled attribute on a HTML element.
* Noop if the element is falsy
*/
ns.SaveController.prototype.setDisabled_ = function (element, isDisabled) {
if (!element) {
return;
}
window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 5000);
if (isDisabled) {
element.setAttribute('disabled', 'disabled');
} else {
element.removeAttribute('disabled');
}
};
})();

View File

@ -1,41 +1,42 @@
(function () {
var ns = $.namespace('pskl.service.storage');
var PISKEL_EXTENSION = '.piskel';
ns.DesktopStorageService = function(piskelController) {
this.piskelController = piskelController || pskl.app.piskelController;
this.hideNotificationTimeoutID = 0;
};
ns.DesktopStorageService.prototype.init = function () {
// activate keyboard shortcuts if this is the desktop version
if (pskl.utils.Environment.detectNodeWebkit()) {
pskl.app.shortcutService.addShortcut('ctrl+o', this.openPiskel.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+s', this.save.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+shift+s', this.savePiskelAs.bind(this));
}
};
ns.DesktopStorageService.prototype.init = function () {};
ns.DesktopStorageService.prototype.save = function () {
var savePath = this.piskelController.getSavePath();
// if we already have a filename, just save the file (using nodejs 'fs' api)
if (savePath) {
this.savePiskel(savePath);
ns.DesktopStorageService.prototype.save = function (piskel, saveAsNew) {
if (piskel.savePath && !saveAsNew) {
return this.saveAtPath_(piskel, piskel.savePath);
} else {
this.savePiskelAs();
var name = piskel.getDescriptor().name;
var filenamePromise = pskl.utils.FileUtilsDesktop.chooseFilenameDialog(name, PISKEL_EXTENSION);
return filenamePromise.then(this.saveAtPath_.bind(this, piskel));
}
};
ns.DesktopStorageService.prototype.savePiskel = function (savePath) {
var serialized = this.piskelController.serialize();
pskl.utils.FileUtilsDesktop.saveToFile(serialized, savePath, function () {
this.onSaveSuccess_();
}.bind(this));
ns.DesktopStorageService.prototype.saveAtPath_ = function (piskel, savePath) {
if (!savePath) {
return Q.reject('Invalid file name');
}
var serialized = pskl.utils.Serializer.serializePiskel(piskel, false);
savePath = this.addExtensionIfNeeded_(savePath);
piskel.savePath = savePath;
piskel.setName(this.extractFilename_(savePath));
return pskl.utils.FileUtilsDesktop.saveToFile(serialized, savePath);
};
ns.DesktopStorageService.prototype.openPiskel = function () {
pskl.utils.FileUtilsDesktop.chooseFileDialog(function (filename) {
var savePath = filename;
pskl.utils.FileUtilsDesktop.readFile(savePath, function (content) {
return pskl.utils.FileUtilsDesktop.chooseFilenameDialog().then(this.load);
};
ns.DesktopStorageService.prototype.load = function (savePath) {
pskl.utils.FileUtilsDesktop.readFile(savePath).then(function (content) {
pskl.utils.PiskelFileUtils.decodePiskelFile(content, function (piskel, descriptor, fps) {
piskel.setDescriptor(descriptor);
// store save path so we can re-save without opening the save dialog
@ -44,26 +45,14 @@
pskl.app.previewController.setFPS(fps);
});
});
});
};
ns.DesktopStorageService.prototype.savePiskelAs = function () {
var serialized = this.piskelController.serialize();
var name = this.piskelController.getPiskel().getDescriptor().name;
// TODO: if there is already a file path, use it for the dialog's
// working directory and filename
pskl.utils.FileUtilsDesktop.saveAs(serialized, name, 'piskel', function (selectedSavePath) {
this.piskelController.setSavePath(selectedSavePath);
var filename = this.extractFilename_(selectedSavePath);
if (filename) {
var descriptor = this.piskelController.getPiskel().getDescriptor();
descriptor.name = filename;
this.piskelController.getPiskel().setDescriptor(descriptor);
ns.DesktopStorageService.prototype.addExtensionIfNeeded_ = function (filename) {
var hasExtension = filename.substr(-PISKEL_EXTENSION.length) === PISKEL_EXTENSION;
if (!hasExtension) {
return filename + PISKEL_EXTENSION;
}
this.onSaveSuccess_();
}.bind(this));
return filename;
};
ns.DesktopStorageService.prototype.extractFilename_ = function (savePath) {
@ -72,15 +61,4 @@
return matches[1];
}
};
ns.DesktopStorageService.prototype.onSaveSuccess_ = function () {
var savePath = this.piskelController.getSavePath();
$.publish(Events.CLOSE_SETTINGS_DRAWER);
$.publish(Events.SHOW_NOTIFICATION, [{'content': 'Successfully saved: ' + savePath}]);
$.publish(Events.PISKEL_SAVED);
// clear the old time out, if any.
window.clearTimeout(this.hideNotificationTimeoutID);
this.hideNotificationTimeoutID = window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 3000);
};
})();

View File

@ -0,0 +1,27 @@
(function () {
var ns = $.namespace('pskl.service.storage');
ns.FileDownloadStorageService = function () {};
ns.FileDownloadStorageService.prototype.init = function () {};
ns.FileDownloadStorageService.prototype.save = function (piskel) {
var serialized = pskl.utils.Serializer.serializePiskel(piskel, false);
var deferred = Q.defer();
pskl.utils.BlobUtils.stringToBlob(serialized, function(blob) {
var piskelName = piskel.getDescriptor().name;
var timestamp = pskl.utils.DateUtils.format(new Date(), '{{Y}}{{M}}{{D}}-{{H}}{{m}}{{s}}');
var fileName = piskelName + '-' + timestamp + '.piskel';
try {
pskl.utils.FileUtils.downloadAsFile(blob, fileName);
deferred.resolve();
} catch (e) {
deferred.reject(e.message);
}
}.bind(this), 'application/piskel+json');
return deferred.promise;
};
})();

View File

@ -7,8 +7,9 @@
ns.GalleryStorageService.prototype.init = function () {};
ns.GalleryStorageService.prototype.store = function (piskel, onSuccess, onError) {
ns.GalleryStorageService.prototype.save = function (piskel) {
var descriptor = piskel.getDescriptor();
var deferred = Q.defer();
var data = {
framesheet : this.piskelController.serialize(),
@ -24,10 +25,30 @@
data.public = true;
}
var errorCallback = function (response) {
onError(response.status);
var successCallback = function (response) {
deferred.resolve();
};
pskl.utils.Xhr.post(Constants.APPENGINE_SAVE_URL, data, onSuccess, errorCallback);
var errorCallback = function (response) {
deferred.reject(this.getErrorMessage_(response));
};
pskl.utils.Xhr.post(Constants.APPENGINE_SAVE_URL, data, successCallback, errorCallback.bind(this));
return deferred.promise;
};
ns.GalleryStorageService.prototype.getErrorMessage_ = function (response) {
var errorMessage = '';
if (response.status === 401) {
errorMessage = 'Session expired, please log in again.';
} else if (response.status === 403) {
errorMessage = 'Unauthorized action, this sprite belongs to another account.';
} else if (response.status === 500) {
errorMessage = 'Unexpected server error, please contact us on Github (piskel) or Twitter (@piskelapp)';
} else {
errorMessage = 'Unknown error';
}
return errorMessage;
};
})();

View File

@ -10,10 +10,26 @@
ns.LocalStorageService.prototype.init = function() {};
ns.LocalStorageService.prototype.save = function(name, description, piskel) {
ns.LocalStorageService.prototype.save = function(piskel) {
var name = piskel.getDescriptor().name;
var description = piskel.getDescriptor().description;
var serialized = pskl.utils.Serializer.serializePiskel(piskel, false);
if (pskl.app.localStorageService.getPiskel(name)) {
var confirmOverwrite = window.confirm('There is already a piskel saved as ' + name + '. Overwrite ?');
if (!confirmOverwrite) {
return Q.reject('Cancelled by user, "' + name + '" already exists');
}
}
try {
this.removeFromKeys_(name);
this.addToKeys_(name, description, Date.now());
window.localStorage.setItem('piskel.' + name, piskel);
window.localStorage.setItem('piskel.' + name, serialized);
return Q.resolve();
} catch (e) {
return Q.reject(e.message);
}
};
ns.LocalStorageService.prototype.load = function(name) {

View File

@ -0,0 +1,82 @@
(function () {
var ns = $.namespace('pskl.service.storage');
ns.StorageService = function (piskelController) {
this.piskelController = piskelController;
this.onSaveSuccess_ = this.onSaveSuccess_.bind(this);
this.onSaveError_ = this.onSaveError_.bind(this);
};
ns.StorageService.prototype.init = function () {
pskl.app.shortcutService.addShortcut('ctrl+o', this.onOpenKey_.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+s', this.onSaveKey_.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+shift+s', this.onSaveAsKey_.bind(this));
};
ns.StorageService.prototype.onOpenKey_ = function () {
if (pskl.utils.Environment.detectNodeWebkit()) {
pskl.app.desktopStorageService.openPiskel();
}
// no other implementation for now
};
ns.StorageService.prototype.onSaveKey_ = function () {
var piskel = this.piskelController.getPiskel();
if (pskl.app.isLoggedIn()) {
this.saveToGallery(this.piskelController.getPiskel());
} else if (pskl.utils.Environment.detectNodeWebkit()) {
this.saveToFileNodeWebkit(this.piskelController.getPiskel());
} else {
this.saveToLocalStorage(this.piskelController.getPiskel());
}
};
ns.StorageService.prototype.onSaveAsKey_ = function () {
if (pskl.utils.Environment.detectNodeWebkit()) {
this.saveToFileNodeWebkit(this.piskelController.getPiskel(), true);
}
// no other implementation for now
};
ns.StorageService.prototype.saveToGallery = function (piskel) {
$.publish(Events.BEFORE_SAVING_PISKEL);
return pskl.app.galleryStorageService.save(piskel).then(this.onSaveSuccess_, this.onSaveError_);
};
ns.StorageService.prototype.saveToLocalStorage = function (piskel) {
$.publish(Events.BEFORE_SAVING_PISKEL);
return pskl.app.localStorageService.save(piskel).then(this.onSaveSuccess_, this.onSaveError_);
};
ns.StorageService.prototype.saveToFileBrowser = function (piskel) {
$.publish(Events.BEFORE_SAVING_PISKEL);
return pskl.app.fileDownloadStorageService.save(piskel).then(this.onSaveSuccess_, this.onSaveError_);
};
ns.StorageService.prototype.saveToFileNodeWebkit = function (piskel, saveAsNew) {
$.publish(Events.BEFORE_SAVING_PISKEL);
return pskl.app.desktopStorageService.save(piskel, saveAsNew).then(this.onSaveSuccess_, this.onSaveError_);
};
ns.StorageService.prototype.onSaveSuccess_ = function () {
$.publish(Events.SHOW_NOTIFICATION, [{'content': 'Successfully saved !'}]);
$.publish(Events.PISKEL_SAVED);
this.afterSaving_();
};
ns.StorageService.prototype.onSaveError_ = function (errorMessage) {
var errorText = 'Saving failed';
if (errorMessage) {
errorText += ' : ' + errorMessage;
}
$.publish(Events.SHOW_NOTIFICATION, [{'content': errorText}]);
this.afterSaving_();
return Q.reject(errorMessage);
};
ns.StorageService.prototype.afterSaving_ = function () {
$.publish(Events.AFTER_SAVING_PISKEL);
window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 5000);
};
})();

View File

@ -1,63 +1,39 @@
(function () {
var ns = $.namespace('pskl.utils');
var stopPropagation = function (e) {
e.stopPropagation();
var getFileInputElement = function (nwsaveas, accept) {
var fileInputElement = document.createElement('INPUT');
fileInputElement.setAttribute('type', 'file');
fileInputElement.setAttribute('nwworkingdir', '');
if (nwsaveas) {
fileInputElement.setAttribute('nwsaveas', nwsaveas);
}
if (accept) {
fileInputElement.setAttribute('accept', accept);
}
return fileInputElement;
};
var CONFIRM_OVERRIDE = 'File already exists, do you want to override it ?';
ns.FileUtilsDesktop = {
chooseFilenameDialog : function (nwsaveas, accept) {
var deferred = Q.defer();
var fileInputElement = getFileInputElement(nwsaveas, accept);
var changeListener = function (evt) {
fileInputElement.removeEventListener('change', changeListener);
document.removeEventListener('click', changeListener);
deferred.resolve(fileInputElement.value);
};
chooseFileDialog: function (callback) {
var tagString = '<input type="file" nwworkingdir=""/>';
var $chooser = $(tagString);
$chooser.change(function (e) {
var filename = $(this).val();
callback(filename);
});
$chooser.trigger('click');
},
fileInputElement.click();
addExtensionIfNeeded : function (filename, extension) {
if (typeof extension == 'string') {
if (extension[0] !== '.') {
extension = '.' + extension;
}
var hasExtension = (filename.substring(filename.length - extension.length) === extension);
if (!hasExtension) {
filename += extension;
}
}
return filename;
},
fileInputElement.addEventListener('change', changeListener);
// there is no way to detect a cancelled fileInput popup
// as a crappy workaround we add a click listener on the document
// on top the change event listener
document.addEventListener('click', changeListener);
/**
*
* @param content
* @param defaultFilename - file name to pre-populate the dialog
* @param extension - if supplied, the selected extension will guaranteed to be on the filename -
* NOTE: there is a possible danger here... If the extension is added to a fileName, but there
* is already another file of the same name *with* the extension, it will get overwritten.
* @param callback
*/
saveAs: function (content, defaultFilename, extension, callback) {
// NodeWebkit has no js api for opening the save dialog.
// Instead, it adds two new attributes to the anchor tag: nwdirectory and nwsaveas
// (see: https://github.com/nwjs/nw.js/wiki/File-dialogs )
defaultFilename = defaultFilename || 'New Piskel';
defaultFilename = pskl.utils.FileUtilsDesktop.addExtensionIfNeeded(defaultFilename, extension);
var tagString = '<input type="file" accept=".piskel" nwsaveas="' + defaultFilename + '" nwworkingdir=""/>';
var $chooser = $(tagString);
$chooser.change(function (e) {
var filename = $(this).val();
filename = pskl.utils.FileUtilsDesktop.addExtensionIfNeeded(filename, extension);
pskl.utils.FileUtilsDesktop.saveToFile(content, filename, function () {
callback(filename);
});
});
$chooser.trigger('click');
return deferred.promise;
},
/**
@ -67,26 +43,32 @@
* @param {string} filename - fill path to the file
* @callback callback
*/
saveToFile : function(content, filename, callback) {
saveToFile : function(content, filename) {
var deferred = Q.defer();
var fs = window.require('fs');
fs.writeFile(filename, content, function (err) {
if (err) {
//throw err;
console.log('FileUtilsDesktop::savetoFile() - error saving file:', filename, 'Error:', err);
deferred.reject('FileUtilsDesktop::savetoFile() - error saving file: ' + filename + ' Error: ' + err);
} else {
deferred.resolve();
}
callback();
});
return deferred.promise;
},
readFile : function(filename, callback) {
readFile : function(filename) {
var deferred = Q.defer();
var fs = window.require('fs');
// NOTE: currently loading everything as utf8, which may not be desirable in future
fs.readFile(filename, 'utf8', function (err, data) {
if (err) {
console.log('FileUtilsDesktop::readFile() - error reading file:', filename, 'Error:', err);
deferred.reject('FileUtilsDesktop::readFile() - error reading file: ' + filename + ' Error: ' + err);
} else {
deferred.resolve(data);
}
callback(data);
});
return deferred.promise;
}
};
})();

View File

@ -136,6 +136,11 @@
"js/widgets/SizeInput.js",
// Services
"js/service/storage/StorageService.js",
"js/service/storage/FileDownloadStorageService.js",
"js/service/storage/LocalStorageService.js",
"js/service/storage/GalleryStorageService.js",
"js/service/storage/DesktopStorageService.js",
"js/service/BackupService.js",
"js/service/BeforeUnloadService.js",
"js/service/HistoryService.js",

View File

@ -25,7 +25,18 @@
<div class="settings-title">Save online</div>
<div class="settings-item">
<input type="button" class="button button-primary" id="save-online-button" value="Save to your gallery" />
<div id="save-online-status" class="save-status"></div>
<div id="save-online-status" class="save-status">
<span>Your piskel will be stored online in your gallery.</span>
</div>
</div>
</script>
<script type="text/template" id="save-online-unavailable-partial">
<div class="settings-title">Save online</div>
<div class="settings-item">
<div id="save-online-status" class="save-status">
<span>Login to <a href="http://piskelapp.com" target="_blank">piskelapp.com</a> to save and share your sprites online !</span>
</div>
</div>
</script>
@ -57,20 +68,4 @@
<div id="save-file-status" class="save-status">Your sprite will be downloaded as a .piskel file.</div>
</div>
</script>
<script type="text/template" id="save-please-login-partial">
<span>Login to <a href="http://piskelapp.com" target="_blank">piskelapp.com</a> to save and share your sprites online !</span>
</script>
<script type="text/template" id="save-online-status-partial">
<span>Your piskel will be stored online in your gallery.</span>
</script>
<script type="text/template" id="save-file-status-template">
<span>Your piskel will be downloaded as: <span class="save-file-name">{{name}}<span></span>
</script>
<script type="text/template" id="save-file-status-desktop-template">
<span>Saving as: <span class="save-desktop-file-name">{{name}}<span></span>
</script>
</div>