Ember Upgrade: 1.0

This commit is contained in:
Robin Ward 2013-09-16 14:08:55 -04:00
parent 01075c5e7a
commit be0ce08cc2
110 changed files with 19597 additions and 8477 deletions

View File

@ -1,2 +0,0 @@
//= require list_view.js
//= require_tree ./admin

View File

@ -0,0 +1,10 @@
<%
if Rails.env.development?
require_asset ("development/list-view.js")
else
require_asset ("production/list-view.js")
end
require_asset("main_include_admin.js")
%>

View File

@ -46,5 +46,12 @@ Discourse.AdminDashboardController = Ember.Controller.extend({
updatedTimestamp: function() {
return moment(this.get('updated_at')).format('LLL');
}.property('updated_at')
}.property('updated_at'),
actions: {
refreshProblems: function() {
this.loadProblems();
}
}
});

View File

@ -24,7 +24,7 @@ Discourse.AdminEmailIndexController = Discourse.Controller.extend({
this.set('sentTestEmail', false);
}.observes('testEmailAddress'),
actions: {
/**
Sends a test email to the currently entered email address
@ -42,5 +42,6 @@ Discourse.AdminEmailIndexController = Discourse.Controller.extend({
});
}
}
});

View File

@ -8,14 +8,21 @@
**/
Discourse.AdminEmailPreviewDigestController = Discourse.ObjectController.extend({
actions: {
refresh: function() {
var model = this.get('model');
var controller = this;
controller.set('loading', true);
var model = this.get('model'),
self = this;
self.set('loading', true);
Discourse.EmailPreview.findDigest(this.get('lastSeen')).then(function (email) {
model.setProperties(email.getProperties('html_content', 'text_content'));
controller.set('loading', false);
self.set('loading', false);
});
},
toggleShowHtml: function() {
this.toggleProperty('showHtml');
}
}
});

View File

@ -8,6 +8,7 @@
**/
Discourse.AdminFlagsController = Ember.ArrayController.extend({
actions: {
/**
Clear all flags on a post
@ -64,6 +65,7 @@ Discourse.AdminFlagsController = Ember.ArrayController.extend({
**/
deleteSpammer: function(item) {
item.get('user').deleteAsSpammer(function() { window.location.reload(); });
}
},
/**

View File

@ -1,19 +1,19 @@
Discourse.AdminGroupsController = Ember.Controller.extend({
itemController: 'adminGroup',
actions: {
edit: function(group){
this.get('model').select(group);
group.load();
},
refreshAutoGroups: function(){
var controller = this;
var self = this;
this.set('refreshingAutoGroups', true);
Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'})
.then(function() {
controller.set('model', Discourse.Group.findAll());
controller.set('refreshingAutoGroups', false);
self.set('refreshingAutoGroups', true);
Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() {
self.set('model', Discourse.Group.findAll());
self.set('refreshingAutoGroups', false);
});
},
@ -34,16 +34,18 @@ Discourse.AdminGroupsController = Ember.Controller.extend({
},
destroy: function(group){
var _this = this;
var self = this;
return bootbox.confirm(I18n.t("admin.groups.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
if (result) {
group.destroy().then(function(deleted) {
if (deleted) {
_this.get("model").removeObject(group);
self.get("model").removeObject(group);
}
});
}
});
}
}
});

View File

@ -4,6 +4,7 @@ Discourse.AdminReportsController = Ember.ObjectController.extend({
viewingTable: Em.computed.equal('viewMode', 'table'),
viewingBarChart: Em.computed.equal('viewMode', 'barChart'),
actions: {
// Changes the current view mode to 'table'
viewAsTable: function() {
this.set('viewMode', 'table');
@ -13,5 +14,6 @@ Discourse.AdminReportsController = Ember.ObjectController.extend({
viewAsBarChart: function() {
this.set('viewMode', 'barChart');
}
}
});

View File

@ -14,12 +14,15 @@ Discourse.AdminSiteContentEditController = Discourse.Controller.extend({
return false;
}.property('saving', 'content.content'),
actions: {
saveChanges: function() {
var controller = this;
controller.setProperties({saving: true, saved: false});
this.get('content').save().then(function () {
controller.setProperties({saving: false, saved: true});
var self = this;
self.setProperties({saving: true, saved: false});
self.get('content').save().then(function () {
self.setProperties({saving: false, saved: true});
});
}
}
});
Discourse.AdminSiteContentsController = Ember.ArrayController.extend({});

View File

@ -39,6 +39,7 @@ Discourse.AdminSiteSettingsController = Ember.ArrayController.extend(Discourse.P
});
}.property('filter', 'content.@each', 'onlyOverridden'),
actions: {
/**
Reset a setting to its default value
@ -69,5 +70,6 @@ Discourse.AdminSiteSettingsController = Ember.ArrayController.extend(Discourse.P
cancel: function(setting) {
setting.resetValue();
}
}
});

View File

@ -9,8 +9,13 @@
Discourse.AdminUserController = Discourse.ObjectController.extend({
editingTitle: false,
showApproval: function() {
return Discourse.SiteSettings.must_approve_users;
}.property(),
actions: {
toggleTitleEdit: function() {
this.set('editingTitle', !this.editingTitle);
this.toggleProperty('editingTitle');
},
saveTitle: function() {
@ -21,10 +26,8 @@ Discourse.AdminUserController = Discourse.ObjectController.extend({
bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body}));
});
this.toggleTitleEdit();
},
this.send('toggleTitleEdit');
}
}
showApproval: function() {
return Discourse.SiteSettings.must_approve_users;
}.property()
});

View File

@ -33,7 +33,7 @@ Discourse.AdminLogsStaffActionLogsRoute = Discourse.Route.extend({
return controller.show();
},
events: {
actions: {
showDetailsModal: function(logRecord) {
Discourse.Route.showModal(this, 'admin_staff_action_log_details', logRecord);
this.controllerFor('modal').set('modalClass', 'log-details-modal');

View File

@ -13,24 +13,11 @@ Discourse.AdminSiteContentEditRoute = Discourse.Route.extend({
},
model: function(params) {
var list = this.controllerFor('adminSiteContents').get('model');
var list = Discourse.SiteContentType.findAll();
// ember routing is fun ... this is what happens
//
// linkTo creates an Ember.LinkView , it marks an <a> with the class "active"
// if the "context" of this dynamic route is equal to the model in the linkTo
// the route "context" is set here, so we want to make sure we have the exact
// same object, from Ember we have:
//
// if (handlerInfo.context !== object) { return false; }
//
// we could avoid this hack if Ember just compared .serialize(model) with .serialize(context)
//
// alternatively we could use some sort of identity map
//
// see also: https://github.com/emberjs/ember.js/issues/3005
return list.findProperty("content_type", params.content_type);
return list.then(function(items) {
return items.findProperty("content_type", params.content_type);
});
},
renderTemplate: function() {

View File

@ -3,21 +3,21 @@
<div class="full-width">
<ul class="nav nav-pills">
<li>{{#linkTo 'admin.dashboard'}}{{i18n admin.dashboard.title}}{{/linkTo}}</li>
<li>{{#link-to 'admin.dashboard'}}{{i18n admin.dashboard.title}}{{/link-to}}</li>
{{#if currentUser.admin}}
<li>{{#linkTo 'admin.site_settings'}}{{i18n admin.site_settings.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/linkTo}}</li>
<li>{{#link-to 'admin.site_settings'}}{{i18n admin.site_settings.title}}{{/link-to}}</li>
<li>{{#link-to 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/link-to}}</li>
{{/if}}
<li>{{#linkTo 'adminUsersList'}}{{i18n admin.users.title}}{{/linkTo}}</li>
<li>{{#link-to 'adminUsersList'}}{{i18n admin.users.title}}{{/link-to}}</li>
{{#if currentUser.admin}}
<li>{{#linkTo 'admin.groups'}}{{i18n admin.groups.title}}{{/linkTo}}</li>
<li>{{#link-to 'admin.groups'}}{{i18n admin.groups.title}}{{/link-to}}</li>
{{/if}}
<li>{{#linkTo 'adminEmail'}}{{i18n admin.email.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminFlags'}}{{i18n admin.flags.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminLogs'}}{{i18n admin.logs.title}}{{/linkTo}}</li>
<li>{{#link-to 'adminEmail'}}{{i18n admin.email.title}}{{/link-to}}</li>
<li>{{#link-to 'adminFlags'}}{{i18n admin.flags.title}}{{/link-to}}</li>
<li>{{#link-to 'adminLogs'}}{{i18n admin.logs.title}}{{/link-to}}</li>
{{#if currentUser.admin}}
<li>{{#linkTo 'admin.customize'}}{{i18n admin.customize.title}}{{/linkTo}}</li>
<li>{{#linkTo 'admin.api'}}{{i18n admin.api.title}}{{/linkTo}}</li>
<li>{{#link-to 'admin.customize'}}{{i18n admin.customize.title}}{{/link-to}}</li>
<li>{{#link-to 'admin.api'}}{{i18n admin.api.title}}{{/link-to}}</li>
{{/if}}
</ul>

View File

@ -13,7 +13,7 @@
</p>
<p class="actions">
<small>{{i18n admin.dashboard.last_checked}}: {{problemsTimestamp}}</small>
<button {{action loadProblems}} class="btn btn-small"><i class="icon icon-refresh"></i>{{i18n admin.dashboard.refresh_problems}}</button>
<button {{action refreshProblems}} class="btn btn-small"><i class="icon icon-refresh"></i>{{i18n admin.dashboard.refresh_problems}}</button>
</p>
</div>
<div class="clearfix"></div>
@ -25,7 +25,7 @@
<div class="problem-messages">
<p>
{{i18n admin.dashboard.no_problems}}
<button {{action loadProblems}} class="btn btn-small"><i class="icon icon-refresh"></i>{{i18n admin.dashboard.refresh_problems}}</button>
<button {{action refreshProblems}} class="btn btn-small"><i class="icon icon-refresh"></i>{{i18n admin.dashboard.refresh_problems}}</button>
</p>
</div>
<div class="clearfix"></div>
@ -121,15 +121,15 @@
<table>
<tr>
<td class="title"><i class='icon icon-trophy'></i> {{i18n admin.dashboard.admins}}</td>
<td class="value">{{#linkTo 'adminUsersList.admins'}}{{admins}}{{/linkTo}}</td>
<td class="value">{{#link-to 'adminUsersList.admins'}}{{admins}}{{/link-to}}</td>
<td class="title"><i class='icon icon-ban-circle'></i> {{i18n admin.dashboard.banned}}</td>
<td class="value">{{#linkTo 'adminUsersList.banned'}}{{banned}}{{/linkTo}}</td>
<td class="value">{{#link-to 'adminUsersList.banned'}}{{banned}}{{/link-to}}</td>
</tr>
<tr>
<td class="title"><i class='icon icon-magic'></i> {{i18n admin.dashboard.moderators}}</td>
<td class="value">{{#linkTo 'adminUsersList.moderators'}}{{moderators}}{{/linkTo}}</td>
<td class="value">{{#link-to 'adminUsersList.moderators'}}{{moderators}}{{/link-to}}</td>
<td class="title"><i class='icon icon-ban-circle'></i> {{i18n admin.dashboard.blocked}}</td>
<td class="value">{{#linkTo 'adminUsersList.blocked'}}{{blocked}}{{/linkTo}}</td>
<td class="value">{{#link-to 'adminUsersList.blocked'}}{{blocked}}{{/link-to}}</td>
</tr>
</table>
</div>
@ -265,7 +265,7 @@
{{#each top_referrers.data}}
<tbody>
<tr>
<td class="title">{{#linkTo 'adminUser' this}}{{unbound username}}{{/linkTo}}</td>
<td class="title">{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}</td>
<td class="value">{{num_clicks}}</td>
<td class="value">{{num_topics}}</td>
</tr>

View File

@ -1,9 +1,9 @@
<div class='admin-controls'>
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#linkTo 'adminEmail.index'}}{{i18n admin.email.settings}}{{/linkTo}}</li>
<li>{{#linkTo 'adminEmail.logs'}}{{i18n admin.email.logs}}{{/linkTo}}</li>
<li>{{#linkTo 'adminEmail.previewDigest'}}{{i18n admin.email.preview_digest}}{{/linkTo}}</li>
<li>{{#link-to 'adminEmail.index'}}{{i18n admin.email.settings}}{{/link-to}}</li>
<li>{{#link-to 'adminEmail.logs'}}{{i18n admin.email.logs}}{{/link-to}}</li>
<li>{{#link-to 'adminEmail.previewDigest'}}{{i18n admin.email.preview_digest}}{{/link-to}}</li>
</ul>
</div>
</div>

View File

@ -15,8 +15,8 @@
<td>{{date created_at}}</td>
<td>
{{#if user}}
{{#linkTo 'adminUser' user}}{{avatar user imageSize="tiny"}}{{/linkTo}}
{{#linkTo 'adminUser' user}}{{user.username}}{{/linkTo}}
{{#link-to 'adminUser' user}}{{avatar user imageSize="tiny"}}{{/link-to}}
{{#link-to 'adminUser' user}}{{user.username}}{{/link-to}}
{{else}}
&mdash;
{{/if}}

View File

@ -11,9 +11,9 @@
<div class="span7 toggle">
<label>{{i18n admin.email.format}}</label>
{{#if showHtml}}
<span>{{i18n admin.email.html}}</span> | <a href='#' {{action toggleProperty 'showHtml'}}>{{i18n admin.email.text}}</a>
<span>{{i18n admin.email.html}}</span> | <a href='#' {{action toggleShowHtml}}>{{i18n admin.email.text}}</a>
{{else}}
<a href='#' {{action toggleProperty 'showHtml'}}>{{i18n admin.email.html}}</a> | <span>{{i18n admin.email.text}}</span>
<a href='#' {{action toggleShowHtml}}>{{i18n admin.email.html}}</a> | <span>{{i18n admin.email.text}}</span>
{{/if}}
</div>
</div>

View File

@ -1,8 +1,8 @@
<div class='admin-controls'>
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#linkTo 'adminFlags.active'}}{{i18n admin.flags.active}}{{/linkTo}}</li>
<li>{{#linkTo 'adminFlags.old'}}{{i18n admin.flags.old}}{{/linkTo}}</li>
<li>{{#link-to 'adminFlags.active'}}{{i18n admin.flags.active}}{{/link-to}}</li>
<li>{{#link-to 'adminFlags.old'}}{{i18n admin.flags.old}}{{/link-to}}</li>
</ul>
</div>
</div>
@ -25,7 +25,7 @@
{{#each flaggedPost in content}}
<tr {{bindAttr class="flaggedPost.extraClasses"}}>
<td class='user'>{{#if flaggedPost.user}}{{#linkTo 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="small"}}{{/linkTo}}{{/if}}</td>
<td class='user'>{{#if flaggedPost.user}}{{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="small"}}{{/link-to}}{{/if}}</td>
<td class='excerpt'>{{#if flaggedPost.topicHidden}}<i title='{{i18n topic_statuses.invisible.help}}' class='icon icon-eye-close'></i> {{/if}}<h3><a href='{{unbound flaggedPost.url}}'>{{flaggedPost.title}}</a></h3><br>{{{flaggedPost.excerpt}}}
</td>
@ -35,7 +35,7 @@
{{#each flaggedPost.flaggers}}
<tr>
<td>
{{#linkTo 'adminUser' this.user}}{{avatar this.user imageSize="small"}} {{/linkTo}}
{{#link-to 'adminUser' this.user}}{{avatar this.user imageSize="small"}} {{/link-to}}
</td>
<td>
{{date this.flaggedAt}}
@ -54,7 +54,7 @@
<tr>
<td></td>
<td class='message'>
<div>{{#linkTo 'adminUser' user}}{{avatar user imageSize="small"}}{{/linkTo}} {{message}} <a href="{{unbound permalink}}"><button class='btn'><i class="icon-reply"></i> {{i18n admin.flags.view_message}}</button></a></div>
<div>{{#link-to 'adminUser' user}}{{avatar user imageSize="small"}}{{/link-to}} {{message}} <a href="{{unbound permalink}}"><button class='btn'><i class="icon-reply"></i> {{i18n admin.flags.view_message}}</button></a></div>
</td>
<td></td>
<td></td>

View File

@ -1,9 +1,9 @@
<div class='admin-controls'>
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#linkTo 'adminLogs.staffActionLogs'}}{{i18n admin.logs.staff_actions.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminLogs.screenedEmails'}}{{i18n admin.logs.screened_emails.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminLogs.screenedUrls'}}{{i18n admin.logs.screened_urls.title}}{{/linkTo}}</li>
<li>{{#link-to 'adminLogs.staffActionLogs'}}{{i18n admin.logs.staff_actions.title}}{{/link-to}}</li>
<li>{{#link-to 'adminLogs.screenedEmails'}}{{i18n admin.logs.screened_emails.title}}{{/link-to}}</li>
<li>{{#link-to 'adminLogs.screenedUrls'}}{{i18n admin.logs.screened_urls.title}}{{/link-to}}</li>
</ul>
</div>
</div>

View File

@ -1,5 +1,5 @@
<div class="col value first staff_user">
{{#linkTo 'adminUser' acting_user}}{{avatar acting_user imageSize="tiny"}}{{/linkTo}}
{{#link-to 'adminUser' acting_user}}{{avatar acting_user imageSize="tiny"}}{{/link-to}}
<a {{action filterByStaffUser acting_user}}>{{acting_user.username}}</a>
</div>
<div class="col value action">
@ -7,7 +7,7 @@
</div>
<div class="col value subject">
{{#if target_user}}
{{#linkTo 'adminUser' target_user}}{{avatar target_user imageSize="tiny"}}{{/linkTo}}
{{#link-to 'adminUser' target_user}}{{avatar target_user imageSize="tiny"}}{{/link-to}}
<a {{action filterByTargetUser target_user}}>{{target_user.username}}</a>
{{/if}}
{{#if subject}}

View File

@ -1,8 +1,8 @@
<tr>
<td class="title">{{title}}</td>
<td class="value">{{#linkTo 'adminUsersList.newuser'}}{{valueAtTrustLevel data 0}}{{/linkTo}}</td>
<td class="value">{{#linkTo 'adminUsersList.basic'}}{{valueAtTrustLevel data 1}}{{/linkTo}}</td>
<td class="value">{{#linkTo 'adminUsersList.regular'}}{{valueAtTrustLevel data 2}}{{/linkTo}}</td>
<td class="value">{{#linkTo 'adminUsersList.leaders'}}{{valueAtTrustLevel data 3}}{{/linkTo}}</td>
<td class="value">{{#linkTo 'adminUsersList.elders'}}{{valueAtTrustLevel data 4}}{{/linkTo}}</td>
<td class="value">{{#link-to 'adminUsersList.newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.basic'}}{{valueAtTrustLevel data 1}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.regular'}}{{valueAtTrustLevel data 2}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.leaders'}}{{valueAtTrustLevel data 3}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.elders'}}{{valueAtTrustLevel data 4}}{{/link-to}}</td>
</tr>

View File

@ -4,7 +4,7 @@
<ul>
{{#each type in model}}
<li>
{{#linkTo 'adminSiteContentEdit' type}}{{type.title}}{{/linkTo}}
{{#link-to 'adminSiteContentEdit' type}}{{type.title}}{{/link-to}}
</li>
{{/each}}
</ul>

View File

@ -4,10 +4,10 @@
<div class='field'>{{i18n user.username.title}}</div>
<div class='value'>{{username}}</div>
<div class='controls'>
{{#linkTo 'userActivity' class="btn"}}
{{#link-to 'userActivity' class="btn"}}
<i class='icon icon-user'></i>
{{i18n admin.user.show_public_profile}}
{{/linkTo}}
{{/link-to}}
{{#if can_impersonate}}
<button class='btn' {{action impersonate target="content"}}>
<i class='icon icon-screenshot'></i>
@ -71,8 +71,8 @@
{{#if approved}}
{{i18n admin.user.approved_by}}
{{#linkTo 'adminUser' approved_by}}{{avatar approved_by imageSize="small"}}{{/linkTo}}
{{#linkTo 'adminUser' approved_by}}{{approved_by.username}}{{/linkTo}}
{{#link-to 'adminUser' approved_by}}{{avatar approved_by imageSize="small"}}{{/link-to}}
{{#link-to 'adminUser' approved_by}}{{approved_by.username}}{{/link-to}}
{{else}}
{{i18n no_value}}
{{/if}}

View File

@ -1,15 +1,15 @@
<div class='admin-controls'>
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#linkTo 'adminUsersList.active'}}{{i18n admin.users.nav.active}}{{/linkTo}}</li>
<li>{{#linkTo 'adminUsersList.new'}}{{i18n admin.users.nav.new}}{{/linkTo}}</li>
<li>{{#link-to 'adminUsersList.active'}}{{i18n admin.users.nav.active}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.new'}}{{i18n admin.users.nav.new}}{{/link-to}}</li>
{{#if Discourse.SiteSettings.must_approve_users}}
<li>{{#linkTo 'adminUsersList.pending'}}{{i18n admin.users.nav.pending}}{{/linkTo}}</li>
<li>{{#link-to 'adminUsersList.pending'}}{{i18n admin.users.nav.pending}}{{/link-to}}</li>
{{/if}}
<li>{{#linkTo 'adminUsersList.admins'}}{{i18n admin.users.nav.admins}}{{/linkTo}}</li>
<li>{{#linkTo 'adminUsersList.moderators'}}{{i18n admin.users.nav.moderators}}{{/linkTo}}</li>
<li>{{#linkTo 'adminUsersList.banned'}}{{i18n admin.users.nav.banned}}{{/linkTo}}</li>
<li>{{#linkTo 'adminUsersList.blocked'}}{{i18n admin.users.nav.blocked}}{{/linkTo}}</li>
<li>{{#link-to 'adminUsersList.admins'}}{{i18n admin.users.nav.admins}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.moderators'}}{{i18n admin.users.nav.moderators}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.banned'}}{{i18n admin.users.nav.banned}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.blocked'}}{{i18n admin.users.nav.blocked}}{{/link-to}}</li>
</ul>
</div>
<div class='span5 username controls'>
@ -61,8 +61,8 @@
{{/if}}
</td>
{{/if}}
<td>{{#linkTo 'adminUser' this}}{{avatar this imageSize="small"}}{{/linkTo}}</td>
<td>{{#linkTo 'adminUser' this}}{{unbound username}}{{/linkTo}}</td>
<td>{{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}</td>
<td>{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}</td>
<td>{{shorten email}}</td>
<td>{{{unbound last_emailed_age}}}</td>
<td>{{{unbound last_seen_age}}}</td>

View File

@ -13,18 +13,18 @@
<%
if Rails.env.development?
require_asset ("./external_development/jquery-2.0.3.js")
require_asset ("development/jquery-2.0.3.js")
else
require_asset ("./external_production/jquery-2.0.3.min.js")
require_asset ("production/jquery-2.0.3.min.js")
end
require_asset ("./external/jquery.ui.widget.js")
require_asset ("./external/handlebars.js")
require_asset ("jquery.ui.widget.js")
require_asset ("handlebars.js")
if Rails.env.development?
require_asset ("./external_development/ember.js")
require_asset ("development/ember.js")
else
require_asset ("./external_production/ember.js")
require_asset ("production/ember.js")
end
require_asset ("./main_include.js")

View File

@ -28,7 +28,7 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
return u + url;
},
resolver: Discourse.Resolver,
Resolver: Discourse.Resolver,
titleChanged: function() {
var title = "";
@ -112,7 +112,7 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
if ($currentTarget.attr('target')) { return; }
if ($currentTarget.data('auto-route')) { return; }
// If it's an ember #linkTo skip it
// If it's an ember #link-to skip it
if ($currentTarget.hasClass('ember-view')) { return; }
if ($currentTarget.hasClass('lightbox')) { return; }

View File

@ -8,15 +8,19 @@
@module Discourse
**/
Discourse.AvatarSelectorController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
actions: {
useUploadedAvatar: function() {
this.set("use_uploaded_avatar", true);
},
useGravatar: function() {
this.set("use_uploaded_avatar", false);
}
},
avatarTemplate: function() {
return this.get("use_uploaded_avatar") ? this.get("uploaded_avatar_template") : this.get("gravatar_template");
}.property("use_uploaded_avatar", "uploaded_avatar_template", "gravatar_template")
});

View File

@ -17,15 +17,6 @@ Discourse.ComposerController = Discourse.Controller.extend({
this.set('similarTopics', Em.A());
},
togglePreview: function() {
this.get('model').togglePreview();
},
// Import a quote from the post
importQuote: function() {
this.get('model').importQuote();
},
updateDraftStatus: function() {
this.get('model').updateDraftStatus();
},
@ -39,6 +30,42 @@ Discourse.ComposerController = Discourse.Controller.extend({
return Discourse.Category.list();
}.property(),
actions: {
// Toggle the reply view
toggle: function() {
this.closeAutocomplete();
switch (this.get('model.composeState')) {
case Discourse.Composer.OPEN:
if (this.blank('model.reply') && this.blank('model.title')) {
this.close();
} else {
this.shrink();
}
break;
case Discourse.Composer.DRAFT:
this.set('model.composeState', Discourse.Composer.OPEN);
break;
case Discourse.Composer.SAVING:
this.close();
}
return false;
},
togglePreview: function() {
this.get('model').togglePreview();
},
// Import a quote from the post
importQuote: function() {
this.get('model').importQuote();
},
cancel: function() {
this.cancelComposer();
},
save: function(force) {
var composer = this.get('model'),
composerController = this;
@ -115,8 +142,10 @@ Discourse.ComposerController = Discourse.Controller.extend({
composer.set('disableDrafts', false);
bootbox.alert(error);
});
}
},
/**
Checks to see if a reply has been typed. This is signaled by a keyUp
event in a view.
@ -230,7 +259,7 @@ Discourse.ComposerController = Discourse.Controller.extend({
} else {
opts.tested = true;
if (!opts.ignoreIfChanged) {
this.cancel().then(function() { composerController.open(opts); },
this.cancelComposer().then(function() { composerController.open(opts); },
function() { return promise.reject(); });
}
return promise;
@ -278,7 +307,7 @@ Discourse.ComposerController = Discourse.Controller.extend({
}
},
cancel: function() {
cancelComposer: function() {
var composerController = this;
return Ember.Deferred.promise(function (promise) {
@ -332,26 +361,6 @@ Discourse.ComposerController = Discourse.Controller.extend({
$('#wmd-input').autocomplete({ cancel: true });
},
// Toggle the reply view
toggle: function() {
this.closeAutocomplete();
switch (this.get('model.composeState')) {
case Discourse.Composer.OPEN:
if (this.blank('model.reply') && this.blank('model.title')) {
this.close();
} else {
this.shrink();
}
break;
case Discourse.Composer.DRAFT:
this.set('model.composeState', Discourse.Composer.OPEN);
break;
case Discourse.Composer.SAVING:
this.close();
}
return false;
},
// ESC key hit
hitEsc: function() {
if (this.get('model.viewOpen')) {

View File

@ -20,6 +20,18 @@ Discourse.ComposerMessagesController = Ember.ArrayController.extend({
this.reset();
},
actions: {
/**
Closes and hides a message.
@method closeMessage
@params {Object} message The message to dismiss
**/
closeMessage: function(message) {
this.removeObject(message);
}
},
/**
Displays a new message
@ -37,16 +49,6 @@ Discourse.ComposerMessagesController = Ember.ArrayController.extend({
}
},
/**
Closes and hides a message.
@method closeMessage
@params {Object} message The message to dismiss
**/
closeMessage: function(message) {
this.removeObject(message);
},
/**
Resets all active messages. For example if composing a new post.

View File

@ -39,18 +39,6 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
this.set('controllers.modal.title', this.get('title'));
}.observes('title'),
selectGeneral: function() {
this.set('selectedTab', 'general');
},
selectSecurity: function() {
this.set('selectedTab', 'security');
},
selectSettings: function() {
this.set('selectedTab', 'settings');
},
disabled: function() {
if (this.get('saving') || this.get('deleting')) return true;
if (!this.get('name')) return true;
@ -103,6 +91,20 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
return I18n.t('category.delete');
}.property(),
actions: {
selectGeneral: function() {
this.set('selectedTab', 'general');
},
selectSecurity: function() {
this.set('selectedTab', 'security');
},
selectSettings: function() {
this.set('selectedTab', 'settings');
},
showCategoryTopic: function() {
this.send('closeModal');
Discourse.URL.routeTo(this.get('topic_url'));
@ -180,6 +182,6 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
}
});
}
}
});

View File

@ -20,12 +20,14 @@ Discourse.EditTopicAutoCloseController = Discourse.ObjectController.extend(Disco
}
}.observes('details.auto_close_at'),
actions: {
saveAutoClose: function() {
this.setAutoClose( parseFloat(this.get('auto_close_days')) );
},
removeAutoClose: function() {
this.setAutoClose(null);
}
},
setAutoClose: function(days) {

View File

@ -13,10 +13,6 @@ Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunc
this.set('selected', null);
},
changePostActionType: function(action) {
this.set('selected', action);
},
submitEnabled: function() {
var selected = this.get('selected');
if (!selected) return false;
@ -46,8 +42,9 @@ Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunc
}
}.property('selected.is_custom_flag'),
actions: {
takeAction: function() {
this.createFlag({takeAction: true});
this.send('createFlag', {takeAction: true});
this.set('hidden', true);
},
@ -67,6 +64,11 @@ Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunc
});
},
changePostActionType: function(action) {
this.set('selected', action);
}
},
canDeleteSpammer: function() {
if (Discourse.User.currentProp('staff') && this.get('selected.name_key') === 'spam') {
return this.get('userDetails.can_be_deleted') && this.get('userDetails.can_delete_all_posts');

View File

@ -10,12 +10,6 @@ Discourse.HeaderController = Discourse.Controller.extend({
topic: null,
showExtraInfo: null,
toggleStar: function() {
var topic = this.get('topic');
if (topic) topic.toggleStar();
return false;
},
categories: function() {
return Discourse.Category.list();
}.property(),
@ -36,9 +30,17 @@ Discourse.HeaderController = Discourse.Controller.extend({
return Discourse.SiteSettings.enable_mobile_theme;
}.property(),
actions: {
toggleStar: function() {
var topic = this.get('topic');
if (topic) topic.toggleStar();
return false;
},
toggleMobileView: function() {
Discourse.Mobile.toggleMobileView();
}
}
});

View File

@ -25,6 +25,7 @@ Discourse.InviteController = Discourse.ObjectController.extend(Discourse.ModalFu
return I18n.t('topic.invite_reply.success', { email: this.get('email') });
}.property('email'),
actions: {
createInvite: function() {
if (this.get('disabled')) return;
@ -42,5 +43,7 @@ Discourse.InviteController = Discourse.ObjectController.extend(Discourse.ModalFu
});
return false;
}
}
});

View File

@ -113,6 +113,7 @@ Discourse.ListController = Discourse.Controller.extend({
}.observes('filterMode', 'category'),
// Create topic button
actions: {
createTopic: function() {
this.get('controllers.composer').open({
categoryId: this.get('category.id'),
@ -121,6 +122,7 @@ Discourse.ListController = Discourse.Controller.extend({
draftKey: this.get('draft_key'),
draftSequence: this.get('draft_sequence')
});
}
},
canEditCategory: function() {

View File

@ -27,6 +27,7 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
}
}.observes('content.draft'),
actions: {
// Star a topic
toggleStar: function(topic) {
topic.toggleStar();
@ -42,7 +43,7 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
},
createTopic: function() {
this.get('controllers.list').createTopic();
this.get('controllers.list').send('createTopic');
},
// Show newly inserted topics
@ -53,6 +54,7 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
this.get('content').loadBefore(tracker.get('newIncoming'));
tracker.resetTracking();
return false;
}
},
allLoaded: function() {

View File

@ -46,7 +46,7 @@ Discourse.MergeTopicController = Discourse.ObjectController.extend(Discourse.Sel
promise.then(function(result) {
// Posts moved
mergeTopicController.send('closeModal');
mergeTopicController.get('topicController').toggleMultiSelect();
mergeTopicController.get('topicController').send('toggleMultiSelect');
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
}, function() {
// Error moving posts

View File

@ -37,8 +37,13 @@ Discourse.PreferencesController = Discourse.ObjectController.extend({
{ name: I18n.t('user.new_topic_duration.after_n_weeks', { count: 1 }), value: 7 * 60 * 24 },
{ name: I18n.t('user.new_topic_duration.last_here'), value: -2 }],
saveButtonText: function() {
return this.get('saving') ? I18n.t('saving') : I18n.t('save');
}.property('saving'),
actions: {
save: function() {
var preferencesController = this;
var self = this;
this.set('saving', true);
this.set('saved', false);
@ -46,44 +51,40 @@ Discourse.PreferencesController = Discourse.ObjectController.extend({
var model = this.get('model');
return model.save().then(function() {
// model was saved
preferencesController.set('saving', false);
self.set('saving', false);
if (Discourse.User.currentProp('id') === model.get('id')) {
Discourse.User.currentProp('name', model.get('name'));
}
preferencesController.set('bio_cooked',
Discourse.Markdown.cook(preferencesController.get('bio_raw')));
preferencesController.set('saved', true);
self.set('bio_cooked', Discourse.Markdown.cook(self.get('bio_raw')));
self.set('saved', true);
}, function() {
// model failed to save
preferencesController.set('saving', false);
self.set('saving', false);
alert(I18n.t('generic_error'));
});
},
saveButtonText: function() {
return this.get('saving') ? I18n.t('saving') : I18n.t('save');
}.property('saving'),
changePassword: function() {
var preferencesController = this;
var self = this;
if (!this.get('passwordProgress')) {
this.set('passwordProgress', I18n.t("user.change_password.in_progress"));
return this.get('model').changePassword().then(function() {
// password changed
preferencesController.setProperties({
self.setProperties({
changePasswordProgress: false,
passwordProgress: I18n.t("user.change_password.success")
});
}, function() {
// password failed to change
preferencesController.setProperties({
self.setProperties({
changePasswordProgress: false,
passwordProgress: I18n.t("user.change_password.error")
});
});
}
}
}
});

View File

@ -22,18 +22,17 @@ Discourse.PreferencesEmailController = Discourse.ObjectController.extend({
return I18n.t("user.change");
}.property('saving'),
actions: {
changeEmail: function() {
var preferencesEmailController = this;
var self = this;
this.set('saving', true);
return this.get('content').changeEmail(this.get('newEmail')).then(function() {
preferencesEmailController.set('success', true);
self.set('success', true);
}, function() {
preferencesEmailController.setProperties({
error: true,
saving: false
});
self.setProperties({ error: true, saving: false });
});
}
}
});

View File

@ -41,6 +41,7 @@ Discourse.PreferencesUsernameController = Discourse.ObjectController.extend({
return I18n.t("user.change");
}.property('saving'),
actions: {
changeUsername: function() {
var preferencesUsernameController = this;
return bootbox.confirm(I18n.t("user.change_username.confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
@ -56,6 +57,8 @@ Discourse.PreferencesUsernameController = Discourse.ObjectController.extend({
}
});
}
}
});

View File

@ -62,14 +62,20 @@ Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, {
}.property('typeFilter', 'loading'),
termChanged: function() {
this.cancelType();
this.cancelTypeFilter();
}.observes('term'),
actions: {
moreOfType: function(type) {
this.set('typeFilter', type);
},
cancelType: function() {
this.cancelTypeFilter();
}
},
cancelTypeFilter: function() {
this.set('typeFilter', null);
},

View File

@ -11,10 +11,12 @@ Discourse.ShareController = Discourse.Controller.extend({
needs: ['topic'],
// Close the share controller
actions: {
close: function() {
this.set('link', '');
this.set('postNumber', '');
return false;
}
},
shareLinks: function() {

View File

@ -42,7 +42,7 @@ Discourse.SplitTopicController = Discourse.ObjectController.extend(Discourse.Sel
}).then(function(result) {
// Posts moved
self.send('closeModal');
self.get('topicController').toggleMultiSelect();
self.get('topicController').send('toggleMultiSelect');
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
}, function() {
// Error moving posts

View File

@ -10,12 +10,14 @@ Discourse.TopicAdminMenuController = Discourse.ObjectController.extend({
menuVisible: false,
needs: ['modal'],
actions: {
show: function() {
this.set('menuVisible', true);
},
hide: function() {
this.set('menuVisible', false);
}
},
showRecover: Em.computed.and('deleted', 'details.can_recover')

View File

@ -21,6 +21,209 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
this.set('selectedReplies', new Em.Set());
},
actions: {
jumpTop: function() {
Discourse.URL.routeTo(this.get('url'));
},
jumpBottom: function() {
Discourse.URL.routeTo(this.get('lastPostUrl'));
},
toggleSummary: function() {
this.toggleProperty('summaryCollapsed');
},
selectAll: function() {
var posts = this.get('postStream.posts');
var selectedPosts = this.get('selectedPosts');
if (posts) {
selectedPosts.addObjects(posts);
}
this.set('allPostsSelected', true);
},
deselectAll: function() {
this.get('selectedPosts').clear();
this.get('selectedReplies').clear();
this.set('allPostsSelected', false);
},
/**
Toggle a participant for filtering
@method toggleParticipant
**/
toggleParticipant: function(user) {
this.get('postStream').toggleParticipant(Em.get(user, 'username'));
},
editTopic: function() {
if (!this.get('details.can_edit')) return false;
this.setProperties({
editingTopic: true,
newTitle: this.get('title'),
newCategoryId: this.get('category_id')
});
return false;
},
// close editing mode
cancelEditingTopic: function() {
this.set('editingTopic', false);
},
toggleMultiSelect: function() {
this.toggleProperty('multiSelect');
},
finishedEditingTopic: function() {
var topicController = this;
if (this.get('editingTopic')) {
var topic = this.get('model');
// Topic title hasn't been sanitized yet, so the template shouldn't trust it.
this.set('topicSaving', true);
// manually update the titles & category
topic.setProperties({
title: this.get('newTitle'),
category_id: parseInt(this.get('newCategoryId'), 10),
fancy_title: this.get('newTitle')
});
// save the modifications
topic.save().then(function(result){
// update the title if it has been changed (cleaned up) server-side
var title = result.basic_topic.title;
var fancy_title = result.basic_topic.fancy_title;
topic.setProperties({
title: title,
fancy_title: fancy_title
});
topicController.set('topicSaving', false);
}, function(error) {
topicController.set('editingTopic', true);
topicController.set('topicSaving', false);
if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors[0]);
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
// close editing mode
topicController.set('editingTopic', false);
}
},
toggledSelectedPost: function(post) {
this.performTogglePost(post);
},
toggledSelectedPostReplies: function(post) {
var selectedReplies = this.get('selectedReplies');
if (this.performTogglePost(post)) {
selectedReplies.addObject(post);
} else {
selectedReplies.removeObject(post);
}
},
deleteSelected: function() {
var self = this;
bootbox.confirm(I18n.t("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
if (result) {
// If all posts are selected, it's the same thing as deleting the topic
if (self.get('allPostsSelected')) {
return self.deleteTopic();
}
var selectedPosts = self.get('selectedPosts'),
selectedReplies = self.get('selectedReplies'),
postStream = self.get('postStream'),
toRemove = new Ember.Set();
Discourse.Post.deleteMany(selectedPosts, selectedReplies);
postStream.get('posts').forEach(function (p) {
if (self.postSelected(p)) { toRemove.addObject(p); }
});
postStream.removePosts(toRemove);
self.send('toggleMultiSelect');
}
});
},
toggleVisibility: function() {
this.get('content').toggleStatus('visible');
},
toggleClosed: function() {
this.get('content').toggleStatus('closed');
},
togglePinned: function() {
this.get('content').toggleStatus('pinned');
},
toggleArchived: function() {
this.get('content').toggleStatus('archived');
},
convertToRegular: function() {
this.get('content').convertArchetype('regular');
},
// Toggle the star on the topic
toggleStar: function() {
this.get('content').toggleStar();
},
/**
Clears the pin from a topic for the currently logged in user
@method clearPin
**/
clearPin: function() {
this.get('content').clearPin();
},
resetRead: function() {
Discourse.ScreenTrack.current().reset();
this.unsubscribe();
var topicController = this;
this.get('model').resetRead().then(function() {
topicController.set('message', I18n.t("topic.read_position_reset"));
topicController.set('postStream.loaded', false);
});
},
replyAsNewTopic: function(post) {
var composerController = this.get('controllers.composer');
var promise = composerController.open({
action: Discourse.Composer.CREATE_TOPIC,
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY
});
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url'));
var postLink = "[" + (this.get('title')) + "](" + postUrl + ")";
promise.then(function() {
Discourse.Post.loadQuote(post.get('id')).then(function(q) {
composerController.appendText(I18n.t("post.continue_discussion", {
postLink: postLink
}) + "\n\n" + q);
});
});
}
},
jumpTopDisabled: function() {
return (this.get('progressPosition') === 1);
}.property('postStream.filteredPostsCount', 'progressPosition'),
@ -78,7 +281,7 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
multiSelectChanged: function() {
// Deselect all posts when multi select is turned off
if (!this.get('multiSelect')) {
this.deselectAll();
this.send('deselectAll');
}
}.observes('multiSelect'),
@ -109,156 +312,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
return false;
},
toggledSelectedPost: function(post) {
var selectedPosts = this.get('selectedPosts');
if (this.postSelected(post)) {
this.deselectPost(post);
return false;
} else {
selectedPosts.addObject(post);
// If the user manually selects all posts, all posts are selected
if (selectedPosts.length === this.get('posts_count')) {
this.set('allPostsSelected', true);
}
return true;
}
},
toggledSelectedPostReplies: function(post) {
var selectedReplies = this.get('selectedReplies');
if (this.toggledSelectedPost(post)) {
selectedReplies.addObject(post);
} else {
selectedReplies.removeObject(post);
}
},
selectAll: function() {
var posts = this.get('postStream.posts');
var selectedPosts = this.get('selectedPosts');
if (posts) {
selectedPosts.addObjects(posts);
}
this.set('allPostsSelected', true);
},
deselectAll: function() {
this.get('selectedPosts').clear();
this.get('selectedReplies').clear();
this.set('allPostsSelected', false);
},
toggleMultiSelect: function() {
this.toggleProperty('multiSelect');
},
toggleSummary: function() {
this.toggleProperty('summaryCollapsed');
},
editTopic: function() {
if (!this.get('details.can_edit')) return false;
this.setProperties({
editingTopic: true,
newTitle: this.get('title'),
newCategoryId: this.get('category_id')
});
return false;
},
// close editing mode
cancelEditingTopic: function() {
this.set('editingTopic', false);
},
finishedEditingTopic: function() {
var topicController = this;
if (this.get('editingTopic')) {
var topic = this.get('model');
// Topic title hasn't been sanitized yet, so the template shouldn't trust it.
this.set('topicSaving', true);
// manually update the titles & category
topic.setProperties({
title: this.get('newTitle'),
category_id: parseInt(this.get('newCategoryId'), 10),
fancy_title: this.get('newTitle')
});
// save the modifications
topic.save().then(function(result){
// update the title if it has been changed (cleaned up) server-side
var title = result.basic_topic.title;
var fancy_title = result.basic_topic.fancy_title;
topic.setProperties({
title: title,
fancy_title: fancy_title
});
topicController.set('topicSaving', false);
}, function(error) {
topicController.set('editingTopic', true);
topicController.set('topicSaving', false);
if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors[0]);
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
// close editing mode
topicController.set('editingTopic', false);
}
},
deleteSelected: function() {
var self = this;
bootbox.confirm(I18n.t("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
if (result) {
// If all posts are selected, it's the same thing as deleting the topic
if (self.get('allPostsSelected')) {
return self.deleteTopic();
}
var selectedPosts = self.get('selectedPosts'),
selectedReplies = self.get('selectedReplies'),
postStream = self.get('postStream'),
toRemove = new Ember.Set();
Discourse.Post.deleteMany(selectedPosts, selectedReplies);
postStream.get('posts').forEach(function (p) {
if (self.postSelected(p)) { toRemove.addObject(p); }
});
postStream.removePosts(toRemove);
self.toggleMultiSelect();
}
});
},
jumpTop: function() {
Discourse.URL.routeTo(this.get('url'));
},
jumpBottom: function() {
Discourse.URL.routeTo(this.get('lastPostUrl'));
},
/**
Toggle a participant for filtering
@method toggleParticipant
**/
toggleParticipant: function(user) {
this.get('postStream').toggleParticipant(Em.get(user, 'username'));
},
showFavoriteButton: function() {
return Discourse.User.current() && !this.get('isPrivateMessage');
}.property('isPrivateMessage'),
@ -272,52 +325,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
this.get('content').destroy(Discourse.User.current());
},
resetRead: function() {
Discourse.ScreenTrack.current().reset();
this.unsubscribe();
var topicController = this;
this.get('model').resetRead().then(function() {
topicController.set('message', I18n.t("topic.read_position_reset"));
topicController.set('postStream.loaded', false);
});
},
toggleVisibility: function() {
this.get('content').toggleStatus('visible');
},
toggleClosed: function() {
this.get('content').toggleStatus('closed');
},
togglePinned: function() {
this.get('content').toggleStatus('pinned');
},
toggleArchived: function() {
this.get('content').toggleStatus('archived');
},
convertToRegular: function() {
this.get('content').convertArchetype('regular');
},
// Toggle the star on the topic
toggleStar: function() {
this.get('content').toggleStar();
},
/**
Clears the pin from a topic for the currently logged in user
@method clearPin
**/
clearPin: function() {
this.get('content').clearPin();
},
// Receive notifications for this topic
subscribe: function() {
@ -383,24 +390,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
return false;
},
replyAsNewTopic: function(post) {
var composerController = this.get('controllers.composer');
var promise = composerController.open({
action: Discourse.Composer.CREATE_TOPIC,
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY
});
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url'));
var postLink = "[" + (this.get('title')) + "](" + postUrl + ")";
promise.then(function() {
Discourse.Post.loadQuote(post.get('id')).then(function(q) {
composerController.appendText(I18n.t("post.continue_discussion", {
postLink: postLink
}) + "\n\n" + q);
});
});
},
// Topic related
reply: function() {
this.replyToPost();
@ -470,6 +459,22 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
}
},
performTogglePost: function(post) {
var selectedPosts = this.get('selectedPosts');
if (this.postSelected(post)) {
this.deselectPost(post);
return false;
} else {
selectedPosts.addObject(post);
// If the user manually selects all posts, all posts are selected
if (selectedPosts.length === this.get('posts_count')) {
this.set('allPostsSelected', true);
}
return true;
}
},
removeAllowedUser: function(username) {
this.get('details').removeAllowedUser(username);
}

View File

@ -11,8 +11,10 @@ Discourse.UploadSelectorController = Discourse.Controller.extend(Discourse.Modal
localSelected: true,
remoteSelected: Em.computed.not('localSelected'),
actions: {
selectLocal: function() { this.set('localSelected', true); },
selectRemote: function() { this.set('localSelected', false); },
selectRemote: function() { this.set('localSelected', false); }
},
localTitle: function() { return Discourse.UploadSelectorController.translate("local_title"); }.property(),
remoteTitle: function() { return Discourse.UploadSelectorController.translate("remote_title"); }.property(),

View File

@ -7,10 +7,14 @@
@module Discourse
**/
Discourse.UserInvitedController = Discourse.ObjectController.extend({
actions: {
rescind: function(invite) {
invite.rescind();
return false;
}
}
});

View File

@ -8,7 +8,7 @@
**/
Discourse.ApplicationRoute = Em.Route.extend({
events: {
actions: {
showLogin: function() {
Discourse.Route.showModal(this, 'login');
},

View File

@ -10,7 +10,7 @@ Discourse.ListCategoriesRoute = Discourse.Route.extend({
redirect: function() { Discourse.redirectIfLoginRequired(this); },
events: {
actions: {
createCategory: function() {
Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({
color: 'AB9364', text_color: 'FFFFFF', hotness: 5, group_permissions: [{group_name: "everyone", permission_type: 1}],

View File

@ -15,7 +15,7 @@ Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
},
events: {
actions: {
showAvatarSelector: function() {
Discourse.Route.showModal(this, 'avatarSelector');
// all the properties needed for displaying the avatar selector modal

View File

@ -10,7 +10,7 @@ Discourse.TopicRoute = Discourse.Route.extend({
redirect: function() { Discourse.redirectIfLoginRequired(this); },
events: {
actions: {
// Modals that can pop up within a topic
showFlags: function(post) {

View File

@ -85,7 +85,7 @@
</li>
<li class='current-user'>
{{#if currentUser}}
{{#linkTo 'userActivity.index' currentUser titleKey="current_user" class="icon"}}{{boundAvatar currentUser imageSize="medium" }}{{/linkTo}}
{{#link-to 'userActivity.index' currentUser titleKey="current_user" class="icon"}}{{boundAvatar currentUser imageSize="medium" }}{{/link-to}}
{{else}}
<div class="icon not-logged-in-avatar" {{action showLogin}}><i class='icon-user'></i></div>
{{/if}}
@ -139,7 +139,7 @@
{{#if categories}}
<ul class="category-links">
<li class='heading' title="{{i18n filters.categories.help}}">
{{#linkTo "list.categories"}}{{i18n filters.categories.title}}{{/linkTo}}
{{#link-to "list.categories"}}{{i18n filters.categories.title}}{{/link-to}}
</li>
{{#each categories}}

View File

@ -60,7 +60,7 @@
<a href='#' {{action createTopic}}>{{i18n topic.suggest_create_topic}}</a>
{{/if}}
{{else}}
{{#linkTo 'list.categories'}}{{i18n topic.browse_all_categories}}{{/linkTo}} {{i18n or}} {{#linkTo 'list.latest'}}{{i18n topic.view_latest_topics}}{{/linkTo}}
{{#link-to 'list.categories'}}{{i18n topic.browse_all_categories}}{{/link-to}} {{i18n or}} {{#link-to 'list.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}}
{{/if}}
{{/if}}
</h3>

View File

@ -43,7 +43,7 @@
<a href='#' {{action createTopic}}>{{i18n topic.suggest_create_topic}}</a>
{{/if}}
{{else}}
{{#linkTo 'list.categories'}}{{i18n topic.browse_all_categories}}{{/linkTo}} {{i18n or}} {{#linkTo 'list.latest'}}{{i18n topic.view_latest_topics}}{{/linkTo}}
{{#link-to 'list.categories'}}{{i18n topic.browse_all_categories}}{{/link-to}} {{i18n or}} {{#link-to 'list.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}}
{{/if}}
{{/if}}
</h3>

View File

@ -1,7 +1,7 @@
<div id='user-info'>
<nav class='buttons'>
{{#if can_edit}}
{{#linkTo "preferences" class="btn"}}{{i18n user.edit}}{{/linkTo}}
{{#link-to "preferences" class="btn"}}{{i18n user.edit}}{{/link-to}}
{{/if}}
<br/>
{{#if can_send_private_message_to_user}}
@ -16,13 +16,13 @@
<ul class='action-list nav-stacked side-nav'>
{{#if privateMessageView}}
<li {{bindAttr class=":noGlyph privateMessagesActive:active"}}>
{{#linkTo 'userPrivateMessages.index' model}}{{i18n user.messages.all}}{{/linkTo}}
{{#link-to 'userPrivateMessages.index' model}}{{i18n user.messages.all}}{{/link-to}}
</li>
<li {{bindAttr class=":noGlyph privateMessagesMineActive:active"}}>
{{#linkTo 'userPrivateMessages.mine' model}}{{i18n user.messages.mine}}{{/linkTo}}
{{#link-to 'userPrivateMessages.mine' model}}{{i18n user.messages.mine}}{{/link-to}}
</li>
<li {{bindAttr class=":noGlyph privateMessagesUnreadActive:active"}}>
{{#linkTo 'userPrivateMessages.unread' model}}{{i18n user.messages.unread}}{{/linkTo}}
{{#link-to 'userPrivateMessages.unread' model}}{{i18n user.messages.unread}}{{/link-to}}
</li>
{{else}}
@ -48,7 +48,7 @@
<dt>{{i18n user.last_seen}}:</dt><dd>{{date last_seen_at}}</dd>
{{/if}}
{{#if invited_by}}
<dt>{{i18n user.invited_by}}:</dt><dd>{{#linkTo 'userActivity' invited_by}}{{invited_by.username}}{{/linkTo}}</dd>
<dt>{{i18n user.invited_by}}:</dt><dd>{{#link-to 'userActivity' invited_by}}{{invited_by.username}}{{/link-to}}</dd>
{{/if}}
{{#if email}}
<dt>{{i18n user.email.title}}:</dt><dd {{bindAttr title="email"}}>{{email}}</dd>

View File

@ -5,7 +5,7 @@
<div class="controls">
<span class='static'>{{username}}</span>
{{#if can_edit_username}}
{{#linkTo "preferences.username" class="btn pad-left"}}<i class="icon-pencil"></i>{{/linkTo}}
{{#link-to "preferences.username" class="btn pad-left"}}<i class="icon-pencil"></i>{{/link-to}}
{{/if}}
</div>
<div class='instructions'>
@ -28,7 +28,7 @@
<div class="controls">
<span class='static'>{{email}}</span>
{{#if can_edit_email}}
{{#linkTo "preferences.email" class="btn pad-left"}}<i class="icon-pencil"></i>{{/linkTo}}
{{#link-to "preferences.email" class="btn pad-left"}}<i class="icon-pencil"></i>{{/link-to}}
{{/if}}
</div>
<div class='instructions'>

View File

@ -12,19 +12,19 @@
{{/if}}
<ul class="nav nav-pills">
<li>
{{#linkTo 'userActivity'}}{{i18n user.activity_stream}}{{/linkTo}}
{{#link-to 'userActivity'}}{{i18n user.activity_stream}}{{/link-to}}
</li>
{{#if canSeePrivateMessages}}
<li>
{{#linkTo 'userPrivateMessages'}}{{i18n user.private_messages}}{{/linkTo}}
{{#link-to 'userPrivateMessages'}}{{i18n user.private_messages}}{{/link-to}}
</li>
{{/if}}
<li>
{{#linkTo 'user.invited'}}{{i18n user.invited.title}}{{/linkTo}}
{{#link-to 'user.invited'}}{{i18n user.invited.title}}{{/link-to}}
</li>
{{#if can_edit}}
<li>
{{#linkTo 'preferences'}}{{i18n user.preferences}}{{/linkTo}}
{{#link-to 'preferences'}}{{i18n user.preferences}}{{/link-to}}
</li>
{{/if}}
</ul>

View File

@ -14,7 +14,7 @@ Discourse.FavoriteButton = Discourse.ButtonView.extend({
shouldRerender: Discourse.View.renderIfChanged('controller.starred'),
click: function() {
this.get('controller').toggleStar();
this.get('controller').send('toggleStar');
},
renderIcon: function(buffer) {

View File

@ -45,7 +45,7 @@ Discourse.ShareView = Discourse.View.extend({
// link is clicked (which is a click event) while the share dialog is showing.
if (shareView.$().has(e.target).length !== 0) { return; }
shareView.get('controller').close();
shareView.get('controller').send('close');
return true;
});
@ -76,7 +76,7 @@ Discourse.ShareView = Discourse.View.extend({
$('html').on('keydown.share-view', function(e){
if (e.keyCode === 27) {
shareView.get('controller').close();
shareView.get('controller').send('close');
}
});
},

View File

@ -13,11 +13,11 @@ Discourse.TopicAdminMenuView = Discourse.View.extend({
},
didInsertElement: function() {
var topicAdminMenuView = this;
var self = this;
$('html').on('mouseup.discourse-topic-admin-menu', function(e) {
var $target = $(e.target);
if ($target.is('button') || topicAdminMenuView.$().has($target).length === 0) {
topicAdminMenuView.get('controller').hide();
if ($target.is('button') || self.$().has($target).length === 0) {
self.get('controller').send('hide');
}
});
}

View File

@ -3,7 +3,7 @@
"description": "This is the EmberJS client to access a Discourse Server",
"url": "http://www.discourse.org/",
"options": {
"exclude": "external,external_production,defer",
"exclude": "development,production,defer",
"outdir": "./build"
}
}

View File

@ -1,7 +1,37 @@
//= require_tree ./discourse/ember
// The rest of the externals
//= require_tree ./external
// The Vendored JS
//= require LAB.js
//= require Markdown.Converter.js
//= require Markdown.Editor.js
//= require Markdown.Sanitizer.js
//= require better_markdown.js
//= require bootbox.js
//= require bootstrap-alert.js
//= require bootstrap-button.js
//= require bootstrap-dropdown.js
//= require bootstrap-modal.js
//= require bootstrap-transition.js
//= require browser-update.js
//= require chosen.jquery.js
//= require ember-renderspeed.js
//= require favcount.js
//= require handlebars.js
//= require jquery.ba-replacetext.js
//= require jquery.ba-resize.min.js
//= require jquery.color.js
//= require jquery.cookie.js
//= require jquery.fileupload.js
//= require jquery.iframe-transport.js
//= require jquery.putcursoratend.js
//= require jquery.tagsinput.js
//= require jquery.ui.widget.js
//= require lodash.js
//= require md5.js
//= require modernizr.custom.95264.js
//= require mousetrap.js
//= require rsvp.js
//= require show-html.js
//= require ./discourse/helpers/i18n_helpers
//= require ./discourse/mixins/ajax
@ -39,4 +69,4 @@
//= require_tree ./discourse/templates
//= require_tree ./discourse/routes
//= require ./external/browser-update.js
//= require browser-update.js

View File

@ -0,0 +1 @@
//= require_tree ./admin

View File

@ -7,7 +7,7 @@ window.PagedownCustom = {
description: I18n.t("composer.quote_post_title"),
execute: function() {
// AWFUL but I can't figure out how to call a controller method from outside our app
return Discourse.__container__.lookup('controller:composer').importQuote();
return Discourse.__container__.lookup('controller:composer').send('importQuote');
}
}
]

View File

@ -29,6 +29,7 @@ class TopicsController < ApplicationController
return wordpress if params[:best].present?
opts = params.slice(:username_filters, :filter, :page, :post_number)
begin
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
rescue Discourse::NotFound

View File

@ -113,8 +113,8 @@ module Discourse
# ember stuff only used for asset precompliation, production variant plays up
config.ember.variant = :development
config.ember.ember_location = "#{Rails.root}/app/assets/javascripts/external_production/ember.js"
config.ember.handlebars_location = "#{Rails.root}/app/assets/javascripts/external/handlebars.js"
config.ember.ember_location = "#{Rails.root}/vendor/assets/javascripts/production/ember.js"
config.ember.handlebars_location = "#{Rails.root}/vendor/assets/javascripts/handlebars.js"
# Since we are using strong_parameters, we can disable and remove
# attr_accessible.

View File

@ -13,9 +13,6 @@ paths:
- test/javascripts/**/*.js
exclude_paths:
- app/assets/javascripts/external/*
- app/assets/javascripts/external_production/*
- app/assets/javascripts/external_development/*
- app/assets/javascripts/defer/*
- app/assets/javascripts/locales/i18n.js

View File

@ -93,11 +93,11 @@ module PrettyText
ctx["helpers"] = Helpers.new
ctx_load(ctx,
"app/assets/javascripts/external/md5.js",
"app/assets/javascripts/external/lodash.js",
"app/assets/javascripts/external/Markdown.Converter.js",
"vendor/assets/javascripts/md5.js",
"vendor/assets/javascripts/lodash.js",
"vendor/assets/javascripts/Markdown.Converter.js",
"lib/headless-ember.js",
"app/assets/javascripts/external/rsvp.js",
"vendor/assets/javascripts/rsvp.js",
Rails.configuration.ember.handlebars_location)
ctx.eval("var Discourse = {}; Discourse.SiteSettings = #{SiteSetting.client_settings_json};")
@ -107,7 +107,7 @@ module PrettyText
decorate_context(ctx)
ctx_load(ctx,
"app/assets/javascripts/external/better_markdown.js",
"vendor/assets/javascripts/better_markdown.js",
"app/assets/javascripts/discourse/dialects/dialect.js",
"app/assets/javascripts/discourse/components/utilities.js",
"app/assets/javascripts/discourse/components/markdown.js")

View File

@ -14,13 +14,13 @@ test("avatarTemplate", function() {
avatarSelector.get("gravatar_template"),
"we are using gravatar by default");
avatarSelectorController.useUploadedAvatar();
avatarSelectorController.send('useUploadedAvatar');
equal(avatarSelectorController.get("avatarTemplate"),
avatarSelector.get("uploaded_avatar_template"),
"calling useUploadedAvatar switches to using the uploaded avatar");
avatarSelectorController.useGravatar();
avatarSelectorController.send('useGravatar');
equal(avatarSelectorController.get("avatarTemplate"),
avatarSelector.get("gravatar_template"),

View File

@ -18,16 +18,16 @@ test("editingMode", function() {
ok(!topicController.get('editingTopic'), "we are not editing by default");
topicController.set('model.details.can_edit', false);
topicController.editTopic();
topicController.send('editTopic');
ok(!topicController.get('editingTopic'), "calling editTopic doesn't enable editing unless the user can edit");
topicController.set('model.details.can_edit', true);
topicController.editTopic();
topicController.send('editTopic');
ok(topicController.get('editingTopic'), "calling editTopic enables editing if the user can edit");
equal(topicController.get('newTitle'), topic.get('title'));
equal(topicController.get('newCategoryId'), topic.get('category_id'));
topicController.cancelEditingTopic();
topicController.send('cancelEditingTopic');
ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value");
});
@ -43,12 +43,12 @@ test("toggledSelectedPost", function() {
equal(tc.get('selectedPostsCount'), 0, "there is a selected post count of 0");
ok(!tc.postSelected(post), "the post is not selected by default");
tc.toggledSelectedPost(post);
tc.send('toggledSelectedPost', post);
present(tc.get('selectedPosts'), "there is a selectedPosts collection");
equal(tc.get('selectedPostsCount'), 1, "there is a selected post now");
ok(tc.postSelected(post), "the post is now selected");
tc.toggledSelectedPost(post);
tc.send('toggledSelectedPost', post);
ok(!tc.postSelected(post), "the post is no longer selected");
});
@ -61,10 +61,10 @@ test("selectAll", function() {
postStream.appendPost(post);
ok(!tc.postSelected(post), "the post is not selected by default");
tc.selectAll();
tc.send('selectAll');
ok(tc.postSelected(post), "the post is now selected");
ok(tc.get('allPostsSelected'), "all posts are selected");
tc.deselectAll();
tc.send('deselectAll');
ok(!tc.postSelected(post), "the post is deselected again");
ok(!tc.get('allPostsSelected'), "all posts are not selected");
@ -80,10 +80,10 @@ test("Automating setting of allPostsSelected", function() {
postStream.appendPost(post);
ok(!tc.get('allPostsSelected'), "all posts are not selected by default");
tc.toggledSelectedPost(post);
tc.send('toggledSelectedPost', post);
ok(tc.get('allPostsSelected'), "all posts are selected if we select the only post");
tc.toggledSelectedPost(post);
tc.send('toggledSelectedPost', post);
ok(!tc.get('allPostsSelected'), "the posts are no longer automatically selected");
});
@ -96,20 +96,20 @@ test("Select Replies when present", function() {
postStream = tc.get('postStream');
ok(!tc.postSelected(p3), "replies are not selected by default");
tc.toggledSelectedPostReplies(p1);
tc.send('toggledSelectedPostReplies', p1);
ok(tc.postSelected(p1), "it selects the post");
ok(!tc.postSelected(p2), "it doesn't select a post that's not a reply");
ok(tc.postSelected(p3), "it selects a post that is a reply");
equal(tc.get('selectedPostsCount'), 2, "it has a selected posts count of two");
// If we deselected the post whose replies are selected...
tc.toggledSelectedPost(p1);
tc.send('toggledSelectedPost', p1);
ok(!tc.postSelected(p1), "it deselects the post");
ok(!tc.postSelected(p3), "it deselects the replies too");
// If we deselect a reply, it should deselect the parent's replies selected attribute. Weird but what else would make sense?
tc.toggledSelectedPostReplies(p1);
tc.toggledSelectedPost(p3);
tc.send('toggledSelectedPostReplies', p1);
tc.send('toggledSelectedPost', p3);
ok(tc.postSelected(p1), "the post stays selected");
ok(!tc.postSelected(p3), "it deselects the replies too");

View File

@ -184,7 +184,5 @@ var jsHintOpts = {
<%= jshint("#{Rails.root}/app/assets/javascripts/**/*.js",
"/app/assets/javascripts/",
[/external\//,
/external_development\//,
/external_production\//,
/defer\//,
/locales\//]) %>

View File

@ -9,10 +9,10 @@
//= require ../../app/assets/javascripts/discourse/components/probes.js
// Externals we need to load first
//= require ../../app/assets/javascripts/external_development/jquery-2.0.3.js
//= require ../../app/assets/javascripts/external/jquery.ui.widget.js
//= require ../../app/assets/javascripts/external/handlebars.js
//= require ../../app/assets/javascripts/external_development/ember.js
//= require development/jquery-2.0.3.js
//= require jquery.ui.widget.js
//= require handlebars.js
//= require development/ember.js
//= require ../../app/assets/javascripts/locales/i18n
//= require ../../app/assets/javascripts/discourse/helpers/i18n_helpers
@ -21,8 +21,36 @@
// Pagedown customizations
//= require ../../app/assets/javascripts/pagedown_custom.js
// The rest of the externals
//= require_tree ../../app/assets/javascripts/external
// The rest of the vendored JS
//= require LAB.js
//= require Markdown.Converter.js
//= require Markdown.Editor.js
//= require Markdown.Sanitizer.js
//= require better_markdown.js
//= require bootbox.js
//= require bootstrap-alert.js
//= require bootstrap-button.js
//= require bootstrap-dropdown.js
//= require bootstrap-modal.js
//= require bootstrap-transition.js
//= require browser-update.js
//= require chosen.jquery.js
//= require ember-renderspeed.js
//= require favcount.js
//= require jquery.ba-replacetext.js
//= require jquery.ba-resize.min.js
//= require jquery.color.js
//= require jquery.cookie.js
//= require jquery.fileupload.js
//= require jquery.iframe-transport.js
//= require jquery.putcursoratend.js
//= require jquery.tagsinput.js
//= require lodash.js
//= require md5.js
//= require modernizr.custom.95264.js
//= require mousetrap.js
//= require rsvp.js
//= require show-html.js
// Stuff we need to load first
//= require main_include

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,6 @@
// Last commit: 1f0c355 (2013-09-18 11:01:11 -0400)
(function() {
var get = Ember.get, set = Ember.set;
@ -18,7 +21,7 @@ function positionElement() {
// TODO: avoid needing this by avoiding unnecessary
// calls to this method in the first place
if (samePosition(position, _position)) { return; }
this._parentView.applyTransform(element, position);
this._parentView.applyTransform(element, position.x, position.y);
this._position = position;
}, this);
@ -186,22 +189,53 @@ Ember.ReusableListItemView = Ember.View.extend(Ember.ListItemViewMixin, {
(function() {
var el = document.createElement('div'), style = el.style;
var propPrefixes = ['Webkit', 'Moz', 'O', 'ms'];
function testProp(prop) {
if (prop in style) return prop;
var uppercaseProp = prop.charAt(0).toUpperCase() + prop.slice(1);
for (var i=0; i<propPrefixes.length; i++) {
var prefixedProp = propPrefixes[i] + uppercaseProp;
if (prefixedProp in style) {
return prefixedProp;
}
}
return null;
}
var transformProp = testProp('transform');
var perspectiveProp = testProp('perspective');
var supports2D = transformProp !== null;
var supports3D = perspectiveProp !== null;
Ember.ListViewHelper = {
transformProp: transformProp,
applyTransform: (function(){
var element = document.createElement('div');
if ('webkitTransform' in element.style){
return function(element, position){
var x = position.x,
y = position.y;
element.style.webkitTransform = 'translate3d(' + x + 'px, ' + y + 'px, 0)';
if (supports2D) {
return function(element, x, y){
element.style[transformProp] = 'translate(' + x + 'px, ' + y + 'px)';
};
}else{
return function(element, position){
var x = position.x,
y = position.y;
} else {
return function(element, x, y){
element.style.top = y + 'px';
element.style.left = x + 'px';
};
}
})(),
apply3DTransform: (function(){
if (supports3D) {
return function(element, x, y){
element.style[transformProp] = 'translate3d(' + x + 'px, ' + y + 'px, 0)';
};
} else if (supports2D) {
return function(element, x, y){
element.style[transformProp] = 'translate(' + x + 'px, ' + y + 'px)';
};
} else {
return function(element, x, y){
element.style.top = y + 'px';
element.style.left = x + 'px';
};
@ -239,10 +273,6 @@ function sortByContentIndex (viewOne, viewTwo){
return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex');
}
function detectListItemViews(childView) {
return Ember.ListItemViewMixin.detect(childView);
}
function notifyMutationListeners() {
if (Ember.View.notifyMutationListeners) {
Ember.run.once(Ember.View, 'notifyMutationListeners');
@ -296,6 +326,7 @@ function enableProfilingOutput() {
*/
Ember.ListViewMixin = Ember.Mixin.create({
itemViewClass: Ember.ListItemView,
emptyViewClass: Ember.View,
classNames: ['ember-list-view'],
attributeBindings: ['style'],
domManager: domManager,
@ -315,13 +346,16 @@ Ember.ListViewMixin = Ember.Mixin.create({
*/
init: function() {
this._super();
enableProfilingOutput();
addContentArrayObserver.call(this);
this._syncChildViews();
this.columnCountDidChange();
this.on('didInsertElement', syncListContainerWidth);
this.columnCountDidChange();
this._syncChildViews();
this._addContentArrayObserver();
},
_addContentArrayObserver: Ember.beforeObserver(function() {
addContentArrayObserver.call(this);
}, 'content'),
/**
Called on your view when it should push strings of HTML into a
`Ember.RenderBuffer`.
@ -599,7 +633,7 @@ Ember.ListViewMixin = Ember.Mixin.create({
maxScrollTop: Ember.computed('height', 'totalHeight', function(){
var totalHeight, viewportHeight;
totalHeight = get(this, 'totalHeight'),
totalHeight = get(this, 'totalHeight');
viewportHeight = get(this, 'height');
return max(0, totalHeight - viewportHeight);
@ -665,7 +699,7 @@ Ember.ListViewMixin = Ember.Mixin.create({
}
}, 'content'),
/**
/**),
@private
@event contentDidChange
*/
@ -768,7 +802,7 @@ Ember.ListViewMixin = Ember.Mixin.create({
scrollTop = get(this, 'scrollTop');
contentLength = get(this, 'content.length');
maxContentIndex = max(contentLength - 1, 0);
childViews = get(this, 'listItemViews');
childViews = this._childViews;
childViewsLength = childViews.length;
startingIndex = this._startingIndex();
@ -786,24 +820,12 @@ Ember.ListViewMixin = Ember.Mixin.create({
}
},
/**
@private
Returns an array of current ListItemView views in the visible area
when you start to scroll.
@property {Ember.ComputedProperty} listItemViews
*/
listItemViews: Ember.computed('[]', function(){
return this.filter(detectListItemViews);
}),
/**
@private
@method positionOrderedChildViews
*/
positionOrderedChildViews: function() {
return get(this, 'listItemViews').sort(sortByContentIndex);
return this._childViews.sort(sortByContentIndex);
},
arrayWillChange: Ember.K,
@ -944,13 +966,7 @@ Ember.ListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
'overflow-scrolling': 'touch'
},
applyTransform: function(element, position){
var x = position.x,
y = position.y;
element.style.top = y + 'px';
element.style.left = x + 'px';
},
applyTransform: Ember.ListViewHelper.applyTransform,
_scrollTo: function(scrollTop) {
var element = get(this, 'element');
@ -1006,6 +1022,148 @@ Ember.ListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
(function() {
var fieldRegex = /input|textarea|select/i,
hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
handleStart, handleMove, handleEnd, handleCancel,
startEvent, moveEvent, endEvent, cancelEvent;
if (hasTouch) {
startEvent = 'touchstart';
handleStart = function (e) {
var touch = e.touches[0],
target = touch && touch.target;
// avoid e.preventDefault() on fields
if (target && fieldRegex.test(target.tagName)) {
return;
}
bindWindow(this.scrollerEventHandlers);
this.willBeginScroll(e.touches, e.timeStamp);
e.preventDefault();
};
moveEvent = 'touchmove';
handleMove = function (e) {
this.continueScroll(e.touches, e.timeStamp);
};
endEvent = 'touchend';
handleEnd = function (e) {
// if we didn't end up scrolling we need to
// synthesize click since we did e.preventDefault()
// on touchstart
if (!this._isScrolling) {
synthesizeClick(e);
}
unbindWindow(this.scrollerEventHandlers);
this.endScroll(e.timeStamp);
};
cancelEvent = 'touchcancel';
handleCancel = function (e) {
unbindWindow(this.scrollerEventHandlers);
this.endScroll(e.timeStamp);
};
} else {
startEvent = 'mousedown';
handleStart = function (e) {
if (e.which !== 1) return;
var target = e.target;
// avoid e.preventDefault() on fields
if (target && fieldRegex.test(target.tagName)) {
return;
}
bindWindow(this.scrollerEventHandlers);
this.willBeginScroll([e], e.timeStamp);
e.preventDefault();
};
moveEvent = 'mousemove';
handleMove = function (e) {
this.continueScroll([e], e.timeStamp);
};
endEvent = 'mouseup';
handleEnd = function (e) {
unbindWindow(this.scrollerEventHandlers);
this.endScroll(e.timeStamp);
};
cancelEvent = 'mouseout';
handleCancel = function (e) {
if (e.relatedTarget) return;
unbindWindow(this.scrollerEventHandlers);
this.endScroll(e.timeStamp);
};
}
function handleWheel(e) {
this.mouseWheel(e);
e.preventDefault();
}
function bindElement(el, handlers) {
el.addEventListener(startEvent, handlers.start, false);
el.addEventListener('mousewheel', handlers.wheel, false);
}
function unbindElement(el, handlers) {
el.removeEventListener(startEvent, handlers.start, false);
el.removeEventListener('mousewheel', handlers.wheel, false);
}
function bindWindow(handlers) {
window.addEventListener(moveEvent, handlers.move, true);
window.addEventListener(endEvent, handlers.end, true);
window.addEventListener(cancelEvent, handlers.cancel, true);
}
function unbindWindow(handlers) {
window.removeEventListener(moveEvent, handlers.move, true);
window.removeEventListener(endEvent, handlers.end, true);
window.removeEventListener(cancelEvent, handlers.cancel, true);
}
Ember.VirtualListScrollerEvents = Ember.Mixin.create({
init: function() {
this.on('didInsertElement', this, 'bindScrollerEvents');
this.on('willDestroyElement', this, 'unbindScrollerEvents');
this.scrollerEventHandlers = {
start: bind(this, handleStart),
move: bind(this, handleMove),
end: bind(this, handleEnd),
cancel: bind(this, handleCancel),
wheel: bind(this, handleWheel)
};
return this._super();
},
bindScrollerEvents: function() {
var el = this.get('element'),
handlers = this.scrollerEventHandlers;
bindElement(el, handlers);
},
unbindScrollerEvents: function() {
var el = this.get('element'),
handlers = this.scrollerEventHandlers;
unbindElement(el, handlers);
unbindWindow(handlers);
}
});
function bind(view, handler) {
return function (evt) {
handler.call(view, evt);
};
}
function synthesizeClick(e) {
var point = e.changedTouches[0],
target = point.target,
ev;
if (target && fieldRegex.test(target.tagName)) {
ev = document.createEvent('MouseEvents');
ev.initMouseEvent('click', true, true, e.view, 1, point.screenX, point.screenY, point.clientX, point.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null);
return target.dispatchEvent(ev);
}
}
})();
(function() {
/*global Scroller*/
var max = Math.max, get = Ember.get, set = Ember.set;
@ -1029,8 +1187,9 @@ function updateScrollerDimensions(target) {
@class VirtualListView
@namespace Ember
*/
Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, Ember.VirtualListScrollerEvents, {
_isScrolling: false,
_mouseWheel: null,
css: {
position: 'relative',
overflow: 'hidden'
@ -1041,7 +1200,7 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
this.setupScroller();
},
_scrollerTop: 0,
applyTransform: Ember.ListViewHelper.applyTransform,
applyTransform: Ember.ListViewHelper.apply3DTransform,
setupScroller: function(){
var view, y;
@ -1052,7 +1211,7 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
if (view.state !== 'inDOM') { return; }
if (view.listContainerElement) {
view.applyTransform(view.listContainerElement, {x: 0, y: -top});
view.applyTransform(view.listContainerElement, 0, -top);
view._scrollerTop = top;
view._scrollContentTo(top);
}
@ -1072,17 +1231,7 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
}, 'width', 'height', 'totalHeight'),
didInsertElement: function() {
var that, listContainerElement;
that = this;
this.listContainerElement = this.$('> .ember-list-container')[0];
this._mouseWheel = function(e) { that.mouseWheel(e); };
this.$().on('mousewheel', this._mouseWheel);
},
willDestroyElement: function() {
this.$().off('mousewheel', this._mouseWheel);
},
willBeginScroll: function(touches, timeStamp) {
@ -1113,6 +1262,10 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
}
},
endScroll: function(timeStamp) {
this.scroller.doTouchEnd(timeStamp);
},
// api
scrollTo: function(y, animate) {
if (animate === undefined) {
@ -1134,48 +1287,6 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
this.scroller.scrollBy(0, delta, true);
}
return false;
},
endScroll: function(timeStamp) {
this.scroller.doTouchEnd(timeStamp);
},
touchStart: function(e){
e = e.originalEvent || e;
this.willBeginScroll(e.touches, e.timeStamp);
return false;
},
touchMove: function(e){
e = e.originalEvent || e;
this.continueScroll(e.touches, e.timeStamp);
return false;
},
touchEnd: function(e){
e = e.originalEvent || e;
this.endScroll(e.timeStamp);
return false;
},
mouseDown: function(e){
this.willBeginScroll([e], e.timeStamp);
return false;
},
mouseMove: function(e){
this.continueScroll([e], e.timeStamp);
return false;
},
mouseUp: function(e){
this.endScroll(e.timeStamp);
return false;
},
mouseLeave: function(e){
this.endScroll(e.timeStamp);
return false;
}
});
@ -1187,3 +1298,4 @@ Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
(function() {
})();

Some files were not shown because too many files have changed in this diff Show More