mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
Convert Discourse.Post
to ES6 and use Store model
- Includes acceptance tests for composer (post, edit) - Supports acceptance testing of bootbox
This commit is contained in:
11
app/assets/javascripts/discourse/adapters/post.js.es6
Normal file
11
app/assets/javascripts/discourse/adapters/post.js.es6
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import RestAdapter from 'discourse/adapters/rest';
|
||||||
|
|
||||||
|
export default RestAdapter.extend({
|
||||||
|
|
||||||
|
// GET /posts doesn't include a type
|
||||||
|
find(store, type, findArgs) {
|
||||||
|
return this._super(store, type, findArgs).then(function(result) {
|
||||||
|
return {post: result};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -61,7 +61,8 @@ export default DiscourseController.extend({
|
|||||||
if (postId) {
|
if (postId) {
|
||||||
this.set('model.loading', true);
|
this.set('model.loading', true);
|
||||||
const composer = this;
|
const composer = this;
|
||||||
return Discourse.Post.load(postId).then(function(post) {
|
|
||||||
|
return this.store.find('post', postId).then(function(post) {
|
||||||
const quote = Discourse.Quote.build(post, post.get("raw"));
|
const quote = Discourse.Quote.build(post, post.get("raw"));
|
||||||
composer.appendBlockAtCursor(quote);
|
composer.appendBlockAtCursor(quote);
|
||||||
composer.set('model.loading', false);
|
composer.set('model.loading', false);
|
||||||
@ -412,7 +413,7 @@ export default DiscourseController.extend({
|
|||||||
composerModel.set('topic', opts.topic);
|
composerModel.set('topic', opts.topic);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
composerModel = composerModel || Discourse.Composer.create();
|
composerModel = composerModel || Discourse.Composer.create({ store: this.store });
|
||||||
composerModel.open(opts);
|
composerModel.open(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,13 @@
|
|||||||
// Adds a listener callback to a DOM element which is fired on a specified
|
// Adds a listener callback to a DOM element which is fired on a specified
|
||||||
// event.
|
// event.
|
||||||
util.addEvent = function (elem, event, listener) {
|
util.addEvent = function (elem, event, listener) {
|
||||||
elem.addEventListener(event, listener, false);
|
var wrapped = function() {
|
||||||
|
var wrappedArgs = Array.prototype.slice(arguments);
|
||||||
|
Ember.run(function() {
|
||||||
|
listener.call(this, wrappedArgs);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
elem.addEventListener(event, wrapped, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -924,8 +930,8 @@
|
|||||||
currentWait = wait;
|
currentWait = wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout) { clearTimeout(timeout); }
|
if (timeout) { Ember.run.cancel(timeout); }
|
||||||
timeout = setTimeout(later, currentWait);
|
timeout = Ember.run.later(later, currentWait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ const CLOSED = 'closed',
|
|||||||
const Composer = Discourse.Model.extend({
|
const Composer = Discourse.Model.extend({
|
||||||
|
|
||||||
archetypes: function() {
|
archetypes: function() {
|
||||||
return Discourse.Site.currentProp('archetypes');
|
return this.site.get('archetypes');
|
||||||
}.property(),
|
}.property(),
|
||||||
|
|
||||||
creatingTopic: Em.computed.equal('action', CREATE_TOPIC),
|
creatingTopic: Em.computed.equal('action', CREATE_TOPIC),
|
||||||
@ -127,21 +127,16 @@ const Composer = Discourse.Model.extend({
|
|||||||
} else {
|
} else {
|
||||||
// has a category? (when needed)
|
// has a category? (when needed)
|
||||||
return this.get('canCategorize') &&
|
return this.get('canCategorize') &&
|
||||||
!Discourse.SiteSettings.allow_uncategorized_topics &&
|
!this.siteSettings.allow_uncategorized_topics &&
|
||||||
!this.get('categoryId') &&
|
!this.get('categoryId') &&
|
||||||
!Discourse.User.currentProp('staff');
|
!this.user.get('staff');
|
||||||
}
|
}
|
||||||
}.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'),
|
}.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'),
|
||||||
|
|
||||||
/**
|
|
||||||
Is the title's length valid?
|
|
||||||
|
|
||||||
@property titleLengthValid
|
|
||||||
**/
|
|
||||||
titleLengthValid: function() {
|
titleLengthValid: function() {
|
||||||
if (Discourse.User.currentProp('admin') && this.get('post.static_doc') && this.get('titleLength') > 0) return true;
|
if (this.user.get('admin') && this.get('post.static_doc') && this.get('titleLength') > 0) return true;
|
||||||
if (this.get('titleLength') < this.get('minimumTitleLength')) return false;
|
if (this.get('titleLength') < this.get('minimumTitleLength')) return false;
|
||||||
return (this.get('titleLength') <= Discourse.SiteSettings.max_topic_title_length);
|
return (this.get('titleLength') <= this.siteSettings.max_topic_title_length);
|
||||||
}.property('minimumTitleLength', 'titleLength', 'post.static_doc'),
|
}.property('minimumTitleLength', 'titleLength', 'post.static_doc'),
|
||||||
|
|
||||||
// The icon for the save button
|
// The icon for the save button
|
||||||
@ -194,9 +189,9 @@ const Composer = Discourse.Model.extend({
|
|||||||
**/
|
**/
|
||||||
minimumTitleLength: function() {
|
minimumTitleLength: function() {
|
||||||
if (this.get('privateMessage')) {
|
if (this.get('privateMessage')) {
|
||||||
return Discourse.SiteSettings.min_private_message_title_length;
|
return this.siteSettings.min_private_message_title_length;
|
||||||
} else {
|
} else {
|
||||||
return Discourse.SiteSettings.min_topic_title_length;
|
return this.siteSettings.min_topic_title_length;
|
||||||
}
|
}
|
||||||
}.property('privateMessage'),
|
}.property('privateMessage'),
|
||||||
|
|
||||||
@ -216,12 +211,12 @@ const Composer = Discourse.Model.extend({
|
|||||||
**/
|
**/
|
||||||
minimumPostLength: function() {
|
minimumPostLength: function() {
|
||||||
if( this.get('privateMessage') ) {
|
if( this.get('privateMessage') ) {
|
||||||
return Discourse.SiteSettings.min_private_message_post_length;
|
return this.siteSettings.min_private_message_post_length;
|
||||||
} else if (this.get('topicFirstPost')) {
|
} else if (this.get('topicFirstPost')) {
|
||||||
// first post (topic body)
|
// first post (topic body)
|
||||||
return Discourse.SiteSettings.min_first_post_length;
|
return this.siteSettings.min_first_post_length;
|
||||||
} else {
|
} else {
|
||||||
return Discourse.SiteSettings.min_post_length;
|
return this.siteSettings.min_post_length;
|
||||||
}
|
}
|
||||||
}.property('privateMessage', 'topicFirstPost'),
|
}.property('privateMessage', 'topicFirstPost'),
|
||||||
|
|
||||||
@ -249,7 +244,7 @@ const Composer = Discourse.Model.extend({
|
|||||||
_setupComposer: function() {
|
_setupComposer: function() {
|
||||||
const val = (Discourse.Mobile.mobileView ? false : (Discourse.KeyValueStore.get('composer.showPreview') || 'true'));
|
const val = (Discourse.Mobile.mobileView ? false : (Discourse.KeyValueStore.get('composer.showPreview') || 'true'));
|
||||||
this.set('showPreview', val === 'true');
|
this.set('showPreview', val === 'true');
|
||||||
this.set('archetypeId', Discourse.Site.currentProp('default_archetype'));
|
this.set('archetypeId', this.site.get('default_archetype'));
|
||||||
}.on('init'),
|
}.on('init'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -349,15 +344,15 @@ const Composer = Discourse.Model.extend({
|
|||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
categoryId: opts.categoryId || this.get('topic.category.id'),
|
categoryId: opts.categoryId || this.get('topic.category.id'),
|
||||||
archetypeId: opts.archetypeId || Discourse.Site.currentProp('default_archetype'),
|
archetypeId: opts.archetypeId || this.site.get('default_archetype'),
|
||||||
metaData: opts.metaData ? Em.Object.create(opts.metaData) : null,
|
metaData: opts.metaData ? Em.Object.create(opts.metaData) : null,
|
||||||
reply: opts.reply || this.get("reply") || ""
|
reply: opts.reply || this.get("reply") || ""
|
||||||
});
|
});
|
||||||
|
|
||||||
if (opts.postId) {
|
if (opts.postId) {
|
||||||
this.set('loading', true);
|
this.set('loading', true);
|
||||||
Discourse.Post.load(opts.postId).then(function(result) {
|
this.store.find('post', opts.postId).then(function(post) {
|
||||||
composer.set('post', result);
|
composer.set('post', post);
|
||||||
composer.set('loading', false);
|
composer.set('loading', false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -370,10 +365,10 @@ const Composer = Discourse.Model.extend({
|
|||||||
|
|
||||||
this.setProperties(topicProps);
|
this.setProperties(topicProps);
|
||||||
|
|
||||||
Discourse.Post.load(opts.post.get('id')).then(function(result) {
|
this.store.find('post', opts.post.get('id')).then(function(post) {
|
||||||
composer.setProperties({
|
composer.setProperties({
|
||||||
reply: result.get('raw'),
|
reply: post.get('raw'),
|
||||||
originalText: result.get('raw'),
|
originalText: post.get('raw'),
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -467,7 +462,7 @@ const Composer = Discourse.Model.extend({
|
|||||||
createPost(opts) {
|
createPost(opts) {
|
||||||
const post = this.get('post'),
|
const post = this.get('post'),
|
||||||
topic = this.get('topic'),
|
topic = this.get('topic'),
|
||||||
currentUser = Discourse.User.current(),
|
user = this.user,
|
||||||
postStream = this.get('topic.postStream');
|
postStream = this.get('topic.postStream');
|
||||||
|
|
||||||
let addedToStream = false;
|
let addedToStream = false;
|
||||||
@ -477,17 +472,17 @@ const Composer = Discourse.Model.extend({
|
|||||||
imageSizes: opts.imageSizes,
|
imageSizes: opts.imageSizes,
|
||||||
cooked: this.getCookedHtml(),
|
cooked: this.getCookedHtml(),
|
||||||
reply_count: 0,
|
reply_count: 0,
|
||||||
name: currentUser.get('name'),
|
name: user.get('name'),
|
||||||
display_username: currentUser.get('name'),
|
display_username: user.get('name'),
|
||||||
username: currentUser.get('username'),
|
username: user.get('username'),
|
||||||
user_id: currentUser.get('id'),
|
user_id: user.get('id'),
|
||||||
user_title: currentUser.get('title'),
|
user_title: user.get('title'),
|
||||||
uploaded_avatar_id: currentUser.get('uploaded_avatar_id'),
|
uploaded_avatar_id: user.get('uploaded_avatar_id'),
|
||||||
user_custom_fields: currentUser.get('custom_fields'),
|
user_custom_fields: user.get('custom_fields'),
|
||||||
post_type: Discourse.Site.currentProp('post_types.regular'),
|
post_type: this.site.get('post_types.regular'),
|
||||||
actions_summary: [],
|
actions_summary: [],
|
||||||
moderator: currentUser.get('moderator'),
|
moderator: user.get('moderator'),
|
||||||
admin: currentUser.get('admin'),
|
admin: user.get('admin'),
|
||||||
yours: true,
|
yours: true,
|
||||||
newPost: true,
|
newPost: true,
|
||||||
read: true
|
read: true
|
||||||
@ -520,7 +515,7 @@ const Composer = Discourse.Model.extend({
|
|||||||
// we would need to handle oneboxes and other bits that are not even in the
|
// we would need to handle oneboxes and other bits that are not even in the
|
||||||
// engine, staging will just cause a blank post to render
|
// engine, staging will just cause a blank post to render
|
||||||
if (!_.isEmpty(createdPost.get('cooked'))) {
|
if (!_.isEmpty(createdPost.get('cooked'))) {
|
||||||
state = postStream.stagePost(createdPost, currentUser);
|
state = postStream.stagePost(createdPost, user);
|
||||||
|
|
||||||
if(state === "alreadyStaging"){
|
if(state === "alreadyStaging"){
|
||||||
return;
|
return;
|
||||||
@ -529,13 +524,12 @@ const Composer = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const composer = this,
|
const composer = this;
|
||||||
promise = new Ember.RSVP.Promise(function(resolve, reject) {
|
|
||||||
composer.set('composeState', SAVING);
|
composer.set('composeState', SAVING);
|
||||||
|
composer.set("stagedPost", state === "staged" && createdPost);
|
||||||
|
|
||||||
createdPost.save(function(result) {
|
return createdPost.save().then(function(result) {
|
||||||
let saving = true;
|
let saving = true;
|
||||||
|
|
||||||
createdPost.updateFromJson(result);
|
createdPost.updateFromJson(result);
|
||||||
|
|
||||||
if (topic) {
|
if (topic) {
|
||||||
@ -550,7 +544,7 @@ const Composer = Discourse.Model.extend({
|
|||||||
saving = false;
|
saving = false;
|
||||||
|
|
||||||
// Update topic_count for the category
|
// Update topic_count for the category
|
||||||
const category = Discourse.Site.currentProp('categories').find(function(x) { return x.get('id') === (parseInt(createdPost.get('category'),10) || 1); });
|
const category = composer.site.get('categories').find(function(x) { return x.get('id') === (parseInt(createdPost.get('category'),10) || 1); });
|
||||||
if (category) category.incrementProperty('topic_count');
|
if (category) category.incrementProperty('topic_count');
|
||||||
Discourse.notifyPropertyChange('globalNotice');
|
Discourse.notifyPropertyChange('globalNotice');
|
||||||
}
|
}
|
||||||
@ -564,8 +558,9 @@ const Composer = Discourse.Model.extend({
|
|||||||
composer.set('composeState', SAVING);
|
composer.set('composeState', SAVING);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve({ post: result });
|
return { post: result };
|
||||||
}, function(error) {
|
}).catch(function(error) {
|
||||||
|
|
||||||
// If an error occurs
|
// If an error occurs
|
||||||
if (postStream) {
|
if (postStream) {
|
||||||
postStream.undoPost(createdPost);
|
postStream.undoPost(createdPost);
|
||||||
@ -585,13 +580,8 @@ const Composer = Discourse.Model.extend({
|
|||||||
catch(ex) {
|
catch(ex) {
|
||||||
parsedError = "Unknown error saving post, try again. Error: " + error.status + " " + error.statusText;
|
parsedError = "Unknown error saving post, try again. Error: " + error.status + " " + error.statusText;
|
||||||
}
|
}
|
||||||
reject(parsedError);
|
throw parsedError;
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
composer.set("stagedPost", state === "staged" && createdPost);
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getCookedHtml() {
|
getCookedHtml() {
|
||||||
@ -604,7 +594,7 @@ const Composer = Discourse.Model.extend({
|
|||||||
// Do not save when there is no reply
|
// Do not save when there is no reply
|
||||||
if (!this.get('reply')) return;
|
if (!this.get('reply')) return;
|
||||||
// Do not save when the reply's length is too small
|
// Do not save when the reply's length is too small
|
||||||
if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return;
|
if (this.get('replyLength') < this.siteSettings.min_post_length) return;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
reply: this.get('reply'),
|
reply: this.get('reply'),
|
||||||
@ -673,6 +663,14 @@ Composer.reopenClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
create(args) {
|
||||||
|
args = args || {};
|
||||||
|
args.user = args.user || Discourse.User.current();
|
||||||
|
args.site = args.site || Discourse.Site.current();
|
||||||
|
args.siteSettings = args.siteSettings || Discourse.SiteSettings;
|
||||||
|
return this._super(args);
|
||||||
|
},
|
||||||
|
|
||||||
serializeToTopic(fieldName, property) {
|
serializeToTopic(fieldName, property) {
|
||||||
if (!property) { property = fieldName; }
|
if (!property) { property = fieldName; }
|
||||||
_edit_topic_serializer[fieldName] = property;
|
_edit_topic_serializer[fieldName] = property;
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
/**
|
const Post = Discourse.Model.extend({
|
||||||
A data model representing a post in a topic
|
|
||||||
|
|
||||||
@class Post
|
init() {
|
||||||
@extends Discourse.Model
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.Post = Discourse.Model.extend({
|
|
||||||
|
|
||||||
init: function() {
|
|
||||||
this.set('replyHistory', []);
|
this.set('replyHistory', []);
|
||||||
},
|
},
|
||||||
|
|
||||||
shareUrl: function() {
|
shareUrl: function() {
|
||||||
var user = Discourse.User.current();
|
const user = Discourse.User.current();
|
||||||
var userSuffix = user ? '?u=' + user.get('username_lower') : '';
|
const userSuffix = user ? '?u=' + user.get('username_lower') : '';
|
||||||
|
|
||||||
if (this.get('firstPost')) {
|
if (this.get('firstPost')) {
|
||||||
return this.get('topic.url') + userSuffix;
|
return this.get('topic.url') + userSuffix;
|
||||||
@ -33,7 +25,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
userDeleted: Em.computed.empty('user_id'),
|
userDeleted: Em.computed.empty('user_id'),
|
||||||
|
|
||||||
showName: function() {
|
showName: function() {
|
||||||
var name = this.get('name');
|
const name = this.get('name');
|
||||||
return name && (name !== this.get('username')) && Discourse.SiteSettings.display_name_on_posts;
|
return name && (name !== this.get('username')) && Discourse.SiteSettings.display_name_on_posts;
|
||||||
}.property('name', 'username'),
|
}.property('name', 'username'),
|
||||||
|
|
||||||
@ -69,17 +61,17 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
}.property("user_id"),
|
}.property("user_id"),
|
||||||
|
|
||||||
wikiChanged: function() {
|
wikiChanged: function() {
|
||||||
var data = { wiki: this.get("wiki") };
|
const data = { wiki: this.get("wiki") };
|
||||||
this._updatePost("wiki", data);
|
this._updatePost("wiki", data);
|
||||||
}.observes('wiki'),
|
}.observes('wiki'),
|
||||||
|
|
||||||
postTypeChanged: function () {
|
postTypeChanged: function () {
|
||||||
var data = { post_type: this.get("post_type") };
|
const data = { post_type: this.get("post_type") };
|
||||||
this._updatePost("post_type", data);
|
this._updatePost("post_type", data);
|
||||||
}.observes("post_type"),
|
}.observes("post_type"),
|
||||||
|
|
||||||
_updatePost: function (field, data) {
|
_updatePost(field, data) {
|
||||||
var self = this;
|
const self = this;
|
||||||
Discourse.ajax("/posts/" + this.get("id") + "/" + field, {
|
Discourse.ajax("/posts/" + this.get("id") + "/" + field, {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data: data
|
data: data
|
||||||
@ -103,7 +95,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
editCount: function() { return this.get('version') - 1; }.property('version'),
|
editCount: function() { return this.get('version') - 1; }.property('version'),
|
||||||
|
|
||||||
flagsAvailable: function() {
|
flagsAvailable: function() {
|
||||||
var post = this;
|
const post = this;
|
||||||
return Discourse.Site.currentProp('flagTypes').filter(function(item) {
|
return Discourse.Site.currentProp('flagTypes').filter(function(item) {
|
||||||
return post.get("actionByName." + item.get('name_key') + ".can_act");
|
return post.get("actionByName." + item.get('name_key') + ".can_act");
|
||||||
});
|
});
|
||||||
@ -119,9 +111,8 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
});
|
});
|
||||||
}.property('actions_summary.@each.users', 'actions_summary.@each.count'),
|
}.property('actions_summary.@each.users', 'actions_summary.@each.count'),
|
||||||
|
|
||||||
// Save a post and call the callback when done.
|
save() {
|
||||||
save: function(complete, error) {
|
const self = this;
|
||||||
var self = this;
|
|
||||||
if (!this.get('newPost')) {
|
if (!this.get('newPost')) {
|
||||||
// We're updating a post
|
// We're updating a post
|
||||||
return Discourse.ajax("/posts/" + (this.get('id')), {
|
return Discourse.ajax("/posts/" + (this.get('id')), {
|
||||||
@ -135,19 +126,17 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
// If we received a category update, update it
|
// If we received a category update, update it
|
||||||
self.set('version', result.post.version);
|
self.set('version', result.post.version);
|
||||||
if (result.category) Discourse.Site.current().updateCategory(result.category);
|
if (result.category) Discourse.Site.current().updateCategory(result.category);
|
||||||
if (complete) complete(Discourse.Post.create(result.post));
|
return Discourse.Post.create(result.post);
|
||||||
}).catch(function(result) {
|
|
||||||
// Post failed to update
|
|
||||||
if (error) error(result);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We're saving a post
|
// We're saving a post
|
||||||
var data = this.getProperties(Discourse.Composer.serializedFieldsForCreate());
|
const data = this.getProperties(Discourse.Composer.serializedFieldsForCreate());
|
||||||
data.reply_to_post_number = this.get('reply_to_post_number');
|
data.reply_to_post_number = this.get('reply_to_post_number');
|
||||||
data.image_sizes = this.get('imageSizes');
|
data.image_sizes = this.get('imageSizes');
|
||||||
|
data.nested_post = true;
|
||||||
|
|
||||||
var metaData = this.get('metaData');
|
const metaData = this.get('metaData');
|
||||||
// Put the metaData into the request
|
// Put the metaData into the request
|
||||||
if (metaData) {
|
if (metaData) {
|
||||||
data.meta_data = {};
|
data.meta_data = {};
|
||||||
@ -158,34 +147,22 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: data
|
data: data
|
||||||
}).then(function(result) {
|
}).then(function(result) {
|
||||||
// Post created
|
return Discourse.Post.create(result.post);
|
||||||
if (complete) complete(Discourse.Post.create(result));
|
|
||||||
}).catch(function(result) {
|
|
||||||
// Failed to create a post
|
|
||||||
if (error) error(result);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// Expands the first post's content, if embedded and shortened.
|
||||||
Expands the first post's content, if embedded and shortened.
|
expand() {
|
||||||
|
const self = this;
|
||||||
@method expandFirstPost
|
|
||||||
**/
|
|
||||||
expand: function() {
|
|
||||||
var self = this;
|
|
||||||
return Discourse.ajax("/posts/" + this.get('id') + "/expand-embed").then(function(post) {
|
return Discourse.ajax("/posts/" + this.get('id') + "/expand-embed").then(function(post) {
|
||||||
self.set('cooked', "<section class='expanded-embed'>" + post.cooked + "</section>" );
|
self.set('cooked', "<section class='expanded-embed'>" + post.cooked + "</section>" );
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// Recover a deleted post
|
||||||
Recover a deleted post
|
recover() {
|
||||||
|
const post = this;
|
||||||
@method recover
|
|
||||||
**/
|
|
||||||
recover: function() {
|
|
||||||
var post = this;
|
|
||||||
post.setProperties({
|
post.setProperties({
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
deleted_by: null,
|
deleted_by: null,
|
||||||
@ -207,11 +184,8 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
/**
|
/**
|
||||||
Changes the state of the post to be deleted. Does not call the server, that should be
|
Changes the state of the post to be deleted. Does not call the server, that should be
|
||||||
done elsewhere.
|
done elsewhere.
|
||||||
|
|
||||||
@method setDeletedState
|
|
||||||
@param {Discourse.User} deletedBy The user deleting the post
|
|
||||||
**/
|
**/
|
||||||
setDeletedState: function(deletedBy) {
|
setDeletedState(deletedBy) {
|
||||||
this.set('oldCooked', this.get('cooked'));
|
this.set('oldCooked', this.get('cooked'));
|
||||||
|
|
||||||
// Moderators can delete posts. Users can only trigger a deleted at message, unless delete_removed_posts_after is 0.
|
// Moderators can delete posts. Users can only trigger a deleted at message, unless delete_removed_posts_after is 0.
|
||||||
@ -237,10 +211,8 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
Changes the state of the post to NOT be deleted. Does not call the server.
|
Changes the state of the post to NOT be deleted. Does not call the server.
|
||||||
This can only be called after setDeletedState was called, but the delete
|
This can only be called after setDeletedState was called, but the delete
|
||||||
failed on the server.
|
failed on the server.
|
||||||
|
|
||||||
@method undoDeletedState
|
|
||||||
**/
|
**/
|
||||||
undoDeleteState: function() {
|
undoDeleteState() {
|
||||||
if (this.get('oldCooked')) {
|
if (this.get('oldCooked')) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
@ -253,13 +225,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
destroy(deletedBy) {
|
||||||
Deletes a post
|
|
||||||
|
|
||||||
@method destroy
|
|
||||||
@param {Discourse.User} deletedBy The user deleting the post
|
|
||||||
**/
|
|
||||||
destroy: function(deletedBy) {
|
|
||||||
this.setDeletedState(deletedBy);
|
this.setDeletedState(deletedBy);
|
||||||
return Discourse.ajax("/posts/" + this.get('id'), {
|
return Discourse.ajax("/posts/" + this.get('id'), {
|
||||||
data: { context: window.location.pathname },
|
data: { context: window.location.pathname },
|
||||||
@ -270,14 +236,11 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
/**
|
/**
|
||||||
Updates a post from another's attributes. This will normally happen when a post is loading but
|
Updates a post from another's attributes. This will normally happen when a post is loading but
|
||||||
is already found in an identity map.
|
is already found in an identity map.
|
||||||
|
|
||||||
@method updateFromPost
|
|
||||||
@param {Discourse.Post} otherPost The post we're updating from
|
|
||||||
**/
|
**/
|
||||||
updateFromPost: function(otherPost) {
|
updateFromPost(otherPost) {
|
||||||
var self = this;
|
const self = this;
|
||||||
Object.keys(otherPost).forEach(function (key) {
|
Object.keys(otherPost).forEach(function (key) {
|
||||||
var value = otherPost[key],
|
let value = otherPost[key],
|
||||||
oldValue = self[key];
|
oldValue = self[key];
|
||||||
|
|
||||||
if (key === "replyHistory") {
|
if (key === "replyHistory") {
|
||||||
@ -287,7 +250,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
if (!value) { value = null; }
|
if (!value) { value = null; }
|
||||||
if (!oldValue) { oldValue = null; }
|
if (!oldValue) { oldValue = null; }
|
||||||
|
|
||||||
var skip = false;
|
let skip = false;
|
||||||
if (typeof value !== "function" && oldValue !== value) {
|
if (typeof value !== "function" && oldValue !== value) {
|
||||||
// wishing for an identity map
|
// wishing for an identity map
|
||||||
if (key === "reply_to_user" && value && oldValue) {
|
if (key === "reply_to_user" && value && oldValue) {
|
||||||
@ -304,17 +267,14 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
/**
|
/**
|
||||||
Updates a post from a JSON packet. This is normally done after the post is saved to refresh any
|
Updates a post from a JSON packet. This is normally done after the post is saved to refresh any
|
||||||
attributes.
|
attributes.
|
||||||
|
|
||||||
@method updateFromJson
|
|
||||||
@param {Object} obj The Json data to update with
|
|
||||||
**/
|
**/
|
||||||
updateFromJson: function(obj) {
|
updateFromJson(obj) {
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
|
|
||||||
var skip, oldVal;
|
let skip, oldVal;
|
||||||
|
|
||||||
// Update all the properties
|
// Update all the properties
|
||||||
var post = this;
|
const post = this;
|
||||||
_.each(obj, function(val,key) {
|
_.each(obj, function(val,key) {
|
||||||
if (key !== 'actions_summary'){
|
if (key !== 'actions_summary'){
|
||||||
oldVal = post[key];
|
oldVal = post[key];
|
||||||
@ -336,12 +296,11 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
// Rebuild actions summary
|
// Rebuild actions summary
|
||||||
this.set('actions_summary', Em.A());
|
this.set('actions_summary', Em.A());
|
||||||
if (obj.actions_summary) {
|
if (obj.actions_summary) {
|
||||||
var lookup = Em.Object.create();
|
const lookup = Em.Object.create();
|
||||||
_.each(obj.actions_summary,function(a) {
|
_.each(obj.actions_summary,function(a) {
|
||||||
var actionSummary;
|
|
||||||
a.post = post;
|
a.post = post;
|
||||||
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
||||||
actionSummary = Discourse.ActionSummary.create(a);
|
const actionSummary = Discourse.ActionSummary.create(a);
|
||||||
post.get('actions_summary').pushObject(actionSummary);
|
post.get('actions_summary').pushObject(actionSummary);
|
||||||
lookup.set(a.actionType.get('name_key'), actionSummary);
|
lookup.set(a.actionType.get('name_key'), actionSummary);
|
||||||
});
|
});
|
||||||
@ -350,7 +309,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Load replies to this post
|
// Load replies to this post
|
||||||
loadReplies: function() {
|
loadReplies() {
|
||||||
if(this.get('loadingReplies')){
|
if(this.get('loadingReplies')){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -358,12 +317,12 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
this.set('loadingReplies', true);
|
this.set('loadingReplies', true);
|
||||||
this.set('replies', []);
|
this.set('replies', []);
|
||||||
|
|
||||||
var self = this;
|
const self = this;
|
||||||
return Discourse.ajax("/posts/" + (this.get('id')) + "/replies")
|
return Discourse.ajax("/posts/" + (this.get('id')) + "/replies")
|
||||||
.then(function(loaded) {
|
.then(function(loaded) {
|
||||||
var replies = self.get('replies');
|
const replies = self.get('replies');
|
||||||
_.each(loaded,function(reply) {
|
_.each(loaded,function(reply) {
|
||||||
var post = Discourse.Post.create(reply);
|
const post = Discourse.Post.create(reply);
|
||||||
post.set('topic', self.get('topic'));
|
post.set('topic', self.get('topic'));
|
||||||
replies.pushObject(post);
|
replies.pushObject(post);
|
||||||
});
|
});
|
||||||
@ -375,7 +334,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
|
|
||||||
// Whether to show replies directly below
|
// Whether to show replies directly below
|
||||||
showRepliesBelow: function() {
|
showRepliesBelow: function() {
|
||||||
var replyCount = this.get('reply_count');
|
const replyCount = this.get('reply_count');
|
||||||
|
|
||||||
// We don't show replies if there aren't any
|
// We don't show replies if there aren't any
|
||||||
if (replyCount === 0) return false;
|
if (replyCount === 0) return false;
|
||||||
@ -387,13 +346,13 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
if (replyCount > 1) return true;
|
if (replyCount > 1) return true;
|
||||||
|
|
||||||
// If we have *exactly* one reply, we have to consider if it's directly below us
|
// If we have *exactly* one reply, we have to consider if it's directly below us
|
||||||
var topic = this.get('topic');
|
const topic = this.get('topic');
|
||||||
return !topic.isReplyDirectlyBelow(this);
|
return !topic.isReplyDirectlyBelow(this);
|
||||||
|
|
||||||
}.property('reply_count'),
|
}.property('reply_count'),
|
||||||
|
|
||||||
expandHidden: function() {
|
expandHidden() {
|
||||||
var self = this;
|
const self = this;
|
||||||
return Discourse.ajax("/posts/" + this.get('id') + "/cooked.json").then(function (result) {
|
return Discourse.ajax("/posts/" + this.get('id') + "/cooked.json").then(function (result) {
|
||||||
self.setProperties({
|
self.setProperties({
|
||||||
cooked: result.cooked,
|
cooked: result.cooked,
|
||||||
@ -402,17 +361,17 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
rebake: function () {
|
rebake() {
|
||||||
return Discourse.ajax("/posts/" + this.get("id") + "/rebake", { type: "PUT" });
|
return Discourse.ajax("/posts/" + this.get("id") + "/rebake", { type: "PUT" });
|
||||||
},
|
},
|
||||||
|
|
||||||
unhide: function () {
|
unhide() {
|
||||||
return Discourse.ajax("/posts/" + this.get("id") + "/unhide", { type: "PUT" });
|
return Discourse.ajax("/posts/" + this.get("id") + "/unhide", { type: "PUT" });
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleBookmark: function() {
|
toggleBookmark() {
|
||||||
var self = this,
|
const self = this;
|
||||||
bookmarkedTopic;
|
let bookmarkedTopic;
|
||||||
|
|
||||||
this.toggleProperty("bookmarked");
|
this.toggleProperty("bookmarked");
|
||||||
|
|
||||||
@ -435,16 +394,16 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Discourse.Post.reopenClass({
|
Post.reopenClass({
|
||||||
|
|
||||||
createActionSummary: function(result) {
|
createActionSummary(result) {
|
||||||
if (result.actions_summary) {
|
if (result.actions_summary) {
|
||||||
var lookup = Em.Object.create();
|
const lookup = Em.Object.create();
|
||||||
// this area should be optimized, it is creating way too many objects per post
|
// this area should be optimized, it is creating way too many objects per post
|
||||||
result.actions_summary = result.actions_summary.map(function(a) {
|
result.actions_summary = result.actions_summary.map(function(a) {
|
||||||
a.post = result;
|
a.post = result;
|
||||||
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
||||||
var actionSummary = Discourse.ActionSummary.create(a);
|
const actionSummary = Discourse.ActionSummary.create(a);
|
||||||
lookup[a.actionType.name_key] = actionSummary;
|
lookup[a.actionType.name_key] = actionSummary;
|
||||||
return actionSummary;
|
return actionSummary;
|
||||||
});
|
});
|
||||||
@ -452,8 +411,8 @@ Discourse.Post.reopenClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
create: function(obj) {
|
create(obj) {
|
||||||
var result = this._super.apply(this, arguments);
|
const result = this._super.apply(this, arguments);
|
||||||
this.createActionSummary(result);
|
this.createActionSummary(result);
|
||||||
if (obj && obj.reply_to_user) {
|
if (obj && obj.reply_to_user) {
|
||||||
result.set('reply_to_user', Discourse.User.create(obj.reply_to_user));
|
result.set('reply_to_user', Discourse.User.create(obj.reply_to_user));
|
||||||
@ -461,14 +420,14 @@ Discourse.Post.reopenClass({
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateBookmark: function(postId, bookmarked) {
|
updateBookmark(postId, bookmarked) {
|
||||||
return Discourse.ajax("/posts/" + postId + "/bookmark", {
|
return Discourse.ajax("/posts/" + postId + "/bookmark", {
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: { bookmarked: bookmarked }
|
data: { bookmarked: bookmarked }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteMany: function(selectedPosts, selectedReplies) {
|
deleteMany(selectedPosts, selectedReplies) {
|
||||||
return Discourse.ajax("/posts/destroy_many", {
|
return Discourse.ajax("/posts/destroy_many", {
|
||||||
type: 'DELETE',
|
type: 'DELETE',
|
||||||
data: {
|
data: {
|
||||||
@ -478,37 +437,33 @@ Discourse.Post.reopenClass({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadRevision: function(postId, version) {
|
loadRevision(postId, version) {
|
||||||
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + ".json").then(function (result) {
|
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + ".json").then(function (result) {
|
||||||
return Ember.Object.create(result);
|
return Ember.Object.create(result);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
hideRevision: function(postId, version) {
|
hideRevision(postId, version) {
|
||||||
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + "/hide", { type: 'PUT' });
|
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + "/hide", { type: 'PUT' });
|
||||||
},
|
},
|
||||||
|
|
||||||
showRevision: function(postId, version) {
|
showRevision(postId, version) {
|
||||||
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + "/show", { type: 'PUT' });
|
return Discourse.ajax("/posts/" + postId + "/revisions/" + version + "/show", { type: 'PUT' });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadQuote: function(postId) {
|
loadQuote(postId) {
|
||||||
return Discourse.ajax("/posts/" + postId + ".json").then(function (result) {
|
return Discourse.ajax("/posts/" + postId + ".json").then(function (result) {
|
||||||
var post = Discourse.Post.create(result);
|
const post = Discourse.Post.create(result);
|
||||||
return Discourse.Quote.build(post, post.get('raw'));
|
return Discourse.Quote.build(post, post.get('raw'));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadRawEmail: function(postId) {
|
loadRawEmail(postId) {
|
||||||
return Discourse.ajax("/posts/" + postId + "/raw-email").then(function (result) {
|
return Discourse.ajax("/posts/" + postId + "/raw-email").then(function (result) {
|
||||||
return result.raw_email;
|
return result.raw_email;
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
load: function(postId) {
|
|
||||||
return Discourse.ajax("/posts/" + postId + ".json").then(function (result) {
|
|
||||||
return Discourse.Post.create(result);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default Post;
|
@ -25,6 +25,7 @@
|
|||||||
//= require ./discourse/lib/safari-hacks
|
//= require ./discourse/lib/safari-hacks
|
||||||
//= require_tree ./discourse/adapters
|
//= require_tree ./discourse/adapters
|
||||||
//= require ./discourse/models/model
|
//= require ./discourse/models/model
|
||||||
|
//= require ./discourse/models/post
|
||||||
//= require ./discourse/models/user_action
|
//= require ./discourse/models/user_action
|
||||||
//= require ./discourse/models/composer
|
//= require ./discourse/models/composer
|
||||||
//= require ./discourse/models/post-stream
|
//= require ./discourse/models/post-stream
|
||||||
|
@ -336,7 +336,11 @@ class PostsController < ApplicationController
|
|||||||
# doesn't return the post as the root JSON object, but as a nested object.
|
# doesn't return the post as the root JSON object, but as a nested object.
|
||||||
# If a param is present it uses that result structure.
|
# If a param is present it uses that result structure.
|
||||||
def backwards_compatible_json(json_obj, success)
|
def backwards_compatible_json(json_obj, success)
|
||||||
json_obj = json_obj[:post] || json_obj['post'] unless params[:nested_post]
|
json_obj.symbolize_keys!
|
||||||
|
if params[:nested_post].blank? && json_obj[:errors].blank?
|
||||||
|
json_obj = json_obj[:post]
|
||||||
|
end
|
||||||
|
|
||||||
render json: json_obj, status: (!!success) ? 200 : 422
|
render json: json_obj, status: (!!success) ? 200 : 422
|
||||||
end
|
end
|
||||||
|
|
||||||
|
117
test/javascripts/acceptance/composer-test.js.es6
Normal file
117
test/javascripts/acceptance/composer-test.js.es6
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Composer", { loggedIn: true });
|
||||||
|
|
||||||
|
test("Tests the Composer controls", () => {
|
||||||
|
visit("/");
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('#create-topic'), 'the create button is visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
click('#create-topic');
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('#wmd-input'), 'the composer input is visible');
|
||||||
|
ok(exists('.title-input .popup-tip.bad.hide'), 'title errors are hidden by default');
|
||||||
|
ok(exists('.textarea-wrapper .popup-tip.bad.hide'), 'body errors are hidden by default');
|
||||||
|
});
|
||||||
|
|
||||||
|
click('a.toggle-preview');
|
||||||
|
andThen(() => {
|
||||||
|
ok(!exists('#wmd-preview:visible'), "clicking the toggle hides the preview");
|
||||||
|
});
|
||||||
|
|
||||||
|
click('a.toggle-preview');
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('#wmd-preview:visible'), "clicking the toggle shows the preview again");
|
||||||
|
});
|
||||||
|
|
||||||
|
click('#reply-control button.create');
|
||||||
|
andThen(() => {
|
||||||
|
ok(!exists('.title-input .popup-tip.bad.hide'), 'it shows the empty title error');
|
||||||
|
ok(!exists('.textarea-wrapper .popup-tip.bad.hide'), 'it shows the empty body error');
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('#reply-title', "this is my new topic title");
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('.title-input .popup-tip.good'), 'the title is now good');
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('#wmd-input', "this is the *content* of a post");
|
||||||
|
andThen(() => {
|
||||||
|
equal(find('#wmd-preview').html(), "<p>this is the <em>content</em> of a post</p>", "it previews content");
|
||||||
|
ok(exists('.textarea-wrapper .popup-tip.good'), 'the body is now good');
|
||||||
|
});
|
||||||
|
|
||||||
|
click('#reply-control a.cancel');
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('.bootbox.modal'), 'it pops up a confirmation dialog');
|
||||||
|
});
|
||||||
|
|
||||||
|
click('.modal-footer a:eq(1)');
|
||||||
|
andThen(() => {
|
||||||
|
ok(!exists('.bootbox.modal'), 'the confirmation can be cancelled');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Create a topic with server side errors", () => {
|
||||||
|
visit("/");
|
||||||
|
click('#create-topic');
|
||||||
|
fillIn('#reply-title', "this title triggers an error");
|
||||||
|
fillIn('#wmd-input', "this is the *content* of a post");
|
||||||
|
click('#reply-control button.create');
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('.bootbox.modal'), 'it pops up an error message');
|
||||||
|
});
|
||||||
|
click('.bootbox.modal a.btn-primary');
|
||||||
|
andThen(() => {
|
||||||
|
ok(!exists('.bootbox.modal'), 'it dismisses the error');
|
||||||
|
ok(exists('#wmd-input'), 'the composer input is visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Create a Topic", () => {
|
||||||
|
visit("/");
|
||||||
|
click('#create-topic');
|
||||||
|
fillIn('#reply-title', "Internationalization Localization");
|
||||||
|
fillIn('#wmd-input', "this is the *content* of a new topic post");
|
||||||
|
click('#reply-control button.create');
|
||||||
|
andThen(() => {
|
||||||
|
equal(currentURL(), "/t/internationalization-localization/280", "it transitions to the newly created topic URL");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Create a Reply", () => {
|
||||||
|
visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
|
click('#topic-footer-buttons .btn.create');
|
||||||
|
andThen(() => {
|
||||||
|
ok(exists('#wmd-input'), 'the composer input is visible');
|
||||||
|
ok(!exists('#reply-title'), 'there is no title since this is a reply');
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('#wmd-input', 'this is the content of my reply');
|
||||||
|
click('#reply-control button.create');
|
||||||
|
andThen(() => {
|
||||||
|
exists('#post_12345', 'it inserts the post into the document');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Edit the first post", () => {
|
||||||
|
visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
|
click('.topic-post:eq(0) button[data-action=showMoreActions]');
|
||||||
|
click('.topic-post:eq(0) button[data-action=edit]');
|
||||||
|
andThen(() => {
|
||||||
|
equal(find('#wmd-input').val().indexOf('Any plans to support'), 0, 'it populates the input with the post text');
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('#wmd-input', "This is the new text for the post");
|
||||||
|
fillIn('#reply-title', "This is the new text for the title");
|
||||||
|
click('#reply-control button.create');
|
||||||
|
andThen(() => {
|
||||||
|
ok(!exists('#wmd-input'), 'it closes the composer');
|
||||||
|
ok(find('#topic-title h1').text().indexOf('This is the new text for the title') !== -1, 'it shows the new title');
|
||||||
|
ok(find('.topic-post:eq(0) .cooked').text().indexOf('This is the new text for the post') !== -1, 'it updates the post');
|
||||||
|
});
|
||||||
|
});
|
@ -1,10 +1,6 @@
|
|||||||
import { acceptance } from "helpers/qunit-helpers";
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
acceptance("Header (Staff)", {
|
acceptance("Header (Staff)", { loggedIn: true });
|
||||||
user: { username: 'test',
|
|
||||||
staff: true,
|
|
||||||
site_flagged_posts_count: 1 }
|
|
||||||
});
|
|
||||||
|
|
||||||
test("header", () => {
|
test("header", () => {
|
||||||
visit("/");
|
visit("/");
|
||||||
|
4
test/javascripts/fixtures/post.js.es6
Normal file
4
test/javascripts/fixtures/post.js.es6
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
"/posts/398": {"id":398,"name":"Uwe Keim","username":"uwe_keim","avatar_template":"/user_avatar/meta.discourse.org/uwe_keim/{size}/5697.png","uploaded_avatar_id":5697,"created_at":"2013-02-05T21:29:00.280Z","cooked":"<p>Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?</p>","post_number":1,"post_type":1,"updated_at":"2013-02-05T21:29:00.280Z","like_count":0,"reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":25,"incoming_link_count":314,"reads":475,"score":1702.25,"yours":false,"topic_id":280,"topic_slug":"internationalization-localization","display_username":"Uwe Keim","primary_group_name":null,"version":2,"can_edit":true,"can_delete":false,"can_recover":true,"user_title":null,"raw":"Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":3,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":4,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":5,"count":0,"hidden":true,"can_act":true,"can_defer_flags":false},{"id":6,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":7,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":8,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false}],"moderator":false,"admin":false,"staff":false,"user_id":255,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}
|
||||||
|
};
|
||||||
|
|
4
test/javascripts/fixtures/session-fixtures.js.es6
Normal file
4
test/javascripts/fixtures/session-fixtures.js.es6
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
"/session/current.json": {"current_user":{"id":19,"username":"eviltrout","uploaded_avatar_id":5275,"avatar_template":"/user_avatar/localhost/eviltrout/{size}/5275.png","name":"Robin Ward","total_unread_notifications":205,"unread_notifications":0,"unread_private_messages":0,"admin":true,"notification_channel_position":null,"site_flagged_posts_count":1,"moderator":true,"staff":true,"title":"co-founder","reply_count":859,"topic_count":36,"enable_quoting":true,"external_links_in_new_tab":false,"dynamic_favicon":true,"trust_level":4,"can_edit":true,"can_invite_to_forum":true,"should_be_redirected_to_top":false,"disable_jump_reply":false,"custom_fields":{},"muted_category_ids":[],"dismissed_banner_key":null,"akismet_review_count":0}}
|
||||||
|
};
|
||||||
|
|
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@ function parsePostData(query) {
|
|||||||
const result = {};
|
const result = {};
|
||||||
query.split("&").forEach(function(part) {
|
query.split("&").forEach(function(part) {
|
||||||
const item = part.split("=");
|
const item = part.split("=");
|
||||||
result[item[0]] = decodeURIComponent(item[1]);
|
result[item[0]] = decodeURIComponent(item[1]).replace(/\+/g, ' ');
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -33,9 +33,16 @@ const _moreWidgets = [
|
|||||||
{id: 224, name: 'Good Repellant'}
|
{id: 224, name: 'Good Repellant'}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function loggedIn() {
|
||||||
|
return !!Discourse.User.current();
|
||||||
|
}
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
|
|
||||||
const server = new Pretender(function() {
|
const server = new Pretender(function() {
|
||||||
|
|
||||||
|
const fixturesByUrl = {};
|
||||||
|
|
||||||
// Load any fixtures automatically
|
// Load any fixtures automatically
|
||||||
const self = this;
|
const self = this;
|
||||||
Ember.keys(require._eak_seen).forEach(function(entry) {
|
Ember.keys(require._eak_seen).forEach(function(entry) {
|
||||||
@ -44,6 +51,7 @@ export default function() {
|
|||||||
if (fixture && fixture.default) {
|
if (fixture && fixture.default) {
|
||||||
const obj = fixture.default;
|
const obj = fixture.default;
|
||||||
Ember.keys(obj).forEach(function(url) {
|
Ember.keys(obj).forEach(function(url) {
|
||||||
|
fixturesByUrl[url] = obj[url];
|
||||||
self.get(url, function() {
|
self.get(url, function() {
|
||||||
return response(obj[url]);
|
return response(obj[url]);
|
||||||
});
|
});
|
||||||
@ -52,6 +60,20 @@ export default function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.get('/composer-messages', () => { return response([]); });
|
||||||
|
|
||||||
|
this.get("/latest.json", () => {
|
||||||
|
const json = fixturesByUrl['/latest.json'];
|
||||||
|
|
||||||
|
if (loggedIn()) {
|
||||||
|
// Stuff to let us post
|
||||||
|
json.topic_list.can_create_topic = true;
|
||||||
|
json.topic_list.draft_key = "new_topic";
|
||||||
|
json.topic_list.draft_sequence = 1;
|
||||||
|
}
|
||||||
|
return response(json);
|
||||||
|
});
|
||||||
|
|
||||||
this.get("/t/id_for/:slug", function() {
|
this.get("/t/id_for/:slug", function() {
|
||||||
return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"});
|
return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"});
|
||||||
});
|
});
|
||||||
@ -99,6 +121,33 @@ export default function() {
|
|||||||
this.delete('/posts/:post_id', success);
|
this.delete('/posts/:post_id', success);
|
||||||
this.put('/posts/:post_id/recover', success);
|
this.put('/posts/:post_id/recover', success);
|
||||||
|
|
||||||
|
this.put('/posts/:post_id', (request) => {
|
||||||
|
return response({ post: {id: request.params.post_id, version: 2 } });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.put('/t/:slug/:id', (request) => {
|
||||||
|
const data = parsePostData(request.requestBody);
|
||||||
|
|
||||||
|
return response(200, { basic_topic: {id: request.params.id,
|
||||||
|
title: data.title,
|
||||||
|
fancy_title: data.title,
|
||||||
|
slug: request.params.slug } })
|
||||||
|
});
|
||||||
|
|
||||||
|
this.post('/posts', function(request) {
|
||||||
|
const data = parsePostData(request.requestBody);
|
||||||
|
|
||||||
|
if (data.title === "this title triggers an error") {
|
||||||
|
return response(422, {errors: ['That title has already been taken']});
|
||||||
|
} else {
|
||||||
|
return response(200, {
|
||||||
|
success: true,
|
||||||
|
action: 'create_post',
|
||||||
|
post: {id: 12345, topic_id: 280, topic_slug: 'internationalization-localization'}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.get('/widgets/:widget_id', function(request) {
|
this.get('/widgets/:widget_id', function(request) {
|
||||||
const w = _widgets.findBy('id', parseInt(request.params.widget_id));
|
const w = _widgets.findBy('id', parseInt(request.params.widget_id));
|
||||||
if (w) {
|
if (w) {
|
||||||
@ -130,8 +179,11 @@ export default function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.delete('/widgets/:widget_id', success);
|
this.delete('/widgets/:widget_id', success);
|
||||||
});
|
|
||||||
|
|
||||||
|
this.post('/topics/timings', function() {
|
||||||
|
return response(200, {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
server.prepareBody = function(body){
|
server.prepareBody = function(body){
|
||||||
if (body && typeof body === "object") {
|
if (body && typeof body === "object") {
|
||||||
|
@ -1,20 +1,59 @@
|
|||||||
/* global asyncTest */
|
/* global asyncTest */
|
||||||
|
|
||||||
|
import sessionFixtures from 'fixtures/session-fixtures';
|
||||||
import siteFixtures from 'fixtures/site_fixtures';
|
import siteFixtures from 'fixtures/site_fixtures';
|
||||||
|
import HeaderView from 'discourse/views/header';
|
||||||
|
|
||||||
|
function currentUser() {
|
||||||
|
return Discourse.User.create(sessionFixtures['/session/current.json'].current_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logIn() {
|
||||||
|
Discourse.User.resetCurrent(currentUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plugin = $.fn.modal;
|
||||||
|
const Modal = Plugin.Constructor;
|
||||||
|
|
||||||
|
function AcceptanceModal(option, _relatedTarget) {
|
||||||
|
return this.each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var data = $this.data('bs.modal');
|
||||||
|
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option === 'object' && option);
|
||||||
|
|
||||||
|
if (!data) $this.data('bs.modal', (data = new Modal(this, options)));
|
||||||
|
data.$body = $('#ember-testing');
|
||||||
|
|
||||||
|
if (typeof option === 'string') data[option](_relatedTarget);
|
||||||
|
else if (options.show) data.show(_relatedTarget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.bootbox.$body = $('#ember-testing');
|
||||||
|
$.fn.modal = AcceptanceModal;
|
||||||
|
|
||||||
|
var oldAvatar = Discourse.Utilities.avatarImg;
|
||||||
|
|
||||||
function acceptance(name, options) {
|
function acceptance(name, options) {
|
||||||
module("Acceptance: " + name, {
|
module("Acceptance: " + name, {
|
||||||
setup: function() {
|
setup: function() {
|
||||||
Ember.run(Discourse, Discourse.advanceReadiness);
|
Ember.run(Discourse, Discourse.advanceReadiness);
|
||||||
|
|
||||||
|
// Don't render avatars in acceptance tests, it's faster and no 404s
|
||||||
|
Discourse.Utilities.avatarImg = () => "";
|
||||||
|
|
||||||
|
// For now don't do scrolling stuff in Test Mode
|
||||||
|
Ember.CloakedCollectionView.scrolled = Ember.K;
|
||||||
|
HeaderView.reopen({examineDockHeader: Ember.K});
|
||||||
|
|
||||||
var siteJson = siteFixtures['site.json'].site;
|
var siteJson = siteFixtures['site.json'].site;
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.setup) {
|
if (options.setup) {
|
||||||
options.setup.call(this);
|
options.setup.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.user) {
|
if (options.loggedIn) {
|
||||||
Discourse.User.resetCurrent(Discourse.User.create(options.user));
|
logIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.settings) {
|
if (options.settings) {
|
||||||
@ -34,6 +73,7 @@ function acceptance(name, options) {
|
|||||||
options.teardown.call(this);
|
options.teardown.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Discourse.Utilities.avatarImg = oldAvatar;
|
||||||
Discourse.reset();
|
Discourse.reset();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -61,4 +101,4 @@ function fixture(selector) {
|
|||||||
return $("#qunit-fixture");
|
return $("#qunit-fixture");
|
||||||
}
|
}
|
||||||
|
|
||||||
export { acceptance, controllerFor, asyncTestDiscourse, fixture };
|
export { acceptance, controllerFor, asyncTestDiscourse, fixture, logIn, currentUser };
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
module("Discourse.Composer", {
|
import { currentUser } from 'helpers/qunit-helpers';
|
||||||
setup: function() {
|
|
||||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('admin').returns(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
teardown: function() {
|
module("model:composer");
|
||||||
Discourse.User.currentProp.restore();
|
|
||||||
|
function createComposer(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts.user = opts.user || currentUser();
|
||||||
|
opts.site = Discourse.Site.current();
|
||||||
|
opts.siteSettings = Discourse.SiteSettings;
|
||||||
|
return Discourse.Composer.create(opts);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
test('replyLength', function() {
|
test('replyLength', function() {
|
||||||
var replyLength = function(val, expectedLength) {
|
const replyLength = function(val, expectedLength) {
|
||||||
var composer = Discourse.Composer.create({ reply: val });
|
const composer = createComposer({ reply: val });
|
||||||
equal(composer.get('replyLength'), expectedLength);
|
equal(composer.get('replyLength'), expectedLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,8 +25,8 @@ test('replyLength', function() {
|
|||||||
|
|
||||||
test('missingReplyCharacters', function() {
|
test('missingReplyCharacters', function() {
|
||||||
Discourse.SiteSettings.min_first_post_length = 40;
|
Discourse.SiteSettings.min_first_post_length = 40;
|
||||||
var missingReplyCharacters = function(val, isPM, isFirstPost, expected, message) {
|
const missingReplyCharacters = function(val, isPM, isFirstPost, expected, message) {
|
||||||
var composer = Discourse.Composer.create({ reply: val, creatingPrivateMessage: isPM, creatingTopic: isFirstPost });
|
const composer = createComposer({ reply: val, creatingPrivateMessage: isPM, creatingTopic: isFirstPost });
|
||||||
equal(composer.get('missingReplyCharacters'), expected, message);
|
equal(composer.get('missingReplyCharacters'), expected, message);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,8 +36,8 @@ test('missingReplyCharacters', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('missingTitleCharacters', function() {
|
test('missingTitleCharacters', function() {
|
||||||
var missingTitleCharacters = function(val, isPM, expected, message) {
|
const missingTitleCharacters = function(val, isPM, expected, message) {
|
||||||
var composer = Discourse.Composer.create({ title: val, creatingPrivateMessage: isPM });
|
const composer = createComposer({ title: val, creatingPrivateMessage: isPM });
|
||||||
equal(composer.get('missingTitleCharacters'), expected, message);
|
equal(composer.get('missingTitleCharacters'), expected, message);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ test('missingTitleCharacters', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('replyDirty', function() {
|
test('replyDirty', function() {
|
||||||
var composer = Discourse.Composer.create();
|
const composer = createComposer();
|
||||||
ok(!composer.get('replyDirty'), "by default it's false");
|
ok(!composer.get('replyDirty'), "by default it's false");
|
||||||
|
|
||||||
composer.setProperties({
|
composer.setProperties({
|
||||||
@ -58,7 +60,7 @@ test('replyDirty', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("appendText", function() {
|
test("appendText", function() {
|
||||||
var composer = Discourse.Composer.create();
|
const composer = createComposer();
|
||||||
|
|
||||||
blank(composer.get('reply'), "the reply is blank by default");
|
blank(composer.get('reply'), "the reply is blank by default");
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ test("appendText", function() {
|
|||||||
test("Title length for regular topics", function() {
|
test("Title length for regular topics", function() {
|
||||||
Discourse.SiteSettings.min_topic_title_length = 5;
|
Discourse.SiteSettings.min_topic_title_length = 5;
|
||||||
Discourse.SiteSettings.max_topic_title_length = 10;
|
Discourse.SiteSettings.max_topic_title_length = 10;
|
||||||
var composer = Discourse.Composer.create();
|
const composer = createComposer();
|
||||||
|
|
||||||
composer.set('title', 'asdf');
|
composer.set('title', 'asdf');
|
||||||
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
||||||
@ -104,7 +106,7 @@ test("Title length for regular topics", function() {
|
|||||||
test("Title length for private messages", function() {
|
test("Title length for private messages", function() {
|
||||||
Discourse.SiteSettings.min_private_message_title_length = 5;
|
Discourse.SiteSettings.min_private_message_title_length = 5;
|
||||||
Discourse.SiteSettings.max_topic_title_length = 10;
|
Discourse.SiteSettings.max_topic_title_length = 10;
|
||||||
var composer = Discourse.Composer.create({action: Discourse.Composer.PRIVATE_MESSAGE});
|
const composer = createComposer({action: Discourse.Composer.PRIVATE_MESSAGE});
|
||||||
|
|
||||||
composer.set('title', 'asdf');
|
composer.set('title', 'asdf');
|
||||||
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
||||||
@ -119,7 +121,7 @@ test("Title length for private messages", function() {
|
|||||||
test("Title length for private messages", function() {
|
test("Title length for private messages", function() {
|
||||||
Discourse.SiteSettings.min_private_message_title_length = 5;
|
Discourse.SiteSettings.min_private_message_title_length = 5;
|
||||||
Discourse.SiteSettings.max_topic_title_length = 10;
|
Discourse.SiteSettings.max_topic_title_length = 10;
|
||||||
var composer = Discourse.Composer.create({action: Discourse.Composer.PRIVATE_MESSAGE});
|
const composer = createComposer({action: Discourse.Composer.PRIVATE_MESSAGE});
|
||||||
|
|
||||||
composer.set('title', 'asdf');
|
composer.set('title', 'asdf');
|
||||||
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
ok(!composer.get('titleLengthValid'), "short titles are not valid");
|
||||||
@ -132,10 +134,10 @@ test("Title length for private messages", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('editingFirstPost', function() {
|
test('editingFirstPost', function() {
|
||||||
var composer = Discourse.Composer.create();
|
const composer = createComposer();
|
||||||
ok(!composer.get('editingFirstPost'), "it's false by default");
|
ok(!composer.get('editingFirstPost'), "it's false by default");
|
||||||
|
|
||||||
var post = Discourse.Post.create({id: 123, post_number: 2});
|
const post = Discourse.Post.create({id: 123, post_number: 2});
|
||||||
composer.setProperties({post: post, action: Discourse.Composer.EDIT });
|
composer.setProperties({post: post, action: Discourse.Composer.EDIT });
|
||||||
ok(!composer.get('editingFirstPost'), "it's false when not editing the first post");
|
ok(!composer.get('editingFirstPost'), "it's false when not editing the first post");
|
||||||
|
|
||||||
@ -145,7 +147,7 @@ test('editingFirstPost', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('clearState', function() {
|
test('clearState', function() {
|
||||||
var composer = Discourse.Composer.create({
|
const composer = createComposer({
|
||||||
originalText: 'asdf',
|
originalText: 'asdf',
|
||||||
reply: 'asdf2',
|
reply: 'asdf2',
|
||||||
post: Discourse.Post.create({id: 1}),
|
post: Discourse.Post.create({id: 1}),
|
||||||
@ -163,61 +165,48 @@ test('clearState', function() {
|
|||||||
|
|
||||||
test('initial category when uncategorized is allowed', function() {
|
test('initial category when uncategorized is allowed', function() {
|
||||||
Discourse.SiteSettings.allow_uncategorized_topics = true;
|
Discourse.SiteSettings.allow_uncategorized_topics = true;
|
||||||
var composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
const composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
||||||
equal(composer.get('categoryId'),undefined,"Uncategorized by default");
|
equal(composer.get('categoryId'),undefined,"Uncategorized by default");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initial category when uncategorized is not allowed', function() {
|
test('initial category when uncategorized is not allowed', function() {
|
||||||
Discourse.SiteSettings.allow_uncategorized_topics = false;
|
Discourse.SiteSettings.allow_uncategorized_topics = false;
|
||||||
var composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
const composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
||||||
ok(composer.get('categoryId') === undefined, "Uncategorized by default. Must choose a category.");
|
ok(composer.get('categoryId') === undefined, "Uncategorized by default. Must choose a category.");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('showPreview', function() {
|
test('showPreview', function() {
|
||||||
var new_composer = function() {
|
const newComposer = function() {
|
||||||
return Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
return Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
|
||||||
};
|
};
|
||||||
|
|
||||||
Discourse.Mobile.mobileView = true;
|
Discourse.Mobile.mobileView = true;
|
||||||
equal(new_composer().get('showPreview'), false, "Don't show preview in mobile view");
|
equal(newComposer().get('showPreview'), false, "Don't show preview in mobile view");
|
||||||
|
|
||||||
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: 'true' });
|
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: 'true' });
|
||||||
equal(new_composer().get('showPreview'), false, "Don't show preview in mobile view even if KeyValueStore wants to");
|
equal(newComposer().get('showPreview'), false, "Don't show preview in mobile view even if KeyValueStore wants to");
|
||||||
Discourse.KeyValueStore.remove('composer.showPreview');
|
Discourse.KeyValueStore.remove('composer.showPreview');
|
||||||
|
|
||||||
Discourse.Mobile.mobileView = false;
|
Discourse.Mobile.mobileView = false;
|
||||||
equal(new_composer().get('showPreview'), true, "Show preview by default in desktop view");
|
equal(newComposer().get('showPreview'), true, "Show preview by default in desktop view");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('open with a quote', function() {
|
test('open with a quote', function() {
|
||||||
var quote = '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
|
const quote = '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
|
||||||
var new_composer = function() {
|
const newComposer = function() {
|
||||||
return Discourse.Composer.open({action: Discourse.Composer.REPLY, draftKey: 'asfd', draftSequence: 1, quote: quote});
|
return Discourse.Composer.open({action: Discourse.Composer.REPLY, draftKey: 'asfd', draftSequence: 1, quote: quote});
|
||||||
};
|
};
|
||||||
|
|
||||||
equal(new_composer().get('originalText'), quote, "originalText is the quote" );
|
equal(newComposer().get('originalText'), quote, "originalText is the quote" );
|
||||||
equal(new_composer().get('replyDirty'), false, "replyDirty is initally false with a quote" );
|
equal(newComposer().get('replyDirty'), false, "replyDirty is initally false with a quote" );
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
module("Discourse.Composer as admin", {
|
|
||||||
setup: function() {
|
|
||||||
Discourse.SiteSettings.min_topic_title_length = 5;
|
|
||||||
Discourse.SiteSettings.max_topic_title_length = 10;
|
|
||||||
sandbox.stub(Discourse.User, 'currentProp').withArgs('admin').returns(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
teardown: function() {
|
|
||||||
Discourse.SiteSettings.min_topic_title_length = 15;
|
|
||||||
Discourse.SiteSettings.max_topic_title_length = 255;
|
|
||||||
Discourse.User.currentProp.restore();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Title length for static page topics as admin", function() {
|
test("Title length for static page topics as admin", function() {
|
||||||
var composer = Discourse.Composer.create();
|
Discourse.SiteSettings.min_topic_title_length = 5;
|
||||||
|
Discourse.SiteSettings.max_topic_title_length = 10;
|
||||||
|
const composer = createComposer();
|
||||||
|
|
||||||
var post = Discourse.Post.create({id: 123, post_number: 2, static_doc: true});
|
const post = Discourse.Post.create({id: 123, post_number: 2, static_doc: true});
|
||||||
composer.setProperties({post: post, action: Discourse.Composer.EDIT });
|
composer.setProperties({post: post, action: Discourse.Composer.EDIT });
|
||||||
|
|
||||||
composer.set('title', 'asdf');
|
composer.set('title', 'asdf');
|
||||||
|
@ -59,7 +59,11 @@ sinon.config = {
|
|||||||
useFakeServer: false
|
useFakeServer: false
|
||||||
};
|
};
|
||||||
|
|
||||||
window.assetPath = function() { return null; };
|
window.assetPath = function(url) {
|
||||||
|
if (url.indexOf('defer') === 0) {
|
||||||
|
return "/assets/" + url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Stop the message bus so we don't get ajax calls
|
// Stop the message bus so we don't get ajax calls
|
||||||
window.MessageBus.stop();
|
window.MessageBus.stop();
|
||||||
|
8
vendor/assets/javascripts/bootbox.js
vendored
8
vendor/assets/javascripts/bootbox.js
vendored
@ -427,16 +427,17 @@ var bootbox = window.bootbox || (function(document, $) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// well, *if* we have a primary - give the first dom element focus
|
// well, *if* we have a primary - give the first dom element focus
|
||||||
div.on('shown', function() {
|
div.on('shown.bs.modal', function() {
|
||||||
div.find("a.btn-primary:first").focus();
|
div.find("a.btn-primary:first").focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
div.on('hidden', function() {
|
div.on('hidden.bs.modal', function() {
|
||||||
div.remove();
|
div.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
// wire up button handlers
|
// wire up button handlers
|
||||||
div.on('click', '.modal-footer a', function(e) {
|
div.on('click', '.modal-footer a', function(e) {
|
||||||
|
Ember.run(function() {
|
||||||
|
|
||||||
var handler = $(this).data("handler"),
|
var handler = $(this).data("handler"),
|
||||||
cb = callbacks[handler],
|
cb = callbacks[handler],
|
||||||
@ -463,9 +464,10 @@ var bootbox = window.bootbox || (function(document, $) {
|
|||||||
div.modal("hide");
|
div.modal("hide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// stick the modal right at the bottom of the main body out of the way
|
// stick the modal right at the bottom of the main body out of the way
|
||||||
$("body").append(div);
|
(that.$body || $("body")).append(div);
|
||||||
|
|
||||||
div.modal({
|
div.modal({
|
||||||
// unless explicitly overridden take whatever our default backdrop value is
|
// unless explicitly overridden take whatever our default backdrop value is
|
||||||
|
340
vendor/assets/javascripts/bootstrap-modal.js
vendored
340
vendor/assets/javascripts/bootstrap-modal.js
vendored
@ -1,67 +1,90 @@
|
|||||||
/* =========================================================
|
/* ========================================================================
|
||||||
* bootstrap-modal.js v2.0.3
|
* Bootstrap: modal.js v3.3.4
|
||||||
* http://twitter.github.com/bootstrap/javascript.html#modals
|
* http://getbootstrap.com/javascript/#modals
|
||||||
* =========================================================
|
* ========================================================================
|
||||||
* Copyright 2012 Twitter, Inc.
|
* Copyright 2011-2015 Twitter, Inc.
|
||||||
*
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* ======================================================================== */
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* ========================================================= */
|
|
||||||
|
|
||||||
|
|
||||||
!function ($) {
|
+function ($) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
"use strict"; // jshint ;_;
|
// MODAL CLASS DEFINITION
|
||||||
|
// ======================
|
||||||
|
|
||||||
|
var Modal = function (element, options) {
|
||||||
/* MODAL CLASS DEFINITION
|
|
||||||
* ====================== */
|
|
||||||
|
|
||||||
var Modal = function (content, options) {
|
|
||||||
this.options = options
|
this.options = options
|
||||||
this.$element = $(content)
|
this.$body = $(document.body)
|
||||||
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
|
this.$element = $(element)
|
||||||
|
this.$dialog = this.$element.find('.modal-dialog')
|
||||||
|
this.$backdrop = null
|
||||||
|
this.isShown = null
|
||||||
|
this.originalBodyPad = null
|
||||||
|
this.scrollbarWidth = 0
|
||||||
|
this.ignoreBackdropClick = false
|
||||||
|
|
||||||
|
if (this.options.remote) {
|
||||||
|
this.$element
|
||||||
|
.find('.modal-content')
|
||||||
|
.load(this.options.remote, $.proxy(function () {
|
||||||
|
this.$element.trigger('loaded.bs.modal')
|
||||||
|
}, this))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Modal.prototype = {
|
Modal.VERSION = '3.3.4'
|
||||||
|
|
||||||
constructor: Modal
|
Modal.TRANSITION_DURATION = 300
|
||||||
|
Modal.BACKDROP_TRANSITION_DURATION = 150
|
||||||
|
|
||||||
, toggle: function () {
|
Modal.DEFAULTS = {
|
||||||
return this[!this.isShown ? 'show' : 'hide']()
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
show: true
|
||||||
}
|
}
|
||||||
|
|
||||||
, show: function () {
|
Modal.prototype.toggle = function (_relatedTarget) {
|
||||||
|
return this.isShown ? this.hide() : this.show(_relatedTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.prototype.show = function (_relatedTarget) {
|
||||||
var that = this
|
var that = this
|
||||||
, e = $.Event('show')
|
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
|
||||||
|
|
||||||
this.$element.trigger(e)
|
this.$element.trigger(e)
|
||||||
|
|
||||||
if (this.isShown || e.isDefaultPrevented()) return
|
if (this.isShown || e.isDefaultPrevented()) return
|
||||||
|
|
||||||
$('body').addClass('modal-open')
|
|
||||||
|
|
||||||
this.isShown = true
|
this.isShown = true
|
||||||
|
|
||||||
escape.call(this)
|
this.checkScrollbar()
|
||||||
backdrop.call(this, function () {
|
this.setScrollbar()
|
||||||
|
this.$body.addClass('modal-open')
|
||||||
|
|
||||||
|
this.escape()
|
||||||
|
this.resize()
|
||||||
|
|
||||||
|
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
|
||||||
|
|
||||||
|
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
|
||||||
|
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
|
||||||
|
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.backdrop(function () {
|
||||||
var transition = $.support.transition && that.$element.hasClass('fade')
|
var transition = $.support.transition && that.$element.hasClass('fade')
|
||||||
|
|
||||||
if (!that.$element.parent().length) {
|
if (!that.$element.parent().length) {
|
||||||
that.$element.appendTo(document.body) //don't move modals dom position
|
that.$element.appendTo(that.$body) // don't move modals dom position
|
||||||
}
|
}
|
||||||
|
|
||||||
that.$element
|
that.$element
|
||||||
.show()
|
.show()
|
||||||
|
.scrollTop(0)
|
||||||
|
|
||||||
|
that.adjustDialog()
|
||||||
|
|
||||||
if (transition) {
|
if (transition) {
|
||||||
that.$element[0].offsetWidth // force reflow
|
that.$element[0].offsetWidth // force reflow
|
||||||
@ -69,19 +92,24 @@
|
|||||||
|
|
||||||
that.$element.addClass('in')
|
that.$element.addClass('in')
|
||||||
|
|
||||||
transition ?
|
that.enforceFocus()
|
||||||
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
|
|
||||||
that.$element.trigger('shown')
|
|
||||||
|
|
||||||
|
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
|
||||||
|
|
||||||
|
transition ?
|
||||||
|
that.$dialog // wait for modal to slide in
|
||||||
|
.one('bsTransitionEnd', function () {
|
||||||
|
that.$element.trigger('focus').trigger(e)
|
||||||
|
})
|
||||||
|
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
||||||
|
that.$element.trigger('focus').trigger(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
, hide: function (e) {
|
Modal.prototype.hide = function (e) {
|
||||||
e && e.preventDefault()
|
if (e) e.preventDefault()
|
||||||
|
|
||||||
var that = this
|
e = $.Event('hide.bs.modal')
|
||||||
|
|
||||||
e = $.Event('hide')
|
|
||||||
|
|
||||||
this.$element.trigger(e)
|
this.$element.trigger(e)
|
||||||
|
|
||||||
@ -89,130 +117,222 @@
|
|||||||
|
|
||||||
this.isShown = false
|
this.isShown = false
|
||||||
|
|
||||||
$('body').removeClass('modal-open')
|
this.escape()
|
||||||
|
this.resize()
|
||||||
|
|
||||||
escape.call(this)
|
$(document).off('focusin.bs.modal')
|
||||||
|
|
||||||
this.$element.removeClass('in')
|
this.$element
|
||||||
|
.removeClass('in')
|
||||||
|
.off('click.dismiss.bs.modal')
|
||||||
|
.off('mouseup.dismiss.bs.modal')
|
||||||
|
|
||||||
|
this.$dialog.off('mousedown.dismiss.bs.modal')
|
||||||
|
|
||||||
$.support.transition && this.$element.hasClass('fade') ?
|
$.support.transition && this.$element.hasClass('fade') ?
|
||||||
hideWithTransition.call(this) :
|
this.$element
|
||||||
hideModal.call(this)
|
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
|
||||||
|
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
||||||
|
this.hideModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Modal.prototype.enforceFocus = function () {
|
||||||
|
$(document)
|
||||||
|
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||||
|
.on('focusin.bs.modal', $.proxy(function (e) {
|
||||||
|
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
||||||
|
this.$element.trigger('focus')
|
||||||
|
}
|
||||||
|
}, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Modal.prototype.escape = function () {
|
||||||
|
if (this.isShown && this.options.keyboard) {
|
||||||
|
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
|
||||||
|
e.which == 27 && this.hide()
|
||||||
|
}, this))
|
||||||
|
} else if (!this.isShown) {
|
||||||
|
this.$element.off('keydown.dismiss.bs.modal')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* MODAL PRIVATE METHODS
|
Modal.prototype.resize = function () {
|
||||||
* ===================== */
|
if (this.isShown) {
|
||||||
|
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
|
||||||
|
} else {
|
||||||
|
$(window).off('resize.bs.modal')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function hideWithTransition() {
|
Modal.prototype.hideModal = function () {
|
||||||
var that = this
|
var that = this
|
||||||
, timeout = setTimeout(function () {
|
this.$element.hide()
|
||||||
that.$element.off($.support.transition.end)
|
this.backdrop(function () {
|
||||||
hideModal.call(that)
|
that.$body.removeClass('modal-open')
|
||||||
}, 500)
|
that.resetAdjustments()
|
||||||
|
that.resetScrollbar()
|
||||||
this.$element.one($.support.transition.end, function () {
|
that.$element.trigger('hidden.bs.modal')
|
||||||
clearTimeout(timeout)
|
|
||||||
hideModal.call(that)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideModal(that) {
|
Modal.prototype.removeBackdrop = function () {
|
||||||
this.$element
|
this.$backdrop && this.$backdrop.remove()
|
||||||
.hide()
|
this.$backdrop = null
|
||||||
.trigger('hidden')
|
|
||||||
|
|
||||||
backdrop.call(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function backdrop(callback) {
|
Modal.prototype.backdrop = function (callback) {
|
||||||
var that = this
|
var that = this
|
||||||
, animate = this.$element.hasClass('fade') ? 'fade' : ''
|
var animate = this.$element.hasClass('fade') ? 'fade' : ''
|
||||||
|
|
||||||
if (this.isShown && this.options.backdrop) {
|
if (this.isShown && this.options.backdrop) {
|
||||||
var doAnimate = $.support.transition && animate
|
var doAnimate = $.support.transition && animate
|
||||||
|
|
||||||
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
|
this.$backdrop = $(document.createElement('div'))
|
||||||
.appendTo(document.body)
|
.addClass('modal-backdrop ' + animate)
|
||||||
|
.appendTo(this.$body)
|
||||||
|
|
||||||
if (this.options.backdrop != 'static') {
|
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
|
||||||
this.$backdrop.click($.proxy(this.hide, this))
|
if (this.ignoreBackdropClick) {
|
||||||
|
this.ignoreBackdropClick = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if (e.target !== e.currentTarget) return
|
||||||
|
this.options.backdrop == 'static'
|
||||||
|
? this.$element[0].focus()
|
||||||
|
: this.hide()
|
||||||
|
}, this))
|
||||||
|
|
||||||
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
|
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
|
||||||
|
|
||||||
this.$backdrop.addClass('in')
|
this.$backdrop.addClass('in')
|
||||||
|
|
||||||
|
if (!callback) return
|
||||||
|
|
||||||
doAnimate ?
|
doAnimate ?
|
||||||
this.$backdrop.one($.support.transition.end, callback) :
|
this.$backdrop
|
||||||
|
.one('bsTransitionEnd', callback)
|
||||||
|
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
} else if (!this.isShown && this.$backdrop) {
|
} else if (!this.isShown && this.$backdrop) {
|
||||||
this.$backdrop.removeClass('in')
|
this.$backdrop.removeClass('in')
|
||||||
|
|
||||||
|
var callbackRemove = function () {
|
||||||
|
that.removeBackdrop()
|
||||||
|
callback && callback()
|
||||||
|
}
|
||||||
$.support.transition && this.$element.hasClass('fade') ?
|
$.support.transition && this.$element.hasClass('fade') ?
|
||||||
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
|
this.$backdrop
|
||||||
removeBackdrop.call(this)
|
.one('bsTransitionEnd', callbackRemove)
|
||||||
|
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
||||||
|
callbackRemove()
|
||||||
|
|
||||||
} else if (callback) {
|
} else if (callback) {
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeBackdrop() {
|
// these following methods are used to handle overflowing modals
|
||||||
this.$backdrop.remove()
|
|
||||||
this.$backdrop = null
|
Modal.prototype.handleUpdate = function () {
|
||||||
|
this.adjustDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
function escape() {
|
Modal.prototype.adjustDialog = function () {
|
||||||
var that = this
|
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
|
||||||
if (this.isShown && this.options.keyboard) {
|
|
||||||
$(document).on('keyup.dismiss.modal', function ( e ) {
|
this.$element.css({
|
||||||
e.which == 27 && that.hide()
|
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
|
||||||
|
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
|
||||||
})
|
})
|
||||||
} else if (!this.isShown) {
|
|
||||||
$(document).off('keyup.dismiss.modal')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Modal.prototype.resetAdjustments = function () {
|
||||||
|
this.$element.css({
|
||||||
|
paddingLeft: '',
|
||||||
|
paddingRight: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.prototype.checkScrollbar = function () {
|
||||||
|
var fullWindowWidth = window.innerWidth
|
||||||
|
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
|
||||||
|
var documentElementRect = document.documentElement.getBoundingClientRect()
|
||||||
|
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
|
||||||
|
}
|
||||||
|
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
|
||||||
|
this.scrollbarWidth = this.measureScrollbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.prototype.setScrollbar = function () {
|
||||||
|
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
|
||||||
|
this.originalBodyPad = document.body.style.paddingRight || ''
|
||||||
|
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.prototype.resetScrollbar = function () {
|
||||||
|
this.$body.css('padding-right', this.originalBodyPad)
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.prototype.measureScrollbar = function () { // thx walsh
|
||||||
|
var scrollDiv = document.createElement('div')
|
||||||
|
scrollDiv.className = 'modal-scrollbar-measure'
|
||||||
|
this.$body.append(scrollDiv)
|
||||||
|
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
|
||||||
|
this.$body[0].removeChild(scrollDiv)
|
||||||
|
return scrollbarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* MODAL PLUGIN DEFINITION
|
// MODAL PLUGIN DEFINITION
|
||||||
* ======================= */
|
// =======================
|
||||||
|
|
||||||
$.fn.modal = function (option) {
|
function Plugin(option, _relatedTarget) {
|
||||||
return this.each(function () {
|
return this.each(function () {
|
||||||
var $this = $(this)
|
var $this = $(this)
|
||||||
, data = $this.data('modal')
|
var data = $this.data('bs.modal')
|
||||||
, options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
|
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||||
if (!data) $this.data('modal', (data = new Modal(this, options)))
|
|
||||||
if (typeof option == 'string') data[option]()
|
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
|
||||||
else if (options.show) data.show()
|
|
||||||
|
if (typeof option == 'string') data[option](_relatedTarget)
|
||||||
|
else if (options.show) data.show(_relatedTarget)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$.fn.modal.defaults = {
|
var old = $.fn.modal
|
||||||
backdrop: true
|
|
||||||
, keyboard: true
|
|
||||||
, show: true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$.fn.modal = Plugin
|
||||||
$.fn.modal.Constructor = Modal
|
$.fn.modal.Constructor = Modal
|
||||||
|
|
||||||
|
|
||||||
/* MODAL DATA-API
|
// MODAL NO CONFLICT
|
||||||
* ============== */
|
// =================
|
||||||
|
|
||||||
$(function () {
|
$.fn.modal.noConflict = function () {
|
||||||
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
|
$.fn.modal = old
|
||||||
var $this = $(this), href
|
return this
|
||||||
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|
}
|
||||||
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
$target.modal(option)
|
// MODAL DATA-API
|
||||||
|
// ==============
|
||||||
|
|
||||||
|
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
|
||||||
|
var $this = $(this)
|
||||||
|
var href = $this.attr('href')
|
||||||
|
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
|
||||||
|
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
|
||||||
|
|
||||||
|
if ($this.is('a')) e.preventDefault()
|
||||||
|
|
||||||
|
$target.one('show.bs.modal', function (showEvent) {
|
||||||
|
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
|
||||||
|
$target.one('hidden.bs.modal', function () {
|
||||||
|
$this.is(':visible') && $this.trigger('focus')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Plugin.call($target, option, this)
|
||||||
|
})
|
||||||
|
|
||||||
}(window.jQuery);
|
}(jQuery);
|
||||||
|
7
vendor/assets/javascripts/ember-cloaking.js
vendored
7
vendor/assets/javascripts/ember-cloaking.js
vendored
@ -148,9 +148,12 @@
|
|||||||
// Find the bottom view and what's onscreen
|
// Find the bottom view and what's onscreen
|
||||||
while (bottomView < childViews.length) {
|
while (bottomView < childViews.length) {
|
||||||
var view = childViews[bottomView],
|
var view = childViews[bottomView],
|
||||||
$view = view.$(),
|
$view = view.$();
|
||||||
|
|
||||||
|
if (!$view) { break; }
|
||||||
|
|
||||||
// in case of not full-window scrolling
|
// in case of not full-window scrolling
|
||||||
scrollOffset = this.get('wrapperTop') || 0,
|
var scrollOffset = this.get('wrapperTop') || 0,
|
||||||
viewTop = $view.offset().top + scrollOffset,
|
viewTop = $view.offset().top + scrollOffset,
|
||||||
viewBottom = viewTop + $view.height();
|
viewBottom = viewTop + $view.height();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user