FIX: Highlighting was not being applied after some rendering.

Also includes a bunch of ES6 stuff.
This commit is contained in:
Robin Ward 2015-02-12 15:37:02 -05:00
parent 96697c7957
commit a519fd5bcf
21 changed files with 314 additions and 503 deletions

View File

@ -1,6 +1,7 @@
import ObjectController from 'discourse/controllers/object';
import BufferedContent from 'discourse/mixins/buffered-content';
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
import Topic from 'discourse/models/topic';
export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedContent, {
multiSelect: false,
@ -272,7 +273,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
var self = this,
props = this.get('buffered.buffer');
Discourse.Topic.update(this.get('model'), props).then(function() {
Topic.update(this.get('model'), props).then(function() {
// Note we roll back on success here because `update` saves
// the properties to the topic.
self.rollbackBuffer();
@ -555,13 +556,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
// Receive notifications for this topic
subscribe: function() {
subscribe() {
// Unsubscribe before subscribing again
this.unsubscribe();
var topicController = this;
const self = this;
Discourse.MessageBus.subscribe("/topic/" + this.get('id'), function(data) {
var topic = topicController.get('model');
const topic = self.get('model');
if (data.notification_level_change) {
topic.set('details.notification_level', data.notification_level_change);
@ -569,7 +570,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return;
}
var postStream = topicController.get('postStream');
const postStream = self.get('postStream');
switch (data.type) {
case "revised":
case "acted":

View File

@ -0,0 +1,11 @@
import { decorateCooked } from 'discourse/lib/plugin-api';
import HighlightSyntax from 'discourse/lib/highlight-syntax';
import Lightbox from 'discourse/lib/lightbox';
export default {
name: "post-decorations",
initialize: function(container) {
decorateCooked(container, HighlightSyntax);
decorateCooked(container, Lightbox);
}
};

View File

@ -0,0 +1,10 @@
/*global hljs:true */
export default function highlightSyntax($elem) {
const selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
$(selector, $elem).each(function(i, e) {
return $LAB.script("/javascripts/highlight.pack.js").wait(function() {
return hljs.highlightBlock(e);
});
});
}

View File

@ -1,54 +0,0 @@
/**
Helper object for lightboxes.
@class Lightbox
@namespace Discourse
@module Discourse
**/
Discourse.Lightbox = {
apply: function($elem) {
$LAB.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
$("a.lightbox", $elem).each(function(i, e) {
var $e = $(e);
// do not lightbox spoiled images
if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; }
$e.magnificPopup({
type: "image",
closeOnContentClick: false,
removalDelay: 300,
mainClass: "mfp-zoom-in",
callbacks: {
open: function() {
var wrap = this.wrap,
img = this.currItem.img,
maxHeight = img.css("max-height");
wrap.on("click.pinhandler", "img", function() {
wrap.toggleClass("mfp-force-scrollbars");
img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight);
});
},
beforeClose: function() {
this.wrap.off("click.pinhandler");
this.wrap.removeClass("mfp-force-scrollbars");
}
},
image: {
titleSrc: function(item) {
var href = item.el.data("download-href") || item.src;
return [
item.el.attr("title"),
$("span.informations", item.el).text().replace('x', '×'),
'<a class="image-source-link" href="' + href + '">' + I18n.t("lightbox.download") + '</a>'
].join(' &middot; ');
}
}
});
});
});
}
};

View File

@ -0,0 +1,45 @@
export default function($elem) {
$("a.lightbox", $elem).each(function(i, e) {
$LAB.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
var $e = $(e);
// do not lightbox spoiled images
if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; }
$e.magnificPopup({
type: "image",
closeOnContentClick: false,
removalDelay: 300,
mainClass: "mfp-zoom-in",
callbacks: {
open: function() {
var wrap = this.wrap,
img = this.currItem.img,
maxHeight = img.css("max-height");
wrap.on("click.pinhandler", "img", function() {
wrap.toggleClass("mfp-force-scrollbars");
img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight);
});
},
beforeClose: function() {
this.wrap.off("click.pinhandler");
this.wrap.removeClass("mfp-force-scrollbars");
}
},
image: {
titleSrc: function(item) {
var href = item.el.data("download-href") || item.src;
return [
item.el.attr("title"),
$("span.informations", item.el).text().replace('x', '&times;'),
'<a class="image-source-link" href="' + href + '">' + I18n.t("lightbox.download") + '</a>'
].join(' &middot; ');
}
}
});
});
});
}

View File

@ -1,4 +1,6 @@
export default function searchForTerm(term, opts) {
import Topic from 'discourse/models/topic';
function searchForTerm(term, opts) {
if (!opts) opts = {};
// Only include the data we have
@ -22,7 +24,7 @@ export default function searchForTerm(term, opts) {
var topicMap = {};
results.topics = results.topics.map(function(topic){
topic = Discourse.Topic.create(topic);
topic = Topic.create(topic);
topicMap[topic.id] = topic;
return topic;
});
@ -66,3 +68,5 @@ export default function searchForTerm(term, opts) {
return noResults ? null : Em.Object.create(results);
});
}
export default searchForTerm;

View File

@ -1,26 +0,0 @@
/*global hljs:true */
/**
Helper object for syntax highlighting. Uses highlight.js which is loaded on demand.
@class SyntaxHighlighting
@namespace Discourse
@module Discourse
**/
Discourse.SyntaxHighlighting = {
/**
Apply syntax highlighting to a jQuery element
@method apply
@param {jQuery.selector} $elem The element we want to apply our highlighting to
**/
apply: function($elem) {
var selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
$(selector, $elem).each(function(i, e) {
return $LAB.script("/javascripts/highlight.pack.js").wait(function() {
return hljs.highlightBlock(e);
});
});
}
};

View File

@ -532,6 +532,7 @@ Discourse.Composer = Discourse.Model.extend({
});
}
// If we're in a topic, we can append the post instantly.
if (postStream) {
// If it's in reply to another post, increase the reply count

View File

@ -1,5 +1,4 @@
Discourse.PostStream = Em.Object.extend({
const PostStream = Ember.Object.extend({
loading: Em.computed.or('loadingAbove', 'loadingBelow', 'loadingFilter', 'stagingPost'),
notLoading: Em.computed.not('loading'),
filteredPostsCount: Em.computed.alias("stream.length"),
@ -45,15 +44,13 @@ Discourse.PostStream = Em.Object.extend({
/**
Returns a JS Object of current stream filter options. It should match the query
params for the stream.
@property streamFilters
**/
streamFilters: function() {
var result = {};
const result = {};
if (this.get('summary')) { result.filter = "summary"; }
if (this.get('show_deleted')) { result.show_deleted = true; }
var userFilters = this.get('userFilters');
const userFilters = this.get('userFilters');
if (!Em.isEmpty(userFilters)) {
result.username_filters = userFilters.join(",");
}
@ -62,27 +59,25 @@ Discourse.PostStream = Em.Object.extend({
}.property('userFilters.[]', 'summary', 'show_deleted'),
hasNoFilters: function() {
var streamFilters = this.get('streamFilters');
const streamFilters = this.get('streamFilters');
return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.username_filters));
}.property('streamFilters.[]', 'topic.posts_count', 'posts.length'),
/**
Returns the window of posts above the current set in the stream, bound to the top of the stream.
This is the collection we'll ask for when scrolling upwards.
@property previousWindow
**/
previousWindow: function() {
// If we can't find the last post loaded, bail
var firstPost = _.first(this.get('posts'));
const firstPost = _.first(this.get('posts'));
if (!firstPost) { return []; }
// Find the index of the last post loaded, if not found, bail
var stream = this.get('stream');
var firstIndex = this.indexOf(firstPost);
const stream = this.get('stream');
const firstIndex = this.indexOf(firstPost);
if (firstIndex === -1) { return []; }
var startIndex = firstIndex - this.get('topic.chunk_size');
let startIndex = firstIndex - this.get('topic.chunk_size');
if (startIndex < 0) { startIndex = 0; }
return stream.slice(startIndex, firstIndex);
@ -91,17 +86,15 @@ Discourse.PostStream = Em.Object.extend({
/**
Returns the window of posts below the current set in the stream, bound by the bottom of the
stream. This is the collection we use when scrolling downwards.
@property nextWindow
**/
nextWindow: function() {
// If we can't find the last post loaded, bail
var lastLoadedPost = this.get('lastLoadedPost');
const lastLoadedPost = this.get('lastLoadedPost');
if (!lastLoadedPost) { return []; }
// Find the index of the last post loaded, if not found, bail
var stream = this.get('stream');
var lastIndex = this.indexOf(lastLoadedPost);
const stream = this.get('stream');
const lastIndex = this.indexOf(lastLoadedPost);
if (lastIndex === -1) { return []; }
if ((lastIndex + 1) >= this.get('highest_post_number')) { return []; }
@ -109,41 +102,26 @@ Discourse.PostStream = Em.Object.extend({
return stream.slice(lastIndex+1, lastIndex + this.get('topic.chunk_size') + 1);
}.property('lastLoadedPost', 'stream.@each'),
/**
Cancel any active filters on the stream.
@method cancelFilter
**/
cancelFilter: function() {
cancelFilter() {
this.set('summary', false);
this.set('show_deleted', false);
this.get('userFilters').clear();
},
/**
Toggle summary mode for the stream.
@method toggleSummary
**/
toggleSummary: function() {
toggleSummary() {
this.get('userFilters').clear();
this.toggleProperty('summary');
return this.refresh();
},
toggleDeleted: function() {
toggleDeleted() {
this.toggleProperty('show_deleted');
return this.refresh();
},
/**
Filter the stream to a particular user.
@method toggleParticipant
**/
toggleParticipant: function(username) {
var userFilters = this.get('userFilters');
// Filter the stream to a particular user.
toggleParticipant(username) {
const userFilters = this.get('userFilters');
this.set('summary', false);
this.set('show_deleted', true);
if (userFilters.contains(username)) {
@ -157,22 +135,16 @@ Discourse.PostStream = Em.Object.extend({
/**
Loads a new set of posts into the stream. If you provide a `nearPost` option and the post
is already loaded, it will simply scroll there and load nothing.
@method refresh
@param {Object} opts Options for loading the stream
@param {Integer} opts.nearPost The post we want to find other posts near to.
@param {Boolean} opts.track_visit Whether or not to track this as a visit to a topic.
@returns {Promise} a promise that is resolved when the posts have been inserted into the stream.
**/
refresh: function(opts) {
refresh(opts) {
opts = opts || {};
opts.nearPost = parseInt(opts.nearPost, 10);
var topic = this.get('topic'),
const topic = this.get('topic'),
self = this;
// Do we already have the post in our list of posts? Jump there.
var postWeWant = this.get('posts').findProperty('post_number', opts.nearPost);
const postWeWant = this.get('posts').findProperty('post_number', opts.nearPost);
if (postWeWant) { return Ember.RSVP.resolve(); }
// TODO: if we have all the posts in the filter, don't go to the server for them.
@ -192,10 +164,10 @@ Discourse.PostStream = Em.Object.extend({
},
hasLoadedData: Em.computed.and('hasPosts', 'hasStream'),
collapsePosts: function(from, to){
var posts = this.get('posts');
var remove = posts.filter(function(post){
var postNumber = post.get('post_number');
collapsePosts(from, to){
const posts = this.get('posts');
const remove = posts.filter(function(post){
const postNumber = post.get('post_number');
return postNumber >= from && postNumber <= to;
});
@ -203,9 +175,9 @@ Discourse.PostStream = Em.Object.extend({
// make gap
this.set('gaps', this.get('gaps') || {before: {}, after: {}});
var before = this.get('gaps.before');
const before = this.get('gaps.before');
var post = posts.find(function(post){
const post = posts.find(function(post){
return post.get('post_number') > to;
});
@ -218,16 +190,9 @@ Discourse.PostStream = Em.Object.extend({
},
/**
Fill in a gap of posts before a particular post
@method fillGapBefore
@paaram {Discourse.Post} post beside gap
@paaram {Array} gap array of post ids to load
@returns {Promise} a promise that's resolved when the posts have been added.
**/
fillGapBefore: function(post, gap) {
var postId = post.get('id'),
// Fill in a gap of posts before a particular post
fillGapBefore(post, gap) {
const postId = post.get('id'),
stream = this.get('stream'),
idx = stream.indexOf(postId),
currentPosts = this.get('posts'),
@ -237,11 +202,11 @@ Discourse.PostStream = Em.Object.extend({
// Insert the gap at the appropriate place
stream.splice.apply(stream, [idx, 0].concat(gap));
var postIdx = currentPosts.indexOf(post);
let postIdx = currentPosts.indexOf(post);
if (postIdx !== -1) {
return this.findPostsByIds(gap).then(function(posts) {
posts.forEach(function(p) {
var stored = self.storePost(p);
const stored = self.storePost(p);
if (!currentPosts.contains(stored)) {
currentPosts.insertAt(postIdx++, stored);
}
@ -256,16 +221,9 @@ Discourse.PostStream = Em.Object.extend({
return Ember.RSVP.resolve();
},
/**
Fill in a gap of posts after a particular post
@method fillGapAfter
@paaram {Discourse.Post} post beside gap
@paaram {Array} gap array of post ids to load
@returns {Promise} a promise that's resolved when the posts have been added.
**/
fillGapAfter: function(post, gap) {
var postId = post.get('id'),
// Fill in a gap of posts after a particular post
fillGapAfter(post, gap) {
const postId = post.get('id'),
stream = this.get('stream'),
idx = stream.indexOf(postId),
self = this;
@ -279,24 +237,19 @@ Discourse.PostStream = Em.Object.extend({
return Ember.RSVP.resolve();
},
/**
Appends the next window of posts to the stream. Call it when scrolling downwards.
@method appendMore
@returns {Promise} a promise that's resolved when the posts have been added.
**/
appendMore: function() {
var self = this;
// Appends the next window of posts to the stream. Call it when scrolling downwards.
appendMore() {
const self = this;
// Make sure we can append more posts
if (!self.get('canAppendMore')) { return Ember.RSVP.resolve(); }
var postIds = self.get('nextWindow');
const postIds = self.get('nextWindow');
if (Ember.isEmpty(postIds)) { return Ember.RSVP.resolve(); }
self.set('loadingBelow', true);
var stopLoading = function() {
const stopLoading = function() {
self.set('loadingBelow', false);
};
@ -308,19 +261,14 @@ Discourse.PostStream = Em.Object.extend({
}, stopLoading);
},
/**
Prepend the previous window of posts to the stream. Call it when scrolling upwards.
@method prependMore
@returns {Promise} a promise that's resolved when the posts have been added.
**/
prependMore: function() {
var postStream = this;
// Prepend the previous window of posts to the stream. Call it when scrolling upwards.
prependMore() {
const postStream = this;
// Make sure we can append more posts
if (!postStream.get('canPrependMore')) { return Ember.RSVP.resolve(); }
var postIds = postStream.get('previousWindow');
const postIds = postStream.get('previousWindow');
if (Ember.isEmpty(postIds)) { return Ember.RSVP.resolve(); }
postStream.set('loadingAbove', true);
@ -336,18 +284,13 @@ Discourse.PostStream = Em.Object.extend({
Stage a post for insertion in the stream. It should be rendered right away under the
assumption that the post will succeed. We can then `commitPost` when it succeeds or
`undoPost` when it fails.
@method stagePost
@param {Discourse.Post} post the post to stage in the stream
@param {Discourse.User} user the user creating the post
**/
stagePost: function(post, user) {
stagePost(post, user) {
// We can't stage two posts simultaneously
if (this.get('stagingPost')) { return false; }
this.set('stagingPost', true);
var topic = this.get('topic');
const topic = this.get('topic');
topic.setProperties({
posts_count: (topic.get('posts_count') || 0) + 1,
last_posted_at: new Date(),
@ -371,13 +314,8 @@ Discourse.PostStream = Em.Object.extend({
return true;
},
/**
Commit the post we staged. Call this after a save succeeds.
@method commitPost
@param {Discourse.Post} the post we saved in the stream.
**/
commitPost: function(post) {
// Commit the post we staged. Call this after a save succeeds.
commitPost(post) {
if (this.get('loadedAllPosts')) {
this.appendPost(post);
}
@ -398,16 +336,13 @@ Discourse.PostStream = Em.Object.extend({
/**
Undo a post we've staged in the stream. Remove it from being rendered and revert the
state we changed.
@method undoPost
@param {Discourse.Post} the post to undo from the stream
**/
undoPost: function(post) {
undoPost(post) {
this.get('stream').removeObject(-1);
this.posts.removeObject(post);
this.get('postIdentityMap').set(-1, null);
var topic = this.get('topic');
const topic = this.get('topic');
this.set('stagingPost', false);
topic.setProperties({
@ -418,44 +353,24 @@ Discourse.PostStream = Em.Object.extend({
// TODO unfudge reply count on parent post
},
/**
Prepends a single post to the stream.
@method prependPost
@param {Discourse.Post} post The post we're prepending
@returns {Discourse.Post} the post that was inserted.
**/
prependPost: function(post) {
prependPost(post) {
this.get('posts').unshiftObject(this.storePost(post));
return post;
},
/**
Appends a single post into the stream.
@method appendPost
@param {Discourse.Post} post The post we're appending
@returns {Discourse.Post} the post that was inserted.
**/
appendPost: function(post) {
var stored = this.storePost(post);
appendPost(post) {
const stored = this.storePost(post);
if (stored) {
this.get('posts').addObject(stored);
}
return post;
},
/**
Removes posts from the stream.
@method removePosts
@param {Array} posts the collection of posts to remove
**/
removePosts: function(posts) {
removePosts(posts) {
if (Em.isEmpty(posts)) { return; }
var postIds = posts.map(function (p) { return p.get('id'); });
var identityMap = this.get('postIdentityMap');
const postIds = posts.map(function (p) { return p.get('id'); });
const identityMap = this.get('postIdentityMap');
this.get('stream').removeObjects(postIds);
this.get('posts').removeObjects(posts);
@ -464,14 +379,8 @@ Discourse.PostStream = Em.Object.extend({
});
},
/**
Returns a post from the identity map if it's been inserted.
@method findLoadedPost
@param {Integer} id The post we want from the identity map.
@returns {Discourse.Post} the post that was inserted.
**/
findLoadedPost: function(id) {
// Returns a post from the identity map if it's been inserted.
findLoadedPost(id) {
return this.get('postIdentityMap').get(id);
},
@ -479,17 +388,14 @@ Discourse.PostStream = Em.Object.extend({
Finds and adds a post to the stream by id. Typically this would happen if we receive a message
from the message bus indicating there's a new post. We'll only insert it if we currently
have no filters.
@method triggerNewPostInStream
@param {Integer} postId The id of the new post to be inserted into the stream
**/
triggerNewPostInStream: function(postId) {
triggerNewPostInStream(postId) {
if (!postId) { return; }
// We only trigger if there are no filters active
if (!this.get('hasNoFilters')) { return; }
var loadedAllPosts = this.get('loadedAllPosts');
const loadedAllPosts = this.get('loadedAllPosts');
if (this.get('stream').indexOf(postId) === -1) {
this.get('stream').addObject(postId);
@ -497,8 +403,8 @@ Discourse.PostStream = Em.Object.extend({
}
},
triggerRecoveredPost: function(postId){
var self = this,
triggerRecoveredPost(postId){
const self = this,
postIdentityMap = this.get('postIdentityMap'),
existing = postIdentityMap.get(postId);
@ -506,15 +412,15 @@ Discourse.PostStream = Em.Object.extend({
this.triggerChangedPost(postId, new Date());
} else {
// need to insert into stream
var url = "/posts/" + postId;
const url = "/posts/" + postId;
Discourse.ajax(url).then(function(p){
var post = Discourse.Post.create(p);
var stream = self.get("stream");
var posts = self.get("posts");
const post = Discourse.Post.create(p);
const stream = self.get("stream");
const posts = self.get("posts");
self.storePost(post);
// we need to zip this into the stream
var index = 0;
let index = 0;
stream.forEach(function(postId){
if(postId < p.id){
index+= 1;
@ -541,13 +447,13 @@ Discourse.PostStream = Em.Object.extend({
}
},
triggerDeletedPost: function(postId){
var self = this,
triggerDeletedPost(postId){
const self = this,
postIdentityMap = this.get('postIdentityMap'),
existing = postIdentityMap.get(postId);
if(existing){
var url = "/posts/" + postId;
const url = "/posts/" + postId;
Discourse.ajax(url).then(
function(p){
self.storePost(Discourse.Post.create(p));
@ -558,30 +464,24 @@ Discourse.PostStream = Em.Object.extend({
}
},
triggerChangedPost: function(postId, updatedAt) {
triggerChangedPost(postId, updatedAt) {
if (!postId) { return; }
var postIdentityMap = this.get('postIdentityMap'),
const postIdentityMap = this.get('postIdentityMap'),
existing = postIdentityMap.get(postId),
self = this;
if (existing && existing.updated_at !== updatedAt) {
var url = "/posts/" + postId;
const url = "/posts/" + postId;
Discourse.ajax(url).then(function(p){
self.storePost(Discourse.Post.create(p));
});
}
},
/**
Returns the "thread" of posts in the history of a post.
@method findReplyHistory
@param {Discourse.Post} post the post whose history we want
@returns {Array} the posts in the history.
**/
findReplyHistory: function(post) {
var postStream = this,
// Returns the "thread" of posts in the history of a post.
findReplyHistory(post) {
const postStream = this,
url = "/posts/" + post.get('id') + "/reply-history.json?max_replies=" + Discourse.SiteSettings.max_reply_history;
return Discourse.ajax(url).then(function(result) {
@ -597,16 +497,11 @@ Discourse.PostStream = Em.Object.extend({
Returns the closest post given a postNumber that may not exist in the stream.
For example, if the user asks for a post that's deleted or otherwise outside the range.
This allows us to set the progress bar with the correct number.
@method closestPostForPostNumber
@param {Number} postNumber the post number we're looking for
@return {Post} the closest post
@see PostStream.closestPostNumberFor
**/
closestPostForPostNumber: function(postNumber) {
closestPostForPostNumber(postNumber) {
if (!this.get('hasPosts')) { return; }
var closest = null;
let closest = null;
this.get('posts').forEach(function (p) {
if (!closest) {
closest = p;
@ -628,17 +523,12 @@ Discourse.PostStream = Em.Object.extend({
@returns {Number} 1-starting index of the post, or 0 if not found
@see PostStream.progressIndexOfPostId
**/
progressIndexOfPost: function(post) {
progressIndexOfPost(post) {
return this.progressIndexOfPostId(post.get('id'));
},
/**
Get the index in the stream of a post id. (Use this for the topic progress bar.)
@param post_id - post id to search for
@returns {Number} 1-starting index of the post, or 0 if not found
**/
progressIndexOfPostId: function(post_id) {
// Get the index in the stream of a post id. (Use this for the topic progress bar.)
progressIndexOfPostId(post_id) {
return this.get('stream').indexOf(post_id) + 1;
},
@ -646,15 +536,11 @@ Discourse.PostStream = Em.Object.extend({
Returns the closest post number given a postNumber that may not exist in the stream.
For example, if the user asks for a post that's deleted or otherwise outside the range.
This allows us to set the progress bar with the correct number.
@method closestPostNumberFor
@param {Number} postNumber the post number we're looking for
@return {Number} a close post number
**/
closestPostNumberFor: function(postNumber) {
closestPostNumberFor(postNumber) {
if (!this.get('hasPosts')) { return; }
var closest = null;
let closest = null;
this.get('posts').forEach(function (p) {
if (closest === postNumber) { return; }
if (!closest) { closest = p.get('post_number'); }
@ -668,41 +554,33 @@ Discourse.PostStream = Em.Object.extend({
},
// Find a postId for a postNumber, respecting gaps
findPostIdForPostNumber: function(postNumber) {
var count = 1,
stream = this.get('stream'),
findPostIdForPostNumber(postNumber) {
const stream = this.get('stream'),
beforeLookup = this.get('gaps.before'),
streamLength = stream.length;
for (var i=0; i<streamLength; i++) {
var pid = stream[i];
let sum = 1;
for (let i=0; i<streamLength; i++) {
const pid = stream[i];
// See if there are posts before this post
if (beforeLookup) {
var before = beforeLookup[pid];
const before = beforeLookup[pid];
if (before) {
for (var j=0; j<before.length; j++) {
if (count === postNumber) { return pid; }
count++;
for (let j=0; j<before.length; j++) {
if (sum === postNumber) { return pid; }
sum++;
}
}
}
if (count === postNumber) { return pid; }
count++;
if (sum === postNumber) { return pid; }
sum++;
}
},
/**
@private
Given a JSON packet, update this stream and the posts that exist in it.
@param {Object} postStreamData The JSON data we want to update from.
@method updateFromJson
**/
updateFromJson: function(postStreamData) {
var postStream = this,
updateFromJson(postStreamData) {
const postStream = this,
posts = this.get('posts');
posts.clear();
@ -720,23 +598,17 @@ Discourse.PostStream = Em.Object.extend({
},
/**
@private
Stores a post in our identity map, and sets up the references it needs to
find associated objects like the topic. It might return a different reference
than you supplied if the post has already been loaded.
@method storePost
@param {Discourse.Post} post The post we're storing in the identity map
@returns {Discourse.Post} the post from the identity map
**/
storePost: function(post) {
storePost(post) {
// Calling `Em.get(undefined` raises an error
if (!post) { return; }
var postId = Em.get(post, 'id');
const postId = Em.get(post, 'id');
if (postId) {
var postIdentityMap = this.get('postIdentityMap'),
const postIdentityMap = this.get('postIdentityMap'),
existing = postIdentityMap.get(post.get('id'));
if (existing) {
@ -752,7 +624,7 @@ Discourse.PostStream = Em.Object.extend({
postIdentityMap.set(post.get('id'), post);
// Update the `highest_post_number` if this post is higher.
var postNumber = post.get('post_number');
const postNumber = post.get('post_number');
if (postNumber && postNumber > (this.get('topic.highest_post_number') || 0)) {
this.set('topic.highest_post_number', postNumber);
}
@ -761,17 +633,11 @@ Discourse.PostStream = Em.Object.extend({
},
/**
@private
Given a list of postIds, returns a list of the posts we don't have in our
identity map and need to load.
@method listUnloadedIds
@param {Array} postIds The post Ids we want to load from the server
@returns {Array} the array of postIds we don't have loaded.
**/
listUnloadedIds: function(postIds) {
var unloaded = Em.A(),
listUnloadedIds(postIds) {
const unloaded = Em.A(),
postIdentityMap = this.get('postIdentityMap');
postIds.forEach(function(p) {
if (!postIdentityMap.has(p)) { unloaded.pushObject(p); }
@ -779,17 +645,8 @@ Discourse.PostStream = Em.Object.extend({
return unloaded;
},
/**
@private
Returns a list of posts in order requested, by id.
@method findPostsByIds
@param {Array} postIds The post Ids we want to retrieve, in order.
@returns {Promise} a promise that will resolve to the posts in the order requested.
**/
findPostsByIds: function(postIds) {
var unloaded = this.listUnloadedIds(postIds),
findPostsByIds(postIds) {
const unloaded = this.listUnloadedIds(postIds),
postIdentityMap = this.get('postIdentityMap');
// Load our unloaded posts by id
@ -800,27 +657,18 @@ Discourse.PostStream = Em.Object.extend({
});
},
/**
@private
Loads a list of posts from the server and inserts them into our identity map.
@method loadIntoIdentityMap
@param {Array} postIds The post Ids we want to insert into the identity map.
@returns {Promise} a promise that will resolve to the posts in the order requested.
**/
loadIntoIdentityMap: function(postIds) {
loadIntoIdentityMap(postIds) {
// If we don't want any posts, return a promise that resolves right away
if (Em.isEmpty(postIds)) {
return Ember.RSVP.resolve();
}
var url = "/t/" + this.get('topic.id') + "/posts.json",
const url = "/t/" + this.get('topic.id') + "/posts.json",
data = { post_ids: postIds },
postStream = this;
return Discourse.ajax(url, {data: data}).then(function(result) {
var posts = Em.get(result, "post_stream.posts");
const posts = Em.get(result, "post_stream.posts");
if (posts) {
posts.forEach(function (p) {
postStream.storePost(Discourse.Post.create(p));
@ -830,33 +678,19 @@ Discourse.PostStream = Em.Object.extend({
},
/**
@private
Returns the index of a particular post in the stream
@method indexOf
@param {Discourse.Post} post The post we're looking for
**/
indexOf: function(post) {
indexOf(post) {
return this.get('stream').indexOf(post.get('id'));
},
/**
@private
Handles an error loading a topic based on a HTTP status code. Updates
the text to the correct values.
@method errorLoading
@param {Integer} status the HTTP status code
@param {Discourse.Topic} topic The topic instance we were trying to load
**/
errorLoading: function(result) {
var status = result.status;
errorLoading(result) {
const status = result.status;
var topic = this.get('topic');
const topic = this.get('topic');
topic.set('loadingFilter', false);
topic.set('errorLoading', true);
@ -887,10 +721,10 @@ Discourse.PostStream = Em.Object.extend({
});
Discourse.PostStream.reopenClass({
PostStream.reopenClass({
create: function() {
var postStream = this._super.apply(this, arguments);
create() {
const postStream = this._super.apply(this, arguments);
postStream.setProperties({
posts: [],
stream: [],
@ -906,9 +740,9 @@ Discourse.PostStream.reopenClass({
return postStream;
},
loadTopicView: function(topicId, args) {
var opts = _.merge({}, args),
url = Discourse.getURL("/t/") + topicId;
loadTopicView(topicId, args) {
const opts = _.merge({}, args);
let url = Discourse.getURL("/t/") + topicId;
if (opts.nearPost) {
url += "/" + opts.nearPost;
}
@ -921,3 +755,5 @@ Discourse.PostStream.reopenClass({
}
});
export default PostStream;

View File

@ -1,16 +1,13 @@
/**
A model representing a Topic's details that aren't always present, such as a list of participants.
When showing topics in lists and such this information should not be required.
@class TopicDetails
@extends Discourse.Model
@namespace Discourse
@module Discourse
**/
Discourse.TopicDetails = Discourse.Model.extend({
const TopicDetails = Discourse.Model.extend({
loaded: false,
updateFromJson: function(details) {
updateFromJson(details) {
const topic = this.get('topic');
if (details.allowed_users) {
details.allowed_users = details.allowed_users.map(function (u) {
return Discourse.User.create(u);
@ -24,10 +21,9 @@ Discourse.TopicDetails = Discourse.Model.extend({
}
if (details.participants) {
var topic = this.get('topic');
details.participants = details.participants.map(function (p) {
p.topic = topic;
return Em.Object.create(p);
return Ember.Object.create(p);
});
}
@ -59,7 +55,7 @@ Discourse.TopicDetails = Discourse.Model.extend({
}.property('notification_level', 'notifications_reason_id'),
updateNotifications: function(v) {
updateNotifications(v) {
this.set('notification_level', v);
this.set('notifications_reason_id', null);
return Discourse.ajax("/t/" + (this.get('topic.id')) + "/notifications", {
@ -68,7 +64,7 @@ Discourse.TopicDetails = Discourse.Model.extend({
});
},
removeAllowedUser: function(user) {
removeAllowedUser(user) {
var users = this.get('allowed_users'),
username = user.get('username');
@ -80,3 +76,5 @@ Discourse.TopicDetails = Discourse.Model.extend({
});
}
});
export default TopicDetails;

View File

@ -1,8 +1,11 @@
Discourse.Topic = Discourse.Model.extend({
import TopicDetails from 'discourse/models/topic-details';
import PostStream from 'discourse/models/post-stream';
const Topic = Discourse.Model.extend({
// returns createdAt if there's no bumped date
bumpedAt: function() {
var bumpedAt = this.get('bumped_at');
const bumpedAt = this.get('bumped_at');
if (bumpedAt) {
return new Date(bumpedAt);
} else {
@ -20,11 +23,11 @@ Discourse.Topic = Discourse.Model.extend({
}.property('created_at'),
postStream: function() {
return Discourse.PostStream.create({topic: this});
return PostStream.create({topic: this});
}.property(),
details: function() {
return Discourse.TopicDetails.create({topic: this});
return TopicDetails.create({topic: this});
}.property(),
invisible: Em.computed.not('visible'),
@ -35,12 +38,12 @@ Discourse.Topic = Discourse.Model.extend({
}.property('id'),
category: function() {
var categoryId = this.get('category_id');
const categoryId = this.get('category_id');
if (categoryId) {
return Discourse.Category.list().findProperty('id', categoryId);
}
var categoryName = this.get('categoryName');
const categoryName = this.get('categoryName');
if (categoryName) {
return Discourse.Category.list().findProperty('name', categoryName);
}
@ -52,12 +55,12 @@ Discourse.Topic = Discourse.Model.extend({
}.property('category.fullSlug'),
shareUrl: function(){
var user = Discourse.User.current();
const user = Discourse.User.current();
return this.get('url') + (user ? '?u=' + user.get('username_lower') : '');
}.property('url'),
url: function() {
var slug = this.get('slug');
let slug = this.get('slug');
if (slug.trim().length === 0) {
slug = "topic";
}
@ -65,8 +68,8 @@ Discourse.Topic = Discourse.Model.extend({
}.property('id', 'slug'),
// Helper to build a Url with a post number
urlForPostNumber: function(postNumber) {
var url = this.get('url');
urlForPostNumber(postNumber) {
let url = this.get('url');
if (postNumber && (postNumber > 0)) {
url += "/" + postNumber;
}
@ -74,7 +77,7 @@ Discourse.Topic = Discourse.Model.extend({
},
totalUnread: function() {
var count = (this.get('unread') || 0) + (this.get('new_posts') || 0);
const count = (this.get('unread') || 0) + (this.get('new_posts') || 0);
return count > 0 ? count : null;
}.property('new_posts', 'unread'),
@ -83,7 +86,7 @@ Discourse.Topic = Discourse.Model.extend({
}.property('url', 'last_read_post_number'),
lastUnreadUrl: function() {
var postNumber = Math.min(this.get('last_read_post_number') + 1, this.get('highest_post_number'));
const postNumber = Math.min(this.get('last_read_post_number') + 1, this.get('highest_post_number'));
return this.urlForPostNumber(postNumber);
}.property('url', 'last_read_post_number', 'highest_post_number'),
@ -107,12 +110,11 @@ Discourse.Topic = Discourse.Model.extend({
// tells us if we are still asynchronously flushing our "recently read" data.
// So take what the browser has seen into consideration.
displayNewPosts: function() {
var delta, result;
var highestSeen = Discourse.Session.currentProp('highestSeenByTopic')[this.get('id')];
const highestSeen = Discourse.Session.currentProp('highestSeenByTopic')[this.get('id')];
if (highestSeen) {
delta = highestSeen - this.get('last_read_post_number');
let delta = highestSeen - this.get('last_read_post_number');
if (delta > 0) {
result = this.get('new_posts') - delta;
let result = this.get('new_posts') - delta;
if (result < 0) {
result = 0;
}
@ -123,7 +125,7 @@ Discourse.Topic = Discourse.Model.extend({
}.property('new_posts', 'id'),
viewsHeat: function() {
var v = this.get('views');
const v = this.get('views');
if( v >= Discourse.SiteSettings.topic_views_heat_high ) return 'heatmap-high';
if( v >= Discourse.SiteSettings.topic_views_heat_medium ) return 'heatmap-med';
if( v >= Discourse.SiteSettings.topic_views_heat_low ) return 'heatmap-low';
@ -137,17 +139,17 @@ Discourse.Topic = Discourse.Model.extend({
isPrivateMessage: Em.computed.equal('archetype', 'private_message'),
isBanner: Em.computed.equal('archetype', 'banner'),
toggleStatus: function(property) {
toggleStatus(property) {
this.toggleProperty(property);
this.saveStatus(property, this.get(property) ? true : false);
},
setStatus: function(property, value) {
setStatus(property, value) {
this.set(property, value);
this.saveStatus(property, value);
},
saveStatus: function(property, value) {
saveStatus(property, value) {
if (property === 'closed' && value === true) {
this.set('details.auto_close_at', null);
}
@ -160,28 +162,28 @@ Discourse.Topic = Discourse.Model.extend({
});
},
makeBanner: function() {
var self = this;
makeBanner() {
const self = this;
return Discourse.ajax('/t/' + this.get('id') + '/make-banner', { type: 'PUT' })
.then(function () { self.set('archetype', 'banner'); });
},
removeBanner: function() {
var self = this;
removeBanner() {
const self = this;
return Discourse.ajax('/t/' + this.get('id') + '/remove-banner', { type: 'PUT' })
.then(function () { self.set('archetype', 'regular'); });
},
estimatedReadingTime: function() {
var wordCount = this.get('word_count');
const wordCount = this.get('word_count');
if (!wordCount) return;
// Avg for 500 words per minute when you account for skimming
return Math.floor(wordCount / 500.0);
}.property('word_count'),
toggleBookmark: function() {
var self = this, firstPost = this.get("postStream.posts")[0];
toggleBookmark() {
const self = this, firstPost = this.get("postStream.posts")[0];
this.toggleProperty('bookmarked');
if (this.get("postStream.firstPostPresent")) { firstPost.toggleProperty("bookmarked"); }
@ -194,8 +196,7 @@ Discourse.Topic = Discourse.Model.extend({
self.toggleProperty('bookmarked');
if (self.get("postStream.firstPostPresent")) { firstPost.toggleProperty('bookmarked'); }
var showGenericError = true;
let showGenericError = true;
if (error && error.responseText) {
try {
bootbox.alert($.parseJSON(error.responseText).errors);
@ -209,13 +210,7 @@ Discourse.Topic = Discourse.Model.extend({
});
},
/**
Invite a user to this topic
@method createInvite
@param {String} emailOrUsername The email or username of the user to be invited
**/
createInvite: function(emailOrUsername, groupNames) {
createInvite(emailOrUsername, groupNames) {
return Discourse.ajax("/t/" + this.get('id') + "/invite", {
type: 'POST',
data: { user: emailOrUsername, group_names: groupNames }
@ -223,7 +218,7 @@ Discourse.Topic = Discourse.Model.extend({
},
// Delete this topic
destroy: function(deleted_by) {
destroy(deleted_by) {
this.setProperties({
deleted_at: new Date(),
deleted_by: deleted_by,
@ -237,7 +232,7 @@ Discourse.Topic = Discourse.Model.extend({
},
// Recover this topic if deleted
recover: function() {
recover() {
this.setProperties({
deleted_at: null,
deleted_by: null,
@ -248,14 +243,14 @@ Discourse.Topic = Discourse.Model.extend({
},
// Update our attributes from a JSON result
updateFromJson: function(json) {
updateFromJson(json) {
this.get('details').updateFromJson(json.details);
var keys = Object.keys(json);
const keys = Object.keys(json);
keys.removeObject('details');
keys.removeObject('post_stream');
var topic = this;
const topic = this;
keys.forEach(function (key) {
topic.set(key, json[key]);
});
@ -266,13 +261,8 @@ Discourse.Topic = Discourse.Model.extend({
return this.get('pinned') && this.get('category.isUncategorizedCategory');
}.property('pinned', 'category.isUncategorizedCategory'),
/**
Clears the pin from a topic for the currently logged in user
@method clearPin
**/
clearPin: function() {
var topic = this;
clearPin() {
const topic = this;
// Clear the pin optimistically from the object
topic.set('pinned', false);
@ -287,7 +277,7 @@ Discourse.Topic = Discourse.Model.extend({
});
},
togglePinnedForUser: function() {
togglePinnedForUser() {
if (this.get('pinned')) {
this.clearPin();
} else {
@ -295,13 +285,8 @@ Discourse.Topic = Discourse.Model.extend({
}
},
/**
Re-pins a topic with a cleared pin
@method rePin
**/
rePin: function() {
var topic = this;
rePin() {
const topic = this;
// Clear the pin optimistically from the object
topic.set('pinned', true);
@ -317,12 +302,12 @@ Discourse.Topic = Discourse.Model.extend({
},
// Is the reply to a post directly below it?
isReplyDirectlyBelow: function(post) {
var posts = this.get('postStream.posts');
var postNumber = post.get('post_number');
isReplyDirectlyBelow(post) {
const posts = this.get('postStream.posts');
const postNumber = post.get('post_number');
if (!posts) return;
var postBelow = posts[posts.indexOf(post) + 1];
const postBelow = posts[posts.indexOf(post) + 1];
// If the post directly below's reply_to_post_number is our post number or we are quoted,
// it's considered directly below.
@ -340,7 +325,7 @@ Discourse.Topic = Discourse.Model.extend({
hasExcerpt: Em.computed.and('pinned', 'excerptNotEmpty'),
excerptTruncated: function() {
var e = this.get('excerpt');
const e = this.get('excerpt');
return( e && e.substr(e.length - 8,8) === '&hellip;' );
}.property('excerpt'),
@ -349,7 +334,7 @@ Discourse.Topic = Discourse.Model.extend({
});
Discourse.Topic.reopenClass({
Topic.reopenClass({
NotificationLevel: {
WATCHING: 3,
TRACKING: 2,
@ -357,13 +342,13 @@ Discourse.Topic.reopenClass({
MUTED: 0
},
createActionSummary: function(result) {
createActionSummary(result) {
if (result.actions_summary) {
var lookup = Em.Object.create();
const lookup = Em.Object.create();
result.actions_summary = result.actions_summary.map(function(a) {
a.post = result;
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
var actionSummary = Discourse.ActionSummary.create(a);
const actionSummary = Discourse.ActionSummary.create(a);
lookup.set(a.actionType.get('name_key'), actionSummary);
return actionSummary;
});
@ -371,7 +356,7 @@ Discourse.Topic.reopenClass({
}
},
update: function(topic, props) {
update(topic, props) {
props = JSON.parse(JSON.stringify(props)) || {};
// We support `category_id` and `categoryId` for compatibility
@ -384,7 +369,7 @@ Discourse.Topic.reopenClass({
// allows us to make a distinction between arrays that were not
// sent and arrays that we specifically want to be empty.
Object.keys(props).forEach(function(k) {
var v = props[k];
const v = props[k];
if (v instanceof Array && v.length === 0) {
props[k + '_empty_array'] = true;
}
@ -400,24 +385,16 @@ Discourse.Topic.reopenClass({
});
},
create: function() {
var result = this._super.apply(this, arguments);
create() {
const result = this._super.apply(this, arguments);
this.createActionSummary(result);
return result;
},
/**
Find similar topics to a given title and body
@method findSimilar
@param {String} title The current title
@param {String} body The current body
@returns A promise that will resolve to the topics
**/
findSimilarTo: function(title, body) {
findSimilarTo(title, body) {
return Discourse.ajax("/topics/similar_to", { data: {title: title, raw: body} }).then(function (results) {
if (Array.isArray(results)) {
return results.map(function(topic) { return Discourse.Topic.create(topic); });
return results.map(function(topic) { return Topic.create(topic); });
} else {
return Ember.A();
}
@ -425,14 +402,13 @@ Discourse.Topic.reopenClass({
},
// Load a topic, but accepts a set of filters
find: function(topicId, opts) {
var url = Discourse.getURL("/t/") + topicId;
find(topicId, opts) {
let url = Discourse.getURL("/t/") + topicId;
if (opts.nearPost) {
url += "/" + opts.nearPost;
}
var data = {};
const data = {};
if (opts.postsAfter) {
data.posts_after = opts.postsAfter;
}
@ -461,8 +437,8 @@ Discourse.Topic.reopenClass({
return Discourse.ajax(url + ".json", {data: data});
},
mergeTopic: function(topicId, destinationTopicId) {
var promise = Discourse.ajax("/t/" + topicId + "/merge-topic", {
mergeTopic(topicId, destinationTopicId) {
const promise = Discourse.ajax("/t/" + topicId + "/merge-topic", {
type: 'POST',
data: {destination_topic_id: destinationTopicId}
}).then(function (result) {
@ -472,8 +448,8 @@ Discourse.Topic.reopenClass({
return promise;
},
movePosts: function(topicId, opts) {
var promise = Discourse.ajax("/t/" + topicId + "/move-posts", {
movePosts(topicId, opts) {
const promise = Discourse.ajax("/t/" + topicId + "/move-posts", {
type: 'POST',
data: opts
}).then(function (result) {
@ -483,8 +459,8 @@ Discourse.Topic.reopenClass({
return promise;
},
changeOwners: function(topicId, opts) {
var promise = Discourse.ajax("/t/" + topicId + "/change-owner", {
changeOwners(topicId, opts) {
const promise = Discourse.ajax("/t/" + topicId + "/change-owner", {
type: 'POST',
data: opts
}).then(function (result) {
@ -494,7 +470,7 @@ Discourse.Topic.reopenClass({
return promise;
},
bulkOperation: function(topics, operation) {
bulkOperation(topics, operation) {
return Discourse.ajax("/topics/bulk", {
type: 'PUT',
data: {
@ -504,8 +480,8 @@ Discourse.Topic.reopenClass({
});
},
bulkOperationByFilter: function(filter, operation, categoryId) {
var data = { filter: filter, operation: operation };
bulkOperationByFilter(filter, operation, categoryId) {
const data = { filter: filter, operation: operation };
if (categoryId) data['category_id'] = categoryId;
return Discourse.ajax("/topics/bulk", {
type: 'PUT',
@ -513,12 +489,13 @@ Discourse.Topic.reopenClass({
});
},
resetNew: function() {
resetNew() {
return Discourse.ajax("/topics/reset-new", {type: 'PUT'});
},
idForSlug: function(slug) {
idForSlug(slug) {
return Discourse.ajax("/t/id_for/" + slug);
}
});
export default Topic;

View File

@ -1,6 +1,8 @@
import Topic from 'discourse/models/topic';
export default Discourse.Route.extend({
model: function(params) {
return Discourse.Topic.idForSlug(params.slug);
return Topic.idForSlug(params.slug);
},
afterModel: function(result) {

View File

@ -4,6 +4,7 @@ var isTransitioning = false,
SCROLL_DELAY = 500;
import ShowFooter from "discourse/mixins/show-footer";
import Topic from 'discourse/models/topic';
var TopicRoute = Discourse.Route.extend(ShowFooter, {
redirect: function() { return this.redirectIfLoginRequired(); },
@ -163,7 +164,7 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
return topic;
});
} else {
return this.setupParams(Discourse.Topic.create(_.omit(params, 'username_filters', 'filter')), queryParams);
return this.setupParams(Topic.create(_.omit(params, 'username_filters', 'filter')), queryParams);
}
},

View File

@ -154,8 +154,6 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
var $wmdPreview = $('#wmd-preview');
if ($wmdPreview.length === 0) return;
Discourse.SyntaxHighlighting.apply($wmdPreview);
var post = this.get('model.post'),
refresh = false;

View File

@ -264,8 +264,6 @@ var PostView = Discourse.GroupedView.extend(Ember.Evented, {
this._showLinkCounts();
Discourse.ScreenTrack.current().track(this.$().prop('id'), postNumber);
Discourse.SyntaxHighlighting.apply($post);
Discourse.Lightbox.apply($post);
this.trigger('postViewInserted', $post);

View File

@ -28,6 +28,8 @@
//= require ./discourse/models/model
//= require ./discourse/models/user_action
//= require ./discourse/models/composer
//= require ./discourse/models/post-stream
//= require ./discourse/models/topic-details
//= require ./discourse/models/topic
//= require ./discourse/models/top-period
//= require ./discourse/controllers/controller

View File

@ -3,8 +3,10 @@ moduleFor('controller:topic', 'controller:topic', {
'controller:search', 'controller:topic-progress', 'controller:application']
});
import Topic from 'discourse/models/topic';
var buildTopic = function() {
return Discourse.Topic.create({
return Topic.create({
title: "Qunit Test Topic",
participants: [
{id: 1234,

View File

@ -1,5 +1,7 @@
module("Discourse.SelectedPostsCount");
import Topic from 'discourse/models/topic';
var buildTestObj = function(params) {
return Ember.Object.createWithMixins(Discourse.SelectedPostsCount, params || {});
};
@ -26,7 +28,7 @@ test("when all posts are selected and there is a posts_count", function() {
test("when all posts are selected and there is topic with a posts_count", function() {
var testObj = buildTestObj({
allPostsSelected: true,
topic: Discourse.Topic.create({ posts_count: 3456 })
topic: Topic.create({ posts_count: 3456 })
});
equal(testObj.get('selectedPostsCount'), 3456, "It returns the topic's posts_count");

View File

@ -1,7 +1,10 @@
module("Discourse.PostStream");
module("model:post-stream");
import PostStream from 'discourse/models/post-stream';
import Topic from 'discourse/models/topic';
var buildStream = function(id, stream) {
var topic = Discourse.Topic.create({id: id, chunk_size: 5});
var topic = Topic.create({id: id, chunk_size: 5});
var ps = topic.get('postStream');
if (stream) {
ps.set('stream', stream);
@ -12,7 +15,7 @@ var buildStream = function(id, stream) {
var participant = {username: 'eviltrout'};
test('create', function() {
ok(Discourse.PostStream.create(), 'it can be created with no parameters');
ok(PostStream.create(), 'it can be created with no parameters');
});
test('defaults', function() {

View File

@ -1,7 +1,9 @@
module("Discourse.TopicDetails");
module("model:topic-details");
import Topic from 'discourse/models/topic';
var buildDetails = function(id) {
var topic = Discourse.Topic.create({id: id});
var topic = Topic.create({id: id});
return topic.get('details');
};
@ -20,13 +22,9 @@ test('updateFromJson', function() {
});
equal(details.get('suggested_topics.length'), 2, 'it loaded the suggested_topics');
containsInstance(details.get('suggested_topics'), Discourse.Topic);
containsInstance(details.get('suggested_topics'), Topic);
equal(details.get('allowed_users.length'), 1, 'it loaded the allowed users');
containsInstance(details.get('allowed_users'), Discourse.User);
});

View File

@ -1,20 +1,22 @@
module("Discourse.Topic");
module("model:topic");
import Topic from 'discourse/models/topic';
test("defaults", function() {
var topic = Discourse.Topic.create({id: 1234});
var topic = Topic.create({id: 1234});
blank(topic.get('deleted_at'), 'deleted_at defaults to blank');
blank(topic.get('deleted_by'), 'deleted_by defaults to blank');
});
test('has details', function() {
var topic = Discourse.Topic.create({id: 1234});
var topic = Topic.create({id: 1234});
var topicDetails = topic.get('details');
present(topicDetails, "a topic has topicDetails after we create it");
equal(topicDetails.get('topic'), topic, "the topicDetails has a reference back to the topic");
});
test('has a postStream', function() {
var topic = Discourse.Topic.create({id: 1234});
var topic = Topic.create({id: 1234});
var postStream = topic.get('postStream');
present(postStream, "a topic has a postStream after we create it");
equal(postStream.get('topic'), topic, "the postStream has a reference back to the topic");
@ -24,13 +26,13 @@ test('has a postStream', function() {
test('category relationship', function() {
// It finds the category by id
var category = Discourse.Category.list()[0],
topic = Discourse.Topic.create({id: 1111, category_id: category.get('id') });
topic = Topic.create({id: 1111, category_id: category.get('id') });
equal(topic.get('category'), category);
});
test("updateFromJson", function() {
var topic = Discourse.Topic.create({id: 1234}),
var topic = Topic.create({id: 1234}),
category = Discourse.Category.list()[0];
topic.updateFromJson({
@ -48,7 +50,7 @@ test("updateFromJson", function() {
test("destroy", function() {
var user = Discourse.User.create({username: 'eviltrout'});
var topic = Discourse.Topic.create({id: 1234});
var topic = Topic.create({id: 1234});
sandbox.stub(Discourse, 'ajax');
@ -60,7 +62,7 @@ test("destroy", function() {
test("recover", function() {
var user = Discourse.User.create({username: 'eviltrout'});
var topic = Discourse.Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user});
var topic = Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user});
sandbox.stub(Discourse, 'ajax');