Replace deprecated `render(buffer)` with a custom buffered renderer

This commit is contained in:
Robin Ward 2016-10-24 15:21:44 -04:00
parent 52b338db62
commit 622dc82481
46 changed files with 198 additions and 430 deletions

View File

@ -1,8 +1,9 @@
/* global ace:true */
import loadScript from 'discourse/lib/load-script';
import { escapeExpression } from 'discourse/lib/utilities';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend({
export default Ember.Component.extend(bufferedRender({
mode: 'css',
classNames: ['ace-wrapper'],
_editor: null,
@ -14,7 +15,7 @@ export default Ember.Component.extend({
}
}.observes('content'),
render(buffer) {
buildBuffer(buffer) {
buffer.push("<div class='ace'>");
if (this.get('content')) {
buffer.push(escapeExpression(this.get('content')));
@ -66,4 +67,4 @@ export default Ember.Component.extend({
});
}.on('didInsertElement')
});
}));

View File

@ -1,8 +1,9 @@
import debounce from 'discourse/lib/debounce';
import { renderSpinner } from 'discourse/helpers/loading-spinner';
import { escapeExpression } from 'discourse/lib/utilities';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend({
export default Ember.Component.extend(bufferedRender({
classNames: ["admin-backups-logs"],
init() {
@ -29,11 +30,11 @@ export default Ember.Component.extend({
// update the formatted logs & cache index
this.setProperties({ formattedLogs: formattedLogs, index: logs.length });
// force rerender
this.rerender();
this.rerenderBuffer();
}
}, 150).observes("logs.[]").on('init'),
render(buffer) {
buildBuffer(buffer) {
const formattedLogs = this.get("formattedLogs");
if (formattedLogs && formattedLogs.length > 0) {
buffer.push("<pre>");
@ -53,4 +54,4 @@ export default Ember.Component.extend({
const $div = this.$()[0];
$div.scrollTop = $div.scrollHeight;
},
});
}));

View File

@ -1,8 +1,8 @@
import computed from 'ember-addons/ember-computed-decorators';
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classes: ["text-muted", "text-danger", "text-successful"],
icons: ["circle-o", "times-circle", "circle"],
@ -21,8 +21,8 @@ export default Ember.Component.extend(StringBuffer, {
return classes[statusId - 1];
},
renderString(buffer) {
buildBuffer(buffer) {
buffer.push(iconHTML(this.get('icon'), { class: this.get('class') }));
buffer.push(I18n.t(`admin.web_hooks.delivery_status.${this.get('status.name')}`));
}
});
}));

View File

@ -1,3 +1,5 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render';
/*global Resumable:true */
/**
@ -10,7 +12,7 @@
uploadText="UPLOAD"
}}
**/
const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: "button",
classNames: ["btn", "ru"],
classNameBindings: ["isUploading"],
@ -36,9 +38,9 @@ const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer,
}
}.property("isUploading", "progress"),
renderString: function(buffer) {
var icon = this.get("isUploading") ? "times" : "upload";
buffer.push("<i class='fa fa-" + icon + "'></i>");
buildBuffer(buffer) {
const icon = this.get("isUploading") ? "times" : "upload";
buffer.push(`<i class="fa fa-${icon}"></i>`);
buffer.push("<span class='ru-label'>" + this.get("text") + "</span>");
buffer.push("<span class='ru-progress' style='width:" + this.get("progress") + "%'></span>");
},
@ -117,6 +119,4 @@ const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer,
}
}.on("willDestroyElement")
});
export default ResumableUploadComponent;
}));

View File

@ -1,13 +1,14 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { on, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
export default Ember.Component.extend(bufferedRender({
tagName: 'select',
attributeBindings: ['tabindex', 'disabled'],
classNames: ['combobox'],
valueAttribute: 'id',
nameProperty: 'name',
render(buffer) {
buildBuffer(buffer) {
const nameProperty = this.get('nameProperty');
const none = this.get('none');
@ -48,11 +49,11 @@ export default Ember.Component.extend({
@observes('content.[]')
_rerenderOnChange() {
this.rerender();
this.rerenderBuffer();
},
@on('didInsertElement')
_initializeCombo() {
didInsertElement() {
this._super();
// Workaround for https://github.com/emberjs/ember.js/issues/9813
// Can be removed when fixed. Without it, the wrong option is selected
@ -60,7 +61,7 @@ export default Ember.Component.extend({
// observer for item names changing (optional)
if (this.get('nameChanges')) {
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerender);
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerenderBuffer);
}
const $elem = this.$();
@ -88,4 +89,4 @@ export default Ember.Component.extend({
this.$().select2('destroy');
}
});
}));

View File

@ -0,0 +1,53 @@
// Ember 2.0 removes buffered rendering, but we can still implement it ourselves.
// In the long term we'll want to remove this.
const Mixin = {
__bufferTimeout: null,
_customRender() {
Ember.run.cancel(this.__bufferTimeout);
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
const buffer = [];
this.buildBuffer(buffer);
this.element.innerHTML = buffer.join('');
},
rerenderBuffer() {
Ember.run.scheduleOnce('render', this, this._customRender);
}
};
export function bufferedRender(obj) {
if (!obj.buildBuffer) {
Ember.warn('Missing `buildBuffer` method');
return obj;
}
const caller = {};
// True in 1.13 or greater
if (Ember.Helper) {
caller.didRender = function() {
this._super();
this._customRender();
};
} else {
caller.didInsertElement = function() {
this._super();
this._customRender();
};
}
const triggers = obj.rerenderTriggers;
if (triggers) {
caller.init = function() {
this._super();
triggers.forEach(k => this.addObserver(k, this.rerenderBuffer));
};
}
delete obj.rerenderTriggers;
return Ember.Mixin.create(Mixin, caller, obj);
}

View File

@ -1,66 +0,0 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import UserAction from "discourse/models/user-action";
export default Ember.Component.extend(StringBuffer, {
tagName: 'li',
classNameBindings: ['active', 'noGlyph'],
rerenderTriggers: ['content.count', 'count'],
noGlyph: Em.computed.empty('icon'),
isIndexStream: function() {
return !this.get('content');
}.property('content.count'),
active: function() {
if (this.get('isIndexStream')) {
return !this.get('userActionType');
}
const content = this.get('content');
if (content) {
return parseInt(this.get('userActionType'), 10) === parseInt(Em.get(content, 'action_type'), 10);
}
}.property('userActionType', 'isIndexStream'),
activityCount: function() {
return this.get('content.count') || this.get('count') || 0;
}.property('content.count', 'count'),
typeKey: function() {
const actionType = this.get('content.action_type');
if (actionType === UserAction.TYPES.messages_received) { return ""; }
const result = UserAction.TYPES_INVERTED[actionType];
if (!result) { return ""; }
// We like our URLS to have hyphens, not underscores
return "/" + result.replace("_", "-");
}.property('content.action_type'),
url: function() {
return "/users/" + this.get('user.username_lower') + "/activity" + this.get('typeKey');
}.property('typeKey', 'user.username_lower'),
description: function() {
return this.get('content.description') || I18n.t("user.filters.all");
}.property('content.description'),
renderString(buffer) {
buffer.push("<a href='" + this.get('url') + "'>");
const icon = this.get('icon');
if (icon) {
buffer.push("<i class='glyph fa fa-" + icon + "'></i> ");
}
buffer.push(this.get('description') + " <span class='count'>(" + this.get('activityCount') + ")</span></a>");
},
icon: function() {
switch(parseInt(this.get('content.action_type'), 10)) {
case UserAction.TYPES.likes_received: return "heart";
case UserAction.TYPES.bookmarks: return "bookmark";
case UserAction.TYPES.edits: return "pencil";
case UserAction.TYPES.replies: return "reply";
case UserAction.TYPES.mentions: return "at";
}
}.property("content.action_type")
});

View File

@ -52,7 +52,5 @@ export default Ember.Component.extend({
}
return false;
}
}
});

View File

@ -35,9 +35,4 @@ export default Ember.Component.extend({
});
}.property('firstCategory', 'hideSubcategories'),
render(buffer) {
if (this.get('hidden')) { return; }
this._super(buffer);
}
});

View File

@ -7,10 +7,5 @@ export default Ember.Component.extend({
@computed('checked')
status(checked) {
return checked ? 'status-checked' : 'status-unchecked';
},
render(buffer) {
const icon = this.get('checked') ? 'check' : 'times';
buffer.push(`<i class='fa fa-${icon}'></i>`);
}
});

View File

@ -1,19 +1,10 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNameBindings: ['containerClass', 'condition:visible'],
containerClass: function() {
return this.get('size') === 'small' ? 'inline-spinner' : undefined;
}.property('size'),
render: function(buffer) {
if (this.get('condition')) {
buffer.push('<div class="spinner ' + this.get('size') + '"}}></div>');
} else {
return this._super(buffer);
}
},
_conditionChanged: function() {
this.rerender();
}.observes('condition')
@computed('size')
containerClass(size) {
return size === 'small' ? 'inline-spinner' : undefined;
}
});

View File

@ -1,8 +1,10 @@
export default Ember.Component.extend(Discourse.StringBuffer, {
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({
tagName: 'span',
rerenderTriggers: ['count', 'suffix'],
renderString: function(buffer) {
buildBuffer(buffer) {
buffer.push(I18n.t(this.get('key') + (this.get('suffix') || ''), { count: this.get('count') }));
}
});
}));

View File

@ -1,5 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
tagName: 'button',
@ -18,24 +17,6 @@ export default Ember.Component.extend({
if (label) return I18n.t(label);
},
@observes('icon')
iconChanged() {
this.rerender();
},
render(buffer) {
const label = this.get('translatedLabel'),
icon = this.get('icon');
if (label || icon) {
if (icon) { buffer.push(iconHTML(icon) + ' '); }
if (label) { buffer.push(label); }
} else {
// If no label or icon is present, yield
return this._super(buffer);
}
},
click() {
this.sendAction("action", this.get("actionParam"));
return false;

View File

@ -1,69 +0,0 @@
import computed from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import interceptClick from 'discourse/lib/intercept-click';
export default Ember.Component.extend({
tagName: 'a',
classNames: ['d-link'],
attributeBindings: ['translatedTitle:title', 'translatedTitle:aria-title', 'href'],
@computed('path')
href(path) {
if (path) { return path; }
const route = this.get('route');
if (route) {
const router = this.container.lookup('router:main');
if (router && router.router) {
const params = [route];
const model = this.get('model');
if (model) {
params.push(model);
}
return Discourse.getURL(router.router.generate.apply(router.router, params));
}
}
return '';
},
@computed("title")
translatedTitle(title) {
if (title) return I18n.t(title);
},
click(e) {
const action = this.get('action');
if (action) {
this.sendAction('action');
return false;
}
return interceptClick(e);
},
render(buffer) {
if (!!this.get('template')) {
return this._super(buffer);
}
const icon = this.get('icon');
if (icon) {
buffer.push(iconHTML(icon));
}
const label = this.get('label');
if (label) {
if (icon) { buffer.push(" "); }
if (this.get('translateLabel') === "false") {
buffer.push(label);
} else {
const count = this.get('count');
buffer.push(I18n.t(label, { count }));
}
}
}
});

View File

@ -1,7 +1,7 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'th',
classNames: ['sortable'],
attributeBindings: ['title'],
@ -12,8 +12,7 @@ export default Ember.Component.extend(StringBuffer, {
return I18n.t(labelKey + '_long', { defaultValue: I18n.t(labelKey) });
}.property('field'),
renderString(buffer) {
buildBuffer(buffer) {
const icon = this.get('icon');
if (icon) {
buffer.push(iconHTML(icon));
@ -37,4 +36,4 @@ export default Ember.Component.extend(StringBuffer, {
this.setProperties({ order: field, asc: null });
}
}
});
}));

View File

@ -1,6 +1,4 @@
import VisibleComponent from "discourse/components/visible";
export default VisibleComponent.extend({
export default Ember.Component.extend({
visible: function () {
var bannerKey = this.get("banner.key"),
@ -14,7 +12,7 @@ export default VisibleComponent.extend({
}.property("user.dismissed_banner_key", "banner.key", "hide"),
actions: {
dismiss: function () {
dismiss() {
if (this.get("user")) {
this.get("user").dismissBanner(this.get("banner.key"));
} else {

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':btn-group', 'hidden'],
rerenderTriggers: ['text', 'longDescription'],
@ -23,7 +23,7 @@ export default Ember.Component.extend(StringBuffer, {
this.$().off('click.dropdown-button', 'ul li');
}.on('willDestroyElement'),
renderString(buffer) {
buildBuffer(buffer) {
const title = this.get('title');
if (title) {
buffer.push("<h4 class='title'>" + title + "</h4>");
@ -56,4 +56,4 @@ export default Ember.Component.extend(StringBuffer, {
buffer.push("</p>");
}
}
});
}));

View File

@ -1,12 +1,12 @@
import { on } from 'ember-addons/ember-computed-decorators';
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import LogsNotice from 'discourse/services/logs-notice';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['site.isReadOnly'],
renderString: function(buffer) {
buildBuffer(buffer) {
let notices = [];
if (this.site.get("isReadOnly")) {
@ -51,7 +51,7 @@ export default Ember.Component.extend(StringBuffer, {
@on('didInsertElement')
_setupLogsNotice() {
LogsNotice.current().addObserver('hidden', () => {
this.rerenderString();
this.rerenderBuffer();
});
this.$().on('click.global-notice', '.alert-logs-notice .close', () => {
@ -63,4 +63,4 @@ export default Ember.Component.extend(StringBuffer, {
_teardownLogsNotice() {
this.$().off('click.global-notice');
}
});
}));

View File

@ -1,17 +1,17 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':tip', 'good', 'bad'],
rerenderTriggers: ['validation'],
bad: Em.computed.alias('validation.failed'),
good: Em.computed.not('bad'),
renderString(buffer) {
buildBuffer(buffer) {
const reason = this.get('validation.reason');
if (reason) {
buffer.push(iconHTML(this.get('good') ? 'check' : 'times') + ' ' + reason);
}
}
});
}));

View File

@ -1,7 +1,7 @@
import computed from "ember-addons/ember-computed-decorators";
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'li',
classNameBindings: ['active', 'content.hasIcon:has-icon'],
attributeBindings: ['title'],
@ -26,7 +26,7 @@ export default Ember.Component.extend(StringBuffer, {
filterMode.indexOf(contentFilterMode) === 0;
},
renderString(buffer) {
buildBuffer(buffer) {
const content = this.get('content');
buffer.push("<a href='" + content.get('href') + "'>");
if (content.get('hasIcon')) {
@ -35,4 +35,4 @@ export default Ember.Component.extend(StringBuffer, {
buffer.push(this.get('content.displayName'));
buffer.push("</a>");
}
});
}));

View File

@ -1,8 +1,8 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':popup-tip', 'good', 'bad', 'lastShownAt::hide'],
animateAttribute: null,
bouncePixels: 6,
@ -37,7 +37,7 @@ export default Ember.Component.extend(StringBuffer, {
}
},
renderString(buffer) {
buildBuffer(buffer) {
const reason = this.get('validation.reason');
if (!reason) { return; }
@ -55,4 +55,4 @@ export default Ember.Component.extend(StringBuffer, {
$elem.animate({ right: '-=' + this.bouncePixels }, this.bounceDelay).animate({ right: '+=' + this.bouncePixels }, this.bounceDelay);
}
}
});
}));

View File

@ -17,10 +17,6 @@ export default Ember.Component.extend({
return "tag-" + this.get('tagId');
}.property('tagId'),
render(buffer) {
buffer.push(Handlebars.Utils.escapeExpression(this.get('tagId')));
},
click(e) {
e.preventDefault();
DiscourseURL.routeTo(this.get('href'));

View File

@ -1,12 +1,9 @@
export default Ember.Component.extend({
_parse: function() {
didInsertElement() {
this._super();
Ember.run.next(null, () => {
this.$().find('hr').remove();
this.$().ellipsis();
});
}.on('didInsertElement'),
render(buffer) {
buffer.push(this.get('text'));
}
});

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
elementId: 'topic-closing-info',
delayedRerender: null,
@ -9,7 +9,7 @@ export default Ember.Component.extend(StringBuffer, {
'topic.details.auto_close_based_on_last_post',
'topic.details.auto_close_hours'],
renderString(buffer) {
buildBuffer(buffer) {
if (!!Ember.isEmpty(this.get('topic.details.auto_close_at'))) return;
if (this.get("topic.closed")) return;
@ -48,4 +48,4 @@ export default Ember.Component.extend(StringBuffer, {
Em.run.cancel(this.get('delayedRerender'));
}
}
});
}));

View File

@ -1,5 +1,5 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import computed from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export function showEntrance(e) {
let target = $(e.target);
@ -16,17 +16,23 @@ export function showEntrance(e) {
}
}
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['bulkSelectEnabled', 'topic.pinned'],
tagName: 'tr',
rawTemplate: 'list/topic-list-item.raw',
classNameBindings: [':topic-list-item', 'unboundClassNames'],
attributeBindings: ['data-topic-id'],
'data-topic-id': Em.computed.alias('topic.id'),
actions: {
toggleBookmark() {
this.get('topic').toggleBookmark().finally(() => this.rerender());
this.get('topic').toggleBookmark().finally(() => this.rerenderBuffer());
}
},
buildBuffer(buffer) {
const template = Discourse.__container__.lookup('template:list/topic-list-item.raw');
if (template) {
buffer.push(template(this));
}
},
@ -142,4 +148,4 @@ export default Ember.Component.extend(StringBuffer, {
}
}.on('didInsertElement')
});
}));

View File

@ -1,4 +1,4 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
// Creates a link
function link(buffer, prop, url, cssClass, i18nKey, text) {
@ -7,15 +7,15 @@ function link(buffer, prop, url, cssClass, i18nKey, text) {
buffer.push(`<a href="${url}" class="badge ${cssClass} badge-notification" title="${title}">${text || prop}</a>\n`);
}
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'span',
classNameBindings: [':topic-post-badges'],
rerenderTriggers: ['url', 'unread', 'newPosts', 'unseen'],
renderString(buffer) {
buildBuffer(buffer) {
const url = this.get('url');
link(buffer, this.get('unread'), url, 'unread', 'unread_posts');
link(buffer, this.get('newPosts'), url, 'new-posts', 'new_posts');
link(buffer, this.get('unseen'), url, 'new-topic', 'new', I18n.t('filters.new.lower_title'));
}
});
}));

View File

@ -1,8 +1,8 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { escapeExpression } from 'discourse/lib/utilities';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNames: ['topic-statuses'],
rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'],
@ -26,7 +26,7 @@ export default Ember.Component.extend(StringBuffer, {
return Discourse.User.current() && !this.get('disableActions');
}.property('disableActions'),
renderString(buffer) {
buildBuffer(buffer) {
const self = this;
const renderIcon = function(name, key, actionable) {
@ -57,4 +57,4 @@ export default Ember.Component.extend(StringBuffer, {
renderIconIf('topic.unpinned', 'thumb-tack', 'unpinned', this.get("canAct"));
renderIconIf('topic.invisible', 'eye-slash', 'invisible');
}
});
}));

View File

@ -1,12 +0,0 @@
export default Ember.Component.extend({
visibleChanged: function(){
this.rerender();
}.observes("visible"),
render: function(buffer){
if (this._state !== 'inDOM' && this._state !== 'preRender' && this._state !== 'inBuffer') { return; }
if (!this.get("visible")) { return; }
return this._super(buffer);
}
});

View File

@ -1,45 +0,0 @@
export default Ember.Mixin.create({
_watchProps: function() {
const args = this.get('rerenderTriggers');
if (!Ember.isNone(args)) {
args.forEach(k => this.addObserver(k, this.rerenderString));
}
}.on('init'),
render(buffer) {
this.renderString(buffer);
},
renderString(buffer){
const template = Discourse.__container__.lookup('template:' + this.rawTemplate);
if (template) {
buffer.push(template(this));
}
},
_rerenderString() {
const $sel = this.$();
if (!$sel) { return; }
const buffer = [];
this.renderString(buffer);
// Chrome likes scrolling after HTML is set
// This happens if you navigate back and forth a few times
// Before removing this code confirm that this does not cause scrolling
// 1. Sort by views
// 2. Go to last post on one of the topics
// 3. Hit back
// 4. Go to last post on same topic
// 5. Expand likes
const scrollTop = $(window).scrollTop();
$sel.html(buffer.join(''));
$(window).scrollTop(scrollTop);
},
rerenderString() {
Ember.run.once(this, '_rerenderString');
}
});

View File

@ -0,0 +1,5 @@
{{#if checked}}
{{fa-icon "check"}}
{{else}}
{{fa-icon "times"}}
{{/if}}

View File

@ -0,0 +1,5 @@
{{#if condition}}
<div class="spinner {{size}}"></div>
{{else}}
{{yield}}
{{/if}}

View File

@ -0,0 +1,3 @@
{{fa-icon icon}}
{{translatedLabel}}
{{yield}}

View File

@ -1,11 +1,13 @@
<div class="row">
<div id="banner" class={{overlay}}>
<div id="banner-content">
<div class="close" {{action "dismiss"}}><i class="fa fa-times" title="{{i18n 'banner.close'}}"></i></div>
{{{banner.html}}}
{{#if currentUser.staff}}
<p><a href="{{banner.url}}">{{{i18n "banner.edit"}}}</a></p>
{{/if}}
{{#if visible}}
<div class="row">
<div id="banner" class={{overlay}}>
<div id="banner-content">
<div class="close" {{action "dismiss"}}><i class="fa fa-times" title="{{i18n 'banner.close'}}"></i></div>
{{{banner.html}}}
{{#if currentUser.staff}}
<p><a href="{{banner.url}}">{{{i18n "banner.edit"}}}</a></p>
{{/if}}
</div>
</div>
</div>
</div>
{{/if}}

View File

@ -0,0 +1 @@
{{tagId}}

View File

@ -0,0 +1 @@
{{{text}}}

View File

@ -25,8 +25,8 @@
{{#if bulkSelectEnabled}}
<div class='fps-select'>
{{d-link action="selectAll" label="search.select_all"}}
{{d-link action="clearAll" label="search.clear_all"}}
<a {{action "selectAll"}}>{{i18n "search.select_all"}}</a>
<a {{action "clearAll"}}>{{i18n "search.clear_all"}}</a>
</div>
{{/if}}

View File

@ -0,0 +1 @@
{{i18n "post.quote_reply"}}

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.View.extend(StringBuffer, {
export default Ember.View.extend(bufferedRender({
tagName: 'button',
classNames: ['btn', 'standard'],
attributeBindings: ['title'],
@ -13,7 +13,7 @@ export default Ember.View.extend(StringBuffer, {
return I18n.t(key);
}.property('archived'),
renderString: function(buffer) {
buildBuffer(buffer) {
if (this.get('archived')){
buffer.push(I18n.t('topic.move_to_inbox.title'));
} else {
@ -22,7 +22,7 @@ export default Ember.View.extend(StringBuffer, {
}
},
click: function() {
click() {
if (!this.get('archiving')) {
if (this.get('archived')) {
this.get('controller').send('moveToInbox');
@ -31,5 +31,5 @@ export default Ember.View.extend(StringBuffer, {
}
}
}
});
}));

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.View.extend(StringBuffer, {
export default Ember.View.extend(bufferedRender({
tagName: 'button',
classNameBindings: [':btn', ':standard', 'dropDownToggle'],
attributeBindings: ['title', 'data-toggle', 'data-share-url'],
@ -14,10 +14,10 @@ export default Ember.View.extend(StringBuffer, {
return I18n.t(this.get('textKey'));
}.property('textKey'),
renderString: function(buffer) {
buildBuffer(buffer) {
if (this.renderIcon) {
this.renderIcon(buffer);
}
buffer.push(this.get('text'));
}
});
}));

View File

@ -1,7 +1,8 @@
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.View.extend({
render: function(buffer) {
export default Ember.View.extend(bufferedRender({
buildBuffer(buffer) {
buffer.push(spinnerHTML);
}
});
}));

View File

@ -13,14 +13,10 @@ export default Ember.View.extend({
isMouseDown: false,
isTouchInProgress: false,
// The button is visible whenever there is something in the buffer
// (ie. something has been selected)
// The button is visible whenever there is something in the buffer
// (ie. something has been selected)
visible: Em.computed.notEmpty('controller.buffer'),
render(buffer) {
buffer.push(I18n.t("post.quote_reply"));
},
/**
Binds to the following global events:
- `mousedown` to clear the quote button if they click elsewhere.

View File

@ -1,17 +0,0 @@
import { renderAvatar } from 'discourse/helpers/user-avatar';
export default Ember.View.extend({
tagName: 'a',
attributeBindings: ['href', 'data-user-card'],
classNameBindings: ['content.extras'],
user: Em.computed.alias('content.user'),
href: Em.computed.alias('user.path'),
'data-user-card': Em.computed.alias('user.username'),
render: function(buffer) {
var av = renderAvatar(this.get('content'), {usernamePath: 'user.username', imageSize: 'small'});
buffer.push(av);
}
});

View File

@ -1,7 +1,9 @@
export default Em.View.extend({
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.View.extend(bufferedRender({
classNameBindings: [':container'],
render: function(buffer) {
buildBuffer(buffer) {
buffer.push(this.get('controller.model'));
}
});
}));

View File

@ -79,7 +79,6 @@
//= require ./discourse/routes/user-activity-stream
//= require ./discourse/routes/topic-from-params
//= require ./discourse/components/text-field
//= require ./discourse/components/visible
//= require ./discourse/components/conditional-loading-spinner
//= require ./discourse/helpers/user-avatar
//= require ./discourse/helpers/cold-age-class

View File

@ -209,6 +209,10 @@ html.anon .topic-list a.title:visited:not(.badge-notification) {color: dark-ligh
}
}
ol.category-breadcrumb.hidden {
display: none;
}
ol.category-breadcrumb {
display: block;
float: left;

View File

@ -1,57 +0,0 @@
import componentTest from 'helpers/component-test';
moduleForComponent('d-link', {integration: true});
componentTest('basic usage', {
template: '{{d-link path="/wat" title="user.preferences" icon="gear"}}',
test(assert) {
const $a = this.$('a');
assert.ok($a.length);
assert.equal($a.attr('href'), '/wat');
assert.equal($a.attr('title'), I18n.t('user.preferences'));
assert.equal($a.attr('aria-title'), I18n.t('user.preferences'));
assert.ok(this.$('i.fa-gear', $a).length, 'shows the icon');
}
});
componentTest('with a label', {
template: '{{d-link label="user.preferences"}}',
test(assert) {
const $a = this.$('a');
assert.equal($a.text(), I18n.t('user.preferences'));
}
});
componentTest('with a label and icon', {
template: '{{d-link label="user.preferences" icon="gear"}}',
test(assert) {
const $a = this.$('a');
assert.ok(this.$('i.fa-gear', $a).length, 'shows the icon');
assert.equal($a.text(), ` ${I18n.t('user.preferences')}`, "includes a space");
}
});
componentTest('block form', {
template: '{{#d-link path="/test"}}hello world{{/d-link}}',
test(assert) {
const $a = this.$('a');
assert.equal($a.attr('href'), '/test');
assert.equal($a.text(), 'hello world');
}
});
componentTest('with an action', {
template: '{{d-link action="doThing" title="user.preferences" icon="gear"}}',
test(assert) {
expect(2);
assert.ok(this.$('a[href]').length, 'href attribute is present to help with styling');
this.on('doThing', () => {
assert.ok(true, 'it fired the action');
});
click('a');
}
});