mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 15:48:43 -05:00
ES6ify some of the remaining files
This commit is contained in:
parent
378087727f
commit
b7e6eaa961
96 changed files with 684 additions and 720 deletions
|
@ -1,13 +1,14 @@
|
|||
import { bufferedProperty } from 'discourse/mixins/buffered-content';
|
||||
import UserField from 'admin/models/user-field';
|
||||
import { bufferedProperty } from 'discourse/mixins/buffered-content';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Component.extend(bufferedProperty('userField'), {
|
||||
editing: Ember.computed.empty('userField.id'),
|
||||
classNameBindings: [':user-field'],
|
||||
|
||||
cantMoveUp: Discourse.computed.propertyEqual('userField', 'firstField'),
|
||||
cantMoveDown: Discourse.computed.propertyEqual('userField', 'lastField'),
|
||||
cantMoveUp: propertyEqual('userField', 'firstField'),
|
||||
cantMoveDown: propertyEqual('userField', 'lastField'),
|
||||
|
||||
userFieldsDescription: function() {
|
||||
return I18n.t('admin.user_fields.description');
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import ScrollTop from 'discourse/mixins/scroll-top';
|
||||
import SiteSetting from 'admin/models/site-setting';
|
||||
import { propertyNotEqual } from 'discourse/lib/computed';
|
||||
|
||||
const CustomTypes = ['bool', 'enum', 'list', 'url_list', 'host_list'];
|
||||
|
||||
export default Ember.Component.extend(BufferedContent, ScrollTop, {
|
||||
classNameBindings: [':row', ':setting', 'setting.overridden', 'typeClass'],
|
||||
content: Ember.computed.alias('setting'),
|
||||
dirty: Discourse.computed.propertyNotEqual('buffered.value', 'setting.value'),
|
||||
dirty: propertyNotEqual('buffered.value', 'setting.value'),
|
||||
validationMessage: null,
|
||||
|
||||
preview: function() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import { propertyNotEqual } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.ObjectController.extend(BufferedContent, {
|
||||
needs: ['admin-badges'],
|
||||
|
@ -12,7 +13,7 @@ export default Ember.ObjectController.extend(BufferedContent, {
|
|||
protectedSystemFields: Em.computed.alias('controllers.admin-badges.protectedSystemFields'),
|
||||
|
||||
readOnly: Ember.computed.alias('buffered.system'),
|
||||
showDisplayName: Discourse.computed.propertyNotEqual('name', 'displayName'),
|
||||
showDisplayName: propertyNotEqual('name', 'displayName'),
|
||||
canEditDescription: Em.computed.none('buffered.translatedDescription'),
|
||||
|
||||
_resetSaving: function() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
const sections = ['css', 'header', 'top', 'footer', 'head-tag', 'body-tag',
|
||||
'mobile-css', 'mobile-header', 'mobile-top', 'mobile-footer',
|
||||
'embedded-css'];
|
||||
|
@ -12,8 +14,8 @@ export default Ember.Controller.extend(activeSections, {
|
|||
maximized: false,
|
||||
section: null,
|
||||
|
||||
previewUrl: Discourse.computed.url("model.key", "/?preview-style=%@"),
|
||||
downloadUrl: Discourse.computed.url('model.id', '/admin/size_customizations/%@'),
|
||||
previewUrl: url("model.key", "/?preview-style=%@"),
|
||||
downloadUrl: url('model.id', '/admin/size_customizations/%@'),
|
||||
|
||||
mobile: function() {
|
||||
return this.get('section').startsWith('mobile-');
|
||||
|
@ -33,8 +35,8 @@ export default Ember.Controller.extend(activeSections, {
|
|||
|
||||
needs: ['adminCustomizeCssHtml'],
|
||||
|
||||
undoPreviewUrl: Discourse.computed.url('/?preview-style='),
|
||||
defaultStyleUrl: Discourse.computed.url('/?preview-style=default'),
|
||||
undoPreviewUrl: url('/?preview-style='),
|
||||
defaultStyleUrl: url('/?preview-style=default'),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
/**
|
||||
This controller supports the default interface when you enter the admin section.
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
@class AdminDashboardController
|
||||
@extends Ember.Controller
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
// This controller supports the default interface when you enter the admin section.
|
||||
export default Ember.Controller.extend({
|
||||
loading: true,
|
||||
versionCheck: null,
|
||||
problemsCheckMinutes: 1,
|
||||
|
||||
showVersionChecks: Discourse.computed.setting('version_checks'),
|
||||
showVersionChecks: setting('version_checks'),
|
||||
|
||||
foundProblems: function() {
|
||||
return(Discourse.User.currentProp('admin') && this.get('problems') && this.get('problems').length > 0);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
|
||||
export default Em.ObjectController.extend({
|
||||
needs: ['adminGroupsType'],
|
||||
|
@ -15,7 +16,7 @@ export default Em.ObjectController.extend({
|
|||
}.property("limit", "user_count"),
|
||||
|
||||
showingFirst: Em.computed.lte("currentPage", 1),
|
||||
showingLast: Discourse.computed.propertyEqual("currentPage", "totalPages"),
|
||||
showingLast: propertyEqual("currentPage", "totalPages"),
|
||||
|
||||
aliasLevelOptions: function() {
|
||||
return [
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import ObjectController from 'discourse/controllers/object';
|
||||
import CanCheckEmails from 'discourse/mixins/can-check-emails';
|
||||
import { propertyNotEqual, setting } from 'discourse/lib/computed';
|
||||
|
||||
export default ObjectController.extend(CanCheckEmails, {
|
||||
editingTitle: false,
|
||||
originalPrimaryGroupId: null,
|
||||
availableGroups: null,
|
||||
|
||||
showApproval: Discourse.computed.setting('must_approve_users'),
|
||||
showBadges: Discourse.computed.setting('enable_badges'),
|
||||
showApproval: setting('must_approve_users'),
|
||||
showBadges: setting('enable_badges'),
|
||||
|
||||
primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
|
||||
primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
|
||||
|
||||
automaticGroups: function() {
|
||||
return this.get("model.automaticGroups").map((g) => g.name).join(", ");
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { i18n } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
query: null,
|
||||
showEmails: false,
|
||||
|
@ -9,7 +11,7 @@ export default Ember.ArrayController.extend({
|
|||
queryPending: Em.computed.equal('query', 'pending'),
|
||||
queryHasApproval: Em.computed.or('queryNew', 'queryPending'),
|
||||
showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'),
|
||||
searchHint: Discourse.computed.i18n('search_hint'),
|
||||
searchHint: i18n('search_hint'),
|
||||
hasSelection: Em.computed.gt('selectedCount', 0),
|
||||
|
||||
selectedCount: function() {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { propertyNotEqual } from 'discourse/lib/computed';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
const AdminUser = Discourse.User.extend({
|
||||
|
@ -144,7 +145,7 @@ const AdminUser = Discourse.User.extend({
|
|||
this.set('originalTrustLevel', this.get('trust_level'));
|
||||
},
|
||||
|
||||
dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
|
||||
dirty: propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
|
||||
|
||||
saveTrustLevel() {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/trust_level", {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import round from "discourse/lib/round";
|
||||
import { fmt } from 'discourse/lib/computed';
|
||||
|
||||
const Report = Discourse.Model.extend({
|
||||
reportUrl: Discourse.computed.fmt("type", "/admin/reports/%@"),
|
||||
reportUrl: fmt("type", "/admin/reports/%@"),
|
||||
|
||||
valueAt(numDaysAgo) {
|
||||
if (this.data) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import { i18n } from 'discourse/lib/computed';
|
||||
|
||||
const UserField = RestModel.extend();
|
||||
|
||||
const UserFieldType = Ember.Object.extend({
|
||||
name: Discourse.computed.i18n('id', 'admin.user_fields.field_types.%@')
|
||||
name: i18n('id', 'admin.user_fields.field_types.%@')
|
||||
});
|
||||
|
||||
UserField.reopenClass({
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import StringBuffer from 'discourse/mixins/string-buffer';
|
||||
import { iconHTML } from 'discourse/helpers/fa-icon';
|
||||
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
export default Ember.Component.extend(StringBuffer, {
|
||||
tagName: 'section',
|
||||
|
@ -57,7 +58,7 @@ export default Ember.Component.extend(StringBuffer, {
|
|||
buffer.push("<div class='post-action'>" +
|
||||
iconHTML('fa-trash-o') + ' ' +
|
||||
Discourse.Utilities.tinyAvatar(post.get('postDeletedBy.avatar_template'), {title: post.get('postDeletedBy.username')}) +
|
||||
Discourse.Formatter.autoUpdatingRelativeAge(new Date(post.get('postDeletedAt'))) +
|
||||
autoUpdatingRelativeAge(new Date(post.get('postDeletedAt'))) +
|
||||
"</div>");
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
var get = Ember.get;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: ['category::no-category', 'categories:has-drop','categoryStyle'],
|
||||
categoryStyle: Discourse.computed.setting('category_style'),
|
||||
categoryStyle: setting('category_style'),
|
||||
|
||||
tagName: 'li',
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import { buildCategoryPanel } from 'discourse/components/edit-category-panel';
|
||||
|
||||
export default buildCategoryPanel('settings', {
|
||||
emailInEnabled: Discourse.computed.setting('email_in'),
|
||||
showPositionInput: Discourse.computed.setting('fixed_category_positions'),
|
||||
emailInEnabled: setting('email_in'),
|
||||
showPositionInput: setting('fixed_category_positions'),
|
||||
});
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
|
||||
export default Em.Component.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active', 'tabClassName'],
|
||||
|
@ -6,7 +8,7 @@ export default Em.Component.extend({
|
|||
return 'edit-category-' + this.get('tab');
|
||||
}.property('tab'),
|
||||
|
||||
active: Discourse.computed.propertyEqual('selectedTab', 'tab'),
|
||||
active: propertyEqual('selectedTab', 'tab'),
|
||||
|
||||
title: function() {
|
||||
return I18n.t('category.' + this.get('tab').replace('-', '_'));
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["title"],
|
||||
|
||||
|
@ -13,10 +15,10 @@ export default Ember.Component.extend({
|
|||
return Discourse.Mobile.mobileView && !Ember.isBlank(this.get('mobileBigLogoUrl'));
|
||||
}.property(),
|
||||
|
||||
smallLogoUrl: Discourse.computed.setting('logo_small_url'),
|
||||
bigLogoUrl: Discourse.computed.setting('logo_url'),
|
||||
mobileBigLogoUrl: Discourse.computed.setting('mobile_logo_url'),
|
||||
title: Discourse.computed.setting('title'),
|
||||
smallLogoUrl: setting('logo_small_url'),
|
||||
bigLogoUrl: setting('logo_url'),
|
||||
mobileBigLogoUrl: setting('mobile_logo_url'),
|
||||
title: setting('title'),
|
||||
|
||||
click: function(e) {
|
||||
// if they want to open in a new tab, let it so
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
const PosterNameComponent = Em.Component.extend({
|
||||
classNames: ['names', 'trigger-user-card'],
|
||||
displayNameOnPosts: Discourse.computed.setting('display_name_on_posts'),
|
||||
displayNameOnPosts: setting('display_name_on_posts'),
|
||||
|
||||
// sanitize name for comparison
|
||||
sanitizeName(name){
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { relativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
const icons = {
|
||||
'closed.enabled': 'lock',
|
||||
'closed.disabled': 'unlock-alt',
|
||||
|
@ -19,7 +21,7 @@ export function actionDescription(actionCode, createdAt) {
|
|||
const ac = this.get(actionCode);
|
||||
if (ac) {
|
||||
const dt = new Date(this.get(createdAt));
|
||||
const when = Discourse.Formatter.relativeAge(dt, {format: 'medium-with-ago'});
|
||||
const when = relativeAge(dt, {format: 'medium-with-ago'});
|
||||
return I18n.t(`action_codes.${ac}`, {when}).htmlSafe();
|
||||
}
|
||||
}.property(actionCode, createdAt);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import { actionDescription } from "discourse/components/small-action";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [":item", "item.hidden", "item.deleted", "moderatorAction"],
|
||||
moderatorAction: Discourse.computed.propertyEqual("item.post_type", "site.post_types.moderator_action"),
|
||||
moderatorAction: propertyEqual("item.post_type", "site.post_types.moderator_action"),
|
||||
actionDescription: actionDescription("item.action_code", "item.created_at"),
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { fmt } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':user-field', 'field.field_type'],
|
||||
layoutName: Discourse.computed.fmt('field.field_type', 'components/user-fields/%@'),
|
||||
layoutName: fmt('field.field_type', 'components/user-fields/%@'),
|
||||
|
||||
noneLabel: function() {
|
||||
if (!this.get('field.required')) {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['user-small'],
|
||||
|
||||
userPath: Discourse.computed.url('user.username', '/users/%@'),
|
||||
userPath: url('user.username', '/users/%@'),
|
||||
|
||||
name: function() {
|
||||
const name = this.get('user.name');
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import Presence from 'discourse/mixins/presence';
|
||||
|
||||
export default Ember.ObjectController.extend(Presence, {
|
||||
|
@ -8,7 +9,7 @@ export default Ember.ObjectController.extend(Presence, {
|
|||
|
||||
showEditReason: false,
|
||||
editReason: null,
|
||||
maxTitleLength: Discourse.computed.setting('max_topic_title_length'),
|
||||
maxTitleLength: setting('max_topic_title_length'),
|
||||
scopedCategoryId: null,
|
||||
similarTopics: null,
|
||||
similarTopicsMessage: null,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import DiscourseController from 'discourse/controllers/controller';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
export default DiscourseController.extend(ModalFunctionality, {
|
||||
needs: ['login'],
|
||||
|
@ -16,10 +17,10 @@ export default DiscourseController.extend(ModalFunctionality, {
|
|||
userFields: null,
|
||||
|
||||
hasAuthOptions: Em.computed.notEmpty('authOptions'),
|
||||
canCreateLocal: Discourse.computed.setting('enable_local_logins'),
|
||||
canCreateLocal: setting('enable_local_logins'),
|
||||
showCreateForm: Em.computed.or('hasAuthOptions', 'canCreateLocal'),
|
||||
maxUsernameLength: Discourse.computed.setting('max_username_length'),
|
||||
minUsernameLength: Discourse.computed.setting('min_username_length'),
|
||||
maxUsernameLength: setting('max_username_length'),
|
||||
minUsernameLength: setting('min_username_length'),
|
||||
|
||||
resetForm() {
|
||||
// We wrap the fields in a structure so we can assign a value
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
me: Discourse.computed.propertyEqual('model.user.id', 'currentUser.id')
|
||||
me: propertyEqual('model.user.id', 'currentUser.id')
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import DiscoveryController from 'discourse/controllers/discovery';
|
||||
import { queryParams } from 'discourse/controllers/discovery-sortable';
|
||||
import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection';
|
||||
import { endWith } from 'discourse/lib/computed';
|
||||
|
||||
const controllerOpts = {
|
||||
needs: ['discovery'],
|
||||
|
@ -102,8 +103,8 @@ const controllerOpts = {
|
|||
|
||||
hasTopics: Em.computed.gt('model.topics.length', 0),
|
||||
allLoaded: Em.computed.empty('model.more_topics_url'),
|
||||
latest: Discourse.computed.endWith('model.filter', 'latest'),
|
||||
new: Discourse.computed.endWith('model.filter', 'new'),
|
||||
latest: endWith('model.filter', 'latest'),
|
||||
new: endWith('model.filter', 'new'),
|
||||
top: Em.computed.notEmpty('period'),
|
||||
yearly: Em.computed.equal('period', 'yearly'),
|
||||
quarterly: Em.computed.equal('period', 'quarterly'),
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import ObjectController from 'discourse/controllers/object';
|
||||
|
||||
// The basic controller for a group
|
||||
export default ObjectController.extend({
|
||||
export default Ember.Controller.extend({
|
||||
counts: null,
|
||||
showing: null,
|
||||
|
||||
|
@ -10,4 +7,3 @@ export default ObjectController.extend({
|
|||
showingIndex: Em.computed.equal('showing', 'index'),
|
||||
showingMembers: Em.computed.equal('showing', 'members')
|
||||
});
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
export default Ember.ObjectController.extend({
|
||||
export default Ember.Controller.extend({
|
||||
loading: false,
|
||||
limit: null,
|
||||
offset: null,
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
if (this.get("loading")) { return; }
|
||||
// we've reached the end
|
||||
if (this.get("model.members.length") >= this.get("user_count")) { return; }
|
||||
if (this.get("model.members.length") >= this.get("model.user_count")) { return; }
|
||||
|
||||
this.set("loading", true);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import DiscourseController from 'discourse/controllers/controller';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
// This is happening outside of the app via popup
|
||||
const AuthErrors =
|
||||
|
@ -13,7 +14,7 @@ export default DiscourseController.extend(ModalFunctionality, {
|
|||
loggingIn: false,
|
||||
loggedIn: false,
|
||||
|
||||
canLoginLocal: Discourse.computed.setting('enable_local_logins'),
|
||||
canLoginLocal: setting('enable_local_logins'),
|
||||
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
|
||||
|
||||
resetForm: function() {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import NavigationDefaultController from 'discourse/controllers/navigation/default';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
export default NavigationDefaultController.extend({
|
||||
subcategoryListSetting: Discourse.computed.setting('show_subcategory_list'),
|
||||
subcategoryListSetting: setting('show_subcategory_list'),
|
||||
showingParentCategory: Em.computed.none('category.parentCategory'),
|
||||
showingSubcategoryList: Em.computed.and('subcategoryListSetting', 'showingParentCategory'),
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
needs: ['header'],
|
||||
loadingNotifications: Em.computed.alias('controllers.header.loadingNotifications'),
|
||||
myNotificationsUrl: Discourse.computed.url('/my/notifications')
|
||||
myNotificationsUrl: url('/my/notifications')
|
||||
});
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import ObjectController from 'discourse/controllers/object';
|
||||
import CanCheckEmails from 'discourse/mixins/can-check-emails';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default ObjectController.extend(CanCheckEmails, {
|
||||
|
||||
allowAvatarUpload: Discourse.computed.setting('allow_uploaded_avatars'),
|
||||
allowUserLocale: Discourse.computed.setting('allow_user_locale'),
|
||||
ssoOverridesAvatar: Discourse.computed.setting('sso_overrides_avatar'),
|
||||
allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'),
|
||||
editHistoryVisible: Discourse.computed.setting('edit_history_visible_to_public'),
|
||||
allowAvatarUpload: setting('allow_uploaded_avatars'),
|
||||
allowUserLocale: setting('allow_user_locale'),
|
||||
ssoOverridesAvatar: setting('sso_overrides_avatar'),
|
||||
allowBackgrounds: setting('allow_profile_backgrounds'),
|
||||
editHistoryVisible: setting('edit_history_visible_to_public'),
|
||||
|
||||
selectedCategories: function(){
|
||||
return [].concat(this.get("model.watchedCategories"),
|
||||
|
@ -40,7 +41,7 @@ export default ObjectController.extend(CanCheckEmails, {
|
|||
cannotDeleteAccount: Em.computed.not('can_delete_account'),
|
||||
deleteDisabled: Em.computed.or('saving', 'deleting', 'cannotDeleteAccount'),
|
||||
|
||||
canEditName: Discourse.computed.setting('enable_names'),
|
||||
canEditName: setting('enable_names'),
|
||||
|
||||
nameInstructions: function() {
|
||||
return I18n.t(Discourse.SiteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import ObjectController from 'discourse/controllers/object';
|
||||
|
||||
/**
|
||||
This controller supports actions related to updating one's email address
|
||||
|
||||
@class PreferencesEmailController
|
||||
@extends ObjectController
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
export default ObjectController.extend({
|
||||
taken: false,
|
||||
saving: false,
|
||||
|
@ -17,7 +10,7 @@ export default ObjectController.extend({
|
|||
|
||||
newEmailEmpty: Em.computed.empty('newEmail'),
|
||||
saveDisabled: Em.computed.or('saving', 'newEmailEmpty', 'taken', 'unchanged'),
|
||||
unchanged: Discourse.computed.propertyEqual('newEmailLower', 'email'),
|
||||
unchanged: propertyEqual('newEmailLower', 'email'),
|
||||
|
||||
newEmailLower: function() {
|
||||
return this.get('newEmail').toLowerCase();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { setting, propertyEqual } from 'discourse/lib/computed';
|
||||
import Presence from 'discourse/mixins/presence';
|
||||
import ObjectController from 'discourse/controllers/object';
|
||||
|
||||
|
@ -8,11 +9,11 @@ export default ObjectController.extend(Presence, {
|
|||
errorMessage: null,
|
||||
newUsername: null,
|
||||
|
||||
maxLength: Discourse.computed.setting('max_username_length'),
|
||||
minLength: Discourse.computed.setting('min_username_length'),
|
||||
maxLength: setting('max_username_length'),
|
||||
minLength: setting('min_username_length'),
|
||||
newUsernameEmpty: Em.computed.empty('newUsername'),
|
||||
saveDisabled: Em.computed.or('saving', 'newUsernameEmpty', 'taken', 'unchanged', 'errorMessage'),
|
||||
unchanged: Discourse.computed.propertyEqual('newUsername', 'username'),
|
||||
unchanged: propertyEqual('newUsername', 'username'),
|
||||
|
||||
checkTaken: function() {
|
||||
if( this.get('newUsername') && this.get('newUsername').length < this.get('minLength') ) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
|
@ -21,7 +22,7 @@ export default Ember.Controller.extend(BufferedContent, {
|
|||
post: Ember.computed.alias('model'),
|
||||
currentlyEditing: Ember.computed.alias('controllers.queued-posts.editing'),
|
||||
|
||||
editing: Discourse.computed.propertyEqual('model', 'currentlyEditing'),
|
||||
editing: propertyEqual('model', 'currentlyEditing'),
|
||||
|
||||
_confirmDelete: updateState('rejected', {deleteUser: true}),
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import Sharing from 'discourse/lib/sharing';
|
||||
import { longDateNoYear } from 'discourse/lib/formatter';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
needs: ['topic'],
|
||||
title: Ember.computed.alias('controllers.topic.model.title'),
|
||||
|
||||
displayDate: function() {
|
||||
return Discourse.Formatter.longDateNoYear(new Date(this.get('date')));
|
||||
return longDateNoYear(new Date(this.get('date')));
|
||||
}.property('date'),
|
||||
|
||||
// Close the share controller
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
needs: ['application', 'header'],
|
||||
|
||||
|
@ -8,7 +10,7 @@ export default Ember.ArrayController.extend({
|
|||
return Discourse.SiteSettings.faq_url ? Discourse.SiteSettings.faq_url : Discourse.getURL('/faq');
|
||||
}.property(),
|
||||
|
||||
badgesUrl: Discourse.computed.url('/badges'),
|
||||
badgesUrl: url('/badges'),
|
||||
|
||||
showKeyboardShortcuts: function(){
|
||||
return !Discourse.Mobile.mobileView && !this.capabilities.touch;
|
||||
|
|
|
@ -3,6 +3,7 @@ import BufferedContent from 'discourse/mixins/buffered-content';
|
|||
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
|
||||
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
|
||||
import Topic from 'discourse/models/topic';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
export default ObjectController.extend(SelectedPostsCount, BufferedContent, {
|
||||
multiSelect: false,
|
||||
|
@ -18,7 +19,7 @@ export default ObjectController.extend(SelectedPostsCount, BufferedContent, {
|
|||
firstPostExpanded: false,
|
||||
retrying: false,
|
||||
|
||||
maxTitleLength: Discourse.computed.setting('max_topic_title_length'),
|
||||
maxTitleLength: setting('max_topic_title_length'),
|
||||
|
||||
contextChanged: function() {
|
||||
this.set('controllers.search.searchContext', this.get('model.searchContext'));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import DiscourseController from 'discourse/controllers/controller';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
|
||||
export default DiscourseController.extend(ModalFunctionality, {
|
||||
remote: Em.computed.not("local"),
|
||||
|
@ -13,7 +14,7 @@ export default DiscourseController.extend(ModalFunctionality, {
|
|||
});
|
||||
}.on('init'),
|
||||
|
||||
maxSize: Discourse.computed.setting('max_attachment_size_kb'),
|
||||
maxSize: setting('max_attachment_size_kb'),
|
||||
allowLocal: Em.computed.gt('maxSize', 0),
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { propertyNotEqual, setting } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
needs: ['topic', 'application'],
|
||||
visible: false,
|
||||
|
@ -16,10 +18,10 @@ export default Ember.Controller.extend({
|
|||
viewingTopic: Em.computed.match('controllers.application.currentPath', /^topic\./),
|
||||
viewingAdmin: Em.computed.match('controllers.application.currentPath', /^admin\./),
|
||||
showFilter: Em.computed.and('viewingTopic', 'postStream.hasNoFilters', 'enoughPostsForFiltering'),
|
||||
showName: Discourse.computed.propertyNotEqual('user.name', 'user.username'),
|
||||
showName: propertyNotEqual('user.name', 'user.username'),
|
||||
hasUserFilters: Em.computed.gt('postStream.userFilters.length', 0),
|
||||
isSuspended: Em.computed.notEmpty('user.suspend_reason'),
|
||||
showBadges: Discourse.computed.setting('enable_badges'),
|
||||
showBadges: setting('enable_badges'),
|
||||
showMoreBadges: Em.computed.gt('moreBadgesCount', 0),
|
||||
showDelete: Em.computed.and("viewingAdmin", "showName", "user.canBeDeleted"),
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default Ember.ObjectController.extend({
|
||||
export default Ember.Controller.extend({
|
||||
needs: ["application"],
|
||||
|
||||
_showFooter: function() {
|
||||
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
|
||||
}.observes("canLoadMore")
|
||||
this.set("controllers.application.showFooter", !this.get("model.canLoadMore"));
|
||||
}.observes("model.canLoadMore")
|
||||
});
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
var safe = Handlebars.SafeString;
|
||||
import registerUnbound from 'discourse/helpers/register-unbound';
|
||||
import avatarTemplate from 'discourse/lib/avatar-template';
|
||||
import { longDate, autoUpdatingRelativeAge, number } from 'discourse/lib/formatter';
|
||||
|
||||
// TODO: Remove me when ES6ified
|
||||
var registerUnbound = require('discourse/helpers/register-unbound', null, null, true).default;
|
||||
var avatarTemplate = require('discourse/lib/avatar-template', null, null, true).default;
|
||||
const safe = Handlebars.SafeString;
|
||||
|
||||
Em.Handlebars.helper('bound-avatar', function(user, size, uploadId) {
|
||||
if (Em.isEmpty(user)) {
|
||||
return new safe("<div class='avatar-placeholder'></div>");
|
||||
}
|
||||
|
||||
var username = Em.get(user, 'username');
|
||||
const username = Em.get(user, 'username');
|
||||
if (arguments.length < 4) { uploadId = Em.get(user, 'uploaded_avatar_id'); }
|
||||
var avatar = Em.get(user, 'avatar_template') || avatarTemplate(username, uploadId);
|
||||
const avatar = Em.get(user, 'avatar_template') || avatarTemplate(username, uploadId);
|
||||
|
||||
return new safe(Discourse.Utilities.avatarImg({ size: size, avatarTemplate: avatar }));
|
||||
}, 'username', 'uploaded_avatar_id', 'avatar_template');
|
||||
|
@ -24,30 +24,30 @@ Em.Handlebars.helper('bound-avatar-template', function(avatarTemplate, size) {
|
|||
});
|
||||
|
||||
registerUnbound('raw-date', function(dt) {
|
||||
return Discourse.Formatter.longDate(new Date(dt));
|
||||
return longDate(new Date(dt));
|
||||
});
|
||||
|
||||
registerUnbound('age-with-tooltip', function(dt) {
|
||||
return new safe(Discourse.Formatter.autoUpdatingRelativeAge(new Date(dt), {title: true}));
|
||||
return new safe(autoUpdatingRelativeAge(new Date(dt), {title: true}));
|
||||
});
|
||||
|
||||
registerUnbound('number', function(orig, params) {
|
||||
orig = parseInt(orig, 10);
|
||||
if (isNaN(orig)) { orig = 0; }
|
||||
|
||||
var title = orig;
|
||||
let title = orig;
|
||||
if (params.numberKey) {
|
||||
title = I18n.t(params.numberKey, { number: orig });
|
||||
}
|
||||
|
||||
var classNames = 'number';
|
||||
let classNames = 'number';
|
||||
if (params['class']) {
|
||||
classNames += ' ' + params['class'];
|
||||
}
|
||||
var result = "<span class='" + classNames + "'";
|
||||
let result = "<span class='" + classNames + "'";
|
||||
|
||||
// Round off the thousands to one decimal place
|
||||
var n = Discourse.Formatter.number(orig);
|
||||
const n = number(orig);
|
||||
if (n !== title) {
|
||||
result += " title='" + Handlebars.Utils.escapeExpression(title) + "'";
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(dt) {
|
||||
return new Handlebars.SafeString(Discourse.Formatter.autoUpdatingRelativeAge(new Date(dt), {format: 'medium', title: true }));
|
||||
return new Handlebars.SafeString(autoUpdatingRelativeAge(new Date(dt), {format: 'medium', title: true }));
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
|
||||
import registerUnbound from 'discourse/helpers/register-unbound';
|
||||
|
||||
registerUnbound('format-age', function(dt) {
|
||||
dt = new Date(dt);
|
||||
return new Handlebars.SafeString(Discourse.Formatter.autoUpdatingRelativeAge(dt));
|
||||
return new Handlebars.SafeString(autoUpdatingRelativeAge(dt));
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import registerUnbound from 'discourse/helpers/register-unbound';
|
||||
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
/**
|
||||
Display logic for dates. It is unbound in Ember but will use jQuery to
|
||||
|
@ -21,6 +22,6 @@ registerUnbound('format-date', function(val, params) {
|
|||
|
||||
if (val) {
|
||||
var date = new Date(val);
|
||||
return new Handlebars.SafeString(Discourse.Formatter.autoUpdatingRelativeAge(date, {format: format, title: title, leaveAgo: leaveAgo}));
|
||||
return new Handlebars.SafeString(autoUpdatingRelativeAge(date, {format: format, title: title, leaveAgo: leaveAgo}));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,5 +7,28 @@ export default {
|
|||
require(entry, null, null, true);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Once things have migrated remove these
|
||||
if (!Discourse.hasOwnProperty('computed')) {
|
||||
const computed = require('discourse/lib/computed');
|
||||
Object.defineProperty(Discourse, 'computed', {
|
||||
get: function() {
|
||||
Ember.warn('DEPRECATION: `Discourse.computed` is deprecated, import the functions as needed.');
|
||||
return computed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Discourse.hasOwnProperty('Formatter')) {
|
||||
const Formatter = require('discourse/lib/formatter');
|
||||
Object.defineProperty(Discourse, 'Formatter', {
|
||||
get: function() {
|
||||
Ember.warn('DEPRECATION: `Discourse.Formatter` is deprecated, import the formatters as needed.');
|
||||
return Formatter;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { cleanDOM } from 'discourse/routes/discourse';
|
||||
import PageTracker from 'discourse/lib/page-tracker';
|
||||
|
||||
export default {
|
||||
name: "page-tracking",
|
||||
after: 'register-discourse-location',
|
||||
|
||||
initialize: function(container) {
|
||||
initialize(container) {
|
||||
|
||||
// Tell our AJAX system to track a page transition
|
||||
var router = container.lookup('router:main');
|
||||
const router = container.lookup('router:main');
|
||||
router.on('willTransition', function() {
|
||||
Discourse.viewTrackingRequired();
|
||||
});
|
||||
|
@ -16,7 +17,7 @@ export default {
|
|||
Em.run.scheduleOnce('afterRender', Ember.Route, cleanDOM);
|
||||
});
|
||||
|
||||
var pageTracker = Discourse.PageTracker.current();
|
||||
const pageTracker = PageTracker.current();
|
||||
pageTracker.start();
|
||||
|
||||
// Out of the box, Discourse tries to track google analytics
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/**
|
||||
Updates the relative ages of dates on the screen.
|
||||
**/
|
||||
import { updateRelativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
// Updates the relative ages of dates on the screen.
|
||||
export default {
|
||||
name: "relative-ages",
|
||||
initialize: function() {
|
||||
setInterval(function(){
|
||||
Discourse.Formatter.updateRelativeAge($('.relative-date'));
|
||||
updateRelativeAge($('.relative-date'));
|
||||
}, 60 * 1000);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
Discourse.computed = {
|
||||
|
||||
/**
|
||||
Returns whether two properties are equal to each other.
|
||||
|
||||
@method propertyEqual
|
||||
@params {String} p1 the first property
|
||||
@params {String} p2 the second property
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
propertyEqual: function(p1, p2) {
|
||||
return Em.computed(function() {
|
||||
return this.get(p1) === this.get(p2);
|
||||
}).property(p1, p2);
|
||||
},
|
||||
|
||||
/**
|
||||
Returns whether two properties are not equal to each other.
|
||||
|
||||
@method propertyNotEqual
|
||||
@params {String} p1 the first property
|
||||
@params {String} p2 the second property
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
propertyNotEqual: function(p1, p2) {
|
||||
return Em.computed(function() {
|
||||
return this.get(p1) !== this.get(p2);
|
||||
}).property(p1, p2);
|
||||
},
|
||||
|
||||
/**
|
||||
Returns i18n version of a string based on a property.
|
||||
|
||||
@method i18n
|
||||
@params {String} properties* to format
|
||||
@params {String} format the i18n format string
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
i18n: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var format = args.pop();
|
||||
var computed = Em.computed(function() {
|
||||
var self = this;
|
||||
return I18n.t(format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
})));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
},
|
||||
|
||||
/**
|
||||
Uses an Ember String `fmt` call to format a string. See:
|
||||
http://emberjs.com/api/classes/Em.String.html#method_fmt
|
||||
|
||||
@method fmt
|
||||
@params {String} properties* to format
|
||||
@params {String} format the format string
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
fmt: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var format = args.pop();
|
||||
var computed = Em.computed(function() {
|
||||
var self = this;
|
||||
return format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
}));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
},
|
||||
|
||||
/**
|
||||
Creates a URL using Discourse.getURL. It takes a fmt string just like
|
||||
fmt does.
|
||||
|
||||
@method url
|
||||
@params {String} properties* to format
|
||||
@params {String} format the format string for the URL
|
||||
@return {Function} computedProperty function returning a URL
|
||||
**/
|
||||
url: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var format = args.pop();
|
||||
var computed = Em.computed(function() {
|
||||
var self = this;
|
||||
return Discourse.getURL(format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
})));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
},
|
||||
|
||||
/**
|
||||
Returns whether properties end with a string
|
||||
|
||||
@method endWith
|
||||
@params {String} properties* to check
|
||||
@params {String} substring the substring
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
endWith: function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var substring = args.pop();
|
||||
var computed = Em.computed(function() {
|
||||
var self = this;
|
||||
return _.all(args.map(function(a) { return self.get(a); }), function(s) {
|
||||
var position = s.length - substring.length,
|
||||
lastIndex = s.lastIndexOf(substring);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
});
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
},
|
||||
|
||||
/**
|
||||
Creates a property from a SiteSetting. In the future the plan is for them to
|
||||
be able to update when changed.
|
||||
|
||||
@method setting
|
||||
@param {String} name of site setting
|
||||
**/
|
||||
setting: function(name) {
|
||||
return Em.computed(function() {
|
||||
return Discourse.SiteSettings[name];
|
||||
}).property();
|
||||
}
|
||||
|
||||
};
|
124
app/assets/javascripts/discourse/lib/computed.js.es6
Normal file
124
app/assets/javascripts/discourse/lib/computed.js.es6
Normal file
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
Returns whether two properties are equal to each other.
|
||||
|
||||
@method propertyEqual
|
||||
@params {String} p1 the first property
|
||||
@params {String} p2 the second property
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
export function propertyEqual(p1, p2) {
|
||||
return Em.computed(function() {
|
||||
return this.get(p1) === this.get(p2);
|
||||
}).property(p1, p2);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns whether two properties are not equal to each other.
|
||||
|
||||
@method propertyNotEqual
|
||||
@params {String} p1 the first property
|
||||
@params {String} p2 the second property
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
export function propertyNotEqual(p1, p2) {
|
||||
return Em.computed(function() {
|
||||
return this.get(p1) !== this.get(p2);
|
||||
}).property(p1, p2);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns i18n version of a string based on a property.
|
||||
|
||||
@method i18n
|
||||
@params {String} properties* to format
|
||||
@params {String} format the i18n format string
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
export function i18n() {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
const format = args.pop();
|
||||
const computed = Em.computed(function() {
|
||||
const self = this;
|
||||
return I18n.t(format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
})));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Uses an Ember String `fmt` call to format a string. See:
|
||||
http://emberjs.com/api/classes/Em.String.html#method_fmt
|
||||
|
||||
@method fmt
|
||||
@params {String} properties* to format
|
||||
@params {String} format the format string
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
export function fmt() {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
const format = args.pop();
|
||||
const computed = Em.computed(function() {
|
||||
const self = this;
|
||||
return format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
}));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a URL using Discourse.getURL. It takes a fmt string just like
|
||||
fmt does.
|
||||
|
||||
@method url
|
||||
@params {String} properties* to format
|
||||
@params {String} format the format string for the URL
|
||||
@return {Function} computedProperty function returning a URL
|
||||
**/
|
||||
export function url() {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
const format = args.pop();
|
||||
const computed = Em.computed(function() {
|
||||
const self = this;
|
||||
return Discourse.getURL(format.fmt.apply(format, args.map(function (a) {
|
||||
return self.get(a);
|
||||
})));
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns whether properties end with a string
|
||||
|
||||
@method endWith
|
||||
@params {String} properties* to check
|
||||
@params {String} substring the substring
|
||||
@return {Function} computedProperty function
|
||||
**/
|
||||
export function endWith() {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
const substring = args.pop();
|
||||
const computed = Em.computed(function() {
|
||||
const self = this;
|
||||
return _.all(args.map(function(a) { return self.get(a); }), function(s) {
|
||||
const position = s.length - substring.length,
|
||||
lastIndex = s.lastIndexOf(substring);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
});
|
||||
});
|
||||
return computed.property.apply(computed, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a property from a SiteSetting. In the future the plan is for them to
|
||||
be able to update when changed.
|
||||
|
||||
@method setting
|
||||
@param {String} name of site setting
|
||||
**/
|
||||
export function setting(name) {
|
||||
return Em.computed(function() {
|
||||
return Discourse.SiteSettings[name];
|
||||
}).property();
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import PageTracker from 'discourse/lib/page-tracker';
|
||||
|
||||
let primaryTab = false;
|
||||
let liveEnabled = false;
|
||||
|
@ -79,7 +80,7 @@ function setupNotifications() {
|
|||
document.addEventListener("scroll", resetIdle);
|
||||
}
|
||||
window.addEventListener("mouseover", resetIdle);
|
||||
Discourse.PageTracker.on("change", resetIdle);
|
||||
PageTracker.on("change", resetIdle);
|
||||
}
|
||||
|
||||
function resetIdle() {
|
||||
|
|
|
@ -1,38 +1,29 @@
|
|||
/**
|
||||
Track visible elemnts on the screen.
|
||||
|
||||
@class Eyeline
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
@uses RSVP.EventTarget
|
||||
**/
|
||||
Discourse.Eyeline = function Eyeline(selector) {
|
||||
// Track visible elemnts on the screen.
|
||||
const Eyeline = function Eyeline(selector) {
|
||||
this.selector = selector;
|
||||
};
|
||||
|
||||
/**
|
||||
Call this whenever you want to consider what is being seen by the browser
|
||||
Eyeline.prototype.update = function() {
|
||||
if (Ember.Test) { return; }
|
||||
|
||||
@method update
|
||||
**/
|
||||
Discourse.Eyeline.prototype.update = function() {
|
||||
var docViewTop = $(window).scrollTop(),
|
||||
const docViewTop = $(window).scrollTop(),
|
||||
windowHeight = $(window).height(),
|
||||
docViewBottom = docViewTop + windowHeight,
|
||||
$elements = $(this.selector),
|
||||
atBottom = false,
|
||||
bottomOffset = $elements.last().offset(),
|
||||
self = this;
|
||||
|
||||
let atBottom = false;
|
||||
if (bottomOffset) {
|
||||
atBottom = (bottomOffset.top <= docViewBottom) && (bottomOffset.top >= docViewTop);
|
||||
}
|
||||
|
||||
return $elements.each(function(i, elem) {
|
||||
var $elem = $(elem),
|
||||
const $elem = $(elem),
|
||||
elemTop = $elem.offset().top,
|
||||
elemBottom = elemTop + $elem.height(),
|
||||
markSeen = false;
|
||||
elemBottom = elemTop + $elem.height();
|
||||
|
||||
let markSeen = false;
|
||||
|
||||
// Make sure the element is visible
|
||||
if (!$elem.is(':visible')) return true;
|
||||
|
@ -67,18 +58,15 @@ Discourse.Eyeline.prototype.update = function() {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
Call this when we know aren't loading any more elements. Mark the rest as seen
|
||||
|
||||
@method flushRest
|
||||
**/
|
||||
Discourse.Eyeline.prototype.flushRest = function() {
|
||||
var self = this;
|
||||
// Call this when we know aren't loading any more elements. Mark the rest as seen
|
||||
Eyeline.prototype.flushRest = function() {
|
||||
if (Ember.Test) { return; }
|
||||
const self = this;
|
||||
$(this.selector).each(function(i, elem) {
|
||||
return self.trigger('saw', { detail: $(elem) });
|
||||
});
|
||||
};
|
||||
|
||||
RSVP.EventTarget.mixin(Discourse.Eyeline.prototype);
|
||||
|
||||
RSVP.EventTarget.mixin(Eyeline.prototype);
|
||||
|
||||
export default Eyeline;
|
|
@ -1,9 +1,5 @@
|
|||
/* global BreakString:true */
|
||||
|
||||
var updateRelativeAge, autoUpdatingRelativeAge, relativeAge, relativeAgeTiny,
|
||||
relativeAgeMedium, relativeAgeMediumSpan, longDate, longDateNoYear, toTitleCase,
|
||||
shortDate, shortDateNoYear, tinyDateYear, relativeAgeTinyShowsYear;
|
||||
|
||||
/*
|
||||
* memoize.js
|
||||
* by @philogb and @addyosmani
|
||||
|
@ -14,15 +10,15 @@ var updateRelativeAge, autoUpdatingRelativeAge, relativeAge, relativeAgeTiny,
|
|||
*
|
||||
* modified with cap by Sam
|
||||
*/
|
||||
var cappedMemoize = function ( fn, max ) {
|
||||
function cappedMemoize(fn, max) {
|
||||
fn.maxMemoize = max;
|
||||
fn.memoizeLength = 0;
|
||||
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments),
|
||||
hash = "",
|
||||
i = args.length;
|
||||
var currentArg = null;
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
let hash = "";
|
||||
let i = args.length;
|
||||
let currentArg = null;
|
||||
while (i--) {
|
||||
currentArg = args[i];
|
||||
hash += (currentArg === new Object(currentArg)) ?
|
||||
|
@ -39,44 +35,45 @@ var cappedMemoize = function ( fn, max ) {
|
|||
fn.memoizeLength = 0;
|
||||
fn.memoize = {};
|
||||
}
|
||||
var result = fn.apply(this, args);
|
||||
const result = fn.apply(this, args);
|
||||
fn.memoize[hash] = result;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
var breakUp = cappedMemoize(function(str, hint){
|
||||
const breakUp = cappedMemoize(function(str, hint){
|
||||
return new BreakString(str).break(hint);
|
||||
}, 100);
|
||||
export { breakUp };
|
||||
|
||||
shortDate = function(date){
|
||||
export function shortDate(date){
|
||||
return moment(date).format(I18n.t("dates.medium.date_year"));
|
||||
};
|
||||
}
|
||||
|
||||
shortDateNoYear = function(date) {
|
||||
function shortDateNoYear(date) {
|
||||
return moment(date).format(I18n.t("dates.tiny.date_month"));
|
||||
};
|
||||
}
|
||||
|
||||
tinyDateYear = function(date) {
|
||||
function tinyDateYear(date) {
|
||||
return moment(date).format(I18n.t("dates.tiny.date_year"));
|
||||
};
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
|
||||
// TODO: locale support ?
|
||||
toTitleCase = function toTitleCase(str) {
|
||||
export function toTitleCase(str) {
|
||||
return str.replace(/\w\S*/g, function(txt){
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
longDate = function(dt) {
|
||||
export function longDate(dt) {
|
||||
if (!dt) return;
|
||||
return moment(dt).longDate();
|
||||
};
|
||||
}
|
||||
|
||||
// suppress year, if current year
|
||||
longDateNoYear = function(dt) {
|
||||
export function longDateNoYear(dt) {
|
||||
if (!dt) return;
|
||||
|
||||
if ((new Date()).getFullYear() !== dt.getFullYear()) {
|
||||
|
@ -84,26 +81,25 @@ longDateNoYear = function(dt) {
|
|||
} else {
|
||||
return moment(dt).format(I18n.t("dates.long_date_without_year"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
updateRelativeAge = function(elems) {
|
||||
export function updateRelativeAge(elems) {
|
||||
// jQuery .each
|
||||
elems.each(function(){
|
||||
var $this = $(this);
|
||||
const $this = $(this);
|
||||
$this.html(relativeAge(new Date($this.data('time')), {format: $this.data('format'), wrapInSpan: false}));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
autoUpdatingRelativeAge = function(date,options) {
|
||||
export function autoUpdatingRelativeAge(date,options) {
|
||||
if (!date) return "";
|
||||
if (+date === +new Date(0)) return "";
|
||||
|
||||
options = options || {};
|
||||
var format = options.format || "tiny";
|
||||
let format = options.format || "tiny";
|
||||
|
||||
var append = "";
|
||||
|
||||
if(format === 'medium') {
|
||||
let append = "";
|
||||
if (format === 'medium') {
|
||||
append = " date";
|
||||
if(options.leaveAgo) {
|
||||
format = 'medium-with-ago';
|
||||
|
@ -111,7 +107,7 @@ autoUpdatingRelativeAge = function(date,options) {
|
|||
options.wrapInSpan = false;
|
||||
}
|
||||
|
||||
var relAge = relativeAge(date, options);
|
||||
const relAge = relativeAge(date, options);
|
||||
|
||||
if (format === 'tiny' && relativeAgeTinyShowsYear(relAge)) {
|
||||
append += " with-year";
|
||||
|
@ -122,16 +118,16 @@ autoUpdatingRelativeAge = function(date,options) {
|
|||
}
|
||||
|
||||
return "<span class='relative-date" + append + "' data-time='" + date.getTime() + "' data-format='" + format + "'>" + relAge + "</span>";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
relativeAgeTiny = function(date){
|
||||
var format = "tiny";
|
||||
var distance = Math.round((new Date() - date) / 1000);
|
||||
var distanceInMinutes = Math.round(distance / 60.0);
|
||||
function relativeAgeTiny(date){
|
||||
const format = "tiny";
|
||||
const distance = Math.round((new Date() - date) / 1000);
|
||||
const distanceInMinutes = Math.round(distance / 60.0);
|
||||
|
||||
var formatted;
|
||||
var t = function(key,opts){
|
||||
let formatted;
|
||||
const t = function(key,opts){
|
||||
return I18n.t("dates." + format + "." + key, opts);
|
||||
};
|
||||
|
||||
|
@ -168,22 +164,21 @@ relativeAgeTiny = function(date){
|
|||
}
|
||||
|
||||
return formatted;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the given tiny date string includes the year.
|
||||
* Useful for checking if the string isn't so tiny.
|
||||
*/
|
||||
relativeAgeTinyShowsYear = function(relativeAgeString) {
|
||||
function relativeAgeTinyShowsYear(relativeAgeString) {
|
||||
return relativeAgeString.match(/'[\d]{2}$/);
|
||||
};
|
||||
}
|
||||
|
||||
relativeAgeMediumSpan = function(distance, leaveAgo) {
|
||||
var formatted, distanceInMinutes;
|
||||
function relativeAgeMediumSpan(distance, leaveAgo) {
|
||||
let formatted;
|
||||
const distanceInMinutes = Math.round(distance / 60.0);
|
||||
|
||||
distanceInMinutes = Math.round(distance / 60.0);
|
||||
|
||||
var t = function(key, opts){
|
||||
const t = function(key, opts){
|
||||
return I18n.t("dates.medium" + (leaveAgo?"_with_ago":"") + "." + key, opts);
|
||||
};
|
||||
|
||||
|
@ -205,24 +200,22 @@ relativeAgeMediumSpan = function(distance, leaveAgo) {
|
|||
break;
|
||||
}
|
||||
return formatted || '&mdash';
|
||||
};
|
||||
}
|
||||
|
||||
relativeAgeMedium = function(date, options){
|
||||
var displayDate, fiveDaysAgo, oneMinuteAgo, fullReadable, leaveAgo;
|
||||
var wrapInSpan = options.wrapInSpan !== false;
|
||||
|
||||
leaveAgo = options.leaveAgo;
|
||||
var distance = Math.round((new Date() - date) / 1000);
|
||||
function relativeAgeMedium(date, options) {
|
||||
const wrapInSpan = options.wrapInSpan !== false;
|
||||
const leaveAgo = options.leaveAgo;
|
||||
const distance = Math.round((new Date() - date) / 1000);
|
||||
|
||||
if (!date) {
|
||||
return "—";
|
||||
}
|
||||
|
||||
fullReadable = longDate(date);
|
||||
displayDate = "";
|
||||
fiveDaysAgo = 432000;
|
||||
oneMinuteAgo = 60;
|
||||
const fullReadable = longDate(date);
|
||||
const fiveDaysAgo = 432000;
|
||||
const oneMinuteAgo = 60;
|
||||
|
||||
let displayDate = "";
|
||||
if (distance < oneMinuteAgo) {
|
||||
displayDate = I18n.t("now");
|
||||
} else if (distance > fiveDaysAgo) {
|
||||
|
@ -239,12 +232,12 @@ relativeAgeMedium = function(date, options){
|
|||
} else {
|
||||
return displayDate;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// mostly lifted from rails with a few amendments
|
||||
relativeAge = function(date, options) {
|
||||
export function relativeAge(date, options) {
|
||||
options = options || {};
|
||||
var format = options.format || "tiny";
|
||||
const format = options.format || "tiny";
|
||||
|
||||
if(format === "tiny") {
|
||||
return relativeAgeTiny(date, options);
|
||||
|
@ -255,10 +248,10 @@ relativeAge = function(date, options) {
|
|||
}
|
||||
|
||||
return "UNKNOWN FORMAT";
|
||||
};
|
||||
}
|
||||
|
||||
var number = function(val) {
|
||||
var formattedNumber;
|
||||
export function number(val) {
|
||||
let formattedNumber;
|
||||
|
||||
val = parseInt(val, 10);
|
||||
if (isNaN(val)) val = 0;
|
||||
|
@ -272,17 +265,5 @@ var number = function(val) {
|
|||
return I18n.t("number.short.thousands", {number: formattedNumber});
|
||||
}
|
||||
return val.toString();
|
||||
};
|
||||
}
|
||||
|
||||
Discourse.Formatter = {
|
||||
longDate: longDate,
|
||||
longDateNoYear: longDateNoYear,
|
||||
relativeAge: relativeAge,
|
||||
autoUpdatingRelativeAge: autoUpdatingRelativeAge,
|
||||
updateRelativeAge: updateRelativeAge,
|
||||
toTitleCase: toTitleCase,
|
||||
shortDate: shortDate,
|
||||
breakUp: breakUp,
|
||||
cappedMemoize: cappedMemoize,
|
||||
number: number
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
import Singleton from 'discourse/mixins/singleton';
|
||||
|
||||
/**
|
||||
Called whenever the "page" changes. This allows us to set up analytics
|
||||
and other tracking.
|
||||
|
@ -5,12 +7,12 @@
|
|||
To get notified when the page changes, you can install a hook like so:
|
||||
|
||||
```javascript
|
||||
Discourse.PageTracker.current().on('change', function(url, title) {
|
||||
PageTracker.current().on('change', function(url, title) {
|
||||
console.log('the page changed to: ' + url + ' and title ' + title);
|
||||
});
|
||||
```
|
||||
**/
|
||||
Discourse.PageTracker = Ember.Object.extend(Ember.Evented, {
|
||||
const PageTracker = Ember.Object.extend(Ember.Evented, {
|
||||
start: function() {
|
||||
if (this.get('started')) { return; }
|
||||
|
||||
|
@ -30,4 +32,6 @@ Discourse.PageTracker = Ember.Object.extend(Ember.Evented, {
|
|||
this.set('started', true);
|
||||
}
|
||||
});
|
||||
Discourse.PageTracker.reopenClass(Discourse.Singleton);
|
||||
PageTracker.reopenClass(Singleton);
|
||||
|
||||
export default PageTracker;
|
|
@ -1,23 +1,18 @@
|
|||
/**
|
||||
We use this class to track how long posts in a topic are on the screen.
|
||||
// We use this class to track how long posts in a topic are on the screen.
|
||||
|
||||
@class ScreenTrack
|
||||
@extends Ember.Object
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
import Singleton from 'discourse/mixins/singleton';
|
||||
|
||||
var PAUSE_UNLESS_SCROLLED = 1000 * 60 * 3,
|
||||
const PAUSE_UNLESS_SCROLLED = 1000 * 60 * 3,
|
||||
MAX_TRACKING_TIME = 1000 * 60 * 6;
|
||||
|
||||
Discourse.ScreenTrack = Ember.Object.extend({
|
||||
const ScreenTrack = Ember.Object.extend({
|
||||
|
||||
init: function() {
|
||||
init() {
|
||||
this.reset();
|
||||
},
|
||||
|
||||
start: function(topicId, topicController) {
|
||||
var currentTopicId = this.get('topicId');
|
||||
start(topicId, topicController) {
|
||||
const currentTopicId = this.get('topicId');
|
||||
if (currentTopicId && (currentTopicId !== topicId)) {
|
||||
this.tick();
|
||||
this.flush();
|
||||
|
@ -27,7 +22,7 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
|
||||
// Create an interval timer if we don't have one.
|
||||
if (!this.get('interval')) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.set('interval', setInterval(function () {
|
||||
self.tick();
|
||||
}, 1000));
|
||||
|
@ -39,7 +34,7 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
this.set('topicController', topicController);
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
stop() {
|
||||
if(!this.get('topicId')) {
|
||||
// already stopped no need to "extra stop"
|
||||
return;
|
||||
|
@ -56,19 +51,19 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
}
|
||||
},
|
||||
|
||||
track: function(elementId, postNumber) {
|
||||
track(elementId, postNumber) {
|
||||
this.get('timings')["#" + elementId] = {
|
||||
time: 0,
|
||||
postNumber: postNumber
|
||||
};
|
||||
},
|
||||
|
||||
stopTracking: function(elementId) {
|
||||
stopTracking(elementId) {
|
||||
delete this.get('timings')['#' + elementId];
|
||||
},
|
||||
|
||||
// Reset our timers
|
||||
reset: function() {
|
||||
reset() {
|
||||
this.setProperties({
|
||||
lastTick: new Date().getTime(),
|
||||
lastScrolled: new Date().getTime(),
|
||||
|
@ -80,17 +75,17 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
});
|
||||
},
|
||||
|
||||
scrolled: function() {
|
||||
scrolled() {
|
||||
this.set('lastScrolled', new Date().getTime());
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
flush() {
|
||||
if (this.get('cancelled')) { return; }
|
||||
|
||||
// We don't log anything unless we're logged in
|
||||
if (!Discourse.User.current()) return;
|
||||
|
||||
var newTimings = {},
|
||||
const newTimings = {},
|
||||
totalTimings = this.get('totalTimings'),
|
||||
self = this;
|
||||
|
||||
|
@ -105,14 +100,14 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
timing.time = 0;
|
||||
});
|
||||
|
||||
var topicId = parseInt(this.get('topicId'), 10),
|
||||
highestSeen = 0;
|
||||
const topicId = parseInt(this.get('topicId'), 10);
|
||||
let highestSeen = 0;
|
||||
|
||||
_.each(newTimings, function(time,postNumber) {
|
||||
highestSeen = Math.max(highestSeen, parseInt(postNumber, 10));
|
||||
});
|
||||
|
||||
var highestSeenByTopic = Discourse.Session.currentProp('highestSeenByTopic');
|
||||
const highestSeenByTopic = Discourse.Session.currentProp('highestSeenByTopic');
|
||||
if ((highestSeenByTopic[topicId] || 0) < highestSeen) {
|
||||
highestSeenByTopic[topicId] = highestSeen;
|
||||
}
|
||||
|
@ -132,9 +127,9 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
'X-SILENCE-LOGGER': 'true'
|
||||
}
|
||||
}).then(function(){
|
||||
var controller = self.get('topicController');
|
||||
const controller = self.get('topicController');
|
||||
if(controller){
|
||||
var postNumbers = Object.keys(newTimings).map(function(v){
|
||||
const postNumbers = Object.keys(newTimings).map(function(v){
|
||||
return parseInt(v,10);
|
||||
});
|
||||
controller.readPosts(topicId, postNumbers);
|
||||
|
@ -146,23 +141,23 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
this.set('lastFlush', 0);
|
||||
},
|
||||
|
||||
tick: function() {
|
||||
tick() {
|
||||
|
||||
// If the user hasn't scrolled the browser in a long time, stop tracking time read
|
||||
var sinceScrolled = new Date().getTime() - this.get('lastScrolled');
|
||||
const sinceScrolled = new Date().getTime() - this.get('lastScrolled');
|
||||
if (sinceScrolled > PAUSE_UNLESS_SCROLLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
var diff = new Date().getTime() - this.get('lastTick');
|
||||
const diff = new Date().getTime() - this.get('lastTick');
|
||||
this.set('lastFlush', this.get('lastFlush') + diff);
|
||||
this.set('lastTick', new Date().getTime());
|
||||
|
||||
var totalTimings = this.get('totalTimings'), timings = this.get('timings');
|
||||
var nextFlush = Discourse.SiteSettings.flush_timings_secs * 1000;
|
||||
const totalTimings = this.get('totalTimings'), timings = this.get('timings');
|
||||
const nextFlush = Discourse.SiteSettings.flush_timings_secs * 1000;
|
||||
|
||||
// rush new post numbers
|
||||
var rush = _.any(_.filter(timings, function(t){return t.time>0;}), function(t){
|
||||
const rush = _.any(_.filter(timings, function(t){return t.time>0;}), function(t){
|
||||
return !totalTimings[t.postNumber];
|
||||
});
|
||||
|
||||
|
@ -174,15 +169,15 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
if (!Discourse.get("hasFocus")) return;
|
||||
|
||||
this.set('topicTime', this.get('topicTime') + diff);
|
||||
var docViewTop = $(window).scrollTop() + $('header').height(),
|
||||
const docViewTop = $(window).scrollTop() + $('header').height(),
|
||||
docViewBottom = docViewTop + $(window).height();
|
||||
|
||||
// TODO: Eyeline has a smarter more accurate function here. It's bad to do jQuery
|
||||
// in a model like component, so we should refactor this out later.
|
||||
_.each(this.get('timings'),function(timing,id) {
|
||||
var $element = $(id);
|
||||
const $element = $(id);
|
||||
if ($element.length === 1) {
|
||||
var elemTop = $element.offset().top,
|
||||
const elemTop = $element.offset().top,
|
||||
elemBottom = elemTop + $element.height();
|
||||
|
||||
// If part of the element is on the screen, increase the counter
|
||||
|
@ -195,5 +190,5 @@ Discourse.ScreenTrack = Ember.Object.extend({
|
|||
});
|
||||
|
||||
|
||||
Discourse.ScreenTrack.reopenClass(Discourse.Singleton);
|
||||
|
||||
ScreenTrack.reopenClass(Singleton);
|
||||
export default ScreenTrack;
|
|
@ -1,6 +1,8 @@
|
|||
import { propertyEqual, setting } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
isOwnEmail: Discourse.computed.propertyEqual("model.id", "currentUser.id"),
|
||||
showEmailOnProfile: Discourse.computed.setting("show_email_on_profile"),
|
||||
isOwnEmail: propertyEqual("model.id", "currentUser.id"),
|
||||
showEmailOnProfile: setting("show_email_on_profile"),
|
||||
canStaffCheckEmails: Em.computed.and("showEmailOnProfile", "currentUser.staff"),
|
||||
canAdminCheckEmails: Em.computed.alias("currentUser.admin"),
|
||||
canCheckEmails: Em.computed.or("isOwnEmail", "canStaffCheckEmails", "canAdminCheckEmails"),
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import Eyeline from 'discourse/lib/eyeline';
|
||||
import Scrolling from 'discourse/mixins/scrolling';
|
||||
|
||||
// Provides the ability to load more items for a view which is scrolled to the bottom.
|
||||
export default Em.Mixin.create(Ember.ViewTargetActionSupport, Discourse.Scrolling, {
|
||||
export default Ember.Mixin.create(Ember.ViewTargetActionSupport, Scrolling, {
|
||||
|
||||
scrolled: function() {
|
||||
const eyeline = this.get('eyeline');
|
||||
|
@ -7,7 +10,7 @@ export default Em.Mixin.create(Ember.ViewTargetActionSupport, Discourse.Scrollin
|
|||
},
|
||||
|
||||
_bindEyeline: function() {
|
||||
const eyeline = new Discourse.Eyeline(this.get('eyelineSelector') + ":last");
|
||||
const eyeline = new Eyeline(this.get('eyelineSelector') + ":last");
|
||||
this.set('eyeline', eyeline);
|
||||
eyeline.on('sawBottom', () => this.send('loadMore'));
|
||||
this.bindScrolling();
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/**
|
||||
This mixin adds support for being notified every time the browser window
|
||||
is scrolled.
|
||||
|
||||
@class Scrolling
|
||||
@extends Ember.Mixin
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
|
||||
Discourse.Scrolling = Em.Mixin.create({
|
||||
|
||||
/**
|
||||
Begin watching for scroll events. By default they will be called at max every 100ms.
|
||||
call with {debounce: N} for a diff time
|
||||
|
||||
@method bindScrolling
|
||||
*/
|
||||
bindScrolling: function(opts) {
|
||||
opts = opts || {debounce: 100};
|
||||
|
||||
// So we can not call the scrolled event while transitioning
|
||||
var router = Discourse.__container__.lookup('router:main').router;
|
||||
|
||||
var self = this,
|
||||
onScrollMethod = function() {
|
||||
if (router.activeTransition) { return; }
|
||||
return Em.run.scheduleOnce('afterRender', self, 'scrolled');
|
||||
};
|
||||
|
||||
if (opts.debounce) {
|
||||
onScrollMethod = Discourse.debounce(onScrollMethod, opts.debounce);
|
||||
}
|
||||
|
||||
Discourse.ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name);
|
||||
Em.run.scheduleOnce('afterRender', onScrollMethod);
|
||||
},
|
||||
|
||||
/**
|
||||
Stop watching for scroll events.
|
||||
|
||||
@method unbindScrolling
|
||||
*/
|
||||
unbindScrolling: function(name) {
|
||||
Discourse.ScrollingDOMMethods.unbindOnScroll(name);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
This object provides the DOM methods we need for our Mixin to bind to scrolling
|
||||
methods in the browser. By removing them from the Mixin we can test them
|
||||
easier.
|
||||
|
||||
@class ScrollingDOMMethods
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ScrollingDOMMethods = {
|
||||
|
||||
bindOnScroll: function(onScrollMethod, name) {
|
||||
name = name || 'default';
|
||||
$(document).bind('touchmove.discourse-' + name, onScrollMethod);
|
||||
$(window).bind('scroll.discourse-' + name, onScrollMethod);
|
||||
},
|
||||
|
||||
unbindOnScroll: function(name) {
|
||||
name = name || 'default';
|
||||
$(window).unbind('scroll.discourse-' + name);
|
||||
$(document).unbind('touchmove.discourse-' + name);
|
||||
}
|
||||
|
||||
};
|
50
app/assets/javascripts/discourse/mixins/scrolling.js.es6
Normal file
50
app/assets/javascripts/discourse/mixins/scrolling.js.es6
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
This object provides the DOM methods we need for our Mixin to bind to scrolling
|
||||
methods in the browser. By removing them from the Mixin we can test them
|
||||
easier.
|
||||
**/
|
||||
const ScrollingDOMMethods = {
|
||||
bindOnScroll: function(onScrollMethod, name) {
|
||||
name = name || 'default';
|
||||
$(document).bind('touchmove.discourse-' + name, onScrollMethod);
|
||||
$(window).bind('scroll.discourse-' + name, onScrollMethod);
|
||||
},
|
||||
|
||||
unbindOnScroll: function(name) {
|
||||
name = name || 'default';
|
||||
$(window).unbind('scroll.discourse-' + name);
|
||||
$(document).unbind('touchmove.discourse-' + name);
|
||||
}
|
||||
};
|
||||
|
||||
const Scrolling = Ember.Mixin.create({
|
||||
|
||||
// Begin watching for scroll events. By default they will be called at max every 100ms.
|
||||
// call with {debounce: N} for a diff time
|
||||
bindScrolling: function(opts) {
|
||||
opts = opts || {debounce: 100};
|
||||
|
||||
// So we can not call the scrolled event while transitioning
|
||||
const router = Discourse.__container__.lookup('router:main').router;
|
||||
|
||||
const self = this;
|
||||
var onScrollMethod = function() {
|
||||
if (router.activeTransition) { return; }
|
||||
return Em.run.scheduleOnce('afterRender', self, 'scrolled');
|
||||
};
|
||||
|
||||
if (opts.debounce) {
|
||||
onScrollMethod = Discourse.debounce(onScrollMethod, opts.debounce);
|
||||
}
|
||||
|
||||
ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name);
|
||||
Em.run.scheduleOnce('afterRender', onScrollMethod);
|
||||
},
|
||||
|
||||
unbindScrolling: function(name) {
|
||||
ScrollingDOMMethods.unbindOnScroll(name);
|
||||
}
|
||||
});
|
||||
|
||||
export { ScrollingDOMMethods };
|
||||
export default Scrolling;
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
// Define your class and apply the Mixin
|
||||
User = Ember.Object.extend({});
|
||||
User.reopenClass(Discourse.Singleton);
|
||||
User.reopenClass(Singleton);
|
||||
|
||||
// Retrieve the current instance:
|
||||
var instance = User.current();
|
||||
|
@ -35,7 +35,7 @@
|
|||
|
||||
// Define your class and apply the Mixin
|
||||
Foot = Ember.Object.extend({});
|
||||
Foot.reopenClass(Discourse.Singleton, {
|
||||
Foot.reopenClass(Singleton, {
|
||||
createCurrent: function() {
|
||||
return Foot.create({toes: 5});
|
||||
}
|
||||
|
@ -44,51 +44,28 @@
|
|||
console.log(Foot.currentProp('toes')); // 5
|
||||
|
||||
```
|
||||
|
||||
@class Discourse.Singleton
|
||||
@extends Ember.Mixin
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.Singleton = Em.Mixin.create({
|
||||
const Singleton = Ember.Mixin.create({
|
||||
|
||||
/**
|
||||
Returns the current singleton instance of the class.
|
||||
|
||||
@method current
|
||||
@returns {Ember.Object} the instance of the singleton
|
||||
**/
|
||||
current: function() {
|
||||
current() {
|
||||
if (!this._current) {
|
||||
this._current = this.createCurrent();
|
||||
}
|
||||
|
||||
return this._current;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
How the singleton instance is created. This can be overridden
|
||||
with logic for creating (or even returning null) your instance.
|
||||
|
||||
By default it just calls `create` with an empty object.
|
||||
|
||||
@method createCurrent
|
||||
@returns {Ember.Object} the instance that will be your singleton
|
||||
**/
|
||||
createCurrent: function() {
|
||||
createCurrent() {
|
||||
return this.create({});
|
||||
},
|
||||
|
||||
/**
|
||||
Returns or sets a property on the singleton instance.
|
||||
|
||||
@method currentProp
|
||||
@param {String} property the property we want to get or set
|
||||
@param {String} value the optional value to set the property to
|
||||
@returns the value of the property
|
||||
**/
|
||||
currentProp: function(property, value) {
|
||||
// Returns OR sets a property on the singleton instance.
|
||||
currentProp(property, value) {
|
||||
var instance = this.current();
|
||||
if (!instance) { return; }
|
||||
|
||||
|
@ -100,14 +77,9 @@ Discourse.Singleton = Em.Mixin.create({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Resets the current singleton. Useful in testing.
|
||||
|
||||
@method resetCurrent
|
||||
**/
|
||||
resetCurrent: function(val) {
|
||||
resetCurrent(val) {
|
||||
this._current = val;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default Singleton;
|
|
@ -1,15 +1,9 @@
|
|||
/**
|
||||
A data model for flagged/deleted posts.
|
||||
import Post from 'discourse/models/post';
|
||||
|
||||
@class AdminPost
|
||||
@extends Discourse.Post
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminPost = Discourse.Post.extend({
|
||||
export default Post.extend({
|
||||
|
||||
_attachCategory: function () {
|
||||
var categoryId = this.get("category_id");
|
||||
const categoryId = this.get("category_id");
|
||||
if (categoryId) {
|
||||
this.set("category", Discourse.Category.findById(categoryId));
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
A data model for archetypes such as polls, tasks, etc.
|
||||
|
||||
@class Archetype
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.Archetype = Discourse.Model.extend({
|
||||
|
||||
hasOptions: Em.computed.gt('options.length', 0),
|
||||
|
||||
site: function() {
|
||||
return Discourse.Site.current();
|
||||
}.property(),
|
||||
|
||||
isDefault: Discourse.computed.propertyEqual('id', 'site.default_archetype'),
|
||||
notDefault: Em.computed.not('isDefault')
|
||||
|
||||
});
|
||||
|
||||
|
8
app/assets/javascripts/discourse/models/archetype.js.es6
Normal file
8
app/assets/javascripts/discourse/models/archetype.js.es6
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
|
||||
export default RestModel.extend({
|
||||
hasOptions: Em.computed.gt('options.length', 0),
|
||||
isDefault: propertyEqual('id', 'site.default_archetype'),
|
||||
notDefault: Em.computed.not('isDefault')
|
||||
});
|
|
@ -1,11 +1,4 @@
|
|||
/**
|
||||
A data model representing a navigation item on the list views
|
||||
|
||||
@class NavItem
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
import { toTitleCase } from 'discourse/lib/formatter';
|
||||
|
||||
const NavItem = Discourse.Model.extend({
|
||||
|
||||
|
@ -22,7 +15,7 @@ const NavItem = Discourse.Model.extend({
|
|||
|
||||
if (categoryName) {
|
||||
name = 'category';
|
||||
extra.categoryName = Discourse.Formatter.toTitleCase(categoryName);
|
||||
extra.categoryName = toTitleCase(categoryName);
|
||||
}
|
||||
return I18n.t("filters." + name.replace("/", ".") + ".title", extra);
|
||||
}.property('categoryName', 'name', 'count'),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import ActionSummary from 'discourse/models/action-summary';
|
||||
import { url, fmt, propertyEqual } from 'discourse/lib/computed';
|
||||
|
||||
const Post = RestModel.extend({
|
||||
|
||||
|
@ -57,7 +58,7 @@ const Post = RestModel.extend({
|
|||
return (this.get('post_number') === 1) ? url + "/1" : url;
|
||||
}.property('post_number', 'url'),
|
||||
|
||||
usernameUrl: Discourse.computed.url('username', '/users/%@'),
|
||||
usernameUrl: url('username', '/users/%@'),
|
||||
|
||||
showUserReplyTab: function() {
|
||||
return this.get('reply_to_user') && (
|
||||
|
@ -66,9 +67,9 @@ const Post = RestModel.extend({
|
|||
);
|
||||
}.property('reply_to_user', 'reply_to_post_number', 'post_number'),
|
||||
|
||||
topicOwner: Discourse.computed.propertyEqual('topic.details.created_by.id', 'user_id'),
|
||||
topicOwner: propertyEqual('topic.details.created_by.id', 'user_id'),
|
||||
hasHistory: Em.computed.gt('version', 1),
|
||||
postElementId: Discourse.computed.fmt('post_number', 'post_%@'),
|
||||
postElementId: fmt('post_number', 'post_%@'),
|
||||
|
||||
canViewRawEmail: function() {
|
||||
return this.get("user_id") === Discourse.User.currentProp("id") || Discourse.User.currentProp('staff');
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import Singleton from 'discourse/mixins/singleton';
|
||||
|
||||
// A data model representing current session data. You can put transient
|
||||
// data here you might want later. It is not stored or serialized anywhere.
|
||||
var Session = Discourse.Model.extend({
|
||||
const Session = RestModel.extend({
|
||||
init: function() {
|
||||
this.set('highestSeenByTopic', {});
|
||||
}
|
||||
});
|
||||
|
||||
Session.reopenClass(Discourse.Singleton);
|
||||
|
||||
Session.reopenClass(Singleton);
|
||||
export default Session;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import Archetype from 'discourse/models/archetype';
|
||||
import PostActionType from 'discourse/models/post-action-type';
|
||||
import Singleton from 'discourse/mixins/singleton';
|
||||
|
||||
const Site = Discourse.Model.extend({
|
||||
|
||||
|
@ -85,11 +87,11 @@ const Site = Discourse.Model.extend({
|
|||
}
|
||||
});
|
||||
|
||||
Site.reopenClass(Discourse.Singleton, {
|
||||
Site.reopenClass(Singleton, {
|
||||
|
||||
// The current singleton will retrieve its attributes from the `PreloadStore`.
|
||||
createCurrent() {
|
||||
return Discourse.Site.create(PreloadStore.get('site'));
|
||||
return Site.create(PreloadStore.get('site'));
|
||||
},
|
||||
|
||||
create() {
|
||||
|
@ -137,7 +139,8 @@ Site.reopenClass(Discourse.Singleton, {
|
|||
|
||||
if (result.archetypes) {
|
||||
result.archetypes = _.map(result.archetypes,function(a) {
|
||||
return Discourse.Archetype.create(a);
|
||||
a.site = result;
|
||||
return Archetype.create(a);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { flushMap } from 'discourse/models/store';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import { longDate } from 'discourse/lib/formatter';
|
||||
|
||||
const Topic = RestModel.extend({
|
||||
message: null,
|
||||
|
@ -20,8 +22,8 @@ const Topic = RestModel.extend({
|
|||
}.property('bumped_at', 'createdAt'),
|
||||
|
||||
bumpedAtTitle: function() {
|
||||
return I18n.t('first_post') + ": " + Discourse.Formatter.longDate(this.get('createdAt')) + "\n" +
|
||||
I18n.t('last_post') + ": " + Discourse.Formatter.longDate(this.get('bumpedAt'));
|
||||
return I18n.t('first_post') + ": " + longDate(this.get('createdAt')) + "\n" +
|
||||
I18n.t('last_post') + ": " + longDate(this.get('bumpedAt'));
|
||||
}.property('bumpedAt'),
|
||||
|
||||
createdAt: function() {
|
||||
|
@ -364,7 +366,7 @@ const Topic = RestModel.extend({
|
|||
return( e && e.substr(e.length - 8,8) === '…' );
|
||||
}.property('excerpt'),
|
||||
|
||||
readLastPost: Discourse.computed.propertyEqual('last_read_post_number', 'highest_post_number'),
|
||||
readLastPost: propertyEqual('last_read_post_number', 'highest_post_number'),
|
||||
canClearPin: Em.computed.and('pinned', 'readLastPost')
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import { fmt } from 'discourse/lib/computed';
|
||||
|
||||
export default RestModel.extend({
|
||||
detailedName: fmt('id', 'name', '%@ - %@')
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
Represents a user's trust level in the system
|
||||
|
||||
@class TrustLevel
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.TrustLevel = Discourse.Model.extend({
|
||||
detailedName: Discourse.computed.fmt('id', 'name', '%@ - %@')
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import RestModel from 'discourse/models/rest';
|
||||
import UserAction from 'discourse/models/user-action';
|
||||
import { i18n } from 'discourse/lib/computed';
|
||||
|
||||
export default RestModel.extend({
|
||||
|
||||
isPM: function() {
|
||||
const actionType = this.get('action_type');
|
||||
return actionType === UserAction.TYPES.messages_sent ||
|
||||
actionType === UserAction.TYPES.messages_received;
|
||||
}.property('action_type'),
|
||||
|
||||
description: i18n('action_type', 'user_action_groups.%@'),
|
||||
|
||||
isResponse: function() {
|
||||
const actionType = this.get('action_type');
|
||||
return actionType === UserAction.TYPES.replies ||
|
||||
actionType === UserAction.TYPES.quotes;
|
||||
}.property('action_type')
|
||||
|
||||
});
|
|
@ -1,4 +1,7 @@
|
|||
var UserActionTypes = {
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
const UserActionTypes = {
|
||||
likes_given: 1,
|
||||
likes_received: 2,
|
||||
bookmarks: 3,
|
||||
|
@ -11,29 +14,24 @@ var UserActionTypes = {
|
|||
messages_sent: 12,
|
||||
messages_received: 13,
|
||||
pending: 14
|
||||
},
|
||||
InvertedActionTypes = {};
|
||||
};
|
||||
const InvertedActionTypes = {};
|
||||
|
||||
_.each(UserActionTypes, function (k, v) {
|
||||
InvertedActionTypes[k] = v;
|
||||
});
|
||||
|
||||
Discourse.UserAction = Discourse.Model.extend({
|
||||
const UserAction = RestModel.extend({
|
||||
|
||||
_attachCategory: function() {
|
||||
var categoryId = this.get('category_id');
|
||||
const categoryId = this.get('category_id');
|
||||
if (categoryId) {
|
||||
this.set('category', Discourse.Category.findById(categoryId));
|
||||
}
|
||||
}.on('init'),
|
||||
|
||||
/**
|
||||
Return an i18n key we will use for the description text of a user action.
|
||||
|
||||
@property descriptionKey
|
||||
**/
|
||||
descriptionKey: function() {
|
||||
var action = this.get('action_type');
|
||||
const action = this.get('action_type');
|
||||
if (action === null || Discourse.UserAction.TO_SHOW.indexOf(action) >= 0) {
|
||||
if (this.get('isPM')) {
|
||||
return this.get('sameUser') ? 'sent_by_you' : 'sent_by_user';
|
||||
|
@ -74,13 +72,13 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
presentName: Em.computed.any('name', 'username'),
|
||||
targetDisplayName: Em.computed.any('target_name', 'target_username'),
|
||||
actingDisplayName: Em.computed.any('acting_name', 'acting_username'),
|
||||
targetUserUrl: Discourse.computed.url('target_username', '/users/%@'),
|
||||
targetUserUrl: url('target_username', '/users/%@'),
|
||||
|
||||
usernameLower: function() {
|
||||
return this.get('username').toLowerCase();
|
||||
}.property('username'),
|
||||
|
||||
userUrl: Discourse.computed.url('usernameLower', '/users/%@'),
|
||||
userUrl: url('usernameLower', '/users/%@'),
|
||||
|
||||
postUrl: function() {
|
||||
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
|
||||
|
@ -102,7 +100,7 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
removableBookmark: Em.computed.and('bookmarkType', 'sameUser'),
|
||||
|
||||
addChild: function(action) {
|
||||
var groups = this.get("childGroups");
|
||||
let groups = this.get("childGroups");
|
||||
if (!groups) {
|
||||
groups = {
|
||||
likes: Discourse.UserActionGroup.create({ icon: "fa fa-heart" }),
|
||||
|
@ -113,7 +111,7 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
}
|
||||
this.set("childGroups", groups);
|
||||
|
||||
var bucket = (function() {
|
||||
const bucket = (function() {
|
||||
switch (action.action_type) {
|
||||
case UserActionTypes.likes_given:
|
||||
case UserActionTypes.likes_received:
|
||||
|
@ -124,15 +122,15 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
return "bookmarks";
|
||||
}
|
||||
})();
|
||||
var current = groups[bucket];
|
||||
const current = groups[bucket];
|
||||
if (current) {
|
||||
current.push(action);
|
||||
}
|
||||
},
|
||||
|
||||
children: function() {
|
||||
var g = this.get("childGroups");
|
||||
var rval = [];
|
||||
const g = this.get("childGroups");
|
||||
let rval = [];
|
||||
if (g) {
|
||||
rval = [g.likes, g.stars, g.edits, g.bookmarks].filter(function(i) {
|
||||
return i.get("items") && i.get("items").length > 0;
|
||||
|
@ -154,20 +152,20 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
}
|
||||
});
|
||||
|
||||
Discourse.UserAction.reopenClass({
|
||||
UserAction.reopenClass({
|
||||
collapseStream: function(stream) {
|
||||
var uniq = {},
|
||||
collapsed = [],
|
||||
pos = 0;
|
||||
const uniq = {};
|
||||
const collapsed = [];
|
||||
let pos = 0;
|
||||
|
||||
stream.forEach(function(item) {
|
||||
var key = "" + item.topic_id + "-" + item.post_number;
|
||||
var found = uniq[key];
|
||||
const key = "" + item.topic_id + "-" + item.post_number;
|
||||
const found = uniq[key];
|
||||
if (found === void 0) {
|
||||
|
||||
var current;
|
||||
let current;
|
||||
if (Discourse.UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
|
||||
current = Discourse.UserAction.create(item);
|
||||
current = UserAction.create(item);
|
||||
item.switchToActing();
|
||||
current.addChild(item);
|
||||
} else {
|
||||
|
@ -177,7 +175,7 @@ Discourse.UserAction.reopenClass({
|
|||
collapsed[pos] = current;
|
||||
pos += 1;
|
||||
} else {
|
||||
if (Discourse.UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
|
||||
if (UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
|
||||
item.switchToActing();
|
||||
collapsed[found].addChild(item);
|
||||
} else {
|
||||
|
@ -208,3 +206,5 @@ Discourse.UserAction.reopenClass({
|
|||
]
|
||||
|
||||
});
|
||||
|
||||
export default UserAction;
|
|
@ -1,12 +1,7 @@
|
|||
/**
|
||||
Represents a user's stream
|
||||
import { url } from 'discourse/lib/computed';
|
||||
import AdminPost from 'discourse/models/admin-post';
|
||||
|
||||
@class UserPostsStream
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserPostsStream = Discourse.Model.extend({
|
||||
export default Discourse.Model.extend({
|
||||
loaded: false,
|
||||
|
||||
_initialize: function () {
|
||||
|
@ -17,9 +12,9 @@ Discourse.UserPostsStream = Discourse.Model.extend({
|
|||
});
|
||||
}.on("init"),
|
||||
|
||||
url: Discourse.computed.url("user.username_lower", "filter", "itemsLoaded", "/posts/%@/%@?offset=%@"),
|
||||
url: url("user.username_lower", "filter", "itemsLoaded", "/posts/%@/%@?offset=%@"),
|
||||
|
||||
filterBy: function (filter) {
|
||||
filterBy(filter) {
|
||||
if (this.get("loaded") && this.get("filter") === filter) { return Ember.RSVP.resolve(); }
|
||||
|
||||
this.setProperties({
|
||||
|
@ -32,15 +27,15 @@ Discourse.UserPostsStream = Discourse.Model.extend({
|
|||
return this.findItems();
|
||||
},
|
||||
|
||||
findItems: function () {
|
||||
var self = this;
|
||||
findItems() {
|
||||
const self = this;
|
||||
if (this.get("loading") || !this.get("canLoadMore")) { return Ember.RSVP.reject(); }
|
||||
|
||||
this.set("loading", true);
|
||||
|
||||
return Discourse.ajax(this.get("url"), { cache: false }).then(function (result) {
|
||||
if (result) {
|
||||
var posts = result.map(function (post) { return Discourse.AdminPost.create(post); });
|
||||
const posts = result.map(function (post) { return AdminPost.create(post); });
|
||||
self.get("content").pushObjects(posts);
|
||||
self.setProperties({
|
||||
loaded: true,
|
|
@ -1,3 +1,4 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
|
||||
export default RestModel.extend({
|
||||
|
@ -22,7 +23,7 @@ export default RestModel.extend({
|
|||
return filter;
|
||||
}.property('filter'),
|
||||
|
||||
baseUrl: Discourse.computed.url('itemsLoaded', 'user.username_lower', '/user_actions.json?offset=%@&username=%@'),
|
||||
baseUrl: url('itemsLoaded', 'user.username_lower', '/user_actions.json?offset=%@&username=%@'),
|
||||
|
||||
filterBy(filter) {
|
||||
this.setProperties({ filter, itemsLoaded: 0, content: [], lastLoadedUrl: null });
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import avatarTemplate from 'discourse/lib/avatar-template';
|
||||
import UserStream from 'discourse/models/user-stream';
|
||||
import UserPostsStream from 'discourse/models/user-posts-stream';
|
||||
import Singleton from 'discourse/mixins/singleton';
|
||||
import { longDate } from 'discourse/lib/formatter';
|
||||
|
||||
const User = RestModel.extend({
|
||||
|
||||
|
@ -10,24 +15,12 @@ const User = RestModel.extend({
|
|||
hasNotPosted: Em.computed.not("hasPosted"),
|
||||
canBeDeleted: Em.computed.and("can_be_deleted", "hasNotPosted"),
|
||||
|
||||
/**
|
||||
The user's stream
|
||||
|
||||
@property stream
|
||||
@type {Discourse.UserStream}
|
||||
**/
|
||||
stream: function() {
|
||||
return Discourse.UserStream.create({ user: this });
|
||||
return UserStream.create({ user: this });
|
||||
}.property(),
|
||||
|
||||
/**
|
||||
The user's posts stream
|
||||
|
||||
@property postsStream
|
||||
@type {Discourse.UserPostsStream}
|
||||
**/
|
||||
postsStream: function() {
|
||||
return Discourse.UserPostsStream.create({ user: this });
|
||||
return UserPostsStream.create({ user: this });
|
||||
}.property(),
|
||||
|
||||
/**
|
||||
|
@ -89,7 +82,7 @@ const User = RestModel.extend({
|
|||
@property adminPath
|
||||
@type {String}
|
||||
**/
|
||||
adminPath: Discourse.computed.url('username_lower', "/admin/users/%@"),
|
||||
adminPath: url('username_lower', "/admin/users/%@"),
|
||||
|
||||
/**
|
||||
This user's username in lowercase.
|
||||
|
@ -123,7 +116,7 @@ const User = RestModel.extend({
|
|||
}.property('suspended_till'),
|
||||
|
||||
suspendedTillDate: function() {
|
||||
return Discourse.Formatter.longDate(this.get('suspended_till'));
|
||||
return longDate(this.get('suspended_till'));
|
||||
}.property('suspended_till'),
|
||||
|
||||
/**
|
||||
|
@ -434,15 +427,15 @@ const User = RestModel.extend({
|
|||
|
||||
});
|
||||
|
||||
User.reopenClass(Discourse.Singleton, {
|
||||
User.reopenClass(Singleton, {
|
||||
|
||||
// Find a `Discourse.User` for a given username.
|
||||
findByUsername: function(username, options) {
|
||||
const user = Discourse.User.create({username: username});
|
||||
const user = User.create({username: username});
|
||||
return user.findDetails(options);
|
||||
},
|
||||
|
||||
// TODO: Use app.register and junk Discourse.Singleton
|
||||
// TODO: Use app.register and junk Singleton
|
||||
createCurrent: function() {
|
||||
var userJson = PreloadStore.get('currentUser');
|
||||
if (userJson) {
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
A data model representing a statistic on a UserAction
|
||||
|
||||
@class UserActionStat
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserActionStat = Discourse.Model.extend({
|
||||
|
||||
isPM: function() {
|
||||
var actionType = this.get('action_type');
|
||||
return actionType === Discourse.UserAction.TYPES.messages_sent ||
|
||||
actionType === Discourse.UserAction.TYPES.messages_received;
|
||||
}.property('action_type'),
|
||||
|
||||
description: Discourse.computed.i18n('action_type', 'user_action_groups.%@'),
|
||||
|
||||
isResponse: function() {
|
||||
var actionType = this.get('action_type');
|
||||
return actionType === Discourse.UserAction.TYPES.replies ||
|
||||
actionType === Discourse.UserAction.TYPES.quotes;
|
||||
}.property('action_type')
|
||||
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import OpenComposer from "discourse/mixins/open-composer";
|
||||
|
||||
|
@ -13,7 +14,7 @@ function unlessReadOnly(method) {
|
|||
|
||||
const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
|
||||
|
||||
siteTitle: Discourse.computed.setting('title'),
|
||||
siteTitle: setting('title'),
|
||||
|
||||
actions: {
|
||||
_collectTitleTokens(tokens) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import ScreenTrack from 'discourse/lib/screen-track';
|
||||
import { queryParams } from 'discourse/controllers/discovery-sortable';
|
||||
|
||||
// A helper to build a topic route for a filter
|
||||
|
@ -82,7 +83,7 @@ export default function(filter, extras) {
|
|||
|
||||
model(data, transition) {
|
||||
// attempt to stop early cause we need this to be called before .sync
|
||||
Discourse.ScreenTrack.current().stop();
|
||||
ScreenTrack.current().stop();
|
||||
|
||||
const findOpts = filterQueryParams(transition.queryParams),
|
||||
extras = { cached: this.isPoppedState(transition) };
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import ScreenTrack from 'discourse/lib/screen-track';
|
||||
|
||||
let isTransitioning = false,
|
||||
scheduledReplace = null,
|
||||
lastScrollPos = null;
|
||||
|
@ -185,7 +187,7 @@ const TopicRoute = Discourse.Route.extend({
|
|||
topicController.set('multiSelect', false);
|
||||
topicController.unsubscribe();
|
||||
this.controllerFor('composer').set('topic', null);
|
||||
Discourse.ScreenTrack.current().stop();
|
||||
ScreenTrack.current().stop();
|
||||
|
||||
const headerController = this.controllerFor('header');
|
||||
if (headerController) {
|
||||
|
@ -226,7 +228,7 @@ const TopicRoute = Discourse.Route.extend({
|
|||
|
||||
this.controllerFor('topic-progress').set('model', model);
|
||||
// We reset screen tracking every time a topic is entered
|
||||
Discourse.ScreenTrack.current().start(model.get('id'), controller);
|
||||
ScreenTrack.current().start(model.get('id'), controller);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{#each p in model.content}}
|
||||
<div {{bind-attr class=":item p.hidden p.deleted moderator_action"}}>
|
||||
<div {{bind-attr class=":item p.hidden p.deleted p.moderator_action"}}>
|
||||
<div class="clearfix info">
|
||||
<a href="{{unbound p.usernameUrl}}" class="avatar-link">
|
||||
<div class="avatar-wrapper">
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import ScreenTrack from 'discourse/lib/screen-track';
|
||||
|
||||
export default Discourse.GroupedView.extend({
|
||||
templateName: 'embedded-post',
|
||||
classNames: ['reply'],
|
||||
|
||||
_startTracking: function() {
|
||||
const post = this.get('content');
|
||||
Discourse.ScreenTrack.current().track(this.get('elementId'), post.get('post_number'));
|
||||
ScreenTrack.current().track(this.get('elementId'), post.get('post_number'));
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_stopTracking: function() {
|
||||
Discourse.ScreenTrack.current().stopTracking(this.get('elementId'));
|
||||
ScreenTrack.current().stopTracking(this.get('elementId'));
|
||||
}.on('willDestroyElement')
|
||||
});
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { fmt } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Object.extend({
|
||||
tagName: "td",
|
||||
ratio: function() {
|
||||
|
@ -26,6 +28,6 @@ export default Ember.Object.extend({
|
|||
return '';
|
||||
}.property(),
|
||||
|
||||
likesHeat: Discourse.computed.fmt('ratioText', 'heatmap-%@'),
|
||||
likesHeat: fmt('ratioText', 'heatmap-%@'),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import ScreenTrack from 'discourse/lib/screen-track';
|
||||
import { number } from 'discourse/lib/formatter';
|
||||
|
||||
const DAY = 60 * 50 * 1000;
|
||||
|
||||
const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||
|
@ -178,7 +181,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
|||
// don't display badge counts on category badge & oneboxes (unless when explicitely stated)
|
||||
if ($link.hasClass("track-link") ||
|
||||
$link.closest('.badge-category,.onebox-result,.onebox-body').length === 0) {
|
||||
$link.append("<span class='badge badge-notification clicks' title='" + I18n.t("topic_map.clicks", {count: lc.clicks}) + "'>" + Discourse.Formatter.number(lc.clicks) + "</span>");
|
||||
$link.append("<span class='badge badge-notification clicks' title='" + I18n.t("topic_map.clicks", {count: lc.clicks}) + "'>" + number(lc.clicks) + "</span>");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -263,7 +266,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
|||
},
|
||||
|
||||
_destroyedPostView: function() {
|
||||
Discourse.ScreenTrack.current().stopTracking(this.get('elementId'));
|
||||
ScreenTrack.current().stopTracking(this.get('elementId'));
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
_postViewInserted: function() {
|
||||
|
@ -272,7 +275,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
|||
|
||||
this._showLinkCounts();
|
||||
|
||||
Discourse.ScreenTrack.current().track($post.prop('id'), postNumber);
|
||||
ScreenTrack.current().track($post.prop('id'), postNumber);
|
||||
|
||||
this.trigger('postViewInserted', $post);
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@ import AddArchetypeClass from 'discourse/mixins/add-archetype-class';
|
|||
import ClickTrack from 'discourse/lib/click-track';
|
||||
import { listenForViewEvent } from 'discourse/lib/app-events';
|
||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import Scrolling from 'discourse/mixins/scrolling';
|
||||
|
||||
const TopicView = Discourse.View.extend(AddCategoryClass, AddArchetypeClass, Discourse.Scrolling, {
|
||||
const TopicView = Discourse.View.extend(AddCategoryClass, AddArchetypeClass, Scrolling, {
|
||||
templateName: 'topic',
|
||||
topicBinding: 'controller.model',
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import CleansUp from 'discourse/mixins/cleans-up';
|
||||
|
||||
import afterTransition from 'discourse/lib/after-transition';
|
||||
|
||||
const clickOutsideEventName = "mousedown.outside-user-card",
|
||||
|
@ -9,7 +9,7 @@ const clickOutsideEventName = "mousedown.outside-user-card",
|
|||
export default Discourse.View.extend(CleansUp, {
|
||||
elementId: 'user-card',
|
||||
classNameBindings: ['controller.visible:show', 'controller.showBadges', 'controller.hasCardBadgeImage'],
|
||||
allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'),
|
||||
allowBackgrounds: setting('allow_profile_backgrounds'),
|
||||
|
||||
addBackground: function() {
|
||||
const url = this.get('controller.user.card_background');
|
||||
|
|
|
@ -8,10 +8,14 @@
|
|||
//= require ./discourse/lib/load-script
|
||||
//= require ./discourse/lib/notification-levels
|
||||
//= require ./discourse/lib/app-events
|
||||
//= require ./discourse/lib/avatar-template
|
||||
//= require ./discourse/helpers/i18n
|
||||
//= require ./discourse/helpers/fa-icon
|
||||
//= require ./discourse/helpers/register-unbound
|
||||
//= require ./discourse/lib/ember_compat_handlebars
|
||||
//= require ./discourse/lib/computed
|
||||
//= require ./discourse/lib/formatter
|
||||
//= require ./discourse/lib/eyeline
|
||||
//= require ./discourse/helpers/register-unbound
|
||||
//= require ./discourse/mixins/scrolling
|
||||
//= require_tree ./discourse/mixins
|
||||
|
@ -36,7 +40,7 @@
|
|||
//= require ./discourse/models/post-stream
|
||||
//= require ./discourse/models/topic-details
|
||||
//= require ./discourse/models/topic
|
||||
//= require ./discourse/models/user_action
|
||||
//= require ./discourse/models/user-action
|
||||
//= require ./discourse/models/composer
|
||||
//= require ./discourse/controllers/controller
|
||||
//= require ./discourse/controllers/discovery-sortable
|
||||
|
@ -58,6 +62,7 @@
|
|||
//= require ./discourse/lib/link-mentions
|
||||
//= require ./discourse/views/composer
|
||||
//= require ./discourse/lib/show-modal
|
||||
//= require ./discourse/lib/screen-track
|
||||
//= require ./discourse/routes/discourse
|
||||
//= require ./discourse/routes/build-topic-route
|
||||
//= require ./discourse/routes/restricted-user
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
moduleFor('controller:admin-user-badges', 'Admin User Badges Controller', {
|
||||
moduleFor('controller:admin-user-badges', {
|
||||
needs: ['controller:adminUser']
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ function setTemplates(lookupTemplateStrings) {
|
|||
});
|
||||
}
|
||||
|
||||
module("Resolver", {
|
||||
module("lib:resolver", {
|
||||
setup: function() {
|
||||
originalTemplates = Ember.TEMPLATES;
|
||||
Ember.TEMPLATES = {};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import avatarTemplate from 'discourse/lib/avatar-template';
|
||||
|
||||
module('avatarTemplate');
|
||||
module('lib:avatar-template');
|
||||
|
||||
test("avatarTemplate", function(){
|
||||
var oldCDN = Discourse.CDN;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module("categoryBadgeHTML");
|
||||
module("lib:category-link");
|
||||
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ var windowOpen,
|
|||
win,
|
||||
redirectTo;
|
||||
|
||||
module("ClickTrack", {
|
||||
module("lib:click-track", {
|
||||
setup: function() {
|
||||
|
||||
// Prevent any of these tests from navigating away
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
module("Discourse.Computed", {
|
||||
import { setting, propertyEqual, propertyNotEqual, fmt, i18n, url } from 'discourse/lib/computed';
|
||||
|
||||
module("lib:computed", {
|
||||
setup: function() {
|
||||
sandbox.stub(I18n, "t", function(scope) {
|
||||
return "%@ translated: " + scope;
|
||||
|
@ -12,8 +14,8 @@ module("Discourse.Computed", {
|
|||
|
||||
test("setting", function() {
|
||||
var t = Em.Object.extend({
|
||||
vehicle: Discourse.computed.setting('vehicle'),
|
||||
missingProp: Discourse.computed.setting('madeUpThing')
|
||||
vehicle: setting('vehicle'),
|
||||
missingProp: setting('madeUpThing')
|
||||
}).create();
|
||||
|
||||
Discourse.SiteSettings.vehicle = "airplane";
|
||||
|
@ -23,7 +25,7 @@ test("setting", function() {
|
|||
|
||||
test("propertyEqual", function() {
|
||||
var t = Em.Object.extend({
|
||||
same: Discourse.computed.propertyEqual('cookies', 'biscuits')
|
||||
same: propertyEqual('cookies', 'biscuits')
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10
|
||||
|
@ -36,7 +38,7 @@ test("propertyEqual", function() {
|
|||
|
||||
test("propertyNotEqual", function() {
|
||||
var t = Em.Object.extend({
|
||||
diff: Discourse.computed.propertyNotEqual('cookies', 'biscuits')
|
||||
diff: propertyNotEqual('cookies', 'biscuits')
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10
|
||||
|
@ -50,8 +52,8 @@ test("propertyNotEqual", function() {
|
|||
|
||||
test("fmt", function() {
|
||||
var t = Em.Object.extend({
|
||||
exclaimyUsername: Discourse.computed.fmt('username', "!!! %@ !!!"),
|
||||
multiple: Discourse.computed.fmt('username', 'mood', "%@ is %@")
|
||||
exclaimyUsername: fmt('username', "!!! %@ !!!"),
|
||||
multiple: fmt('username', 'mood', "%@ is %@")
|
||||
}).create({
|
||||
username: 'eviltrout',
|
||||
mood: "happy"
|
||||
|
@ -69,8 +71,8 @@ test("fmt", function() {
|
|||
|
||||
test("i18n", function() {
|
||||
var t = Em.Object.extend({
|
||||
exclaimyUsername: Discourse.computed.i18n('username', "!!! %@ !!!"),
|
||||
multiple: Discourse.computed.i18n('username', 'mood', "%@ is %@")
|
||||
exclaimyUsername: i18n('username', "!!! %@ !!!"),
|
||||
multiple: i18n('username', 'mood', "%@ is %@")
|
||||
}).create({
|
||||
username: 'eviltrout',
|
||||
mood: "happy"
|
||||
|
@ -90,7 +92,7 @@ test("url", function() {
|
|||
var t, testClass;
|
||||
|
||||
testClass = Em.Object.extend({
|
||||
userUrl: Discourse.computed.url('username', "/users/%@")
|
||||
userUrl: url('username', "/users/%@")
|
||||
});
|
||||
|
||||
t = testClass.create({ username: 'eviltrout' });
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
var clock;
|
||||
|
||||
module("Discourse.Formatter", {
|
||||
import { relativeAge, autoUpdatingRelativeAge, updateRelativeAge, breakUp, number } from 'discourse/lib/formatter';
|
||||
|
||||
module("lib:formatter", {
|
||||
setup: function() {
|
||||
clock = sinon.useFakeTimers(new Date(2012,11,31,12,0).getTime());
|
||||
},
|
||||
|
@ -17,7 +19,7 @@ var mins_ago = function(mins){
|
|||
};
|
||||
|
||||
var formatMins = function(mins) {
|
||||
return Discourse.Formatter.relativeAge(mins_ago(mins), {format: format, leaveAgo: leaveAgo});
|
||||
return relativeAge(mins_ago(mins), {format: format, leaveAgo: leaveAgo});
|
||||
};
|
||||
|
||||
var formatHours = function(hours) {
|
||||
|
@ -141,26 +143,24 @@ test("formating tiny dates", function() {
|
|||
Discourse.SiteSettings.relative_date_duration = originalValue;
|
||||
});
|
||||
|
||||
module("Discourse.Formatter");
|
||||
|
||||
test("autoUpdatingRelativeAge", function() {
|
||||
var d = moment().subtract(1, 'day').toDate();
|
||||
|
||||
var $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d));
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
equal($elem.data('format'), "tiny");
|
||||
equal($elem.data('time'), d.getTime());
|
||||
equal($elem.attr('title'), undefined);
|
||||
|
||||
$elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d, {title: true}));
|
||||
$elem = $(autoUpdatingRelativeAge(d, {title: true}));
|
||||
equal($elem.attr('title'), moment(d).longDate());
|
||||
|
||||
$elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d,{format: 'medium', title: true, leaveAgo: true}));
|
||||
$elem = $(autoUpdatingRelativeAge(d,{format: 'medium', title: true, leaveAgo: true}));
|
||||
equal($elem.data('format'), "medium-with-ago");
|
||||
equal($elem.data('time'), d.getTime());
|
||||
equal($elem.attr('title'), moment(d).longDate());
|
||||
equal($elem.html(), '1 day ago');
|
||||
|
||||
$elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d,{format: 'medium'}));
|
||||
$elem = $(autoUpdatingRelativeAge(d,{format: 'medium'}));
|
||||
equal($elem.data('format'), "medium");
|
||||
equal($elem.data('time'), d.getTime());
|
||||
equal($elem.attr('title'), undefined);
|
||||
|
@ -170,25 +170,25 @@ test("autoUpdatingRelativeAge", function() {
|
|||
test("updateRelativeAge", function(){
|
||||
|
||||
var d = new Date();
|
||||
var $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d));
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
$elem.data('time', d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
Discourse.Formatter.updateRelativeAge($elem);
|
||||
updateRelativeAge($elem);
|
||||
|
||||
equal($elem.html(), "2m");
|
||||
|
||||
d = new Date();
|
||||
$elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d, {format: 'medium', leaveAgo: true}));
|
||||
$elem = $(autoUpdatingRelativeAge(d, {format: 'medium', leaveAgo: true}));
|
||||
$elem.data('time', d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
Discourse.Formatter.updateRelativeAge($elem);
|
||||
updateRelativeAge($elem);
|
||||
|
||||
equal($elem.html(), "2 mins ago");
|
||||
});
|
||||
|
||||
test("breakUp", function(){
|
||||
|
||||
var b = function(s,hint){ return Discourse.Formatter.breakUp(s,hint); };
|
||||
var b = function(s,hint){ return breakUp(s,hint); };
|
||||
|
||||
equal(b("hello"), "hello");
|
||||
equal(b("helloworld"), "helloworld");
|
||||
|
@ -201,9 +201,9 @@ test("breakUp", function(){
|
|||
});
|
||||
|
||||
test("number", function() {
|
||||
equal(Discourse.Formatter.number(123), "123", "it returns a string version of the number");
|
||||
equal(Discourse.Formatter.number("123"), "123", "it works with a string command");
|
||||
equal(Discourse.Formatter.number(NaN), "0", "it returns 0 for NaN");
|
||||
equal(Discourse.Formatter.number(3333), "3.3k", "it abbreviates thousands");
|
||||
equal(Discourse.Formatter.number(2499999), "2.5M", "it abbreviates millions");
|
||||
equal(number(123), "123", "it returns a string version of the number");
|
||||
equal(number("123"), "123", "it works with a string command");
|
||||
equal(number(NaN), "0", "it returns 0 for NaN");
|
||||
equal(number(3333), "3.3k", "it abbreviates thousands");
|
||||
equal(number(2499999), "2.5M", "it abbreviates millions");
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module("SelectedPostsCount");
|
||||
module("mixin:selected-posts-count");
|
||||
|
||||
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
module("Discourse.Singleton");
|
||||
import Singleton from 'discourse/mixins/singleton';
|
||||
|
||||
module("mixin:singleton");
|
||||
|
||||
test("current", function() {
|
||||
var DummyModel = Ember.Object.extend({});
|
||||
DummyModel.reopenClass(Discourse.Singleton);
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
var current = DummyModel.current();
|
||||
present(current, 'current returns the current instance');
|
||||
|
@ -12,7 +14,7 @@ test("current", function() {
|
|||
|
||||
test("currentProp reading", function() {
|
||||
var DummyModel = Ember.Object.extend({});
|
||||
DummyModel.reopenClass(Discourse.Singleton);
|
||||
DummyModel.reopenClass(Singleton);
|
||||
var current = DummyModel.current();
|
||||
|
||||
blank(DummyModel.currentProp('evil'), 'by default attributes are blank');
|
||||
|
@ -22,7 +24,7 @@ test("currentProp reading", function() {
|
|||
|
||||
test("currentProp writing", function() {
|
||||
var DummyModel = Ember.Object.extend({});
|
||||
DummyModel.reopenClass(Discourse.Singleton);
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
blank(DummyModel.currentProp('adventure'), 'by default attributes are blank');
|
||||
var result = DummyModel.currentProp('adventure', 'time');
|
||||
|
@ -38,7 +40,7 @@ test("currentProp writing", function() {
|
|||
|
||||
test("createCurrent", function() {
|
||||
var Shoe = Ember.Object.extend({});
|
||||
Shoe.reopenClass(Discourse.Singleton, {
|
||||
Shoe.reopenClass(Singleton, {
|
||||
createCurrent: function() {
|
||||
return Shoe.create({toes: 5});
|
||||
}
|
||||
|
@ -50,7 +52,7 @@ test("createCurrent", function() {
|
|||
|
||||
test("createCurrent that returns null", function() {
|
||||
var Missing = Ember.Object.extend({});
|
||||
Missing.reopenClass(Discourse.Singleton, {
|
||||
Missing.reopenClass(Singleton, {
|
||||
createCurrent: function() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Session from "discourse/models/session";
|
||||
|
||||
module("Discourse.Session");
|
||||
module("model:session");
|
||||
|
||||
test('highestSeenByTopic', function() {
|
||||
var session = Session.current();
|
||||
const session = Session.current();
|
||||
deepEqual(session.get('highestSeenByTopic'), {}, "by default it returns an empty object");
|
||||
});
|
||||
|
|
|
@ -79,6 +79,7 @@ var origDebounce = Ember.run.debounce,
|
|||
createPretendServer = require('helpers/create-pretender', null, null, false).default,
|
||||
fixtures = require('fixtures/site_fixtures', null, null, false).default,
|
||||
flushMap = require('discourse/models/store', null, null, false).flushMap,
|
||||
ScrollingDOMMethods = require('discourse/mixins/scrolling', null, null, false).ScrollingDOMMethods,
|
||||
server;
|
||||
|
||||
function dup(obj) {
|
||||
|
@ -92,6 +93,7 @@ QUnit.testStart(function(ctx) {
|
|||
Discourse.SiteSettings = dup(Discourse.SiteSettingsOriginal);
|
||||
Discourse.BaseUri = "/";
|
||||
Discourse.BaseUrl = "localhost";
|
||||
Discourse.Session.resetCurrent();
|
||||
Discourse.User.resetCurrent();
|
||||
Discourse.Site.resetCurrent(Discourse.Site.create(dup(fixtures['site.json'].site)));
|
||||
|
||||
|
@ -103,8 +105,8 @@ QUnit.testStart(function(ctx) {
|
|||
PreloadStore.reset();
|
||||
|
||||
window.sandbox = sinon.sandbox.create();
|
||||
window.sandbox.stub(Discourse.ScrollingDOMMethods, "bindOnScroll");
|
||||
window.sandbox.stub(Discourse.ScrollingDOMMethods, "unbindOnScroll");
|
||||
window.sandbox.stub(ScrollingDOMMethods, "bindOnScroll");
|
||||
window.sandbox.stub(ScrollingDOMMethods, "unbindOnScroll");
|
||||
|
||||
// Don't debounce in test unless we're testing debouncing
|
||||
if (ctx.module.indexOf('debounce') === -1) {
|
||||
|
|
Loading…
Reference in a new issue