Fixed ContainerView code to build views correctly

This commit is contained in:
Robin Ward 2013-06-06 16:33:26 -04:00
parent 8f32aed944
commit ba5f2d23a1
23 changed files with 312 additions and 208 deletions

View File

@ -70,7 +70,6 @@ Discourse.HistoryController = Discourse.ObjectController.extend(Discourse.ModalF
Em.String.i18n("changed_by", { author: item.display_username });
});
console.log('wat');
historyController.setProperties({
loading: false,
versionLeft: result.first(),

View File

@ -0,0 +1,29 @@
/**
A button for clearing a pinned topic
@class ClearPinButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.ClearPinButton = Discourse.ButtonView.extend({
textKey: 'topic.clear_pin.title',
helpKey: 'topic.clear_pin.help',
classNameBindings: ['unpinned'],
// Hide the button if it becomes unpinned
unpinned: function() {
// When not logged in don't show the button
if (!Discourse.User.current()) return 'hidden'
return this.get('controller.pinned') ? null : 'hidden';
}.property('controller.pinned'),
click: function(buffer) {
this.get('controller').clearPin();
},
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-pushpin'></i>");
}
});

View File

@ -1,5 +1,5 @@
/**
This view handles rendering of a button in a drop down
This view handles rendering of a button with an associated drop down
@class DropdownButtonView
@extends Discourse.View
@ -11,16 +11,19 @@ Discourse.DropdownButtonView = Discourse.View.extend({
attributeBindings: ['data-not-implemented'],
didInsertElement: function(e) {
var _this = this;
return this.$('ul li').on('click', function(e) {
e.preventDefault();
_this.clicked($(e.currentTarget).data('id'));
return false;
});
// If there's a click handler, call it
if (this.clicked) {
var dropDownButtonView = this;
this.$('ul li').on('click', function(e) {
e.preventDefault();
dropDownButtonView.clicked($(e.currentTarget).data('id'));
return false;
});
}
},
clicked: function(id) {
return null;
willDestroyElement: function(e) {
this.$('ul li').off('click');
},
textChanged: function() {
@ -28,24 +31,27 @@ Discourse.DropdownButtonView = Discourse.View.extend({
}.observes('text', 'longDescription'),
render: function(buffer) {
var desc;
buffer.push("<h4 class='title'>" + this.get('title') + "</h4>");
buffer.push("<button class='btn standard dropdown-toggle' data-toggle='dropdown'>");
buffer.push(this.get('text'));
buffer.push("</button>");
buffer.push("<ul class='dropdown-menu'>");
this.get('dropDownContent').each(function(row) {
var id = row[0],
textKey = row[1],
title = Em.String.i18n("" + textKey + ".title"),
description = Em.String.i18n("" + textKey + ".description");
title = Em.String.i18n(textKey + ".title"),
description = Em.String.i18n(textKey + ".description");
buffer.push("<li data-id=\"" + id + "\"><a href='#'>");
buffer.push("<span class='title'>" + title + "</span>");
buffer.push("<span>" + description + "</span>");
buffer.push("</a></li>");
});
buffer.push("</ul>");
if (desc = this.get('longDescription')) {
var desc = this.get('longDescription');
if (desc) {
buffer.push("<p>");
buffer.push(desc);
buffer.push("</p>");

View File

@ -0,0 +1,27 @@
/**
A button for favoriting a topic
@class FavoriteButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.FavoriteButton = Discourse.ButtonView.extend({
textKey: 'favorite.title',
helpKeyBinding: 'controller.content.favoriteTooltipKey',
favoriteChanged: function() {
this.rerender();
}.observes('controller.content.starred'),
click: function() {
this.get('controller').toggleStar();
},
renderIcon: function(buffer) {
buffer.push("<i class='icon-star " +
(this.get('controller.content.starred') ? ' starred' : '') +
"'></i>");
}
});

View File

@ -0,0 +1,22 @@
/**
A button for inviting users to reply to a topic
@class InviteReplyButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.InviteReplyButton = Discourse.ButtonView.extend({
textKey: 'topic.invite_reply.title',
helpKey: 'topic.invite_reply.help',
attributeBindings: ['disabled'],
disabled: Em.computed.or('controller.content.archived', 'controller.content.closed'),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-group'></i>");
},
click: function() {
return this.get('controller').send('showInvite');
}
});

View File

@ -0,0 +1,15 @@
/**
A button prompting users to login to reply to a topic
@class LoginReplyButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.LoginReplyButton = Discourse.ButtonView.extend({
textKey: 'topic.login_reply',
classNames: ['btn', 'btn-primary', 'create'],
click: function() {
this.get('controller').send('showLogin');
}
});

View File

@ -0,0 +1,46 @@
/**
A button for favoriting a topic
@class NotificationsButton
@extends Discourse.DropdownButtonView
@namespace Discourse
@module Discourse
**/
Discourse.NotificationsButton = Discourse.DropdownButtonView.extend({
title: Em.String.i18n('topic.notifications.title'),
longDescriptionBinding: 'topic.notificationReasonText',
dropDownContent: [
[Discourse.Topic.NotificationLevel.WATCHING, 'topic.notifications.watching'],
[Discourse.Topic.NotificationLevel.TRACKING, 'topic.notifications.tracking'],
[Discourse.Topic.NotificationLevel.REGULAR, 'topic.notifications.regular'],
[Discourse.Topic.NotificationLevel.MUTE, 'topic.notifications.muted']
],
text: function() {
var key = (function() {
switch (this.get('topic.notification_level')) {
case Discourse.Topic.NotificationLevel.WATCHING: return 'watching';
case Discourse.Topic.NotificationLevel.TRACKING: return 'tracking';
case Discourse.Topic.NotificationLevel.REGULAR: return 'regular';
case Discourse.Topic.NotificationLevel.MUTE: return 'muted';
}
}).call(this);
var icon = (function() {
switch (key) {
case 'watching': return '<i class="icon-circle heatmap-high"></i>&nbsp;';
case 'tracking': return '<i class="icon-circle heatmap-low"></i>&nbsp;';
case 'regular': return '';
case 'muted': return '<i class="icon-remove-sign"></i>&nbsp;';
}
})();
return icon + (Ember.String.i18n("topic.notifications." + key + ".title")) + "<span class='caret'></span>";
}.property('topic.notification_level'),
clicked: function(id) {
return this.get('topic').updateNotifications(id);
}
});

View File

@ -0,0 +1,31 @@
/**
A button for replying to a topic
@class ReplyButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.ReplyButton = Discourse.ButtonView.extend({
classNames: ['btn', 'btn-primary', 'create'],
attributeBindings: ['disabled'],
helpKey: 'topic.reply.help',
disabled: Em.computed.not('controller.content.can_create_post'),
text: function() {
var archetypeCapitalized = this.get('controller.content.archetype').capitalize();
var customTitle = this.get("parentView.replyButtonText" + archetypeCapitalized);
if (customTitle) { return customTitle; }
return Em.String.i18n("topic.reply.title");
}.property(),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-plus'></i>");
},
click: function() {
this.get('controller').reply();
}
});

View File

@ -0,0 +1,18 @@
/**
A button for sharing a link to a topic
@class ShareButton
@extends Discourse.ButtonView
@namespace Discourse
@module Discourse
**/
Discourse.ShareButton = Discourse.ButtonView.extend({
textKey: 'topic.share.title',
helpKey: 'topic.share.help',
'data-share-url': Em.computed.alias('topic.shareUrl'),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-share'></i>");
}
});

View File

@ -0,0 +1,38 @@
/**
Our own containerView with a helper method for attaching views
@class ContainerView
@extends Ember.ContainerView
@namespace Discourse
@uses Discourse.Presence
@module Discourse
**/
Discourse.ContainerView = Ember.ContainerView.extend(Discourse.Presence, {
/**
Attaches a view and wires up the container properly
@method attachViewWithArgs
@param {Object} viewArgs The arguments to pass when creating the view
@param {Class} klass The view class we want to create
@return {Ember.View} the view we created
**/
attachViewWithArgs: function(viewArgs, viewClass) {
if (!viewClass) { viewClass = Ember.View.extend() };
var view = this.createChildView(viewClass, viewArgs);
this.pushObject(view);
return view;
},
/**
Attaches a view with no arguments and wires up the container properly
@method attachViewClass
@param {Class} klass The view class we want to create
@return {Ember.View} the view we created
**/
attachViewClass: function(viewClass) {
this.attachViewWithArgs(null, viewClass);
}
});

View File

@ -2,26 +2,23 @@
This view handles the rendering of an archetype as an option
@class ArchetypeOptionsView
@extends Discourse.View
@extends Discourse.ContainerView
@namespace Discourse
@module Discourse
**/
Discourse.ArchetypeOptionsView = Em.ContainerView.extend({
Discourse.ArchetypeOptionsView = Discourse.ContainerView.extend({
metaDataBinding: 'parentView.metaData',
init: function() {
var metaData,
_this = this;
this._super();
metaData = this.get('metaData');
var metaData = this.get('metaData');
var archetypeOptionsView = this;
return this.get('archetype.options').forEach(function(a) {
var checked;
if (a.option_type === 1) {
checked = _this.pushObject(Discourse.OptionBooleanView.create({
archetypeOptionsView.attachViewWithArgs({
content: a,
checked: metaData.get(a.key) === 'true'
}));
}, Discourse.OptionBooleanView);
}
});

View File

@ -2,11 +2,11 @@
This view shows an array of buttons for selection of a color from a predefined set.
@class ColorPickerView
@extends Ember.ContainerView
@extends Discourse.ContainerView
@namespace Discourse
@module Discourse
**/
Discourse.ColorPickerView = Ember.ContainerView.extend({
Discourse.ColorPickerView = Discourse.ContainerView.extend({
classNames: 'colors-container',
init: function() {
@ -16,24 +16,26 @@ Discourse.ColorPickerView = Ember.ContainerView.extend({
createButtons: function() {
var colors = this.get('colors');
var _this = this;
var colorPickerView = this;
var isUsed, usedColors = this.get('usedColors') || [];
if (!colors) return;
colors.each(function(color) {
colors.forEach(function(color) {
isUsed = usedColors.indexOf(color.toUpperCase()) >= 0;
_this.addObject(Discourse.View.create({
colorPickerView.attachViewWithArgs({
tagName: 'button',
attributeBindings: ['style', 'title'],
classNames: ['colorpicker'].concat( isUsed ? ['used-color'] : ['unused-color'] ),
style: 'background-color: #' + color + ';',
title: isUsed ? I18n.t("js.category.already_used") : null,
click: function() {
_this.set("value", color);
colorPickerView.set("value", color);
return false;
}
}));
});
});
}
});

View File

@ -2,7 +2,7 @@
A base class for helping us display modal content
@class ModalView
@extends Ember.ContainerView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/

View File

@ -4,11 +4,11 @@
A control to support using PageDown as an Ember view.
@class PagedownEditor
@extends Ember.ContainerView
@extends Discourse.ContainerView
@namespace Discourse
@module Discourse
**/
Discourse.PagedownEditor = Ember.ContainerView.extend({
Discourse.PagedownEditor = Discourse.ContainerView.extend({
elementId: 'pagedown-editor',
init: function() {
@ -20,28 +20,35 @@ Discourse.PagedownEditor = Ember.ContainerView.extend({
this.pushObject(Em.View.create({ elementId: 'wmd-button-bar' }));
this.pushObject(Em.TextArea.create({ valueBinding: 'parentView.value', elementId: 'wmd-input' }));
this.pushObject(Discourse.View.createWithMixins({
elementId: 'wmd-preview',
classNameBindings: [':preview', 'hidden'],
hidden: (function() {
return this.blank('parentView.value');
}).property('parentView.value')
}));
this.attachViewClass(Discourse.PagedownPreviewView);
},
didInsertElement: function() {
var $wmdInput = $('#wmd-input');
$wmdInput.data('init', true);
$('#wmd-input').data('init', true);
this.set('editor', Discourse.Markdown.createEditor());
return this.get('editor').run();
},
observeValue: (function() {
observeValue: function() {
var editor = this.get('editor');
if (!editor) return;
Ember.run.next(null, function() { editor.refreshPreview(); });
}).observes('value')
}.observes('value')
});
Discourse.View.registerHelper('pagedown', Discourse.PagedownEditor);
Discourse.View.registerHelper('pagedown', Discourse.PagedownEditor);
/**
A helper view to display a preview of the pagedown content
@class PagedownPreviewView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
Discourse.PagedownPreviewView = Discourse.View.extend({
elementId: 'wmd-preview',
classNameBindings: [':preview', 'hidden'],
hidden: Em.computed.empty('parentView.value')
});

View File

@ -4,7 +4,7 @@
A control to support embedding a post as a parent of the current post (in reply to)
@class ParentView
@extends Ember.ContainerView
@extends Discourse.EmbeddedPostView
@namespace Discourse
@module Discourse
**/
@ -13,9 +13,9 @@ Discourse.ParentView = Discourse.EmbeddedPostView.extend({
// Nice animation for when the replies appear
didInsertElement: function() {
var $parentPost;
this._super();
$parentPost = this.get('postView').$('section.parent-post');
var $parentPost = this.get('postView').$('section.parent-post');
// Animate unless we're on a touch device
if (Discourse.get('touch')) {

View File

@ -2,11 +2,11 @@
This view allows us to prepend content to a post (for use in plugins)
@class PrependPostView
@extends Discourse.View
@extends Discourse.ContainerView
@namespace Discourse
@module Discourse
**/
Discourse.PrependPostView = Em.ContainerView.extend({
Discourse.PrependPostView = Discourse.ContainerView.extend({
init: function() {
this._super();
return this.trigger('prependPostContent');

View File

@ -3,7 +3,7 @@
automatically close.
@class TopicClosingView
@extends Ember.ContainerView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
@ -17,7 +17,7 @@ Discourse.TopicClosingView = Discourse.View.extend({
render: function(buffer) {
if (!this.present('topic.auto_close_at')) return;
var autoCloseAt = Date.create(this.get('topic.auto_close_at'));
if (autoCloseAt.isPast()) return;

View File

@ -2,177 +2,42 @@
This view is used for rendering the buttons at the footer of the topic
@class TopicFooterButtonsView
@extends Ember.ContainerView
@extends Discourse.ContainerView
@namespace Discourse
@module Discourse
**/
Discourse.TopicFooterButtonsView = Ember.ContainerView.extend({
Discourse.TopicFooterButtonsView = Discourse.ContainerView.extend({
elementId: 'topic-footer-buttons',
topicBinding: 'controller.content',
init: function() {
this._super();
return this.createButtons();
this.createButtons();
},
// Add the buttons below a topic
createButtons: function() {
var topic;
topic = this.get('topic');
var topic = this.get('topic');
if (Discourse.User.current()) {
if (!topic.get('isPrivateMessage')) {
// We hide some controls from private messages
if (this.get('topic.can_invite_to')) {
this.addObject(Discourse.ButtonView.createWithMixins({
textKey: 'topic.invite_reply.title',
helpKey: 'topic.invite_reply.help',
attributeBindings: ['disabled'],
disabled: function(){
return this.get('controller.content.archived') || this.get('controller.content.closed');
}.property('controller.content.archived', 'controller.content.closed'),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-group'></i>");
},
click: function() {
return this.get('controller').send('showInvite');
}
}));
this.attachViewClass(Discourse.InviteReplyButton);
}
this.addObject(Discourse.ButtonView.createWithMixins({
textKey: 'favorite.title',
helpKeyBinding: 'controller.content.favoriteTooltipKey',
favoriteChanged: (function() {
this.rerender();
}).observes('controller.content.starred'),
click: function() {
this.get('controller').toggleStar();
},
renderIcon: function(buffer) {
var extraClass;
if (this.get('controller.content.starred')) {
extraClass = 'starred';
}
return buffer.push("<i class='icon-star " + extraClass + "'></i>");
}
}));
this.addObject(Discourse.ButtonView.create({
textKey: 'topic.share.title',
helpKey: 'topic.share.help',
'data-share-url': topic.get('shareUrl'),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-share'></i>");
}
}));
// Add our clear pin button
this.addObject(Discourse.ButtonView.createWithMixins({
textKey: 'topic.clear_pin.title',
helpKey: 'topic.clear_pin.help',
classNameBindings: ['unpinned'],
// Hide the button if it becomes unpinned
unpinned: function() {
// When not logged in don't show the button
if (!Discourse.User.current()) return 'hidden'
return this.get('controller.pinned') ? null : 'hidden';
}.property('controller.pinned'),
click: function(buffer) {
this.get('controller').clearPin();
},
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-pushpin'></i>");
}
}));
this.attachViewClass(Discourse.FavoriteButton);
this.attachViewWithArgs({topic: topic}, Discourse.ShareButton);
this.attachViewClass(Discourse.ClearPinButton);
}
this.addObject(Discourse.ButtonView.createWithMixins({
classNames: ['btn', 'btn-primary', 'create'],
attributeBindings: ['disabled'],
helpKey: 'topic.reply.help',
disabled: !this.get('controller.content.can_create_post'),
text: function() {
var archetype, customTitle;
archetype = this.get('controller.content.archetype');
if (customTitle = this.get("parentView.replyButtonText" + (archetype.capitalize()))) {
return customTitle;
}
return Em.String.i18n("topic.reply.title");
}.property(),
renderIcon: function(buffer) {
buffer.push("<i class='icon icon-plus'></i>");
},
click: function() {
this.get('controller').reply();
}
}));
this.attachViewClass(Discourse.ReplyButton);
if (!topic.get('isPrivateMessage')) {
this.addObject(Discourse.DropdownButtonView.createWithMixins({
topic: topic,
title: Em.String.i18n('topic.notifications.title'),
longDescriptionBinding: 'topic.notificationReasonText',
dropDownContent: [
[Discourse.Topic.NotificationLevel.WATCHING, 'topic.notifications.watching'],
[Discourse.Topic.NotificationLevel.TRACKING, 'topic.notifications.tracking'],
[Discourse.Topic.NotificationLevel.REGULAR, 'topic.notifications.regular'],
[Discourse.Topic.NotificationLevel.MUTE, 'topic.notifications.muted']
],
text: function() {
var key = (function() {
switch (this.get('topic.notification_level')) {
case Discourse.Topic.NotificationLevel.WATCHING: return 'watching';
case Discourse.Topic.NotificationLevel.TRACKING: return 'tracking';
case Discourse.Topic.NotificationLevel.REGULAR: return 'regular';
case Discourse.Topic.NotificationLevel.MUTE: return 'muted';
}
}).call(this);
var icon = (function() {
switch (key) {
case 'watching': return '<i class="icon-circle heatmap-high"></i>&nbsp;';
case 'tracking': return '<i class="icon-circle heatmap-low"></i>&nbsp;';
case 'regular': return '';
case 'muted': return '<i class="icon-remove-sign"></i>&nbsp;';
}
})();
return icon + (Ember.String.i18n("topic.notifications." + key + ".title")) + "<span class='caret'></span>";
}.property('topic.notification_level'),
clicked: function(id) {
return this.get('topic').updateNotifications(id);
}
}));
this.attachViewWithArgs({topic: topic}, Discourse.NotificationsButton);
}
return this.trigger('additionalButtons', this);
this.trigger('additionalButtons', this);
} else {
// If not logged in give them a login control
return this.addObject(Discourse.ButtonView.create({
textKey: 'topic.login_reply',
classNames: ['btn', 'btn-primary', 'create'],
click: function() {
this.get('controller').send('showLogin');
}
}));
this.attachViewClass(Discourse.LoginReplyButton);
}
}
});

View File

@ -6,7 +6,7 @@
@namespace Discourse
@module Discourse
**/
Discourse.TopicSummaryView = Ember.ContainerView.extend(Discourse.Presence, {
Discourse.TopicSummaryView = Discourse.ContainerView.extend({
topicBinding: 'controller.content',
classNameBindings: ['hidden', ':topic-summary'],
LINKS_SHOWN: 5,
@ -42,11 +42,12 @@ Discourse.TopicSummaryView = Ember.ContainerView.extend(Discourse.Presence, {
this._super();
if (this.get('hidden')) return;
this.pushObject(Em.View.create({
this.attachViewWithArgs({
templateName: 'topic_summary/info',
topic: this.get('topic'),
summaryView: this
}));
})
this.trigger('appendSummaryInformation', this);
},
@ -58,20 +59,20 @@ Discourse.TopicSummaryView = Ember.ContainerView.extend(Discourse.Presence, {
// If we have a best of view
if (this.get('controller.has_best_of')) {
container.pushObject(Em.View.create({
container.attachViewWithArgs({
templateName: 'topic_summary/best_of_toggle',
tagName: 'section',
classNames: ['information']
}));
});
}
// If we have a private message
if (this.get('topic.isPrivateMessage')) {
return container.pushObject(Em.View.create({
container.attachViewWithArgs({
templateName: 'topic_summary/private_message',
tagName: 'section',
classNames: ['information']
}));
});
}
}
});

View File

@ -9,7 +9,6 @@
**/
Discourse.View = Ember.View.extend(Discourse.Presence, {});
Discourse.View.reopenClass({
/**

View File

@ -28548,4 +28548,4 @@ Ember
@module ember
*/
})();
})();

View File

@ -17,6 +17,8 @@
//= require ./discourse/controllers/object_controller
//= require ./discourse/views/modal/modal_body_view
//= require ./discourse/views/combobox_view
//= require ./discourse/views/buttons/button_view
//= require ./discourse/views/buttons/dropdown_button_view
//= require ./discourse/models/model
//= require ./discourse/routes/discourse_route
//= require ./discourse/routes/discourse_restricted_user_route