Add save as button

This commit is contained in:
jdescottes 2015-04-23 23:59:05 +02:00
parent c570d8fd75
commit 473bd4705c
6 changed files with 120 additions and 59 deletions

3
.gitignore vendored
View File

@ -5,6 +5,9 @@
node_modules node_modules
npm-debug.log npm-debug.log
# node webkit cache
cache
# sublime text stuff (the -project should actually be shared, but then we'd have to share the same disk location) # sublime text stuff (the -project should actually be shared, but then we'd have to share the same disk location)
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace

View File

@ -14,64 +14,69 @@
// timestamp used to generate unique name when saving as .piskel // timestamp used to generate unique name when saving as .piskel
this.timestamp = new Date(); this.timestamp = new Date();
this.insertPartials_();
// Only available in app-engine mode // Only available in app-engine mode
this.piskelName = document.querySelector('.piskel-name'); this.piskelName = document.querySelector('.piskel-name');
this.saveOnlineStatus = document.querySelector('#save-online-status'); this.saveOnlineStatus = document.querySelector('#save-online-status');
this.saveFileStatus = document.querySelector('#save-file-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]');
var descriptor = this.piskelController.getPiskel().getDescriptor(); var descriptor = this.piskelController.getPiskel().getDescriptor();
this.descriptionInput = document.querySelector('#save-description');
this.descriptionInput.value = descriptor.description; this.descriptionInput.value = descriptor.description;
this.nameInput.value = descriptor.name;
this.isPublicCheckbox = document.querySelector('input[name=save-public-checkbox]');
if (descriptor.isPublic) { if (descriptor.isPublic) {
this.isPublicCheckbox.setAttribute('checked', true); this.isPublicCheckbox.setAttribute('checked', true);
} }
if (pskl.utils.Environment.detectNodeWebkit()) { if (pskl.utils.Environment.detectNodeWebkit()) {
// hide 'save in browser' this.addEventListener('#save-as-button', 'click', this.saveAs_);
var saveInBrowserSection = document.querySelector('#save-in-browser');
saveInBrowserSection.style.display = 'none';
} }
this.addEventListener('#save-file-button', 'click', this.saveFile_); this.addEventListener('#save-file-button', 'click', this.saveFile_);
this.addEventListener('#save-browser-button', 'click', this.saveLocal_); this.addEventListener('#save-browser-button', 'click', this.saveLocal_);
this.saveOnlineButton = document.querySelector('#save-online-button');
this.addEventListener(this.saveOnlineButton, 'click', this.saveOnline_); this.addEventListener(this.saveOnlineButton, 'click', this.saveOnline_);
this.addEventListener('form[name=save-form]', 'submit', this.onSaveFormSubmit_); this.addEventListener('form[name=save-form]', 'submit', this.onSaveFormSubmit_);
this.nameInput = document.querySelector('#save-name');
this.nameInput.value = descriptor.name;
this.addEventListener(this.nameInput, 'keyup', this.updateLocalStatusFilename_);
if (pskl.app.isLoggedIn()) { if (pskl.app.isLoggedIn()) {
this.saveOnlineStatus.innerHTML = pskl.utils.Template.get('save-online-status-partial'); pskl.utils.Template.insert(this.saveOnlineStatus, 'beforeend', 'save-online-status-partial');
} else { } else {
this.saveOnlineStatus.innerHTML = pskl.utils.Template.get('save-please-login-partial'); pskl.utils.Template.insert(this.saveOnlineStatus, 'beforeend', 'save-please-login-partial');
var container = document.querySelector('.setting-save-section'); var container = document.querySelector('.setting-save-section');
container.classList.add('anonymous'); container.classList.add('anonymous');
} }
this.updateLocalStatusFilename_();
}; };
ns.SaveController.prototype.updateLocalStatusFilename_ = function () { ns.SaveController.prototype.insertPartials_ = function () {
var partials = [];
if (pskl.utils.Environment.detectNodeWebkit()) { if (pskl.utils.Environment.detectNodeWebkit()) {
var fileName = this.piskelController.getSavePath(); partials = [
if (fileName !== null) { 'save-file-nw-partial',
this.saveFileStatus.innerHTML = pskl.utils.Template.getAndReplace('save-file-status-desktop-template', { 'save-localstorage-partial',
name : this.piskelController.getSavePath() 'save-online-partial'
}); ];
} } else if (pskl.app.isLoggedIn()) {
partials = [
'save-online-partial',
'save-localstorage-partial',
'save-file-partial'
];
} else { } else {
this.saveFileStatus.innerHTML = pskl.utils.Template.getAndReplace('save-file-status-template', { partials = [
name : this.getLocalFilename_() 'save-file-partial',
}); 'save-localstorage-partial',
'save-online-partial'
];
} }
var container = document.querySelector('.save-form');
partials.forEach(function (partial) {
pskl.utils.Template.insert(container, 'beforeend', partial);
});
}; };
ns.SaveController.prototype.getLocalFilename_ = function () { ns.SaveController.prototype.getLocalFilename_ = function () {
@ -141,12 +146,16 @@
ns.SaveController.prototype.saveFile_ = function () { ns.SaveController.prototype.saveFile_ = function () {
// detect if this is running in NodeWebkit // detect if this is running in NodeWebkit
if (pskl.utils.Environment.detectNodeWebkit()) { if (pskl.utils.Environment.detectNodeWebkit()) {
this.saveFileDesktop_(); pskl.app.desktopStorageService.save();
} else { } else {
this.saveFileBrowser_(); this.saveFileBrowser_();
} }
}; };
ns.SaveController.prototype.saveAs_ = function () {
pskl.app.desktopStorageService.savePiskelAs();
};
ns.SaveController.prototype.saveFileBrowser_ = function () { ns.SaveController.prototype.saveFileBrowser_ = function () {
this.beforeSaving_(); this.beforeSaving_();
pskl.utils.BlobUtils.stringToBlob(pskl.app.piskelController.serialize(), function(blob) { pskl.utils.BlobUtils.stringToBlob(pskl.app.piskelController.serialize(), function(blob) {
@ -156,10 +165,6 @@
}.bind(this), 'application/piskel+json'); }.bind(this), 'application/piskel+json');
}; };
ns.SaveController.prototype.saveFileDesktop_ = function () {
pskl.app.desktopStorageService.save();
};
ns.SaveController.prototype.getName = function () { ns.SaveController.prototype.getName = function () {
return this.nameInput.value; return this.nameInput.value;
}; };

View File

@ -21,7 +21,7 @@
if (savePath) { if (savePath) {
this.savePiskel(savePath); this.savePiskel(savePath);
} else { } else {
this.savePiskelAs(savePath); this.savePiskelAs();
} }
}; };
@ -47,16 +47,32 @@
}); });
}; };
ns.DesktopStorageService.prototype.savePiskelAs = function (savePath) { ns.DesktopStorageService.prototype.savePiskelAs = function () {
var serialized = this.piskelController.serialize(); 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 // TODO: if there is already a file path, use it for the dialog's
// working directory and filename // working directory and filename
pskl.utils.FileUtilsDesktop.saveAs(serialized, null, 'piskel', function (selectedSavePath) { pskl.utils.FileUtilsDesktop.saveAs(serialized, name, 'piskel', function (selectedSavePath) {
this.piskelController.setSavePath(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);
}
this.onSaveSuccess_(); this.onSaveSuccess_();
}.bind(this)); }.bind(this));
}; };
ns.DesktopStorageService.prototype.extractFilename_ = function (savePath) {
var matches = (/[\/\\]([^\/\\]*)\.piskel$/gi).exec(savePath);
if (matches && matches[1]) {
return matches[1];
}
};
ns.DesktopStorageService.prototype.onSaveSuccess_ = function () { ns.DesktopStorageService.prototype.onSaveSuccess_ = function () {
var savePath = this.piskelController.getSavePath(); var savePath = this.piskelController.getSavePath();
$.publish(Events.CLOSE_SETTINGS_DRAWER); $.publish(Events.CLOSE_SETTINGS_DRAWER);

View File

@ -5,6 +5,8 @@
e.stopPropagation(); e.stopPropagation();
}; };
var CONFIRM_OVERRIDE = 'File already exists, do you want to override it ?';
ns.FileUtilsDesktop = { ns.FileUtilsDesktop = {
chooseFileDialog: function (callback) { chooseFileDialog: function (callback) {
@ -17,37 +19,44 @@
$chooser.trigger('click'); $chooser.trigger('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;
},
/** /**
* *
* @param content * @param content
* @param defaultFileName - file name to pre-populate the dialog * @param defaultFilename - file name to pre-populate the dialog
* @param extension - if supplied, the selected extension will guaranteed to be on the filename - * @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 * 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. * is already another file of the same name *with* the extension, it will get overwritten.
* @param callback * @param callback
*/ */
saveAs: function (content, defaultFileName, extension, callback) { saveAs: function (content, defaultFilename, extension, callback) {
// NodeWebkit has no js api for opening the save dialog. // NodeWebkit has no js api for opening the save dialog.
// Instead, it adds two new attributes to the anchor tag: nwdirectory and nwsaveas // Instead, it adds two new attributes to the anchor tag: nwdirectory and nwsaveas
// (see: https://github.com/nwjs/nw.js/wiki/File-dialogs ) // (see: https://github.com/nwjs/nw.js/wiki/File-dialogs )
defaultFileName = defaultFileName || ''; defaultFilename = defaultFilename || 'New Piskel';
var tagString = '<input type="file" nwsaveas="' + defaultFileName + '" nwworkingdir=""/>'; defaultFilename = pskl.utils.FileUtilsDesktop.addExtensionIfNeeded(defaultFilename, extension);
var tagString = '<input type="file" accept=".piskel" nwsaveas="' + defaultFilename + '" nwworkingdir=""/>';
var $chooser = $(tagString); var $chooser = $(tagString);
$chooser.change(function (e) { $chooser.change(function (e) {
var filename = $(this).val(); var filename = $(this).val();
if (typeof extension == 'string') { filename = pskl.utils.FileUtilsDesktop.addExtensionIfNeeded(filename, extension);
if (extension[0] !== '.') {
extension = '.' + extension;
}
var hasExtension = (filename.substring(filename.length - extension.length) === extension);
if (!hasExtension) {
filename += extension;
}
}
pskl.utils.FileUtilsDesktop.saveToFile(content, filename, function () { pskl.utils.FileUtilsDesktop.saveToFile(content, filename, function () {
callback(filename); callback(filename);
}); });
}); });
$chooser.trigger('click'); $chooser.trigger('click');
}, },

View File

@ -21,6 +21,11 @@
return dummyEl.children[0]; return dummyEl.children[0];
}, },
insert : function (parent, position, templateId, dict) {
var html = pskl.utils.Template.getAndReplace(templateId, dict);
parent.insertAdjacentHTML(position, html);
},
getAndReplace : function (templateId, dict) { getAndReplace : function (templateId, dict) {
var result = ''; var result = '';
var tpl = pskl.utils.Template.get(templateId); var tpl = pskl.utils.Template.get(templateId);

View File

@ -1,5 +1,5 @@
<div class="settings-section setting-save-section"> <div class="settings-section setting-save-section">
<form action="" method="POST" name="save-form"> <form action="" method="POST" class="save-form" name="save-form">
<div class="settings-title">Describe your piskel</div> <div class="settings-title">Describe your piskel</div>
<div class="settings-item"> <div class="settings-item">
<div class="settings-form-section" style="overflow:hidden"> <div class="settings-form-section" style="overflow:hidden">
@ -16,24 +16,47 @@
</label> </label>
</div> </div>
</div> </div>
</form>
<!-- PARTIALS -->
<!-- save-online-partial -->
<script type="text/template" id="save-online-partial">
<div class="settings-title">Save online</div> <div class="settings-title">Save online</div>
<div class="settings-item"> <div class="settings-item">
<input type="button" class="button button-primary" id="save-online-button" value="Save to your gallery" /> <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"></div>
</div> </div>
</script>
<!-- save-localstorage-partial -->
<script type="text/template" id="save-localstorage-partial">
<div id="save-in-browser"> <div id="save-in-browser">
<div class="settings-title">Save offline in Browser</div> <div class="settings-title">Save offline in Browser</div>
<div class="settings-item"> <div class="settings-item">
<input type="button" class="button button-primary" id="save-browser-button" value="Save in Browser" /> <input type="button" class="button button-primary" id="save-browser-button" value="Save in Browser" />
<div id="save-browser-status" class="save-status">Your piskel will be saved locally and will only be accessible from this browser.</div> <div id="save-browser-status" class="save-status">Your piskel will be saved locally and will only be accessible from this browser.</div>
</div> </div>
</div> </div>
</script>
<!-- save-file-partial -->
<script type="text/template" id="save-file-nw-partial">
<div class="settings-title">Save as File</div>
<div class="settings-item">
<input type="button" class="button button-primary" id="save-file-button" value="Save" />
<input type="button" class="button button-primary" id="save-as-button" value="Save as ..." />
<div id="save-file-status" class="save-status">Your sprite will be saved as a .piskel file.</div>
</div>
</script>
<script type="text/template" id="save-file-partial">
<div class="settings-title">Save offline as File</div> <div class="settings-title">Save offline as File</div>
<div class="settings-item"> <div class="settings-item">
<input type="button" class="button button-primary" id="save-file-button" value="Save as File" /> <input type="button" class="button button-primary" id="save-file-button" value="Save as ..." />
<div id="save-file-status" class="save-status"></div> <div id="save-file-status" class="save-status">Your sprite will be downloaded as a .piskel file.</div>
</div> </div>
</form> </script>
<script type="text/template" id="save-please-login-partial"> <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> <span>Login to <a href="http://piskelapp.com" target="_blank">piskelapp.com</a> to save and share your sprites online !</span>