Ghost/core/clientold/views/editor-actions-widget.js
Hannah Wolfe c746a88b2e Merge branch 'master' into ember
Conflicts:
	Gruntfile.js
	core/client/assets/lib/showdown/extensions/ghostdown.js
	core/client/views/editor.js
	core/clientold/assets/lib/showdown/extensions/ghostdown.js
	core/shared/lib/showdown/extensions/ghostimagepreview.js
	core/test/unit/showdown_client_integrated_spec.js
	core/test/unit/showdown_ghostimagepreview_spec.js
2014-04-07 22:26:53 +01:00

255 lines
8.9 KiB
JavaScript

// The Save / Publish button
/*global $, _, Ghost, shortcut */
(function () {
'use strict';
// The Publish, Queue, Publish Now buttons
// ----------------------------------------
Ghost.View.EditorActionsWidget = Ghost.View.extend({
events: {
'click [data-set-status]': 'handleStatus',
'click .js-publish-button': 'handlePostButton'
},
statusMap: null,
createStatusMap: {
'draft': 'Save Draft',
'published': 'Publish Now'
},
updateStatusMap: {
'draft': 'Unpublish',
'published': 'Update Post'
},
//TODO: This has to be moved to the I18n localization file.
//This structure is supposed to be close to the i18n-localization which will be used soon.
messageMap: {
errors: {
post: {
published: {
'published': 'Your post could not be updated.',
'draft': 'Your post could not be saved as a draft.'
},
draft: {
'published': 'Your post could not be published.',
'draft': 'Your post could not be saved as a draft.'
}
}
},
success: {
post: {
published: {
'published': 'Your post has been updated.',
'draft': 'Your post has been saved as a draft.'
},
draft: {
'published': 'Your post has been published.',
'draft': 'Your post has been saved as a draft.'
}
}
}
},
initialize: function () {
var self = this;
// Toggle publish
shortcut.add('Ctrl+Alt+P', function () {
self.toggleStatus();
});
shortcut.add('Ctrl+S', function () {
self.updatePost();
});
shortcut.add('Meta+S', function () {
self.updatePost();
});
this.listenTo(this.model, 'change:status', this.render);
},
toggleStatus: function () {
var self = this,
keys = Object.keys(this.statusMap),
model = self.model,
prevStatus = model.get('status'),
currentIndex = keys.indexOf(prevStatus),
newIndex,
status;
newIndex = currentIndex + 1 > keys.length - 1 ? 0 : currentIndex + 1;
status = keys[newIndex];
this.setActiveStatus(keys[newIndex], this.statusMap[status], prevStatus);
this.savePost({
status: keys[newIndex]
}).then(function () {
self.reportSaveSuccess(status, prevStatus);
}, function (xhr) {
// Show a notification about the error
self.reportSaveError(xhr, model, status, prevStatus);
});
},
setActiveStatus: function (newStatus, displayText, currentStatus) {
var isPublishing = (newStatus === 'published' && currentStatus !== 'published'),
isUnpublishing = (newStatus === 'draft' && currentStatus === 'published'),
// Controls when background of button has the splitbutton-delete/button-delete classes applied
isImportantStatus = (isPublishing || isUnpublishing);
$('.js-publish-splitbutton')
.removeClass(isImportantStatus ? 'splitbutton-save' : 'splitbutton-delete')
.addClass(isImportantStatus ? 'splitbutton-delete' : 'splitbutton-save');
// Set the publish button's action and proper coloring
$('.js-publish-button')
.attr('data-status', newStatus)
.text(displayText)
.removeClass(isImportantStatus ? 'button-save' : 'button-delete')
.addClass(isImportantStatus ? 'button-delete' : 'button-save');
// Remove the animated popup arrow
$('.js-publish-splitbutton > a')
.removeClass('active');
// Set the active action in the popup
$('.js-publish-splitbutton .editor-options li')
.removeClass('active')
.filter(['li[data-set-status="', newStatus, '"]'].join(''))
.addClass('active');
},
handleStatus: function (e) {
if (e) { e.preventDefault(); }
var status = $(e.currentTarget).attr('data-set-status'),
currentStatus = this.model.get('status');
this.setActiveStatus(status, this.statusMap[status], currentStatus);
// Dismiss the popup menu
$('body').find('.overlay:visible').fadeOut();
},
handlePostButton: function (e) {
if (e) { e.preventDefault(); }
var status = $(e.currentTarget).attr('data-status');
this.updatePost(status);
},
updatePost: function (status) {
var self = this,
model = this.model,
prevStatus = model.get('status');
// Default to same status if not passed in
status = status || prevStatus;
model.trigger('willSave');
this.savePost({
status: status
}).then(function () {
self.reportSaveSuccess(status, prevStatus);
// Refresh publish button and all relevant controls with updated status.
self.render();
}, function (xhr) {
// Set the model status back to previous
model.set({ status: prevStatus });
// Set appropriate button status
self.setActiveStatus(status, self.statusMap[status], prevStatus);
// Show a notification about the error
self.reportSaveError(xhr, model, status, prevStatus);
});
},
savePost: function (data) {
var publishButton = $('.js-publish-button'),
saved,
enablePublish = function (deferred) {
deferred.always(function () {
publishButton.prop('disabled', false);
});
return deferred;
};
publishButton.prop('disabled', true);
_.each(this.model.blacklist, function (item) {
this.model.unset(item);
}, this);
saved = this.model.save(_.extend({
title: this.options.$title.val(),
markdown: this.options.editor.value()
}, data));
// TODO: Take this out if #2489 gets merged in Backbone. Or patch Backbone
// ourselves for more consistent promises.
if (saved) {
return enablePublish(saved);
}
return enablePublish($.Deferred().reject());
},
reportSaveSuccess: function (status, prevStatus) {
Ghost.notifications.clearEverything();
Ghost.notifications.addItem({
type: 'success',
message: this.messageMap.success.post[prevStatus][status],
status: 'passive'
});
this.options.editor.setDirty(false);
},
reportSaveError: function (response, model, status, prevStatus) {
var message = this.messageMap.errors.post[prevStatus][status];
if (response) {
// Get message from response
message += ' ' + Ghost.Views.Utils.getRequestErrorMessage(response);
} else if (model.validationError) {
// Grab a validation error
message += ' ' + model.validationError;
}
Ghost.notifications.clearEverything();
Ghost.notifications.addItem({
type: 'error',
message: message,
status: 'passive'
});
},
setStatusLabels: function (statusMap) {
_.each(statusMap, function (label, status) {
$('li[data-set-status="' + status + '"] > a').text(label);
});
},
render: function () {
var status = this.model.get('status');
// Assume that we're creating a new post
if (status !== 'published') {
this.statusMap = this.createStatusMap;
} else {
this.statusMap = this.updateStatusMap;
}
// Populate the publish menu with the appropriate verbiage
this.setStatusLabels(this.statusMap);
// Default the selected publish option to the current status of the post.
this.setActiveStatus(status, this.statusMap[status], status);
}
});
}());