|
|
|
|
@@ -21,13 +21,6 @@ var CLOSED = 'closed',
|
|
|
|
|
|
|
|
|
|
Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
|
|
|
|
|
init: function() {
|
|
|
|
|
this._super();
|
|
|
|
|
var val = Discourse.KeyValueStore.get('composer.showPreview') || 'true';
|
|
|
|
|
this.set('showPreview', val === 'true');
|
|
|
|
|
this.set('archetypeId', Discourse.Site.instance().get('default_archetype'));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
archetypes: function() {
|
|
|
|
|
return Discourse.Site.instance().get('archetypes');
|
|
|
|
|
}.property(),
|
|
|
|
|
@@ -40,6 +33,8 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
viewOpen: Em.computed.equal('composeState', OPEN),
|
|
|
|
|
viewDraft: Em.computed.equal('composeState', DRAFT),
|
|
|
|
|
|
|
|
|
|
notCreatingPrivateMessage: Em.computed.not('creatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
archetype: function() {
|
|
|
|
|
return this.get('archetypes').findProperty('id', this.get('archetypeId'));
|
|
|
|
|
}.property('archetypeId'),
|
|
|
|
|
@@ -48,42 +43,14 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
return this.set('metaData', Em.Object.create());
|
|
|
|
|
}.observes('archetype'),
|
|
|
|
|
|
|
|
|
|
editTitle: function() {
|
|
|
|
|
if (this.get('creatingTopic') || this.get('creatingPrivateMessage')) return true;
|
|
|
|
|
if (this.get('editingPost') && this.get('post.post_number') === 1) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}.property('editingPost', 'creatingTopic', 'post.post_number'),
|
|
|
|
|
|
|
|
|
|
canCategorize: function() {
|
|
|
|
|
return (this.get('editTitle') && !this.get('creatingPrivateMessage'));
|
|
|
|
|
}.property('editTitle', 'creatingPrivateMessage'),
|
|
|
|
|
editingFirstPost: Em.computed.and('editingPost', 'post.firstPost'),
|
|
|
|
|
canEditTitle: Em.computed.or('creatingTopic', 'creatingPrivateMessage', 'editingFirstPost'),
|
|
|
|
|
canCategorize: Em.computed.and('canEditTitle', 'notCreatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
showAdminOptions: function() {
|
|
|
|
|
if (this.get('creatingTopic') && Discourse.User.current('staff')) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}.property('editTitle'),
|
|
|
|
|
|
|
|
|
|
togglePreview: function() {
|
|
|
|
|
this.toggleProperty('showPreview');
|
|
|
|
|
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
importQuote: function() {
|
|
|
|
|
// If there is no current post, use the post id from the stream
|
|
|
|
|
var postId = this.get('post.id') || this.get('topic.postStream.firstPostId');
|
|
|
|
|
if (postId) {
|
|
|
|
|
this.set('loading', true);
|
|
|
|
|
var composer = this;
|
|
|
|
|
return Discourse.Post.load(postId).then(function(post) {
|
|
|
|
|
composer.appendText(Discourse.BBCode.buildQuoteBBCode(post, post.get('raw')));
|
|
|
|
|
composer.set('loading', false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
appendText: function(text) {
|
|
|
|
|
this.set('reply', (this.get('reply') || '') + text);
|
|
|
|
|
},
|
|
|
|
|
}.property('canEditTitle'),
|
|
|
|
|
|
|
|
|
|
// Determine the appropriate title for this action
|
|
|
|
|
actionTitle: function() {
|
|
|
|
|
@@ -144,7 +111,7 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
// - editing the 1st post
|
|
|
|
|
// - creating a private message
|
|
|
|
|
|
|
|
|
|
if (this.get('editTitle') && !this.get('titleLengthValid')) return true;
|
|
|
|
|
if (this.get('canEditTitle') && !this.get('titleLengthValid')) return true;
|
|
|
|
|
|
|
|
|
|
// Need at least one user when sending a private message
|
|
|
|
|
if ( this.get('creatingPrivateMessage') &&
|
|
|
|
|
@@ -159,16 +126,17 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
if (this.get('canCategorize') && !Discourse.SiteSettings.allow_uncategorized_topics && !this.get('categoryName')) return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}.property('loading', 'editTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryName', 'missingReplyCharacters'),
|
|
|
|
|
}.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryName', 'missingReplyCharacters'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Is the title's length valid?
|
|
|
|
|
|
|
|
|
|
@property titleLengthValid
|
|
|
|
|
**/
|
|
|
|
|
titleLengthValid: function() {
|
|
|
|
|
if (this.get('creatingPrivateMessage')) {
|
|
|
|
|
if (this.get('titleLength') < Discourse.SiteSettings.min_private_message_title_length) return false;
|
|
|
|
|
} else {
|
|
|
|
|
if (this.get('titleLength') < Discourse.SiteSettings.min_topic_title_length) return false;
|
|
|
|
|
}
|
|
|
|
|
if (this.get('titleLength') < this.get('minimumTitleLength')) return false;
|
|
|
|
|
return (this.get('titleLength') <= Discourse.SiteSettings.max_topic_title_length);
|
|
|
|
|
}.property('creatingPrivateMessage', 'titleLength'),
|
|
|
|
|
}.property('minimumTitleLength', 'titleLength'),
|
|
|
|
|
|
|
|
|
|
// The text for the save button
|
|
|
|
|
saveText: function() {
|
|
|
|
|
@@ -185,9 +153,139 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
return metaData ? Em.empty(Em.keys(this.get('metaData'))) : false;
|
|
|
|
|
}.property('metaData'),
|
|
|
|
|
|
|
|
|
|
wouldLoseChanges: function() {
|
|
|
|
|
/**
|
|
|
|
|
Did the user make changes to the reply?
|
|
|
|
|
|
|
|
|
|
@property replyDirty
|
|
|
|
|
**/
|
|
|
|
|
replyDirty: function() {
|
|
|
|
|
return this.get('reply') !== this.get('originalText');
|
|
|
|
|
}.property('reply', 'save'),
|
|
|
|
|
}.property('reply', 'originalText'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Number of missing characters in the title until valid.
|
|
|
|
|
|
|
|
|
|
@property missingTitleCharacters
|
|
|
|
|
**/
|
|
|
|
|
missingTitleCharacters: function() {
|
|
|
|
|
return this.get('minimumTitleLength') - this.get('titleLength');
|
|
|
|
|
}.property('minimumTitleLength', 'titleLength'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Minimum number of characters for a title to be valid.
|
|
|
|
|
|
|
|
|
|
@property minimumTitleLength
|
|
|
|
|
**/
|
|
|
|
|
minimumTitleLength: function() {
|
|
|
|
|
if (this.get('creatingPrivateMessage')) {
|
|
|
|
|
return Discourse.SiteSettings.min_private_message_title_length;
|
|
|
|
|
} else {
|
|
|
|
|
return Discourse.SiteSettings.min_topic_title_length;
|
|
|
|
|
}
|
|
|
|
|
}.property('creatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Number of missing characters in the reply until valid.
|
|
|
|
|
|
|
|
|
|
@property missingReplyCharacters
|
|
|
|
|
**/
|
|
|
|
|
missingReplyCharacters: function() {
|
|
|
|
|
return this.get('minimumPostLength') - this.get('replyLength');
|
|
|
|
|
}.property('minimumPostLength', 'replyLength'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Minimum number of characters for a post body to be valid.
|
|
|
|
|
|
|
|
|
|
@property minimumPostLength
|
|
|
|
|
**/
|
|
|
|
|
minimumPostLength: function() {
|
|
|
|
|
if( this.get('creatingPrivateMessage') ) {
|
|
|
|
|
return Discourse.SiteSettings.min_private_message_post_length;
|
|
|
|
|
} else {
|
|
|
|
|
return Discourse.SiteSettings.min_post_length;
|
|
|
|
|
}
|
|
|
|
|
}.property('creatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Computes the length of the title minus non-significant whitespaces
|
|
|
|
|
|
|
|
|
|
@property titleLength
|
|
|
|
|
**/
|
|
|
|
|
titleLength: function() {
|
|
|
|
|
var title = this.get('title') || "";
|
|
|
|
|
return title.replace(/\s+/img, " ").trim().length;
|
|
|
|
|
}.property('title'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Computes the length of the reply minus the quote(s) and non-significant whitespaces
|
|
|
|
|
|
|
|
|
|
@property replyLength
|
|
|
|
|
**/
|
|
|
|
|
replyLength: function() {
|
|
|
|
|
var reply = this.get('reply') || "";
|
|
|
|
|
while (Discourse.BBCode.QUOTE_REGEXP.test(reply)) { reply = reply.replace(Discourse.BBCode.QUOTE_REGEXP, ""); }
|
|
|
|
|
return reply.replace(/\s+/img, " ").trim().length;
|
|
|
|
|
}.property('reply'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateDraftStatus: function() {
|
|
|
|
|
var $title = $('#reply-title'),
|
|
|
|
|
$reply = $('#wmd-input');
|
|
|
|
|
|
|
|
|
|
// 'title' is focused
|
|
|
|
|
if ($title.is(':focus')) {
|
|
|
|
|
var titleDiff = this.get('missingTitleCharacters');
|
|
|
|
|
if (titleDiff > 0) {
|
|
|
|
|
this.flashDraftStatusForNewUser();
|
|
|
|
|
return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_title', { n: titleDiff }));
|
|
|
|
|
}
|
|
|
|
|
// 'reply' is focused
|
|
|
|
|
} else if ($reply.is(':focus')) {
|
|
|
|
|
var replyDiff = this.get('missingReplyCharacters');
|
|
|
|
|
if (replyDiff > 0) {
|
|
|
|
|
return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_reply', { n: replyDiff }));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hide the counters if the currently focused text field is OK
|
|
|
|
|
this.set('draftStatus', null);
|
|
|
|
|
|
|
|
|
|
}.observes('missingTitleCharacters', 'missingReplyCharacters'),
|
|
|
|
|
|
|
|
|
|
init: function() {
|
|
|
|
|
this._super();
|
|
|
|
|
var val = Discourse.KeyValueStore.get('composer.showPreview') || 'true';
|
|
|
|
|
this.set('showPreview', val === 'true');
|
|
|
|
|
this.set('archetypeId', Discourse.Site.instance().get('default_archetype'));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Append text to the current reply
|
|
|
|
|
|
|
|
|
|
@method appendText
|
|
|
|
|
@param {String} text the text to append
|
|
|
|
|
**/
|
|
|
|
|
appendText: function(text) {
|
|
|
|
|
this.set('reply', (this.get('reply') || '') + text);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
togglePreview: function() {
|
|
|
|
|
this.toggleProperty('showPreview');
|
|
|
|
|
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
importQuote: function() {
|
|
|
|
|
// If there is no current post, use the post id from the stream
|
|
|
|
|
var postId = this.get('post.id') || this.get('topic.postStream.firstPostId');
|
|
|
|
|
if (postId) {
|
|
|
|
|
this.set('loading', true);
|
|
|
|
|
var composer = this;
|
|
|
|
|
return Discourse.Post.load(postId).then(function(post) {
|
|
|
|
|
composer.appendText(Discourse.BBCode.buildQuoteBBCode(post, post.get('raw')));
|
|
|
|
|
composer.set('loading', false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Open a composer
|
|
|
|
|
@@ -440,97 +538,7 @@ Discourse.Composer = Discourse.Model.extend({
|
|
|
|
|
$draftStatus.toggleClass('flash', true);
|
|
|
|
|
setTimeout(function() { $draftStatus.removeClass('flash'); }, 250);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateDraftStatus: function() {
|
|
|
|
|
var $title = $('#reply-title'),
|
|
|
|
|
$reply = $('#wmd-input');
|
|
|
|
|
|
|
|
|
|
// 'title' is focused
|
|
|
|
|
if ($title.is(':focus')) {
|
|
|
|
|
var titleDiff = this.get('missingTitleCharacters');
|
|
|
|
|
if (titleDiff > 0) {
|
|
|
|
|
this.flashDraftStatusForNewUser();
|
|
|
|
|
return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_title', { n: titleDiff }));
|
|
|
|
|
}
|
|
|
|
|
// 'reply' is focused
|
|
|
|
|
} else if ($reply.is(':focus')) {
|
|
|
|
|
var replyDiff = this.get('missingReplyCharacters');
|
|
|
|
|
if (replyDiff > 0) {
|
|
|
|
|
return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_reply', { n: replyDiff }));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hide the counters if the currently focused text field is OK
|
|
|
|
|
this.set('draftStatus', null);
|
|
|
|
|
|
|
|
|
|
}.observes('missingTitleCharacters', 'missingReplyCharacters'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Number of missing characters in the title until valid.
|
|
|
|
|
|
|
|
|
|
@property missingTitleCharacters
|
|
|
|
|
**/
|
|
|
|
|
missingTitleCharacters: function() {
|
|
|
|
|
return this.get('minimumTitleLength') - this.get('titleLength');
|
|
|
|
|
}.property('minimumTitleLength', 'titleLength'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Minimum number of characters for a title to be valid.
|
|
|
|
|
|
|
|
|
|
@property minimumTitleLength
|
|
|
|
|
**/
|
|
|
|
|
minimumTitleLength: function() {
|
|
|
|
|
if (this.get('creatingPrivateMessage')) {
|
|
|
|
|
return Discourse.SiteSettings.min_private_message_title_length;
|
|
|
|
|
} else {
|
|
|
|
|
return Discourse.SiteSettings.min_topic_title_length;
|
|
|
|
|
}
|
|
|
|
|
}.property('creatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Number of missing characters in the reply until valid.
|
|
|
|
|
|
|
|
|
|
@property missingReplyCharacters
|
|
|
|
|
**/
|
|
|
|
|
missingReplyCharacters: function() {
|
|
|
|
|
return this.get('minimumPostLength') - this.get('replyLength');
|
|
|
|
|
}.property('minimumPostLength', 'replyLength'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Minimum number of characters for a post body to be valid.
|
|
|
|
|
|
|
|
|
|
@property minimumPostLength
|
|
|
|
|
**/
|
|
|
|
|
minimumPostLength: function() {
|
|
|
|
|
if( this.get('creatingPrivateMessage') ) {
|
|
|
|
|
return Discourse.SiteSettings.min_private_message_post_length;
|
|
|
|
|
} else {
|
|
|
|
|
return Discourse.SiteSettings.min_post_length;
|
|
|
|
|
}
|
|
|
|
|
}.property('creatingPrivateMessage'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Computes the length of the title minus non-significant whitespaces
|
|
|
|
|
|
|
|
|
|
@property titleLength
|
|
|
|
|
**/
|
|
|
|
|
titleLength: function() {
|
|
|
|
|
var title = this.get('title') || "";
|
|
|
|
|
return title.replace(/\s+/img, " ").trim().length;
|
|
|
|
|
}.property('title'),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Computes the length of the reply minus the quote(s) and non-significant whitespaces
|
|
|
|
|
|
|
|
|
|
@property replyLength
|
|
|
|
|
**/
|
|
|
|
|
replyLength: function() {
|
|
|
|
|
var reply = this.get('reply') || "";
|
|
|
|
|
while (Discourse.BBCode.QUOTE_REGEXP.test(reply)) { reply = reply.replace(Discourse.BBCode.QUOTE_REGEXP, ""); }
|
|
|
|
|
return reply.replace(/\s+/img, " ").trim().length;
|
|
|
|
|
}.property('reply')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|